@hua-labs/ui 2.0.2 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -161
- package/dist/{ComponentLayout-btJq4TjA.d.mts → ComponentLayout-DrZpz0yv.d.mts} +1 -1
- package/dist/Section-BWzyshgX.d.mts +67 -0
- package/dist/advanced/dashboard.d.ts.map +1 -1
- package/dist/advanced-dashboard.d.mts +1 -1
- package/dist/advanced-dashboard.js +4 -4
- package/dist/advanced-dashboard.js.map +1 -1
- package/dist/advanced-dashboard.mjs +3 -3
- package/dist/advanced-dashboard.mjs.map +1 -1
- package/dist/advanced-emotion.mjs +1 -1
- package/dist/advanced-motion.d.mts +65 -1
- package/dist/advanced-motion.js +14 -14
- package/dist/advanced-motion.js.map +1 -1
- package/dist/advanced-motion.mjs +1 -1
- package/dist/advanced.d.mts +4 -4
- package/dist/advanced.js +16 -16
- package/dist/advanced.js.map +1 -1
- package/dist/advanced.mjs +3 -3
- package/dist/advanced.mjs.map +1 -1
- package/dist/chunk-3CCF7U3P.mjs +3 -0
- package/dist/{chunk-IFSEJVOR.mjs.map → chunk-3CCF7U3P.mjs.map} +1 -1
- package/dist/chunk-3GAUTZXQ.mjs +3 -0
- package/dist/{chunk-X7ZIWYRC.mjs.map → chunk-3GAUTZXQ.mjs.map} +1 -1
- package/dist/chunk-42RGFEL2.mjs +3 -0
- package/dist/chunk-42RGFEL2.mjs.map +1 -0
- package/dist/chunk-4NJE7D6X.mjs +3 -0
- package/dist/chunk-4NJE7D6X.mjs.map +1 -0
- package/dist/chunk-6HVJFEDA.mjs +3 -0
- package/dist/{chunk-FSL373O6.mjs.map → chunk-6HVJFEDA.mjs.map} +1 -1
- package/dist/chunk-7OYT3QSY.mjs +3 -0
- package/dist/chunk-7OYT3QSY.mjs.map +1 -0
- package/dist/chunk-ANYZ56VB.mjs +3 -0
- package/dist/{chunk-QQCELXFD.mjs.map → chunk-ANYZ56VB.mjs.map} +1 -1
- package/dist/chunk-AOSXB5JJ.mjs +4 -0
- package/dist/{chunk-GLZKT7JN.mjs.map → chunk-AOSXB5JJ.mjs.map} +1 -1
- package/dist/chunk-B544MRF7.mjs +3 -0
- package/dist/{chunk-SDFHJ4GB.mjs.map → chunk-B544MRF7.mjs.map} +1 -1
- package/dist/chunk-CVWWS25A.mjs +3 -0
- package/dist/chunk-CVWWS25A.mjs.map +1 -0
- package/dist/chunk-DYNBM24D.mjs +3 -0
- package/dist/{chunk-OSCMSA2Q.mjs.map → chunk-DYNBM24D.mjs.map} +1 -1
- package/dist/{chunk-NBJUE7NR.mjs → chunk-FX57OSYG.mjs} +2 -2
- package/dist/{chunk-NBJUE7NR.mjs.map → chunk-FX57OSYG.mjs.map} +1 -1
- package/dist/chunk-IJSYSNM5.mjs +3 -0
- package/dist/{chunk-IN7RWQCJ.mjs.map → chunk-IJSYSNM5.mjs.map} +1 -1
- package/dist/chunk-KJZGOL2Z.mjs +3 -0
- package/dist/{chunk-LOYAJIWO.mjs.map → chunk-KJZGOL2Z.mjs.map} +1 -1
- package/dist/chunk-KYRIUUQP.mjs +3 -0
- package/dist/{chunk-PAEKNQWW.mjs.map → chunk-KYRIUUQP.mjs.map} +1 -1
- package/dist/chunk-LSA7DU3N.mjs +73 -0
- package/dist/chunk-LSA7DU3N.mjs.map +1 -0
- package/dist/chunk-MDLCJASB.mjs +3 -0
- package/dist/{chunk-LH77I6HO.mjs.map → chunk-MDLCJASB.mjs.map} +1 -1
- package/dist/chunk-N56BUOCD.mjs +3 -0
- package/dist/{chunk-XV3Y7QVU.mjs.map → chunk-N56BUOCD.mjs.map} +1 -1
- package/dist/chunk-OFYITQXI.mjs +13 -0
- package/dist/chunk-OFYITQXI.mjs.map +1 -0
- package/dist/chunk-OZNST3EZ.mjs +3 -0
- package/dist/{chunk-SGEP3CQE.mjs.map → chunk-OZNST3EZ.mjs.map} +1 -1
- package/dist/chunk-RS6RKW5U.mjs +13 -0
- package/dist/{chunk-6KTHJ3EL.mjs.map → chunk-RS6RKW5U.mjs.map} +1 -1
- package/dist/{chunk-C4OACMTB.mjs → chunk-TXBZZJNR.mjs} +2 -2
- package/dist/{chunk-C4OACMTB.mjs.map → chunk-TXBZZJNR.mjs.map} +1 -1
- package/dist/chunk-TZ4YSHMC.mjs +3 -0
- package/dist/{chunk-UWHCM3S6.mjs.map → chunk-TZ4YSHMC.mjs.map} +1 -1
- package/dist/chunk-U6CTBZ2U.mjs +3 -0
- package/dist/chunk-U6CTBZ2U.mjs.map +1 -0
- package/dist/{chunk-5BMH7223.mjs → chunk-WP7VFE77.mjs} +2 -2
- package/dist/{chunk-5BMH7223.mjs.map → chunk-WP7VFE77.mjs.map} +1 -1
- package/dist/{chunk-FFH4ZFKS.mjs → chunk-XCZMLKPK.mjs} +2 -2
- package/dist/{chunk-FFH4ZFKS.mjs.map → chunk-XCZMLKPK.mjs.map} +1 -1
- package/dist/chunk-XGHT7WMO.mjs +3 -0
- package/dist/chunk-XGHT7WMO.mjs.map +1 -0
- package/dist/chunk-XL4KTJ4L.mjs +3 -0
- package/dist/{chunk-VWSBJUNI.mjs.map → chunk-XL4KTJ4L.mjs.map} +1 -1
- package/dist/chunk-Z74YUUVT.mjs +3 -0
- package/dist/chunk-Z74YUUVT.mjs.map +1 -0
- package/dist/chunk-ZXZIHU7J.mjs +8 -0
- package/dist/{chunk-N7M6RIN4.mjs.map → chunk-ZXZIHU7J.mjs.map} +1 -1
- package/dist/components/DatePicker.d.ts.map +1 -1
- package/dist/components/Modal.d.ts.map +1 -1
- package/dist/components/Popover.d.ts +2 -0
- package/dist/components/Popover.d.ts.map +1 -1
- package/dist/components/Progress.d.ts +1 -0
- package/dist/components/Progress.d.ts.map +1 -1
- package/dist/components/Section.d.ts +44 -0
- package/dist/components/Section.d.ts.map +1 -0
- package/dist/components/advanced/AnimatedGradient.d.ts.map +1 -1
- package/dist/components/advanced/DotNav.d.ts +26 -0
- package/dist/components/advanced/DotNav.d.ts.map +1 -0
- package/dist/components/advanced/HorizontalScroll.d.ts +20 -0
- package/dist/components/advanced/HorizontalScroll.d.ts.map +1 -0
- package/dist/components/advanced/ImageReveal.d.ts +24 -0
- package/dist/components/advanced/ImageReveal.d.ts.map +1 -0
- package/dist/components/advanced/index.d.ts +6 -0
- package/dist/components/advanced/index.d.ts.map +1 -1
- package/dist/data.mjs +2 -2
- package/dist/data.mjs.map +1 -1
- package/dist/feedback.mjs +1 -1
- package/dist/form.js +3 -3
- package/dist/form.js.map +1 -1
- package/dist/form.mjs +4 -4
- package/dist/form.mjs.map +1 -1
- package/dist/{icons-Bj_nr8Ba.d.mts → icons-DmhQEH_E.d.mts} +6 -1
- package/dist/index.d.mts +9 -27
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/interactive.js +1 -1
- package/dist/interactive.js.map +1 -1
- package/dist/interactive.mjs +1 -1
- package/dist/interactive.mjs.map +1 -1
- package/dist/landing/LandingAbout.d.ts +3 -0
- package/dist/landing/LandingAbout.d.ts.map +1 -0
- package/dist/landing/LandingCTA.d.ts +3 -0
- package/dist/landing/LandingCTA.d.ts.map +1 -0
- package/dist/landing/LandingContact.d.ts +3 -0
- package/dist/landing/LandingContact.d.ts.map +1 -0
- package/dist/landing/LandingExperience.d.ts +3 -0
- package/dist/landing/LandingExperience.d.ts.map +1 -0
- package/dist/landing/LandingFeatures.d.ts +3 -0
- package/dist/landing/LandingFeatures.d.ts.map +1 -0
- package/dist/landing/LandingHero.d.ts +3 -0
- package/dist/landing/LandingHero.d.ts.map +1 -0
- package/dist/landing/LandingLogoCloud.d.ts +3 -0
- package/dist/landing/LandingLogoCloud.d.ts.map +1 -0
- package/dist/landing/LandingMetrics.d.ts +3 -0
- package/dist/landing/LandingMetrics.d.ts.map +1 -0
- package/dist/landing/LandingProjects.d.ts +3 -0
- package/dist/landing/LandingProjects.d.ts.map +1 -0
- package/dist/landing/LandingProvider.d.ts +4 -0
- package/dist/landing/LandingProvider.d.ts.map +1 -0
- package/dist/landing/LandingShowcase.d.ts +3 -0
- package/dist/landing/LandingShowcase.d.ts.map +1 -0
- package/dist/landing/LandingSkills.d.ts +3 -0
- package/dist/landing/LandingSkills.d.ts.map +1 -0
- package/dist/landing/LandingStats.d.ts +3 -0
- package/dist/landing/LandingStats.d.ts.map +1 -0
- package/dist/landing/LandingTestimonials.d.ts +3 -0
- package/dist/landing/LandingTestimonials.d.ts.map +1 -0
- package/dist/landing/index.d.ts +47 -0
- package/dist/landing/index.d.ts.map +1 -0
- package/dist/landing/themes/app.d.ts +3 -0
- package/dist/landing/themes/app.d.ts.map +1 -0
- package/dist/landing/themes/corporate.d.ts +3 -0
- package/dist/landing/themes/corporate.d.ts.map +1 -0
- package/dist/landing/themes/dashboard.d.ts +3 -0
- package/dist/landing/themes/dashboard.d.ts.map +1 -0
- package/dist/landing/themes/immersive.d.ts +3 -0
- package/dist/landing/themes/immersive.d.ts.map +1 -0
- package/dist/landing/themes/index.d.ts +15 -0
- package/dist/landing/themes/index.d.ts.map +1 -0
- package/dist/landing/themes/marketing.d.ts +3 -0
- package/dist/landing/themes/marketing.d.ts.map +1 -0
- package/dist/landing/themes/portfolio.d.ts +3 -0
- package/dist/landing/themes/portfolio.d.ts.map +1 -0
- package/dist/landing/themes/product.d.ts +3 -0
- package/dist/landing/themes/product.d.ts.map +1 -0
- package/dist/landing/types.d.ts +346 -0
- package/dist/landing/types.d.ts.map +1 -0
- package/dist/landing.d.mts +417 -0
- package/dist/landing.js +100 -0
- package/dist/landing.js.map +1 -0
- package/dist/landing.mjs +31 -0
- package/dist/landing.mjs.map +1 -0
- package/dist/lib/icons.d.ts +6 -1
- package/dist/lib/icons.d.ts.map +1 -1
- package/dist/navigation.d.mts +1 -1
- package/dist/navigation.js +2 -2
- package/dist/navigation.js.map +1 -1
- package/dist/navigation.mjs +1 -1
- package/dist/navigation.mjs.map +1 -1
- package/dist/overlay.d.mts +2 -0
- package/dist/overlay.js +1 -1
- package/dist/overlay.js.map +1 -1
- package/dist/overlay.mjs +1 -1
- package/dist/overlay.mjs.map +1 -1
- package/dist/sdui.js +4 -4
- package/dist/sdui.js.map +1 -1
- package/dist/sdui.mjs +1 -1
- package/dist/sdui.mjs.map +1 -1
- package/package.json +18 -7
- package/src/styles/landing.css +107 -0
- package/src/styles/utilities.css +58 -0
- package/dist/chunk-6KTHJ3EL.mjs +0 -13
- package/dist/chunk-COR6CDMA.mjs +0 -83
- package/dist/chunk-COR6CDMA.mjs.map +0 -1
- package/dist/chunk-FSL373O6.mjs +0 -3
- package/dist/chunk-GLZKT7JN.mjs +0 -4
- package/dist/chunk-HN5LSP6L.mjs +0 -3
- package/dist/chunk-HN5LSP6L.mjs.map +0 -1
- package/dist/chunk-IFSEJVOR.mjs +0 -3
- package/dist/chunk-IN7RWQCJ.mjs +0 -3
- package/dist/chunk-LH77I6HO.mjs +0 -3
- package/dist/chunk-LOYAJIWO.mjs +0 -3
- package/dist/chunk-LPAG7DCA.mjs +0 -3
- package/dist/chunk-LPAG7DCA.mjs.map +0 -1
- package/dist/chunk-N7M6RIN4.mjs +0 -8
- package/dist/chunk-OSCMSA2Q.mjs +0 -3
- package/dist/chunk-PAEKNQWW.mjs +0 -3
- package/dist/chunk-QQCELXFD.mjs +0 -3
- package/dist/chunk-RPUS7G7Q.mjs +0 -3
- package/dist/chunk-RPUS7G7Q.mjs.map +0 -1
- package/dist/chunk-SDFHJ4GB.mjs +0 -3
- package/dist/chunk-SGEP3CQE.mjs +0 -3
- package/dist/chunk-UUHAXGMO.mjs +0 -3
- package/dist/chunk-UUHAXGMO.mjs.map +0 -1
- package/dist/chunk-UWHCM3S6.mjs +0 -3
- package/dist/chunk-VWSBJUNI.mjs +0 -3
- package/dist/chunk-X7ZIWYRC.mjs +0 -3
- package/dist/chunk-XV3Y7QVU.mjs +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/Dropdown.tsx","../src/components/Drawer.tsx"],"names":["Dropdown","React","className","trigger","children","controlledOpen","onOpenChange","placement","align","offset","disabled","showArrow","props","ref","internalOpen","setInternalOpen","coords","setCoords","triggerRef","dropdownRef","isControlled","isOpen","handleOpenChange","newOpen","handleTriggerClick","updatePosition","triggerRect","dropdownRect","viewportWidth","viewportHeight","x","y","handleClickOutside","event","getPlacementClasses","getArrowClasses","jsxs","merge","jsx","DropdownItem","icon","variant","DropdownSeparator","DropdownLabel","DropdownMenu","DropdownGroup","Drawer","onClose","side","size","showBackdrop","backdropClassName","closeOnBackdropClick","closeOnEscape","closable","_isOpen","handleClose","isVisible","setIsVisible","isAnimating","setIsAnimating","timer","handleEscapeKey","e","sizeClasses","sideClasses","transformClasses","DrawerHeader","showCloseButton","Icon","DrawerContent","DrawerFooter"],"mappings":"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","file":"chunk-QQCELXFD.mjs","sourcesContent":["\"use client\"\n\nimport React from \"react\"\nimport { merge } from \"../lib/utils\"\n\n/**\n * Dropdown 컴포넌트의 props / Dropdown component props\n * @typedef {Object} DropdownProps\n * @property {React.ReactNode} trigger - Dropdown을 열기 위한 트리거 요소 / Trigger element to open dropdown\n * @property {React.ReactNode} children - Dropdown 내용 / Dropdown content\n * @property {boolean} [open] - 제어 모드에서 열림/닫힘 상태 / Open/close state in controlled mode\n * @property {(open: boolean) => void} [onOpenChange] - 상태 변경 콜백 / State change callback\n * @property {\"top\" | \"bottom\" | \"left\" | \"right\"} [placement=\"bottom\"] - Dropdown 표시 위치 / Dropdown display position\n * @property {\"start\" | \"center\" | \"end\"} [align=\"start\"] - Dropdown 정렬 / Dropdown alignment\n * @property {number} [offset=8] - 트리거와 Dropdown 사이 간격 (px) / Spacing between trigger and dropdown (px)\n * @property {boolean} [disabled=false] - Dropdown 비활성화 여부 / Disable dropdown\n * @property {boolean} [showArrow=true] - 화살표 표시 여부 / Show arrow\n * @extends {React.HTMLAttributes<HTMLDivElement>}\n */\nexport interface DropdownProps extends React.HTMLAttributes<HTMLDivElement> {\n trigger: React.ReactNode\n children: React.ReactNode\n open?: boolean\n onOpenChange?: (open: boolean) => void\n placement?: \"top\" | \"bottom\" | \"left\" | \"right\"\n align?: \"start\" | \"center\" | \"end\"\n offset?: number\n disabled?: boolean\n showArrow?: boolean\n}\n\n/**\n * Dropdown 컴포넌트 / Dropdown component\n * \n * 트리거 요소를 클릭하면 표시되는 드롭다운 메뉴 컴포넌트입니다.\n * 외부 클릭 시 자동으로 닫히며, 뷰포트 경계를 자동으로 감지하여 위치를 조정합니다.\n * \n * Dropdown menu component that appears when the trigger element is clicked.\n * Automatically closes on outside click and adjusts position by detecting viewport boundaries.\n * \n * @component\n * @example\n * // 기본 사용 / Basic usage\n * <Dropdown trigger={<Button>메뉴</Button>}>\n * <Menu>\n * <MenuItem>항목 1</MenuItem>\n * <MenuItem>항목 2</MenuItem>\n * </Menu>\n * </Dropdown>\n * \n * @example\n * // 제어 모드, 화살표 없음 / Controlled mode, no arrow\n * const [open, setOpen] = useState(false)\n * <Dropdown \n * open={open}\n * onOpenChange={setOpen}\n * trigger={<Button>제어 모드</Button>}\n * placement=\"top\"\n * showArrow={false}\n * >\n * <div className=\"p-4\">내용</div>\n * </Dropdown>\n * \n * @param {DropdownProps} props - Dropdown 컴포넌트의 props / Dropdown component props\n * @param {React.Ref<HTMLDivElement>} ref - div 요소 ref / div element ref\n * @returns {JSX.Element} Dropdown 컴포넌트 / Dropdown component\n */\nconst Dropdown = React.forwardRef<HTMLDivElement, DropdownProps>(\n ({ \n className, \n trigger,\n children,\n open: controlledOpen,\n onOpenChange,\n placement = \"bottom\",\n align = \"start\",\n offset = 8,\n disabled = false,\n showArrow = true,\n ...props \n }, ref) => {\n const [internalOpen, setInternalOpen] = React.useState(false)\n const [coords, setCoords] = React.useState({ x: 0, y: 0 })\n const triggerRef = React.useRef<HTMLDivElement>(null)\n const dropdownRef = React.useRef<HTMLDivElement>(null)\n const isControlled = controlledOpen !== undefined\n const isOpen = isControlled ? controlledOpen : internalOpen\n\n const handleOpenChange = React.useCallback((newOpen: boolean) => {\n if (disabled) return\n\n if (!isControlled) {\n setInternalOpen(newOpen)\n }\n onOpenChange?.(newOpen)\n }, [disabled, isControlled, onOpenChange])\n\n const handleTriggerClick = () => {\n handleOpenChange(!isOpen)\n }\n\n const updatePosition = React.useCallback(() => {\n if (!triggerRef.current || !dropdownRef.current) return\n\n const triggerRect = triggerRef.current.getBoundingClientRect()\n const dropdownRect = dropdownRef.current.getBoundingClientRect()\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n let x = 0\n let y = 0\n\n // 기본 위치 계산\n switch (placement) {\n case \"top\":\n x = triggerRect.left\n y = triggerRect.top - offset\n break\n case \"bottom\":\n x = triggerRect.left\n y = triggerRect.bottom + offset\n break\n case \"left\":\n x = triggerRect.left - offset\n y = triggerRect.top\n break\n case \"right\":\n x = triggerRect.right + offset\n y = triggerRect.top\n break\n }\n\n // 정렬 조정\n switch (align) {\n case \"center\":\n if (placement === \"top\" || placement === \"bottom\") {\n x = triggerRect.left + triggerRect.width / 2 - dropdownRect.width / 2\n } else {\n y = triggerRect.top + triggerRect.height / 2 - dropdownRect.height / 2\n }\n break\n case \"end\":\n if (placement === \"top\" || placement === \"bottom\") {\n x = triggerRect.right - dropdownRect.width\n } else {\n y = triggerRect.bottom - dropdownRect.height\n }\n break\n case \"start\":\n default:\n // 기본값은 이미 start 정렬\n break\n }\n\n // 뷰포트 경계 확인 및 조정\n if (x < 8) x = 8 // 8px 여백\n if (x + dropdownRect.width > viewportWidth - 8) {\n x = viewportWidth - dropdownRect.width - 8 // 8px 여백\n }\n if (y < 8) y = 8 // 8px 여백\n if (y + dropdownRect.height > viewportHeight - 8) {\n y = viewportHeight - dropdownRect.height - 8 // 8px 여백\n }\n\n setCoords({ x, y })\n }, [placement, align, offset])\n\n React.useEffect(() => {\n if (isOpen) {\n updatePosition()\n window.addEventListener('resize', updatePosition)\n window.addEventListener('scroll', updatePosition)\n \n return () => {\n window.removeEventListener('resize', updatePosition)\n window.removeEventListener('scroll', updatePosition)\n }\n }\n }, [isOpen, updatePosition])\n\n React.useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n triggerRef.current &&\n dropdownRef.current &&\n !triggerRef.current.contains(event.target as Node) &&\n !dropdownRef.current.contains(event.target as Node)\n ) {\n handleOpenChange(false)\n }\n }\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside)\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n }\n }\n }, [isOpen, handleOpenChange])\n\n const getPlacementClasses = () => {\n switch (placement) {\n case \"top\":\n return \"bottom-full left-0 mb-2\" // 8px 간격\n case \"bottom\":\n return \"top-full left-0 mt-2\" // 8px 간격\n case \"left\":\n return \"right-full top-0 mr-2\" // 8px 간격\n case \"right\":\n return \"left-full top-0 ml-2\" // 8px 간격\n default:\n return \"top-full left-0 mt-2\"\n }\n }\n\n const getArrowClasses = () => {\n switch (placement) {\n case \"top\":\n return \"top-full left-4 -translate-x-1/2 border-t-gray-100 dark:border-t-gray-800\"\n case \"bottom\":\n return \"bottom-full left-4 -translate-x-1/2 border-b-gray-100 dark:border-b-gray-800\"\n case \"left\":\n return \"left-full top-4 -translate-y-1/2 border-l-gray-100 dark:border-l-gray-800\"\n case \"right\":\n return \"right-full top-4 -translate-y-1/2 border-r-gray-100 dark:border-r-gray-800\"\n default:\n return \"bottom-full left-4 -translate-x-1/2 border-b-gray-100 dark:border-b-gray-800\"\n }\n }\n\n return (\n <div ref={ref} className={merge(\"relative\", className)} {...props}>\n {/* 트리거 */}\n <div\n ref={triggerRef}\n onClick={handleTriggerClick}\n className=\"inline-block cursor-pointer\"\n >\n {trigger}\n </div>\n\n {/* 드롭다운 */}\n {/* CSS 변수 기반 배경색 (Tailwind v4 dark: + bg-* 충돌 우회) */}\n {isOpen && (\n <div\n ref={dropdownRef}\n className={merge(\n \"absolute z-50 bg-[var(--dropdown-bg)] rounded-lg shadow-xl backdrop-blur-sm\",\n \"min-w-[200px] py-2\",\n getPlacementClasses()\n )}\n style={{\n transform: `translate(${coords.x}px, ${coords.y}px)`,\n boxShadow: \"0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)\"\n }}\n >\n {/* 화살표 */}\n {showArrow && (\n <div\n className={merge(\n \"absolute w-0 h-0 border-4 border-transparent\",\n getArrowClasses()\n )}\n />\n )}\n \n {/* 내용 */}\n <div className=\"relative z-10\">\n {children}\n </div>\n </div>\n )}\n </div>\n )\n }\n)\nDropdown.displayName = \"Dropdown\"\n\n// 드롭다운 아이템 컴포넌트들\nexport interface DropdownItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n icon?: React.ReactNode\n variant?: \"default\" | \"destructive\" | \"disabled\"\n}\n\nconst DropdownItem = React.forwardRef<HTMLButtonElement, DropdownItemProps>(\n ({ \n className, \n icon,\n variant = \"default\",\n children,\n disabled,\n ...props \n }, ref) => {\n const getVariantClasses = () => {\n switch (variant) {\n case \"destructive\":\n return \"text-destructive hover:bg-destructive/10\"\n case \"disabled\":\n return \"text-muted-foreground cursor-not-allowed\"\n default:\n return \"text-foreground hover:bg-muted\"\n }\n }\n\n return (\n <button\n ref={ref}\n className={merge(\n \"w-full flex items-center gap-3 px-4 py-3 text-sm font-medium transition-colors duration-200 ease-in-out focus-visible:outline-none focus-visible:bg-muted\", // 16px, 12px 패딩\n getVariantClasses(),\n className\n )}\n disabled={disabled || variant === \"disabled\"}\n {...props}\n >\n {icon && (\n <div className=\"flex-shrink-0 w-4 h-4\">\n {icon}\n </div>\n )}\n <span className=\"flex-1 text-left\">{children}</span>\n </button>\n )\n }\n)\nDropdownItem.displayName = \"DropdownItem\"\n\nexport interface DropdownSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nconst DropdownSeparator = React.forwardRef<HTMLDivElement, DropdownSeparatorProps>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={merge(\"h-px bg-border my-2\", className)} // 8px 여백\n {...props}\n />\n )\n)\nDropdownSeparator.displayName = \"DropdownSeparator\"\n\nexport interface DropdownLabelProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nconst DropdownLabel = React.forwardRef<HTMLDivElement, DropdownLabelProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={merge(\"px-4 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wide\", className)} // 16px, 8px 패딩\n {...props}\n >\n {children}\n </div>\n )\n)\nDropdownLabel.displayName = \"DropdownLabel\"\n\n// 편의 컴포넌트들\nconst DropdownMenu = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={merge(\"py-1\", className)} // 4px 패딩\n {...props}\n >\n {children}\n </div>\n )\n)\nDropdownMenu.displayName = \"DropdownMenu\"\n\nconst DropdownGroup = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={merge(\"space-y-1\", className)} // 4px 간격\n {...props}\n >\n {children}\n </div>\n )\n)\nDropdownGroup.displayName = \"DropdownGroup\"\n\nexport { Dropdown, DropdownItem, DropdownSeparator, DropdownLabel, DropdownMenu, DropdownGroup } ","\"use client\"\n\nimport React from \"react\"\nimport { merge } from \"../lib/utils\"\nimport { Icon } from \"./Icon\"\n\n/**\n * Drawer 컴포넌트의 props / Drawer component props\n * @typedef {Object} DrawerProps\n * @property {boolean} open - Drawer 열림/닫힘 상태 / Drawer open/close state\n * @property {(open: boolean) => void} onOpenChange - 상태 변경 콜백 / State change callback\n * @property {React.ReactNode} children - Drawer 내용 / Drawer content\n * @property {string} [className] - 추가 CSS 클래스 / Additional CSS class\n * @property {\"left\" | \"right\" | \"top\" | \"bottom\"} [side=\"right\"] - Drawer 표시 위치 / Drawer display position\n * @property {\"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\"} [size=\"md\"] - Drawer 크기 / Drawer size\n * @property {boolean} [showBackdrop=true] - 배경 오버레이 표시 여부 / Show backdrop overlay\n * @property {string} [backdropClassName] - 배경 오버레이 추가 CSS 클래스 / Backdrop overlay additional CSS class\n * @property {boolean} [closeOnBackdropClick=true] - 배경 클릭 시 닫기 여부 / Close on backdrop click\n * @property {boolean} [closeOnEscape=true] - ESC 키로 닫기 여부 / Close on ESC key\n */\ninterface DrawerProps {\n /** Drawer 열림/닫힘 상태 / Drawer open/close state */\n isOpen?: boolean\n /** Drawer 닫기 콜백 / Drawer close callback */\n onClose?: () => void\n /** Drawer 내용 / Drawer content */\n children: React.ReactNode\n /** 추가 CSS 클래스 / Additional CSS class */\n className?: string\n /** Drawer 표시 위치 / Drawer display position */\n side?: \"left\" | \"right\" | \"top\" | \"bottom\"\n /** Drawer 크기 / Drawer size */\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\"\n /** 배경 오버레이 표시 여부 / Show backdrop overlay */\n showBackdrop?: boolean\n /** 배경 오버레이 추가 CSS 클래스 / Backdrop overlay additional CSS class */\n backdropClassName?: string\n /** 배경 클릭 시 닫기 여부 / Close on backdrop click */\n closeOnBackdropClick?: boolean\n /** ESC 키로 닫기 여부 / Close on ESC key */\n closeOnEscape?: boolean\n /** 닫기 버튼 표시 여부 / Show close button */\n closable?: boolean\n}\n\n/**\n * Drawer 컴포넌트 / Drawer component\n * \n * 사이드에서 슬라이드되는 패널 컴포넌트입니다.\n * Modal과 유사하지만 특정 방향에서 슬라이드되는 애니메이션을 제공합니다.\n * ESC 키로 닫기, 배경 클릭으로 닫기 기능을 지원합니다.\n * \n * Panel component that slides from the side.\n * Similar to Modal but provides slide animation from a specific direction.\n * Supports closing with ESC key and backdrop click.\n * \n * @component\n * @example\n * // 기본 사용 / Basic usage\n * const [open, setOpen] = useState(false)\n * \n * <Drawer open={open} onOpenChange={setOpen}>\n * <DrawerHeader>제목</DrawerHeader>\n * <DrawerContent>내용</DrawerContent>\n * <DrawerFooter>\n * <Button onClick={() => setOpen(false)}>닫기</Button>\n * </DrawerFooter>\n * </Drawer>\n * \n * @example\n * // 왼쪽에서 열기 / Open from left\n * <Drawer open={open} onOpenChange={setOpen} side=\"left\" size=\"lg\">\n * <DrawerContent>사이드바 내용</DrawerContent>\n * </Drawer>\n * \n * @param {DrawerProps} props - Drawer 컴포넌트의 props / Drawer component props\n * @param {React.Ref<HTMLDivElement>} ref - Drawer 컨테이너 ref / Drawer container ref\n * @returns {JSX.Element} Drawer 컴포넌트 / Drawer component\n * \n * @todo 접근성 개선: role=\"dialog\", aria-modal=\"true\" 추가 필요 / Accessibility: Add role=\"dialog\", aria-modal=\"true\"\n * @todo 접근성 개선: aria-labelledby, aria-describedby 연결 필요 / Accessibility: Connect aria-labelledby, aria-describedby\n */\nconst Drawer = React.forwardRef<HTMLDivElement, DrawerProps>(\n ({\n isOpen,\n onClose,\n children,\n className,\n side = \"right\",\n size = \"md\",\n showBackdrop = true,\n backdropClassName,\n closeOnBackdropClick = true,\n closeOnEscape = true,\n closable = true,\n ...props\n }, ref) => {\n const _isOpen = isOpen ?? false\n const handleClose = () => {\n onClose?.()\n }\n\n const [isVisible, setIsVisible] = React.useState(false)\n const [isAnimating, setIsAnimating] = React.useState(false)\n\n React.useEffect(() => {\n if (_isOpen) {\n setIsVisible(true)\n setIsAnimating(true)\n // 애니메이션 시작을 위한 지연\n const timer = setTimeout(() => setIsAnimating(false), 50)\n return () => clearTimeout(timer)\n } else {\n setIsAnimating(true)\n const timer = setTimeout(() => {\n setIsVisible(false)\n setIsAnimating(false)\n }, 300) // 애니메이션 완료 후 숨김\n return () => clearTimeout(timer)\n }\n }, [_isOpen])\n\n React.useEffect(() => {\n if (!closeOnEscape) return\n\n const handleEscapeKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && _isOpen) {\n handleClose()\n }\n }\n\n if (_isOpen) {\n document.addEventListener(\"keydown\", handleEscapeKey)\n document.body.style.overflow = \"hidden\"\n }\n\n return () => {\n document.removeEventListener(\"keydown\", handleEscapeKey)\n document.body.style.overflow = \"\"\n }\n }, [_isOpen, closeOnEscape])\n\n if (!isVisible) return null\n\n const sizeClasses = {\n sm: side === \"left\" || side === \"right\" ? \"w-80\" : \"h-64\",\n md: side === \"left\" || side === \"right\" ? \"w-96\" : \"h-96\",\n lg: side === \"left\" || side === \"right\" ? \"w-[28rem]\" : \"h-[32rem]\",\n xl: side === \"left\" || side === \"right\" ? \"w-[32rem]\" : \"h-[40rem]\",\n full: side === \"left\" || side === \"right\" ? \"w-full\" : \"h-full\"\n }\n\n const sideClasses = {\n left: \"left-0 top-0 h-full\",\n right: \"right-0 top-0 h-full\",\n top: \"top-0 left-0 w-full\",\n bottom: \"bottom-0 left-0 w-full\"\n }\n\n // Transform: _isOpen=true -> visible position, _isOpen=false -> hidden position\n const transformClasses = {\n left: _isOpen ? \"translate-x-0\" : \"-translate-x-full\",\n right: _isOpen ? \"translate-x-0\" : \"translate-x-full\",\n top: _isOpen ? \"translate-y-0\" : \"-translate-y-full\",\n bottom: _isOpen ? \"translate-y-0\" : \"translate-y-full\"\n }\n\n return (\n <div className=\"fixed inset-0 z-50\">\n {/* Backdrop */}\n {showBackdrop && (\n <div\n className={merge(\n \"absolute inset-0 bg-black/60 backdrop-blur-md transition-opacity duration-300\",\n isAnimating ? (_isOpen ? \"opacity-100\" : \"opacity-0\") : \"\",\n backdropClassName\n )}\n onClick={closeOnBackdropClick ? handleClose : undefined}\n />\n )}\n\n {/* Drawer Content */}\n <div\n ref={ref}\n className={merge(\n \"absolute bg-background/95 backdrop-blur-xl border border-border/50 shadow-2xl transition-transform duration-300 ease-out\",\n sizeClasses[size],\n sideClasses[side],\n transformClasses[side],\n className\n )}\n {...props}\n >\n {children}\n </div>\n </div>\n )\n }\n)\nDrawer.displayName = \"Drawer\"\n\n/**\n * DrawerHeader 컴포넌트의 props / DrawerHeader component props\n * @typedef {Object} DrawerHeaderProps\n * @property {React.ReactNode} children - 헤더 내용 / Header content\n * @property {string} [className] - 추가 CSS 클래스 / Additional CSS class\n * @property {boolean} [showCloseButton=true] - 닫기 버튼 표시 여부 / Show close button\n * @property {() => void} [onClose] - 닫기 버튼 클릭 콜백 / Close button click callback\n */\ninterface DrawerHeaderProps {\n children: React.ReactNode\n className?: string\n showCloseButton?: boolean\n onClose?: () => void\n}\n\n/**\n * DrawerHeader 컴포넌트 / DrawerHeader component\n * Drawer의 헤더 영역을 표시합니다.\n * Displays the header area of a Drawer.\n * \n * @component\n * @param {DrawerHeaderProps} props - DrawerHeader 컴포넌트의 props / DrawerHeader component props\n * @param {React.Ref<HTMLDivElement>} ref - div 요소 ref / div element ref\n * @returns {JSX.Element} DrawerHeader 컴포넌트 / DrawerHeader component\n */\nconst DrawerHeader = React.forwardRef<HTMLDivElement, DrawerHeaderProps>(\n ({ children, className, showCloseButton = true, onClose, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={merge(\"flex items-center justify-between p-6 border-b border-border/50\", className)}\n {...props}\n >\n <div className=\"flex-1\">{children}</div>\n {showCloseButton && (\n <button\n onClick={onClose}\n className=\"p-2 rounded-lg hover:bg-muted transition-colors\"\n >\n <Icon name=\"close\" size={20} />\n </button>\n )}\n </div>\n )\n }\n)\nDrawerHeader.displayName = \"DrawerHeader\"\n\n/**\n * DrawerContent 컴포넌트의 props / DrawerContent component props\n * @typedef {Object} DrawerContentProps\n * @property {React.ReactNode} children - 콘텐츠 / Content\n * @property {string} [className] - 추가 CSS 클래스 / Additional CSS class\n */\ninterface DrawerContentProps {\n children: React.ReactNode\n className?: string\n}\n\n/**\n * DrawerContent 컴포넌트 / DrawerContent component\n * Drawer의 메인 콘텐츠 영역을 표시합니다.\n * Displays the main content area of a Drawer.\n * \n * @component\n * @param {DrawerContentProps} props - DrawerContent 컴포넌트의 props / DrawerContent component props\n * @param {React.Ref<HTMLDivElement>} ref - div 요소 ref / div element ref\n * @returns {JSX.Element} DrawerContent 컴포넌트 / DrawerContent component\n */\nconst DrawerContent = React.forwardRef<HTMLDivElement, DrawerContentProps>(\n ({ children, className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={merge(\"flex-1 p-6 overflow-y-auto\", className)}\n {...props}\n >\n {children}\n </div>\n )\n }\n)\nDrawerContent.displayName = \"DrawerContent\"\n\n/**\n * DrawerFooter 컴포넌트의 props / DrawerFooter component props\n * @typedef {Object} DrawerFooterProps\n * @property {React.ReactNode} children - 푸터 내용 / Footer content\n * @property {string} [className] - 추가 CSS 클래스 / Additional CSS class\n */\ninterface DrawerFooterProps {\n children: React.ReactNode\n className?: string\n}\n\n/**\n * DrawerFooter 컴포넌트 / DrawerFooter component\n * Drawer의 푸터 영역을 표시합니다. 주로 액션 버튼을 배치합니다.\n * Displays the footer area of a Drawer. Typically used for action buttons.\n * \n * @component\n * @param {DrawerFooterProps} props - DrawerFooter 컴포넌트의 props / DrawerFooter component props\n * @param {React.Ref<HTMLDivElement>} ref - div 요소 ref / div element ref\n * @returns {JSX.Element} DrawerFooter 컴포넌트 / DrawerFooter component\n */\nconst DrawerFooter = React.forwardRef<HTMLDivElement, DrawerFooterProps>(\n ({ children, className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={merge(\"flex items-center justify-end gap-3 p-6 border-t border-border/50\", className)}\n {...props}\n >\n {children}\n </div>\n )\n }\n)\nDrawerFooter.displayName = \"DrawerFooter\"\n\nexport { Drawer, DrawerHeader, DrawerContent, DrawerFooter } "]}
|
|
1
|
+
{"version":3,"sources":["../src/components/Dropdown.tsx","../src/components/Drawer.tsx"],"names":["Dropdown","React","className","trigger","children","controlledOpen","onOpenChange","placement","align","offset","disabled","showArrow","props","ref","internalOpen","setInternalOpen","coords","setCoords","triggerRef","dropdownRef","isControlled","isOpen","handleOpenChange","newOpen","handleTriggerClick","updatePosition","triggerRect","dropdownRect","viewportWidth","viewportHeight","x","y","handleClickOutside","event","getPlacementClasses","getArrowClasses","jsxs","merge","jsx","DropdownItem","icon","variant","DropdownSeparator","DropdownLabel","DropdownMenu","DropdownGroup","Drawer","onClose","side","size","showBackdrop","backdropClassName","closeOnBackdropClick","closeOnEscape","closable","_isOpen","handleClose","isVisible","setIsVisible","isAnimating","setIsAnimating","timer","handleEscapeKey","e","sizeClasses","sideClasses","transformClasses","DrawerHeader","showCloseButton","Icon","DrawerContent","DrawerFooter"],"mappings":"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","file":"chunk-ANYZ56VB.mjs","sourcesContent":["\"use client\"\n\nimport React from \"react\"\nimport { merge } from \"../lib/utils\"\n\n/**\n * Dropdown 컴포넌트의 props / Dropdown component props\n * @typedef {Object} DropdownProps\n * @property {React.ReactNode} trigger - Dropdown을 열기 위한 트리거 요소 / Trigger element to open dropdown\n * @property {React.ReactNode} children - Dropdown 내용 / Dropdown content\n * @property {boolean} [open] - 제어 모드에서 열림/닫힘 상태 / Open/close state in controlled mode\n * @property {(open: boolean) => void} [onOpenChange] - 상태 변경 콜백 / State change callback\n * @property {\"top\" | \"bottom\" | \"left\" | \"right\"} [placement=\"bottom\"] - Dropdown 표시 위치 / Dropdown display position\n * @property {\"start\" | \"center\" | \"end\"} [align=\"start\"] - Dropdown 정렬 / Dropdown alignment\n * @property {number} [offset=8] - 트리거와 Dropdown 사이 간격 (px) / Spacing between trigger and dropdown (px)\n * @property {boolean} [disabled=false] - Dropdown 비활성화 여부 / Disable dropdown\n * @property {boolean} [showArrow=true] - 화살표 표시 여부 / Show arrow\n * @extends {React.HTMLAttributes<HTMLDivElement>}\n */\nexport interface DropdownProps extends React.HTMLAttributes<HTMLDivElement> {\n trigger: React.ReactNode\n children: React.ReactNode\n open?: boolean\n onOpenChange?: (open: boolean) => void\n placement?: \"top\" | \"bottom\" | \"left\" | \"right\"\n align?: \"start\" | \"center\" | \"end\"\n offset?: number\n disabled?: boolean\n showArrow?: boolean\n}\n\n/**\n * Dropdown 컴포넌트 / Dropdown component\n * \n * 트리거 요소를 클릭하면 표시되는 드롭다운 메뉴 컴포넌트입니다.\n * 외부 클릭 시 자동으로 닫히며, 뷰포트 경계를 자동으로 감지하여 위치를 조정합니다.\n * \n * Dropdown menu component that appears when the trigger element is clicked.\n * Automatically closes on outside click and adjusts position by detecting viewport boundaries.\n * \n * @component\n * @example\n * // 기본 사용 / Basic usage\n * <Dropdown trigger={<Button>메뉴</Button>}>\n * <Menu>\n * <MenuItem>항목 1</MenuItem>\n * <MenuItem>항목 2</MenuItem>\n * </Menu>\n * </Dropdown>\n * \n * @example\n * // 제어 모드, 화살표 없음 / Controlled mode, no arrow\n * const [open, setOpen] = useState(false)\n * <Dropdown \n * open={open}\n * onOpenChange={setOpen}\n * trigger={<Button>제어 모드</Button>}\n * placement=\"top\"\n * showArrow={false}\n * >\n * <div className=\"p-4\">내용</div>\n * </Dropdown>\n * \n * @param {DropdownProps} props - Dropdown 컴포넌트의 props / Dropdown component props\n * @param {React.Ref<HTMLDivElement>} ref - div 요소 ref / div element ref\n * @returns {JSX.Element} Dropdown 컴포넌트 / Dropdown component\n */\nconst Dropdown = React.forwardRef<HTMLDivElement, DropdownProps>(\n ({ \n className, \n trigger,\n children,\n open: controlledOpen,\n onOpenChange,\n placement = \"bottom\",\n align = \"start\",\n offset = 8,\n disabled = false,\n showArrow = true,\n ...props \n }, ref) => {\n const [internalOpen, setInternalOpen] = React.useState(false)\n const [coords, setCoords] = React.useState({ x: 0, y: 0 })\n const triggerRef = React.useRef<HTMLDivElement>(null)\n const dropdownRef = React.useRef<HTMLDivElement>(null)\n const isControlled = controlledOpen !== undefined\n const isOpen = isControlled ? controlledOpen : internalOpen\n\n const handleOpenChange = React.useCallback((newOpen: boolean) => {\n if (disabled) return\n\n if (!isControlled) {\n setInternalOpen(newOpen)\n }\n onOpenChange?.(newOpen)\n }, [disabled, isControlled, onOpenChange])\n\n const handleTriggerClick = () => {\n handleOpenChange(!isOpen)\n }\n\n const updatePosition = React.useCallback(() => {\n if (!triggerRef.current || !dropdownRef.current) return\n\n const triggerRect = triggerRef.current.getBoundingClientRect()\n const dropdownRect = dropdownRef.current.getBoundingClientRect()\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n let x = 0\n let y = 0\n\n // 기본 위치 계산\n switch (placement) {\n case \"top\":\n x = triggerRect.left\n y = triggerRect.top - offset\n break\n case \"bottom\":\n x = triggerRect.left\n y = triggerRect.bottom + offset\n break\n case \"left\":\n x = triggerRect.left - offset\n y = triggerRect.top\n break\n case \"right\":\n x = triggerRect.right + offset\n y = triggerRect.top\n break\n }\n\n // 정렬 조정\n switch (align) {\n case \"center\":\n if (placement === \"top\" || placement === \"bottom\") {\n x = triggerRect.left + triggerRect.width / 2 - dropdownRect.width / 2\n } else {\n y = triggerRect.top + triggerRect.height / 2 - dropdownRect.height / 2\n }\n break\n case \"end\":\n if (placement === \"top\" || placement === \"bottom\") {\n x = triggerRect.right - dropdownRect.width\n } else {\n y = triggerRect.bottom - dropdownRect.height\n }\n break\n case \"start\":\n default:\n // 기본값은 이미 start 정렬\n break\n }\n\n // 뷰포트 경계 확인 및 조정\n if (x < 8) x = 8 // 8px 여백\n if (x + dropdownRect.width > viewportWidth - 8) {\n x = viewportWidth - dropdownRect.width - 8 // 8px 여백\n }\n if (y < 8) y = 8 // 8px 여백\n if (y + dropdownRect.height > viewportHeight - 8) {\n y = viewportHeight - dropdownRect.height - 8 // 8px 여백\n }\n\n setCoords({ x, y })\n }, [placement, align, offset])\n\n React.useEffect(() => {\n if (isOpen) {\n updatePosition()\n window.addEventListener('resize', updatePosition)\n window.addEventListener('scroll', updatePosition)\n \n return () => {\n window.removeEventListener('resize', updatePosition)\n window.removeEventListener('scroll', updatePosition)\n }\n }\n }, [isOpen, updatePosition])\n\n React.useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n triggerRef.current &&\n dropdownRef.current &&\n !triggerRef.current.contains(event.target as Node) &&\n !dropdownRef.current.contains(event.target as Node)\n ) {\n handleOpenChange(false)\n }\n }\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside)\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n }\n }\n }, [isOpen, handleOpenChange])\n\n const getPlacementClasses = () => {\n switch (placement) {\n case \"top\":\n return \"bottom-full left-0 mb-2\" // 8px 간격\n case \"bottom\":\n return \"top-full left-0 mt-2\" // 8px 간격\n case \"left\":\n return \"right-full top-0 mr-2\" // 8px 간격\n case \"right\":\n return \"left-full top-0 ml-2\" // 8px 간격\n default:\n return \"top-full left-0 mt-2\"\n }\n }\n\n const getArrowClasses = () => {\n switch (placement) {\n case \"top\":\n return \"top-full left-4 -translate-x-1/2 border-t-gray-100 dark:border-t-gray-800\"\n case \"bottom\":\n return \"bottom-full left-4 -translate-x-1/2 border-b-gray-100 dark:border-b-gray-800\"\n case \"left\":\n return \"left-full top-4 -translate-y-1/2 border-l-gray-100 dark:border-l-gray-800\"\n case \"right\":\n return \"right-full top-4 -translate-y-1/2 border-r-gray-100 dark:border-r-gray-800\"\n default:\n return \"bottom-full left-4 -translate-x-1/2 border-b-gray-100 dark:border-b-gray-800\"\n }\n }\n\n return (\n <div ref={ref} className={merge(\"relative\", className)} {...props}>\n {/* 트리거 */}\n <div\n ref={triggerRef}\n onClick={handleTriggerClick}\n className=\"inline-block cursor-pointer\"\n >\n {trigger}\n </div>\n\n {/* 드롭다운 */}\n {/* CSS 변수 기반 배경색 (Tailwind v4 dark: + bg-* 충돌 우회) */}\n {isOpen && (\n <div\n ref={dropdownRef}\n className={merge(\n \"absolute z-50 bg-[var(--dropdown-bg)] rounded-lg shadow-xl backdrop-blur-sm\",\n \"min-w-[200px] py-2\",\n getPlacementClasses()\n )}\n style={{\n transform: `translate(${coords.x}px, ${coords.y}px)`,\n boxShadow: \"0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)\"\n }}\n >\n {/* 화살표 */}\n {showArrow && (\n <div\n className={merge(\n \"absolute w-0 h-0 border-4 border-transparent\",\n getArrowClasses()\n )}\n />\n )}\n \n {/* 내용 */}\n <div className=\"relative z-10\">\n {children}\n </div>\n </div>\n )}\n </div>\n )\n }\n)\nDropdown.displayName = \"Dropdown\"\n\n// 드롭다운 아이템 컴포넌트들\nexport interface DropdownItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n icon?: React.ReactNode\n variant?: \"default\" | \"destructive\" | \"disabled\"\n}\n\nconst DropdownItem = React.forwardRef<HTMLButtonElement, DropdownItemProps>(\n ({ \n className, \n icon,\n variant = \"default\",\n children,\n disabled,\n ...props \n }, ref) => {\n const getVariantClasses = () => {\n switch (variant) {\n case \"destructive\":\n return \"text-destructive hover:bg-destructive/10\"\n case \"disabled\":\n return \"text-muted-foreground cursor-not-allowed\"\n default:\n return \"text-foreground hover:bg-muted\"\n }\n }\n\n return (\n <button\n ref={ref}\n className={merge(\n \"w-full flex items-center gap-3 px-4 py-3 text-sm font-medium transition-colors duration-200 ease-in-out focus-visible:outline-none focus-visible:bg-muted\", // 16px, 12px 패딩\n getVariantClasses(),\n className\n )}\n disabled={disabled || variant === \"disabled\"}\n {...props}\n >\n {icon && (\n <div className=\"flex-shrink-0 w-4 h-4\">\n {icon}\n </div>\n )}\n <span className=\"flex-1 text-left\">{children}</span>\n </button>\n )\n }\n)\nDropdownItem.displayName = \"DropdownItem\"\n\nexport interface DropdownSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nconst DropdownSeparator = React.forwardRef<HTMLDivElement, DropdownSeparatorProps>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={merge(\"h-px bg-border my-2\", className)} // 8px 여백\n {...props}\n />\n )\n)\nDropdownSeparator.displayName = \"DropdownSeparator\"\n\nexport interface DropdownLabelProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nconst DropdownLabel = React.forwardRef<HTMLDivElement, DropdownLabelProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={merge(\"px-4 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wide\", className)} // 16px, 8px 패딩\n {...props}\n >\n {children}\n </div>\n )\n)\nDropdownLabel.displayName = \"DropdownLabel\"\n\n// 편의 컴포넌트들\nconst DropdownMenu = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={merge(\"py-1\", className)} // 4px 패딩\n {...props}\n >\n {children}\n </div>\n )\n)\nDropdownMenu.displayName = \"DropdownMenu\"\n\nconst DropdownGroup = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={merge(\"space-y-1\", className)} // 4px 간격\n {...props}\n >\n {children}\n </div>\n )\n)\nDropdownGroup.displayName = \"DropdownGroup\"\n\nexport { Dropdown, DropdownItem, DropdownSeparator, DropdownLabel, DropdownMenu, DropdownGroup } ","\"use client\"\n\nimport React from \"react\"\nimport { merge } from \"../lib/utils\"\nimport { Icon } from \"./Icon\"\n\n/**\n * Drawer 컴포넌트의 props / Drawer component props\n * @typedef {Object} DrawerProps\n * @property {boolean} open - Drawer 열림/닫힘 상태 / Drawer open/close state\n * @property {(open: boolean) => void} onOpenChange - 상태 변경 콜백 / State change callback\n * @property {React.ReactNode} children - Drawer 내용 / Drawer content\n * @property {string} [className] - 추가 CSS 클래스 / Additional CSS class\n * @property {\"left\" | \"right\" | \"top\" | \"bottom\"} [side=\"right\"] - Drawer 표시 위치 / Drawer display position\n * @property {\"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\"} [size=\"md\"] - Drawer 크기 / Drawer size\n * @property {boolean} [showBackdrop=true] - 배경 오버레이 표시 여부 / Show backdrop overlay\n * @property {string} [backdropClassName] - 배경 오버레이 추가 CSS 클래스 / Backdrop overlay additional CSS class\n * @property {boolean} [closeOnBackdropClick=true] - 배경 클릭 시 닫기 여부 / Close on backdrop click\n * @property {boolean} [closeOnEscape=true] - ESC 키로 닫기 여부 / Close on ESC key\n */\ninterface DrawerProps {\n /** Drawer 열림/닫힘 상태 / Drawer open/close state */\n isOpen?: boolean\n /** Drawer 닫기 콜백 / Drawer close callback */\n onClose?: () => void\n /** Drawer 내용 / Drawer content */\n children: React.ReactNode\n /** 추가 CSS 클래스 / Additional CSS class */\n className?: string\n /** Drawer 표시 위치 / Drawer display position */\n side?: \"left\" | \"right\" | \"top\" | \"bottom\"\n /** Drawer 크기 / Drawer size */\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\"\n /** 배경 오버레이 표시 여부 / Show backdrop overlay */\n showBackdrop?: boolean\n /** 배경 오버레이 추가 CSS 클래스 / Backdrop overlay additional CSS class */\n backdropClassName?: string\n /** 배경 클릭 시 닫기 여부 / Close on backdrop click */\n closeOnBackdropClick?: boolean\n /** ESC 키로 닫기 여부 / Close on ESC key */\n closeOnEscape?: boolean\n /** 닫기 버튼 표시 여부 / Show close button */\n closable?: boolean\n}\n\n/**\n * Drawer 컴포넌트 / Drawer component\n * \n * 사이드에서 슬라이드되는 패널 컴포넌트입니다.\n * Modal과 유사하지만 특정 방향에서 슬라이드되는 애니메이션을 제공합니다.\n * ESC 키로 닫기, 배경 클릭으로 닫기 기능을 지원합니다.\n * \n * Panel component that slides from the side.\n * Similar to Modal but provides slide animation from a specific direction.\n * Supports closing with ESC key and backdrop click.\n * \n * @component\n * @example\n * // 기본 사용 / Basic usage\n * const [open, setOpen] = useState(false)\n * \n * <Drawer open={open} onOpenChange={setOpen}>\n * <DrawerHeader>제목</DrawerHeader>\n * <DrawerContent>내용</DrawerContent>\n * <DrawerFooter>\n * <Button onClick={() => setOpen(false)}>닫기</Button>\n * </DrawerFooter>\n * </Drawer>\n * \n * @example\n * // 왼쪽에서 열기 / Open from left\n * <Drawer open={open} onOpenChange={setOpen} side=\"left\" size=\"lg\">\n * <DrawerContent>사이드바 내용</DrawerContent>\n * </Drawer>\n * \n * @param {DrawerProps} props - Drawer 컴포넌트의 props / Drawer component props\n * @param {React.Ref<HTMLDivElement>} ref - Drawer 컨테이너 ref / Drawer container ref\n * @returns {JSX.Element} Drawer 컴포넌트 / Drawer component\n * \n * @todo 접근성 개선: role=\"dialog\", aria-modal=\"true\" 추가 필요 / Accessibility: Add role=\"dialog\", aria-modal=\"true\"\n * @todo 접근성 개선: aria-labelledby, aria-describedby 연결 필요 / Accessibility: Connect aria-labelledby, aria-describedby\n */\nconst Drawer = React.forwardRef<HTMLDivElement, DrawerProps>(\n ({\n isOpen,\n onClose,\n children,\n className,\n side = \"right\",\n size = \"md\",\n showBackdrop = true,\n backdropClassName,\n closeOnBackdropClick = true,\n closeOnEscape = true,\n closable = true,\n ...props\n }, ref) => {\n const _isOpen = isOpen ?? false\n const handleClose = () => {\n onClose?.()\n }\n\n const [isVisible, setIsVisible] = React.useState(false)\n const [isAnimating, setIsAnimating] = React.useState(false)\n\n React.useEffect(() => {\n if (_isOpen) {\n setIsVisible(true)\n setIsAnimating(true)\n // 애니메이션 시작을 위한 지연\n const timer = setTimeout(() => setIsAnimating(false), 50)\n return () => clearTimeout(timer)\n } else {\n setIsAnimating(true)\n const timer = setTimeout(() => {\n setIsVisible(false)\n setIsAnimating(false)\n }, 300) // 애니메이션 완료 후 숨김\n return () => clearTimeout(timer)\n }\n }, [_isOpen])\n\n React.useEffect(() => {\n if (!closeOnEscape) return\n\n const handleEscapeKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && _isOpen) {\n handleClose()\n }\n }\n\n if (_isOpen) {\n document.addEventListener(\"keydown\", handleEscapeKey)\n document.body.style.overflow = \"hidden\"\n }\n\n return () => {\n document.removeEventListener(\"keydown\", handleEscapeKey)\n document.body.style.overflow = \"\"\n }\n }, [_isOpen, closeOnEscape])\n\n if (!isVisible) return null\n\n const sizeClasses = {\n sm: side === \"left\" || side === \"right\" ? \"w-80\" : \"h-64\",\n md: side === \"left\" || side === \"right\" ? \"w-96\" : \"h-96\",\n lg: side === \"left\" || side === \"right\" ? \"w-[28rem]\" : \"h-[32rem]\",\n xl: side === \"left\" || side === \"right\" ? \"w-[32rem]\" : \"h-[40rem]\",\n full: side === \"left\" || side === \"right\" ? \"w-full\" : \"h-full\"\n }\n\n const sideClasses = {\n left: \"left-0 top-0 h-full\",\n right: \"right-0 top-0 h-full\",\n top: \"top-0 left-0 w-full\",\n bottom: \"bottom-0 left-0 w-full\"\n }\n\n // Transform: _isOpen=true -> visible position, _isOpen=false -> hidden position\n const transformClasses = {\n left: _isOpen ? \"translate-x-0\" : \"-translate-x-full\",\n right: _isOpen ? \"translate-x-0\" : \"translate-x-full\",\n top: _isOpen ? \"translate-y-0\" : \"-translate-y-full\",\n bottom: _isOpen ? \"translate-y-0\" : \"translate-y-full\"\n }\n\n return (\n <div className=\"fixed inset-0 z-50\">\n {/* Backdrop */}\n {showBackdrop && (\n <div\n className={merge(\n \"absolute inset-0 bg-black/85 backdrop-blur-md transition-opacity duration-300\",\n isAnimating ? (_isOpen ? \"opacity-100\" : \"opacity-0\") : \"\",\n backdropClassName\n )}\n onClick={closeOnBackdropClick ? handleClose : undefined}\n />\n )}\n\n {/* Drawer Content */}\n <div\n ref={ref}\n className={merge(\n \"absolute bg-background/95 backdrop-blur-xl border border-border/50 shadow-2xl transition-transform duration-300 ease-out\",\n sizeClasses[size],\n sideClasses[side],\n transformClasses[side],\n className\n )}\n {...props}\n >\n {children}\n </div>\n </div>\n )\n }\n)\nDrawer.displayName = \"Drawer\"\n\n/**\n * DrawerHeader 컴포넌트의 props / DrawerHeader component props\n * @typedef {Object} DrawerHeaderProps\n * @property {React.ReactNode} children - 헤더 내용 / Header content\n * @property {string} [className] - 추가 CSS 클래스 / Additional CSS class\n * @property {boolean} [showCloseButton=true] - 닫기 버튼 표시 여부 / Show close button\n * @property {() => void} [onClose] - 닫기 버튼 클릭 콜백 / Close button click callback\n */\ninterface DrawerHeaderProps {\n children: React.ReactNode\n className?: string\n showCloseButton?: boolean\n onClose?: () => void\n}\n\n/**\n * DrawerHeader 컴포넌트 / DrawerHeader component\n * Drawer의 헤더 영역을 표시합니다.\n * Displays the header area of a Drawer.\n * \n * @component\n * @param {DrawerHeaderProps} props - DrawerHeader 컴포넌트의 props / DrawerHeader component props\n * @param {React.Ref<HTMLDivElement>} ref - div 요소 ref / div element ref\n * @returns {JSX.Element} DrawerHeader 컴포넌트 / DrawerHeader component\n */\nconst DrawerHeader = React.forwardRef<HTMLDivElement, DrawerHeaderProps>(\n ({ children, className, showCloseButton = true, onClose, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={merge(\"flex items-center justify-between p-6 border-b border-border/50\", className)}\n {...props}\n >\n <div className=\"flex-1\">{children}</div>\n {showCloseButton && (\n <button\n onClick={onClose}\n className=\"p-2 rounded-lg hover:bg-muted transition-colors\"\n >\n <Icon name=\"close\" size={20} />\n </button>\n )}\n </div>\n )\n }\n)\nDrawerHeader.displayName = \"DrawerHeader\"\n\n/**\n * DrawerContent 컴포넌트의 props / DrawerContent component props\n * @typedef {Object} DrawerContentProps\n * @property {React.ReactNode} children - 콘텐츠 / Content\n * @property {string} [className] - 추가 CSS 클래스 / Additional CSS class\n */\ninterface DrawerContentProps {\n children: React.ReactNode\n className?: string\n}\n\n/**\n * DrawerContent 컴포넌트 / DrawerContent component\n * Drawer의 메인 콘텐츠 영역을 표시합니다.\n * Displays the main content area of a Drawer.\n * \n * @component\n * @param {DrawerContentProps} props - DrawerContent 컴포넌트의 props / DrawerContent component props\n * @param {React.Ref<HTMLDivElement>} ref - div 요소 ref / div element ref\n * @returns {JSX.Element} DrawerContent 컴포넌트 / DrawerContent component\n */\nconst DrawerContent = React.forwardRef<HTMLDivElement, DrawerContentProps>(\n ({ children, className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={merge(\"flex-1 p-6 overflow-y-auto\", className)}\n {...props}\n >\n {children}\n </div>\n )\n }\n)\nDrawerContent.displayName = \"DrawerContent\"\n\n/**\n * DrawerFooter 컴포넌트의 props / DrawerFooter component props\n * @typedef {Object} DrawerFooterProps\n * @property {React.ReactNode} children - 푸터 내용 / Footer content\n * @property {string} [className] - 추가 CSS 클래스 / Additional CSS class\n */\ninterface DrawerFooterProps {\n children: React.ReactNode\n className?: string\n}\n\n/**\n * DrawerFooter 컴포넌트 / DrawerFooter component\n * Drawer의 푸터 영역을 표시합니다. 주로 액션 버튼을 배치합니다.\n * Displays the footer area of a Drawer. Typically used for action buttons.\n * \n * @component\n * @param {DrawerFooterProps} props - DrawerFooter 컴포넌트의 props / DrawerFooter component props\n * @param {React.Ref<HTMLDivElement>} ref - div 요소 ref / div element ref\n * @returns {JSX.Element} DrawerFooter 컴포넌트 / DrawerFooter component\n */\nconst DrawerFooter = React.forwardRef<HTMLDivElement, DrawerFooterProps>(\n ({ children, className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={merge(\"flex items-center justify-end gap-3 p-6 border-t border-border/50\", className)}\n {...props}\n >\n {children}\n </div>\n )\n }\n)\nDrawerFooter.displayName = \"DrawerFooter\"\n\nexport { Drawer, DrawerHeader, DrawerContent, DrawerFooter } "]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {d}from'./chunk-XCZMLKPK.mjs';import {b}from'./chunk-U6CTBZ2U.mjs';import I,{useState,useEffect,useCallback}from'react';import {jsxs,jsx,Fragment}from'react/jsx-runtime';var U=I.forwardRef(({className:p,height:v=2,color:h="gradient",position:y="top",animated:A=true,showPercentage:w=false,...a},x)=>{let[u,b$1]=useState(0);useEffect(()=>{let d=()=>{let m=window.scrollY,n=document.documentElement.scrollHeight-window.innerHeight,k=n>0?m/n*100:0;b$1(k);};return d(),window.addEventListener("scroll",d,{passive:true}),window.addEventListener("resize",d,{passive:true}),()=>{window.removeEventListener("scroll",d),window.removeEventListener("resize",d);}},[]);let f={default:"bg-foreground",primary:"bg-primary",secondary:"bg-muted-foreground",gradient:"bg-gradient-to-r from-teal-600 via-cyan-500 to-teal-600"};return jsxs("div",{ref:x,className:b("fixed z-50",{top:"top-0 left-0 right-0",bottom:"bottom-0 left-0 right-0"}[y],p),style:{height:`${v}px`},...a,children:[jsx("div",{className:"absolute inset-0 w-full h-full bg-border/30"}),jsx("div",{className:b("absolute top-0 left-0 h-full origin-left transition-all duration-100 ease-out",f[h]||f.gradient),style:{width:`${u}%`,transformOrigin:"left"}}),w&&jsxs("div",{className:"absolute top-2 right-2 text-xs text-muted-foreground bg-card px-2 py-1 rounded border border-border",children:[Math.round(u),"%"]})]})});U.displayName="ScrollProgress";var X=I.forwardRef(({className:p,title:v,subtitle:h,description:y,primaryAction:A,secondaryAction:w,slides:a,autoPlay:x=false,interval:u=5e3,indicator:b$1="dots",showControls:f=true,pauseOnHover:N=true,background:d$1="gradient",customBackground:m,size:n="lg",fullBleed:k=false,...T},B)=>{let[g,S]=useState(0),[H,L]=useState(false),r=a&&a.length>0,c=(a==null?void 0:a.length)||0,C=useCallback(()=>{r&&S(s=>(s+1)%c);},[r,c]),$=useCallback(()=>{r&&S(s=>(s-1+c)%c);},[r,c]),E=useCallback(s=>{S(s);},[]);useEffect(()=>{if(!x||!r||H)return;let s=setInterval(C,u);return ()=>clearInterval(s)},[x,r,H,u,C]);let t=r?a[g]:{title:v||"",subtitle:h,description:y||"",primaryAction:A,secondaryAction:w,background:d$1},_={sm:"min-h-[400px]",md:"min-h-[500px]",lg:"min-h-[600px]",xl:"min-h-[700px]",full:"min-h-screen"},F={sm:"text-2xl sm:text-3xl md:text-4xl leading-tight",md:"text-3xl sm:text-4xl md:text-5xl leading-tight",lg:"text-3xl sm:text-4xl md:text-5xl lg:text-6xl leading-tight",xl:"text-3xl sm:text-4xl md:text-5xl lg:text-6xl leading-tight",full:"text-4xl sm:text-5xl md:text-6xl lg:text-7xl leading-tight"},G={sm:"text-base sm:text-lg md:text-xl leading-snug",md:"text-lg sm:text-xl md:text-2xl leading-snug",lg:"text-lg sm:text-xl md:text-2xl lg:text-3xl leading-snug",xl:"text-xl sm:text-2xl md:text-3xl leading-snug",full:"text-xl sm:text-2xl md:text-3xl lg:text-4xl leading-snug"},W={sm:"text-sm sm:text-base md:text-lg leading-relaxed",md:"text-base sm:text-lg md:text-xl leading-relaxed",lg:"text-base sm:text-lg md:text-xl leading-relaxed",xl:"text-base sm:text-lg md:text-xl leading-relaxed",full:"text-lg sm:text-xl md:text-2xl leading-relaxed"},D=r&&t.background||d$1,O={none:null,gradient:jsxs("div",{className:"absolute inset-0 z-0 pointer-events-none",children:[jsx("div",{className:"absolute top-0 left-0 w-80 h-80 sm:w-96 sm:h-96 md:w-[500px] md:h-[500px] -translate-x-1/3 -translate-y-1/3 rounded-full bg-gradient-to-br from-teal-400 via-cyan-500 to-teal-600 opacity-40 blur-3xl"}),jsx("div",{className:"absolute bottom-0 right-0 w-72 h-72 sm:w-80 sm:h-80 md:w-[400px] md:h-[400px] translate-x-1/4 translate-y-1/4 rounded-full bg-gradient-to-tr from-cyan-400 via-teal-500 to-emerald-500 opacity-35 blur-3xl"}),jsx("div",{className:"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-48 h-48 sm:w-64 sm:h-64 md:w-80 md:h-80 rounded-full bg-teal-500/20 blur-2xl"})]}),particles:jsx("div",{className:"absolute inset-0 z-0 pointer-events-none",children:jsx("div",{className:"absolute inset-0 bg-gradient-to-br from-secondary/50 via-background to-secondary/30"})}),video:m?jsx("div",{className:"absolute inset-0 z-0 pointer-events-none",children:jsx("video",{autoPlay:true,loop:true,muted:true,playsInline:true,className:"absolute inset-0 w-full h-full object-cover opacity-20",children:jsx("source",{src:m,type:"video/mp4"})})}):null,image:m||r&&t.backgroundImage?jsxs("div",{className:"absolute inset-0 z-0 pointer-events-none",children:[jsx("img",{src:r&&t.backgroundImage||m,alt:"",className:"absolute inset-0 w-full h-full object-cover opacity-30"}),jsx("div",{className:"absolute inset-0 bg-gradient-to-t from-background via-background/50 to-transparent"})]}):null},Y=()=>{if(!r||b$1==="none")return null;switch(b$1){case "dots":return jsx("div",{className:"flex gap-2 justify-center mt-8",children:a.map((s,l)=>jsx("button",{onClick:()=>E(l),className:b("w-2.5 h-2.5 rounded-full transition-all duration-300",g===l?"bg-primary w-8":"bg-muted-foreground/30 hover:bg-muted-foreground/50"),"aria-label":`Go to slide ${l+1}`},l))});case "line":return jsx("div",{className:"flex gap-1 justify-center mt-8 max-w-xs mx-auto",children:a.map((s,l)=>jsx("button",{onClick:()=>E(l),className:"flex-1 h-1 rounded-full overflow-hidden bg-muted-foreground/20","aria-label":`Go to slide ${l+1}`,children:jsx("div",{className:b("h-full bg-primary transition-all duration-300",g===l?"w-full":"w-0")})},l))});case "numbers":return jsxs("div",{className:"flex items-center justify-center gap-2 mt-8 text-sm text-muted-foreground",children:[jsx("span",{className:"text-foreground font-semibold",children:g+1}),jsx("span",{children:"/"}),jsx("span",{children:c})]});default:return null}};return jsxs("section",{ref:B,className:b("relative w-full flex flex-col justify-center items-center text-center px-4 sm:px-6 lg:px-8 overflow-hidden",_[n],k&&"-mt-16 pt-16",p),onMouseEnter:()=>N&&L(true),onMouseLeave:()=>N&&L(false),...T,children:[O[D],jsxs("div",{className:"relative z-10 max-w-4xl mx-auto",children:[jsxs("div",{className:"animate-in fade-in slide-in-from-bottom-4 duration-500",children:[jsxs("h1",{className:b("font-extrabold mb-4 sm:mb-6 text-foreground",F[n]),children:[jsx("span",{className:"block gradient-text",children:t.title}),t.subtitle&&jsx("span",{className:b("block font-semibold mt-2 sm:mt-4 text-muted-foreground",G[n]),children:t.subtitle})]}),jsx("div",{className:b("text-muted-foreground mb-6 sm:mb-8 md:mb-10 max-w-2xl mx-auto",W[n]),children:t.description.split(`
|
|
3
|
+
`).map((s,l,q)=>jsxs(I.Fragment,{children:[s,l<q.length-1&&jsx("br",{})]},l))}),(t.primaryAction||t.secondaryAction)&&jsxs("div",{className:"flex flex-col sm:flex-row gap-4 justify-center",children:[t.primaryAction&&jsxs(d,{href:t.primaryAction.href,size:n==="xl"||n==="full"?"lg":"md",hover:"scale",className:"inline-flex items-center gap-2",children:[t.primaryAction.icon,t.primaryAction.label]}),t.secondaryAction&&jsxs(d,{href:t.secondaryAction.href,variant:"outline",size:n==="xl"||n==="full"?"lg":"md",hover:"scale",className:"inline-flex items-center gap-2",children:[t.secondaryAction.icon,t.secondaryAction.label]})]})]},r?g:0),Y()]}),r&&f&&c>1&&jsxs(Fragment,{children:[jsx("button",{onClick:$,className:"absolute left-4 top-1/2 -translate-y-1/2 z-20 p-2 rounded-full bg-card/80 backdrop-blur-sm border border-border text-foreground hover:bg-card transition-colors","aria-label":"Previous slide",children:jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M15 19l-7-7 7-7"})})}),jsx("button",{onClick:C,className:"absolute right-4 top-1/2 -translate-y-1/2 z-20 p-2 rounded-full bg-card/80 backdrop-blur-sm border border-border text-foreground hover:bg-card transition-colors","aria-label":"Next slide",children:jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5l7 7-7 7"})})})]})]})});X.displayName="HeroSection";export{U as a,X as b};//# sourceMappingURL=chunk-AOSXB5JJ.mjs.map
|
|
4
|
+
//# sourceMappingURL=chunk-AOSXB5JJ.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ScrollProgress.tsx","../src/components/HeroSection.tsx"],"names":["ScrollProgress","React","className","height","color","position","_animated","showPercentage","props","ref","progress","setProgress","useState","useEffect","updateProgress","scrollTop","docHeight","currentProgress","progressColors","jsxs","merge","jsx","HeroSection","title","subtitle","description","primaryAction","secondaryAction","slides","autoPlay","interval","indicator","showControls","pauseOnHover","background","customBackground","size","fullBleed","currentSlide","setCurrentSlide","isPaused","setIsPaused","isSlideMode","slideCount","nextSlide","useCallback","prev","prevSlide","goToSlide","index","timer","currentContent","sizeClasses","titleSizeClasses","subtitleSizeClasses","descriptionSizeClasses","currentBg","backgroundContent","renderIndicator","_","line","i","arr","Button","Fragment"],"mappings":"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","file":"chunk-GLZKT7JN.mjs","sourcesContent":["'use client'\n\nimport React, { useState, useEffect } from 'react'\nimport { merge } from '../lib/utils'\n\n/**\n * ScrollProgress 컴포넌트의 props / ScrollProgress component props\n * @typedef {Object} ScrollProgressProps\n * @property {string} [className] - 추가 CSS 클래스 / Additional CSS class\n * @property {number} [height=2] - 진행률 바 높이 (px) / Progress bar height (px)\n * @property {'default' | 'primary' | 'secondary' | 'gradient'} [color='gradient'] - 진행률 바 색상 / Progress bar color\n * @property {'top' | 'bottom'} [position='top'] - 표시 위치 / Display position\n * @property {boolean} [animated=true] - 애니메이션 활성화 여부 / Enable animation\n * @property {boolean} [showPercentage=false] - 퍼센트 표시 여부 / Show percentage\n */\nexport interface ScrollProgressProps {\n className?: string\n height?: number\n color?: 'default' | 'primary' | 'secondary' | 'gradient'\n position?: 'top' | 'bottom'\n animated?: boolean\n showPercentage?: boolean\n}\n\n/**\n * ScrollProgress 컴포넌트 / ScrollProgress component\n * \n * 페이지 스크롤 진행률을 표시하는 컴포넌트입니다.\n * 페이지 상단 또는 하단에 고정되어 표시됩니다.\n * \n * Component that displays page scroll progress.\n * Fixed at top or bottom of the page.\n * \n * @component\n * @example\n * // 기본 사용 / Basic usage\n * <ScrollProgress />\n * \n * @example\n * // 하단에 표시, 퍼센트 포함 / Display at bottom with percentage\n * <ScrollProgress \n * position=\"bottom\"\n * color=\"primary\"\n * showPercentage\n * height={4}\n * />\n * \n * @param {ScrollProgressProps} props - ScrollProgress 컴포넌트의 props / ScrollProgress component props\n * @param {React.Ref<HTMLDivElement>} ref - div 요소 ref / div element ref\n * @returns {JSX.Element} ScrollProgress 컴포넌트 / ScrollProgress component\n */\nconst ScrollProgress = React.forwardRef<HTMLDivElement, ScrollProgressProps>(({\n className,\n height = 2,\n color = 'gradient',\n position = 'top',\n animated: _animated = true,\n showPercentage = false,\n ...props\n}, ref) => {\n const [progress, setProgress] = useState(0)\n\n useEffect(() => {\n const updateProgress = () => {\n const scrollTop = window.scrollY\n const docHeight = document.documentElement.scrollHeight - window.innerHeight\n const currentProgress = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0\n setProgress(currentProgress)\n }\n\n // 초기 실행\n updateProgress()\n\n window.addEventListener('scroll', updateProgress, { passive: true })\n window.addEventListener('resize', updateProgress, { passive: true })\n \n return () => {\n window.removeEventListener('scroll', updateProgress)\n window.removeEventListener('resize', updateProgress)\n }\n }, [])\n\n // 색상 옵션 (Teal 브랜드 기반)\n const progressColors: Record<string, string> = {\n default: 'bg-foreground',\n primary: 'bg-primary',\n secondary: 'bg-muted-foreground',\n gradient: 'bg-gradient-to-r from-teal-600 via-cyan-500 to-teal-600'\n }\n\n const positionClasses = {\n top: 'top-0 left-0 right-0',\n bottom: 'bottom-0 left-0 right-0'\n }\n\n return (\n <div\n ref={ref}\n className={merge(\n 'fixed z-50',\n positionClasses[position],\n className\n )}\n style={{ height: `${height}px` }}\n {...props}\n >\n {/* 배경 바 */}\n <div className=\"absolute inset-0 w-full h-full bg-border/30\" />\n \n {/* 진행률 바 - absolute로 배경 위에 표시 */}\n <div\n className={merge(\n 'absolute top-0 left-0 h-full origin-left transition-all duration-100 ease-out',\n progressColors[color] || progressColors.gradient\n )}\n style={{\n width: `${progress}%`,\n transformOrigin: 'left'\n }}\n />\n \n {/* 퍼센트 표시 (선택사항) */}\n {showPercentage && (\n <div className=\"absolute top-2 right-2 text-xs text-muted-foreground bg-card px-2 py-1 rounded border border-border\">\n {Math.round(progress)}%\n </div>\n )}\n </div>\n )\n})\n\nScrollProgress.displayName = 'ScrollProgress'\n\nexport { ScrollProgress } ","\"use client\"\n\nimport React, { useState, useEffect, useCallback } from \"react\"\nimport { merge } from \"../lib/utils\"\nimport { Button } from \"./Button\"\n\n/**\n * 슬라이드 아이템 인터페이스\n */\nexport interface HeroSlide {\n title: string\n subtitle?: string\n description: string\n primaryAction?: {\n label: string\n href: string\n icon?: React.ReactNode\n }\n secondaryAction?: {\n label: string\n href: string\n icon?: React.ReactNode\n }\n background?: \"none\" | \"gradient\" | \"particles\" | \"image\"\n backgroundImage?: string\n}\n\n/**\n * HeroSection 컴포넌트의 props\n */\nexport interface HeroSectionProps extends React.HTMLAttributes<HTMLElement> {\n // 단일 모드 props\n title?: string\n subtitle?: string\n description?: string\n primaryAction?: {\n label: string\n href: string\n icon?: React.ReactNode\n }\n secondaryAction?: {\n label: string\n href: string\n icon?: React.ReactNode\n }\n // 슬라이드 모드 props\n slides?: HeroSlide[]\n autoPlay?: boolean\n interval?: number\n indicator?: \"dots\" | \"line\" | \"numbers\" | \"none\"\n showControls?: boolean\n pauseOnHover?: boolean\n // 공통 props\n background?: \"none\" | \"gradient\" | \"particles\" | \"video\" | \"image\"\n customBackground?: string\n /**\n * 히어로 섹션 크기\n * - sm: 400px, md: 500px, lg: 600px, xl: 700px\n * - full: 100vh (뷰포트 전체)\n */\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\"\n /**\n * 헤더 뒤까지 확장 (fixed header가 있을 때)\n * true면 -mt-16 적용되어 헤더 뒤로 들어감\n */\n fullBleed?: boolean\n}\n\n/**\n * HeroSection 컴포넌트\n *\n * 단일 히어로 또는 슬라이드 히어로를 지원합니다.\n * slides prop이 있으면 슬라이드 모드로 동작합니다.\n */\nconst HeroSection = React.forwardRef<HTMLElement, HeroSectionProps>(\n ({\n className,\n // 단일 모드\n title,\n subtitle,\n description,\n primaryAction,\n secondaryAction,\n // 슬라이드 모드\n slides,\n autoPlay = false,\n interval = 5000,\n indicator = \"dots\",\n showControls = true,\n pauseOnHover = true,\n // 공통\n background = \"gradient\",\n customBackground,\n size = \"lg\",\n fullBleed = false,\n ...props\n }, ref) => {\n const [currentSlide, setCurrentSlide] = useState(0)\n const [isPaused, setIsPaused] = useState(false)\n\n // 슬라이드 모드 여부\n const isSlideMode = slides && slides.length > 0\n const slideCount = slides?.length || 0\n\n // 다음 슬라이드\n const nextSlide = useCallback(() => {\n if (!isSlideMode) return\n setCurrentSlide((prev) => (prev + 1) % slideCount)\n }, [isSlideMode, slideCount])\n\n // 이전 슬라이드\n const prevSlide = useCallback(() => {\n if (!isSlideMode) return\n setCurrentSlide((prev) => (prev - 1 + slideCount) % slideCount)\n }, [isSlideMode, slideCount])\n\n // 특정 슬라이드로 이동\n const goToSlide = useCallback((index: number) => {\n setCurrentSlide(index)\n }, [])\n\n // 자동 재생\n useEffect(() => {\n if (!autoPlay || !isSlideMode || isPaused) return\n\n const timer = setInterval(nextSlide, interval)\n return () => clearInterval(timer)\n }, [autoPlay, isSlideMode, isPaused, interval, nextSlide])\n\n // 현재 표시할 콘텐츠\n const currentContent = isSlideMode ? slides[currentSlide] : {\n title: title || \"\",\n subtitle,\n description: description || \"\",\n primaryAction,\n secondaryAction,\n background,\n }\n\n const sizeClasses = {\n sm: \"min-h-[400px]\",\n md: \"min-h-[500px]\",\n lg: \"min-h-[600px]\",\n xl: \"min-h-[700px]\",\n full: \"min-h-screen\"\n }\n\n const titleSizeClasses = {\n sm: \"text-2xl sm:text-3xl md:text-4xl leading-tight\",\n md: \"text-3xl sm:text-4xl md:text-5xl leading-tight\",\n lg: \"text-3xl sm:text-4xl md:text-5xl lg:text-6xl leading-tight\",\n xl: \"text-3xl sm:text-4xl md:text-5xl lg:text-6xl leading-tight\",\n full: \"text-4xl sm:text-5xl md:text-6xl lg:text-7xl leading-tight\"\n }\n\n const subtitleSizeClasses = {\n sm: \"text-base sm:text-lg md:text-xl leading-snug\",\n md: \"text-lg sm:text-xl md:text-2xl leading-snug\",\n lg: \"text-lg sm:text-xl md:text-2xl lg:text-3xl leading-snug\",\n xl: \"text-xl sm:text-2xl md:text-3xl leading-snug\",\n full: \"text-xl sm:text-2xl md:text-3xl lg:text-4xl leading-snug\"\n }\n\n const descriptionSizeClasses = {\n sm: \"text-sm sm:text-base md:text-lg leading-relaxed\",\n md: \"text-base sm:text-lg md:text-xl leading-relaxed\",\n lg: \"text-base sm:text-lg md:text-xl leading-relaxed\",\n xl: \"text-base sm:text-lg md:text-xl leading-relaxed\",\n full: \"text-lg sm:text-xl md:text-2xl leading-relaxed\"\n }\n\n const currentBg = isSlideMode ? (currentContent.background || background) : background\n\n const backgroundContent: Record<string, React.ReactNode> = {\n none: null,\n gradient: (\n <div className=\"absolute inset-0 z-0 pointer-events-none\">\n {/* 왼쪽 위 - 메인 그라데이션 */}\n <div className=\"absolute top-0 left-0 w-80 h-80 sm:w-96 sm:h-96 md:w-[500px] md:h-[500px] -translate-x-1/3 -translate-y-1/3 rounded-full bg-gradient-to-br from-teal-400 via-cyan-500 to-teal-600 opacity-40 blur-3xl\" />\n {/* 오른쪽 아래 - 보조 그라데이션 */}\n <div className=\"absolute bottom-0 right-0 w-72 h-72 sm:w-80 sm:h-80 md:w-[400px] md:h-[400px] translate-x-1/4 translate-y-1/4 rounded-full bg-gradient-to-tr from-cyan-400 via-teal-500 to-emerald-500 opacity-35 blur-3xl\" />\n {/* 중앙 액센트 */}\n <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-48 h-48 sm:w-64 sm:h-64 md:w-80 md:h-80 rounded-full bg-teal-500/20 blur-2xl\" />\n </div>\n ),\n particles: (\n <div className=\"absolute inset-0 z-0 pointer-events-none\">\n <div className=\"absolute inset-0 bg-gradient-to-br from-secondary/50 via-background to-secondary/30\" />\n </div>\n ),\n video: customBackground ? (\n <div className=\"absolute inset-0 z-0 pointer-events-none\">\n <video\n autoPlay\n loop\n muted\n playsInline\n className=\"absolute inset-0 w-full h-full object-cover opacity-20\"\n >\n <source src={customBackground} type=\"video/mp4\" />\n </video>\n </div>\n ) : null,\n image: (customBackground || (isSlideMode && (currentContent as HeroSlide).backgroundImage)) ? (\n <div className=\"absolute inset-0 z-0 pointer-events-none\">\n <img\n src={(isSlideMode && (currentContent as HeroSlide).backgroundImage) || customBackground}\n alt=\"\"\n className=\"absolute inset-0 w-full h-full object-cover opacity-30\"\n />\n <div className=\"absolute inset-0 bg-gradient-to-t from-background via-background/50 to-transparent\" />\n </div>\n ) : null,\n }\n\n // 인디케이터 렌더링\n const renderIndicator = () => {\n if (!isSlideMode || indicator === \"none\") return null\n\n switch (indicator) {\n case \"dots\":\n return (\n <div className=\"flex gap-2 justify-center mt-8\">\n {slides.map((_, index) => (\n <button\n key={index}\n onClick={() => goToSlide(index)}\n className={merge(\n \"w-2.5 h-2.5 rounded-full transition-all duration-300\",\n currentSlide === index\n ? \"bg-primary w-8\"\n : \"bg-muted-foreground/30 hover:bg-muted-foreground/50\"\n )}\n aria-label={`Go to slide ${index + 1}`}\n />\n ))}\n </div>\n )\n\n case \"line\":\n return (\n <div className=\"flex gap-1 justify-center mt-8 max-w-xs mx-auto\">\n {slides.map((_, index) => (\n <button\n key={index}\n onClick={() => goToSlide(index)}\n className=\"flex-1 h-1 rounded-full overflow-hidden bg-muted-foreground/20\"\n aria-label={`Go to slide ${index + 1}`}\n >\n <div\n className={merge(\n \"h-full bg-primary transition-all duration-300\",\n currentSlide === index ? \"w-full\" : \"w-0\"\n )}\n />\n </button>\n ))}\n </div>\n )\n\n case \"numbers\":\n return (\n <div className=\"flex items-center justify-center gap-2 mt-8 text-sm text-muted-foreground\">\n <span className=\"text-foreground font-semibold\">{currentSlide + 1}</span>\n <span>/</span>\n <span>{slideCount}</span>\n </div>\n )\n\n default:\n return null\n }\n }\n\n return (\n <section\n ref={ref}\n className={merge(\n \"relative w-full flex flex-col justify-center items-center text-center px-4 sm:px-6 lg:px-8 overflow-hidden\",\n sizeClasses[size],\n fullBleed && \"-mt-16 pt-16\",\n className\n )}\n onMouseEnter={() => pauseOnHover && setIsPaused(true)}\n onMouseLeave={() => pauseOnHover && setIsPaused(false)}\n {...props}\n >\n {backgroundContent[currentBg]}\n\n {/* 슬라이드 콘텐츠 */}\n <div className=\"relative z-10 max-w-4xl mx-auto\">\n <div\n key={isSlideMode ? currentSlide : 0}\n className=\"animate-in fade-in slide-in-from-bottom-4 duration-500\"\n >\n <h1 className={merge(\n \"font-extrabold mb-4 sm:mb-6 text-foreground\",\n titleSizeClasses[size]\n )}>\n <span className=\"block gradient-text\">\n {currentContent.title}\n </span>\n {currentContent.subtitle && (\n <span className={merge(\n \"block font-semibold mt-2 sm:mt-4 text-muted-foreground\",\n subtitleSizeClasses[size]\n )}>\n {currentContent.subtitle}\n </span>\n )}\n </h1>\n\n <div className={merge(\n \"text-muted-foreground mb-6 sm:mb-8 md:mb-10 max-w-2xl mx-auto\",\n descriptionSizeClasses[size]\n )}>\n {currentContent.description.split('\\n').map((line, i, arr) => (\n <React.Fragment key={i}>\n {line}\n {i < arr.length - 1 && <br />}\n </React.Fragment>\n ))}\n </div>\n\n {(currentContent.primaryAction || currentContent.secondaryAction) && (\n <div className=\"flex flex-col sm:flex-row gap-4 justify-center\">\n {currentContent.primaryAction && (\n <Button\n href={currentContent.primaryAction.href}\n size={size === \"xl\" || size === \"full\" ? \"lg\" : \"md\"}\n hover=\"scale\"\n className=\"inline-flex items-center gap-2\"\n >\n {currentContent.primaryAction.icon}\n {currentContent.primaryAction.label}\n </Button>\n )}\n\n {currentContent.secondaryAction && (\n <Button\n href={currentContent.secondaryAction.href}\n variant=\"outline\"\n size={size === \"xl\" || size === \"full\" ? \"lg\" : \"md\"}\n hover=\"scale\"\n className=\"inline-flex items-center gap-2\"\n >\n {currentContent.secondaryAction.icon}\n {currentContent.secondaryAction.label}\n </Button>\n )}\n </div>\n )}\n </div>\n\n {/* 인디케이터 */}\n {renderIndicator()}\n </div>\n\n {/* 좌우 컨트롤 */}\n {isSlideMode && showControls && slideCount > 1 && (\n <>\n <button\n onClick={prevSlide}\n className=\"absolute left-4 top-1/2 -translate-y-1/2 z-20 p-2 rounded-full bg-card/80 backdrop-blur-sm border border-border text-foreground hover:bg-card transition-colors\"\n aria-label=\"Previous slide\"\n >\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n <button\n onClick={nextSlide}\n className=\"absolute right-4 top-1/2 -translate-y-1/2 z-20 p-2 rounded-full bg-card/80 backdrop-blur-sm border border-border text-foreground hover:bg-card transition-colors\"\n aria-label=\"Next slide\"\n >\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </>\n )}\n </section>\n )\n }\n)\n\nHeroSection.displayName = \"HeroSection\"\n\nexport { HeroSection }\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/ScrollProgress.tsx","../src/components/HeroSection.tsx"],"names":["ScrollProgress","React","className","height","color","position","_animated","showPercentage","props","ref","progress","setProgress","useState","useEffect","updateProgress","scrollTop","docHeight","currentProgress","progressColors","jsxs","merge","jsx","HeroSection","title","subtitle","description","primaryAction","secondaryAction","slides","autoPlay","interval","indicator","showControls","pauseOnHover","background","customBackground","size","fullBleed","currentSlide","setCurrentSlide","isPaused","setIsPaused","isSlideMode","slideCount","nextSlide","useCallback","prev","prevSlide","goToSlide","index","timer","currentContent","sizeClasses","titleSizeClasses","subtitleSizeClasses","descriptionSizeClasses","currentBg","backgroundContent","renderIndicator","_","line","i","arr","Button","Fragment"],"mappings":"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","file":"chunk-AOSXB5JJ.mjs","sourcesContent":["'use client'\n\nimport React, { useState, useEffect } from 'react'\nimport { merge } from '../lib/utils'\n\n/**\n * ScrollProgress 컴포넌트의 props / ScrollProgress component props\n * @typedef {Object} ScrollProgressProps\n * @property {string} [className] - 추가 CSS 클래스 / Additional CSS class\n * @property {number} [height=2] - 진행률 바 높이 (px) / Progress bar height (px)\n * @property {'default' | 'primary' | 'secondary' | 'gradient'} [color='gradient'] - 진행률 바 색상 / Progress bar color\n * @property {'top' | 'bottom'} [position='top'] - 표시 위치 / Display position\n * @property {boolean} [animated=true] - 애니메이션 활성화 여부 / Enable animation\n * @property {boolean} [showPercentage=false] - 퍼센트 표시 여부 / Show percentage\n */\nexport interface ScrollProgressProps {\n className?: string\n height?: number\n color?: 'default' | 'primary' | 'secondary' | 'gradient'\n position?: 'top' | 'bottom'\n animated?: boolean\n showPercentage?: boolean\n}\n\n/**\n * ScrollProgress 컴포넌트 / ScrollProgress component\n * \n * 페이지 스크롤 진행률을 표시하는 컴포넌트입니다.\n * 페이지 상단 또는 하단에 고정되어 표시됩니다.\n * \n * Component that displays page scroll progress.\n * Fixed at top or bottom of the page.\n * \n * @component\n * @example\n * // 기본 사용 / Basic usage\n * <ScrollProgress />\n * \n * @example\n * // 하단에 표시, 퍼센트 포함 / Display at bottom with percentage\n * <ScrollProgress \n * position=\"bottom\"\n * color=\"primary\"\n * showPercentage\n * height={4}\n * />\n * \n * @param {ScrollProgressProps} props - ScrollProgress 컴포넌트의 props / ScrollProgress component props\n * @param {React.Ref<HTMLDivElement>} ref - div 요소 ref / div element ref\n * @returns {JSX.Element} ScrollProgress 컴포넌트 / ScrollProgress component\n */\nconst ScrollProgress = React.forwardRef<HTMLDivElement, ScrollProgressProps>(({\n className,\n height = 2,\n color = 'gradient',\n position = 'top',\n animated: _animated = true,\n showPercentage = false,\n ...props\n}, ref) => {\n const [progress, setProgress] = useState(0)\n\n useEffect(() => {\n const updateProgress = () => {\n const scrollTop = window.scrollY\n const docHeight = document.documentElement.scrollHeight - window.innerHeight\n const currentProgress = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0\n setProgress(currentProgress)\n }\n\n // 초기 실행\n updateProgress()\n\n window.addEventListener('scroll', updateProgress, { passive: true })\n window.addEventListener('resize', updateProgress, { passive: true })\n \n return () => {\n window.removeEventListener('scroll', updateProgress)\n window.removeEventListener('resize', updateProgress)\n }\n }, [])\n\n // 색상 옵션 (Teal 브랜드 기반)\n const progressColors: Record<string, string> = {\n default: 'bg-foreground',\n primary: 'bg-primary',\n secondary: 'bg-muted-foreground',\n gradient: 'bg-gradient-to-r from-teal-600 via-cyan-500 to-teal-600'\n }\n\n const positionClasses = {\n top: 'top-0 left-0 right-0',\n bottom: 'bottom-0 left-0 right-0'\n }\n\n return (\n <div\n ref={ref}\n className={merge(\n 'fixed z-50',\n positionClasses[position],\n className\n )}\n style={{ height: `${height}px` }}\n {...props}\n >\n {/* 배경 바 */}\n <div className=\"absolute inset-0 w-full h-full bg-border/30\" />\n \n {/* 진행률 바 - absolute로 배경 위에 표시 */}\n <div\n className={merge(\n 'absolute top-0 left-0 h-full origin-left transition-all duration-100 ease-out',\n progressColors[color] || progressColors.gradient\n )}\n style={{\n width: `${progress}%`,\n transformOrigin: 'left'\n }}\n />\n \n {/* 퍼센트 표시 (선택사항) */}\n {showPercentage && (\n <div className=\"absolute top-2 right-2 text-xs text-muted-foreground bg-card px-2 py-1 rounded border border-border\">\n {Math.round(progress)}%\n </div>\n )}\n </div>\n )\n})\n\nScrollProgress.displayName = 'ScrollProgress'\n\nexport { ScrollProgress } ","\"use client\"\n\nimport React, { useState, useEffect, useCallback } from \"react\"\nimport { merge } from \"../lib/utils\"\nimport { Button } from \"./Button\"\n\n/**\n * 슬라이드 아이템 인터페이스\n */\nexport interface HeroSlide {\n title: string\n subtitle?: string\n description: string\n primaryAction?: {\n label: string\n href: string\n icon?: React.ReactNode\n }\n secondaryAction?: {\n label: string\n href: string\n icon?: React.ReactNode\n }\n background?: \"none\" | \"gradient\" | \"particles\" | \"image\"\n backgroundImage?: string\n}\n\n/**\n * HeroSection 컴포넌트의 props\n */\nexport interface HeroSectionProps extends React.HTMLAttributes<HTMLElement> {\n // 단일 모드 props\n title?: string\n subtitle?: string\n description?: string\n primaryAction?: {\n label: string\n href: string\n icon?: React.ReactNode\n }\n secondaryAction?: {\n label: string\n href: string\n icon?: React.ReactNode\n }\n // 슬라이드 모드 props\n slides?: HeroSlide[]\n autoPlay?: boolean\n interval?: number\n indicator?: \"dots\" | \"line\" | \"numbers\" | \"none\"\n showControls?: boolean\n pauseOnHover?: boolean\n // 공통 props\n background?: \"none\" | \"gradient\" | \"particles\" | \"video\" | \"image\"\n customBackground?: string\n /**\n * 히어로 섹션 크기\n * - sm: 400px, md: 500px, lg: 600px, xl: 700px\n * - full: 100vh (뷰포트 전체)\n */\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\"\n /**\n * 헤더 뒤까지 확장 (fixed header가 있을 때)\n * true면 -mt-16 적용되어 헤더 뒤로 들어감\n */\n fullBleed?: boolean\n}\n\n/**\n * HeroSection 컴포넌트\n *\n * 단일 히어로 또는 슬라이드 히어로를 지원합니다.\n * slides prop이 있으면 슬라이드 모드로 동작합니다.\n */\nconst HeroSection = React.forwardRef<HTMLElement, HeroSectionProps>(\n ({\n className,\n // 단일 모드\n title,\n subtitle,\n description,\n primaryAction,\n secondaryAction,\n // 슬라이드 모드\n slides,\n autoPlay = false,\n interval = 5000,\n indicator = \"dots\",\n showControls = true,\n pauseOnHover = true,\n // 공통\n background = \"gradient\",\n customBackground,\n size = \"lg\",\n fullBleed = false,\n ...props\n }, ref) => {\n const [currentSlide, setCurrentSlide] = useState(0)\n const [isPaused, setIsPaused] = useState(false)\n\n // 슬라이드 모드 여부\n const isSlideMode = slides && slides.length > 0\n const slideCount = slides?.length || 0\n\n // 다음 슬라이드\n const nextSlide = useCallback(() => {\n if (!isSlideMode) return\n setCurrentSlide((prev) => (prev + 1) % slideCount)\n }, [isSlideMode, slideCount])\n\n // 이전 슬라이드\n const prevSlide = useCallback(() => {\n if (!isSlideMode) return\n setCurrentSlide((prev) => (prev - 1 + slideCount) % slideCount)\n }, [isSlideMode, slideCount])\n\n // 특정 슬라이드로 이동\n const goToSlide = useCallback((index: number) => {\n setCurrentSlide(index)\n }, [])\n\n // 자동 재생\n useEffect(() => {\n if (!autoPlay || !isSlideMode || isPaused) return\n\n const timer = setInterval(nextSlide, interval)\n return () => clearInterval(timer)\n }, [autoPlay, isSlideMode, isPaused, interval, nextSlide])\n\n // 현재 표시할 콘텐츠\n const currentContent = isSlideMode ? slides[currentSlide] : {\n title: title || \"\",\n subtitle,\n description: description || \"\",\n primaryAction,\n secondaryAction,\n background,\n }\n\n const sizeClasses = {\n sm: \"min-h-[400px]\",\n md: \"min-h-[500px]\",\n lg: \"min-h-[600px]\",\n xl: \"min-h-[700px]\",\n full: \"min-h-screen\"\n }\n\n const titleSizeClasses = {\n sm: \"text-2xl sm:text-3xl md:text-4xl leading-tight\",\n md: \"text-3xl sm:text-4xl md:text-5xl leading-tight\",\n lg: \"text-3xl sm:text-4xl md:text-5xl lg:text-6xl leading-tight\",\n xl: \"text-3xl sm:text-4xl md:text-5xl lg:text-6xl leading-tight\",\n full: \"text-4xl sm:text-5xl md:text-6xl lg:text-7xl leading-tight\"\n }\n\n const subtitleSizeClasses = {\n sm: \"text-base sm:text-lg md:text-xl leading-snug\",\n md: \"text-lg sm:text-xl md:text-2xl leading-snug\",\n lg: \"text-lg sm:text-xl md:text-2xl lg:text-3xl leading-snug\",\n xl: \"text-xl sm:text-2xl md:text-3xl leading-snug\",\n full: \"text-xl sm:text-2xl md:text-3xl lg:text-4xl leading-snug\"\n }\n\n const descriptionSizeClasses = {\n sm: \"text-sm sm:text-base md:text-lg leading-relaxed\",\n md: \"text-base sm:text-lg md:text-xl leading-relaxed\",\n lg: \"text-base sm:text-lg md:text-xl leading-relaxed\",\n xl: \"text-base sm:text-lg md:text-xl leading-relaxed\",\n full: \"text-lg sm:text-xl md:text-2xl leading-relaxed\"\n }\n\n const currentBg = isSlideMode ? (currentContent.background || background) : background\n\n const backgroundContent: Record<string, React.ReactNode> = {\n none: null,\n gradient: (\n <div className=\"absolute inset-0 z-0 pointer-events-none\">\n {/* 왼쪽 위 - 메인 그라데이션 */}\n <div className=\"absolute top-0 left-0 w-80 h-80 sm:w-96 sm:h-96 md:w-[500px] md:h-[500px] -translate-x-1/3 -translate-y-1/3 rounded-full bg-gradient-to-br from-teal-400 via-cyan-500 to-teal-600 opacity-40 blur-3xl\" />\n {/* 오른쪽 아래 - 보조 그라데이션 */}\n <div className=\"absolute bottom-0 right-0 w-72 h-72 sm:w-80 sm:h-80 md:w-[400px] md:h-[400px] translate-x-1/4 translate-y-1/4 rounded-full bg-gradient-to-tr from-cyan-400 via-teal-500 to-emerald-500 opacity-35 blur-3xl\" />\n {/* 중앙 액센트 */}\n <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-48 h-48 sm:w-64 sm:h-64 md:w-80 md:h-80 rounded-full bg-teal-500/20 blur-2xl\" />\n </div>\n ),\n particles: (\n <div className=\"absolute inset-0 z-0 pointer-events-none\">\n <div className=\"absolute inset-0 bg-gradient-to-br from-secondary/50 via-background to-secondary/30\" />\n </div>\n ),\n video: customBackground ? (\n <div className=\"absolute inset-0 z-0 pointer-events-none\">\n <video\n autoPlay\n loop\n muted\n playsInline\n className=\"absolute inset-0 w-full h-full object-cover opacity-20\"\n >\n <source src={customBackground} type=\"video/mp4\" />\n </video>\n </div>\n ) : null,\n image: (customBackground || (isSlideMode && (currentContent as HeroSlide).backgroundImage)) ? (\n <div className=\"absolute inset-0 z-0 pointer-events-none\">\n <img\n src={(isSlideMode && (currentContent as HeroSlide).backgroundImage) || customBackground}\n alt=\"\"\n className=\"absolute inset-0 w-full h-full object-cover opacity-30\"\n />\n <div className=\"absolute inset-0 bg-gradient-to-t from-background via-background/50 to-transparent\" />\n </div>\n ) : null,\n }\n\n // 인디케이터 렌더링\n const renderIndicator = () => {\n if (!isSlideMode || indicator === \"none\") return null\n\n switch (indicator) {\n case \"dots\":\n return (\n <div className=\"flex gap-2 justify-center mt-8\">\n {slides.map((_, index) => (\n <button\n key={index}\n onClick={() => goToSlide(index)}\n className={merge(\n \"w-2.5 h-2.5 rounded-full transition-all duration-300\",\n currentSlide === index\n ? \"bg-primary w-8\"\n : \"bg-muted-foreground/30 hover:bg-muted-foreground/50\"\n )}\n aria-label={`Go to slide ${index + 1}`}\n />\n ))}\n </div>\n )\n\n case \"line\":\n return (\n <div className=\"flex gap-1 justify-center mt-8 max-w-xs mx-auto\">\n {slides.map((_, index) => (\n <button\n key={index}\n onClick={() => goToSlide(index)}\n className=\"flex-1 h-1 rounded-full overflow-hidden bg-muted-foreground/20\"\n aria-label={`Go to slide ${index + 1}`}\n >\n <div\n className={merge(\n \"h-full bg-primary transition-all duration-300\",\n currentSlide === index ? \"w-full\" : \"w-0\"\n )}\n />\n </button>\n ))}\n </div>\n )\n\n case \"numbers\":\n return (\n <div className=\"flex items-center justify-center gap-2 mt-8 text-sm text-muted-foreground\">\n <span className=\"text-foreground font-semibold\">{currentSlide + 1}</span>\n <span>/</span>\n <span>{slideCount}</span>\n </div>\n )\n\n default:\n return null\n }\n }\n\n return (\n <section\n ref={ref}\n className={merge(\n \"relative w-full flex flex-col justify-center items-center text-center px-4 sm:px-6 lg:px-8 overflow-hidden\",\n sizeClasses[size],\n fullBleed && \"-mt-16 pt-16\",\n className\n )}\n onMouseEnter={() => pauseOnHover && setIsPaused(true)}\n onMouseLeave={() => pauseOnHover && setIsPaused(false)}\n {...props}\n >\n {backgroundContent[currentBg]}\n\n {/* 슬라이드 콘텐츠 */}\n <div className=\"relative z-10 max-w-4xl mx-auto\">\n <div\n key={isSlideMode ? currentSlide : 0}\n className=\"animate-in fade-in slide-in-from-bottom-4 duration-500\"\n >\n <h1 className={merge(\n \"font-extrabold mb-4 sm:mb-6 text-foreground\",\n titleSizeClasses[size]\n )}>\n <span className=\"block gradient-text\">\n {currentContent.title}\n </span>\n {currentContent.subtitle && (\n <span className={merge(\n \"block font-semibold mt-2 sm:mt-4 text-muted-foreground\",\n subtitleSizeClasses[size]\n )}>\n {currentContent.subtitle}\n </span>\n )}\n </h1>\n\n <div className={merge(\n \"text-muted-foreground mb-6 sm:mb-8 md:mb-10 max-w-2xl mx-auto\",\n descriptionSizeClasses[size]\n )}>\n {currentContent.description.split('\\n').map((line, i, arr) => (\n <React.Fragment key={i}>\n {line}\n {i < arr.length - 1 && <br />}\n </React.Fragment>\n ))}\n </div>\n\n {(currentContent.primaryAction || currentContent.secondaryAction) && (\n <div className=\"flex flex-col sm:flex-row gap-4 justify-center\">\n {currentContent.primaryAction && (\n <Button\n href={currentContent.primaryAction.href}\n size={size === \"xl\" || size === \"full\" ? \"lg\" : \"md\"}\n hover=\"scale\"\n className=\"inline-flex items-center gap-2\"\n >\n {currentContent.primaryAction.icon}\n {currentContent.primaryAction.label}\n </Button>\n )}\n\n {currentContent.secondaryAction && (\n <Button\n href={currentContent.secondaryAction.href}\n variant=\"outline\"\n size={size === \"xl\" || size === \"full\" ? \"lg\" : \"md\"}\n hover=\"scale\"\n className=\"inline-flex items-center gap-2\"\n >\n {currentContent.secondaryAction.icon}\n {currentContent.secondaryAction.label}\n </Button>\n )}\n </div>\n )}\n </div>\n\n {/* 인디케이터 */}\n {renderIndicator()}\n </div>\n\n {/* 좌우 컨트롤 */}\n {isSlideMode && showControls && slideCount > 1 && (\n <>\n <button\n onClick={prevSlide}\n className=\"absolute left-4 top-1/2 -translate-y-1/2 z-20 p-2 rounded-full bg-card/80 backdrop-blur-sm border border-border text-foreground hover:bg-card transition-colors\"\n aria-label=\"Previous slide\"\n >\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n <button\n onClick={nextSlide}\n className=\"absolute right-4 top-1/2 -translate-y-1/2 z-20 p-2 rounded-full bg-card/80 backdrop-blur-sm border border-border text-foreground hover:bg-card transition-colors\"\n aria-label=\"Next slide\"\n >\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </>\n )}\n </section>\n )\n }\n)\n\nHeroSection.displayName = \"HeroSection\"\n\nexport { HeroSection }\n"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {b}from'./chunk-U6CTBZ2U.mjs';import o from'react';import {createPortal}from'react-dom';import {jsxs,jsx}from'react/jsx-runtime';function W(...a){return o.useCallback(n=>{a.forEach(t=>{t&&(typeof t=="function"?t(n):t.current=n);});},a)}var j=o.forwardRef(({className:a,isOpen:n,onClose:t,children:v,size:g="md",closable:l,closeOnOverlayClick:p=true,title:r,description:u,showBackdrop:x=true,backdropClassName:h,centered:y=true},k)=>{let m=l!=null?l:true,w=o.useRef(null),N=W(k,w);o.useEffect(()=>{let i=c=>{c.key==="Escape"&&t();};if(n){document.addEventListener("keydown",i);let c=window.innerWidth-document.documentElement.clientWidth;document.body.style.overflow="hidden",document.body.style.paddingRight=`${c}px`;}return ()=>{document.removeEventListener("keydown",i),document.body.style.overflow="unset",document.body.style.paddingRight="unset";}},[n,t]);let R=i=>{p&&i.target===i.currentTarget&&t();},M={sm:"max-w-xs",md:"max-w-sm",lg:"max-w-md",xl:"max-w-lg","2xl":"max-w-xl","3xl":"max-w-2xl"},E=o.useId(),C=o.useId(),f=r?`modal-title-${E}`:void 0,b$1=u?`modal-description-${C}`:void 0,[L,T]=o.useState(false);if(o.useEffect(()=>{T(true);},[]),!n)return null;let z=jsxs("div",{className:b("fixed inset-0 z-50 overflow-y-auto",a),onClick:R,role:"dialog","aria-modal":"true","aria-labelledby":f,"aria-describedby":b$1,children:[x&&jsx("div",{className:b("fixed inset-0 bg-black/85 backdrop-blur-md transition-opacity duration-300 pointer-events-none",h)}),jsx("div",{className:b("flex h-full justify-center p-4",y?"items-center":"items-start pt-16"),children:jsxs("div",{ref:N,className:b("relative bg-[var(--modal-bg)] rounded-lg shadow-2xl border border-[var(--modal-border)] transform transition-all duration-300 ease-out",M[g]),style:{animation:"modalSlideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1)"},children:[r&&jsxs("div",{className:"relative z-10 px-6 pt-6 pb-4 border-b border-border/50",children:[jsxs("div",{className:"flex items-center justify-between gap-4 mb-2",children:[jsx("h2",{id:f,className:"text-xl font-semibold text-foreground flex-1 min-w-0",children:r}),m&&jsx("button",{onClick:t,className:"flex-shrink-0 p-2 text-muted-foreground hover:text-foreground transition-all duration-200 rounded-full hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 z-20","aria-label":"\uB2EB\uAE30",children:jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]}),u&&jsx("p",{id:b$1,className:"text-sm text-muted-foreground",children:u})]}),!r&&m&&jsx("button",{onClick:t,className:"absolute top-4 right-4 p-2 text-muted-foreground hover:text-foreground transition-all duration-200 rounded-full hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 z-20","aria-label":"\uB2EB\uAE30",children:jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})}),jsx("div",{className:`relative z-10 ${r?"px-6 mb-6":"p-6"}`,children:v})]})})]});return L&&typeof document!="undefined"?createPortal(z,document.body):null});j.displayName="Modal";export{j as a};//# sourceMappingURL=chunk-B544MRF7.mjs.map
|
|
3
|
+
//# sourceMappingURL=chunk-B544MRF7.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/Modal.tsx"],"names":["useCombinedRefs","refs","React","node","ref","Modal","className","isOpen","onClose","children","size","closable","closeOnOverlayClick","title","description","showBackdrop","backdropClassName","centered","_closable","modalRef","combinedRef","handleEscape","e","scrollbarWidth","handleOverlayClick","sizeClasses","generatedTitleId","generatedDescId","titleId","descriptionId","mounted","setMounted","modalContent","jsxs","merge","jsx","createPortal"],"mappings":"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","file":"chunk-SDFHJ4GB.mjs","sourcesContent":["\"use client\"\n\nimport React from \"react\"\nimport { createPortal } from \"react-dom\"\nimport { merge } from \"../lib/utils\"\n\n/**\n * Modal 컴포넌트의 props / Modal component props\n * @typedef {Object} ModalProps\n * @property {boolean} isOpen - 모달 열림/닫힘 상태 / Modal open/close state\n * @property {() => void} onClose - 모달 닫기 콜백 함수 / Modal close callback function\n * @property {React.ReactNode} children - 모달 내용 / Modal content\n * @property {\"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\"} [size=\"md\"] - 모달 크기 / Modal size\n * @property {boolean} [closable=true] - 닫기 버튼 표시 여부 / Show close button\n * @property {boolean} [closeOnOverlayClick=true] - 오버레이 클릭 시 닫기 여부 / Close on overlay click\n * @property {string} [title] - 모달 제목 / Modal title\n * @property {string} [description] - 모달 설명 / Modal description\n * @property {boolean} [showBackdrop=true] - 배경 오버레이 표시 여부 / Show backdrop overlay\n * @property {string} [backdropClassName] - 배경 오버레이 추가 CSS 클래스 / Additional CSS class for backdrop\n * @property {boolean} [centered=true] - 모달을 화면 중앙에 배치할지 여부 / Center modal on screen\n * @property {string} [className] - 모달 컨테이너 추가 CSS 클래스 / Additional CSS class for modal container\n */\nexport interface ModalProps {\n /** 모달 열림/닫힘 상태 / Modal open/close state */\n isOpen: boolean\n /** 모달 닫기 콜백 함수 / Modal close callback function */\n onClose: () => void\n /** 모달 내용 / Modal content */\n children: React.ReactNode\n /** 모달 크기 / Modal size */\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\"\n /** 닫기 버튼 표시 여부 / Show close button */\n closable?: boolean\n /** 오버레이 클릭 시 닫기 여부 / Close on overlay click */\n closeOnOverlayClick?: boolean\n /** 모달 제목 / Modal title */\n title?: string\n /** 모달 설명 / Modal description */\n description?: string\n /** 배경 오버레이 표시 여부 / Show backdrop overlay */\n showBackdrop?: boolean\n /** 배경 오버레이 추가 CSS 클래스 / Additional CSS class for backdrop */\n backdropClassName?: string\n /** 모달을 화면 중앙에 배치할지 여부 / Center modal on screen */\n centered?: boolean\n /** 모달 컨테이너 추가 CSS 클래스 / Additional CSS class for modal container */\n className?: string\n}\n\n// Ref 병합 유틸리티\nfunction useCombinedRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> {\n return React.useCallback(\n (node: T) => {\n refs.forEach((ref) => {\n if (!ref) return;\n if (typeof ref === \"function\") {\n ref(node);\n } else {\n (ref as React.MutableRefObject<T | null>).current = node;\n }\n });\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n refs\n );\n}\n\n/**\n * Modal 컴포넌트 / Modal component\n * \n * 오버레이와 함께 표시되는 모달 다이얼로그 컴포넌트입니다.\n * ESC 키로 닫기, 오버레이 클릭으로 닫기, 접근성 속성(ARIA)을 지원합니다.\n * \n * Modal dialog component displayed with overlay.\n * Supports closing with ESC key, overlay click, and ARIA accessibility attributes.\n * \n * @component\n * @example\n * // 기본 사용 / Basic usage\n * const [isOpen, setIsOpen] = useState(false)\n * \n * <Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>\n * <p>모달 내용</p>\n * </Modal>\n * \n * @example\n * // 제목과 설명 포함 / With title and description\n * <Modal\n * isOpen={isOpen}\n * onClose={() => setIsOpen(false)}\n * title=\"확인\"\n * description=\"이 작업을 계속하시겠습니까?\"\n * >\n * <Button onClick={handleConfirm}>확인</Button>\n * </Modal>\n * \n * @example\n * // 큰 크기 모달 / Large size modal\n * <Modal\n * isOpen={isOpen}\n * onClose={() => setIsOpen(false)}\n * size=\"xl\"\n * closeOnOverlayClick={false}\n * >\n * <div>큰 모달 내용</div>\n * </Modal>\n * \n * @param {ModalProps} props - Modal 컴포넌트의 props / Modal component props\n * @param {React.Ref<HTMLDivElement>} ref - 모달 컨테이너 ref / Modal container ref\n * @returns {JSX.Element} Modal 컴포넌트 / Modal component\n */\nexport const Modal = React.forwardRef<HTMLDivElement, ModalProps>(\n ({\n className,\n isOpen,\n onClose,\n children,\n size = \"md\",\n closable,\n closeOnOverlayClick = true,\n title,\n description,\n showBackdrop = true,\n backdropClassName,\n centered = true\n }, ref) => {\n const _closable = closable ?? true\n const modalRef = React.useRef<HTMLDivElement>(null)\n const combinedRef = useCombinedRefs(ref, modalRef)\n\n // ESC 키로 모달 닫기 및 스크롤 잠금 (화면 흔들림 방지)\n React.useEffect(() => {\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n onClose()\n }\n }\n\n if (isOpen) {\n document.addEventListener(\"keydown\", handleEscape)\n // 스크롤바 너비 계산하여 padding 추가 (화면 흔들림 방지)\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth\n document.body.style.overflow = \"hidden\"\n document.body.style.paddingRight = `${scrollbarWidth}px`\n }\n\n return () => {\n document.removeEventListener(\"keydown\", handleEscape)\n document.body.style.overflow = \"unset\"\n document.body.style.paddingRight = \"unset\"\n }\n }, [isOpen, onClose])\n\n // 모달 외부 클릭으로 닫기\n const handleOverlayClick = (e: React.MouseEvent) => {\n if (closeOnOverlayClick && e.target === e.currentTarget) {\n onClose()\n }\n }\n\n // 모달 크기 클래스 (max-w 기반, 콘텐츠에 맞게 자연스럽게 축소)\n const sizeClasses = {\n sm: \"max-w-xs\", // 320px\n md: \"max-w-sm\", // 384px\n lg: \"max-w-md\", // 448px\n xl: \"max-w-lg\", // 512px\n \"2xl\": \"max-w-xl\", // 576px\n \"3xl\": \"max-w-2xl\" // 672px\n }\n\n // 접근성을 위한 ID 생성\n const generatedTitleId = React.useId()\n const generatedDescId = React.useId()\n const titleId = title ? `modal-title-${generatedTitleId}` : undefined;\n const descriptionId = description ? `modal-description-${generatedDescId}` : undefined;\n\n // SSR에서는 document가 없으므로 체크\n const [mounted, setMounted] = React.useState(false)\n React.useEffect(() => {\n setMounted(true)\n }, [])\n\n if (!isOpen) return null\n\n const modalContent = (\n <div\n className={merge(\n \"fixed inset-0 z-50 overflow-y-auto\",\n className\n )}\n onClick={handleOverlayClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={descriptionId}\n >\n {/* 배경 오버레이 - pointer-events-none으로 클릭이 뒤로 전달됨 */}\n {showBackdrop && (\n <div className={merge(\n \"fixed inset-0 bg-black/60 backdrop-blur-md transition-opacity duration-300 pointer-events-none\",\n backdropClassName\n )} />\n )}\n\n {/* 센터링 컨테이너 */}\n <div className={merge(\n \"flex h-full justify-center p-4\",\n centered ? \"items-center\" : \"items-start pt-16\"\n )}>\n {/* 모달 컨테이너 */}\n {/* CSS 변수 기반 배경색 (Tailwind v4 dark: + bg-* 충돌 우회) */}\n <div\n ref={combinedRef}\n className={merge(\n \"relative bg-[var(--modal-bg)] rounded-lg shadow-2xl border border-[var(--modal-border)] transform transition-all duration-300 ease-out\",\n sizeClasses[size]\n )}\n style={{\n animation: \"modalSlideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1)\"\n }}\n >\n \n {/* 헤더 */}\n {title && (\n <div className=\"relative z-10 px-6 pt-6 pb-4 border-b border-border/50\">\n {/* 타이틀과 닫기 버튼 - 같은 줄, 양쪽 끝 */}\n <div className=\"flex items-center justify-between gap-4 mb-2\">\n <h2 id={titleId} className=\"text-xl font-semibold text-foreground flex-1 min-w-0\">{title}</h2>\n {/* 닫기 버튼 - 타이틀과 같은 계층의 오른쪽 끝 */}\n {_closable && (\n <button\n onClick={onClose}\n className=\"flex-shrink-0 p-2 text-muted-foreground hover:text-foreground transition-all duration-200 rounded-full hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 z-20\"\n aria-label=\"닫기\"\n >\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </div>\n {/* 설명 - 아래 줄 */}\n {description && (\n <p id={descriptionId} className=\"text-sm text-muted-foreground\">{description}</p>\n )}\n </div>\n )}\n\n {/* 타이틀이 없을 때만 별도 닫기 버튼 */}\n {!title && _closable && (\n <button\n onClick={onClose}\n className=\"absolute top-4 right-4 p-2 text-muted-foreground hover:text-foreground transition-all duration-200 rounded-full hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 z-20\"\n aria-label=\"닫기\"\n >\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n \n {/* 모달 내용 */}\n <div className={`relative z-10 ${title ? 'px-6 mb-6' : 'p-6'}`}>\n {children}\n </div>\n </div>\n </div>\n </div>\n )\n\n // 브라우저에서만 createPortal 사용 (SSR 호환)\n if (mounted && typeof document !== 'undefined') {\n return createPortal(modalContent, document.body)\n }\n\n // SSR fallback - 그냥 렌더링 (첫 렌더링에는 보이지 않음)\n return null\n})\n\nModal.displayName = \"Modal\" "]}
|
|
1
|
+
{"version":3,"sources":["../src/components/Modal.tsx"],"names":["useCombinedRefs","refs","React","node","ref","Modal","className","isOpen","onClose","children","size","closable","closeOnOverlayClick","title","description","showBackdrop","backdropClassName","centered","_closable","modalRef","combinedRef","handleEscape","e","scrollbarWidth","handleOverlayClick","sizeClasses","generatedTitleId","generatedDescId","titleId","descriptionId","mounted","setMounted","modalContent","jsxs","merge","jsx","createPortal"],"mappings":"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","file":"chunk-B544MRF7.mjs","sourcesContent":["\"use client\"\n\nimport React from \"react\"\nimport { createPortal } from \"react-dom\"\nimport { merge } from \"../lib/utils\"\n\n/**\n * Modal 컴포넌트의 props / Modal component props\n * @typedef {Object} ModalProps\n * @property {boolean} isOpen - 모달 열림/닫힘 상태 / Modal open/close state\n * @property {() => void} onClose - 모달 닫기 콜백 함수 / Modal close callback function\n * @property {React.ReactNode} children - 모달 내용 / Modal content\n * @property {\"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\"} [size=\"md\"] - 모달 크기 / Modal size\n * @property {boolean} [closable=true] - 닫기 버튼 표시 여부 / Show close button\n * @property {boolean} [closeOnOverlayClick=true] - 오버레이 클릭 시 닫기 여부 / Close on overlay click\n * @property {string} [title] - 모달 제목 / Modal title\n * @property {string} [description] - 모달 설명 / Modal description\n * @property {boolean} [showBackdrop=true] - 배경 오버레이 표시 여부 / Show backdrop overlay\n * @property {string} [backdropClassName] - 배경 오버레이 추가 CSS 클래스 / Additional CSS class for backdrop\n * @property {boolean} [centered=true] - 모달을 화면 중앙에 배치할지 여부 / Center modal on screen\n * @property {string} [className] - 모달 컨테이너 추가 CSS 클래스 / Additional CSS class for modal container\n */\nexport interface ModalProps {\n /** 모달 열림/닫힘 상태 / Modal open/close state */\n isOpen: boolean\n /** 모달 닫기 콜백 함수 / Modal close callback function */\n onClose: () => void\n /** 모달 내용 / Modal content */\n children: React.ReactNode\n /** 모달 크기 / Modal size */\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\"\n /** 닫기 버튼 표시 여부 / Show close button */\n closable?: boolean\n /** 오버레이 클릭 시 닫기 여부 / Close on overlay click */\n closeOnOverlayClick?: boolean\n /** 모달 제목 / Modal title */\n title?: string\n /** 모달 설명 / Modal description */\n description?: string\n /** 배경 오버레이 표시 여부 / Show backdrop overlay */\n showBackdrop?: boolean\n /** 배경 오버레이 추가 CSS 클래스 / Additional CSS class for backdrop */\n backdropClassName?: string\n /** 모달을 화면 중앙에 배치할지 여부 / Center modal on screen */\n centered?: boolean\n /** 모달 컨테이너 추가 CSS 클래스 / Additional CSS class for modal container */\n className?: string\n}\n\n// Ref 병합 유틸리티\nfunction useCombinedRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> {\n return React.useCallback(\n (node: T) => {\n refs.forEach((ref) => {\n if (!ref) return;\n if (typeof ref === \"function\") {\n ref(node);\n } else {\n (ref as React.MutableRefObject<T | null>).current = node;\n }\n });\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n refs\n );\n}\n\n/**\n * Modal 컴포넌트 / Modal component\n * \n * 오버레이와 함께 표시되는 모달 다이얼로그 컴포넌트입니다.\n * ESC 키로 닫기, 오버레이 클릭으로 닫기, 접근성 속성(ARIA)을 지원합니다.\n * \n * Modal dialog component displayed with overlay.\n * Supports closing with ESC key, overlay click, and ARIA accessibility attributes.\n * \n * @component\n * @example\n * // 기본 사용 / Basic usage\n * const [isOpen, setIsOpen] = useState(false)\n * \n * <Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>\n * <p>모달 내용</p>\n * </Modal>\n * \n * @example\n * // 제목과 설명 포함 / With title and description\n * <Modal\n * isOpen={isOpen}\n * onClose={() => setIsOpen(false)}\n * title=\"확인\"\n * description=\"이 작업을 계속하시겠습니까?\"\n * >\n * <Button onClick={handleConfirm}>확인</Button>\n * </Modal>\n * \n * @example\n * // 큰 크기 모달 / Large size modal\n * <Modal\n * isOpen={isOpen}\n * onClose={() => setIsOpen(false)}\n * size=\"xl\"\n * closeOnOverlayClick={false}\n * >\n * <div>큰 모달 내용</div>\n * </Modal>\n * \n * @param {ModalProps} props - Modal 컴포넌트의 props / Modal component props\n * @param {React.Ref<HTMLDivElement>} ref - 모달 컨테이너 ref / Modal container ref\n * @returns {JSX.Element} Modal 컴포넌트 / Modal component\n */\nexport const Modal = React.forwardRef<HTMLDivElement, ModalProps>(\n ({\n className,\n isOpen,\n onClose,\n children,\n size = \"md\",\n closable,\n closeOnOverlayClick = true,\n title,\n description,\n showBackdrop = true,\n backdropClassName,\n centered = true\n }, ref) => {\n const _closable = closable ?? true\n const modalRef = React.useRef<HTMLDivElement>(null)\n const combinedRef = useCombinedRefs(ref, modalRef)\n\n // ESC 키로 모달 닫기 및 스크롤 잠금 (화면 흔들림 방지)\n React.useEffect(() => {\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n onClose()\n }\n }\n\n if (isOpen) {\n document.addEventListener(\"keydown\", handleEscape)\n // 스크롤바 너비 계산하여 padding 추가 (화면 흔들림 방지)\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth\n document.body.style.overflow = \"hidden\"\n document.body.style.paddingRight = `${scrollbarWidth}px`\n }\n\n return () => {\n document.removeEventListener(\"keydown\", handleEscape)\n document.body.style.overflow = \"unset\"\n document.body.style.paddingRight = \"unset\"\n }\n }, [isOpen, onClose])\n\n // 모달 외부 클릭으로 닫기\n const handleOverlayClick = (e: React.MouseEvent) => {\n if (closeOnOverlayClick && e.target === e.currentTarget) {\n onClose()\n }\n }\n\n // 모달 크기 클래스 (max-w 기반, 콘텐츠에 맞게 자연스럽게 축소)\n const sizeClasses = {\n sm: \"max-w-xs\", // 320px\n md: \"max-w-sm\", // 384px\n lg: \"max-w-md\", // 448px\n xl: \"max-w-lg\", // 512px\n \"2xl\": \"max-w-xl\", // 576px\n \"3xl\": \"max-w-2xl\" // 672px\n }\n\n // 접근성을 위한 ID 생성\n const generatedTitleId = React.useId()\n const generatedDescId = React.useId()\n const titleId = title ? `modal-title-${generatedTitleId}` : undefined;\n const descriptionId = description ? `modal-description-${generatedDescId}` : undefined;\n\n // SSR에서는 document가 없으므로 체크\n const [mounted, setMounted] = React.useState(false)\n React.useEffect(() => {\n setMounted(true)\n }, [])\n\n if (!isOpen) return null\n\n const modalContent = (\n <div\n className={merge(\n \"fixed inset-0 z-50 overflow-y-auto\",\n className\n )}\n onClick={handleOverlayClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={descriptionId}\n >\n {/* 배경 오버레이 - pointer-events-none으로 클릭이 뒤로 전달됨 */}\n {showBackdrop && (\n <div\n className={merge(\n \"fixed inset-0 bg-black/85 backdrop-blur-md transition-opacity duration-300 pointer-events-none\",\n backdropClassName\n )}\n />\n )}\n\n {/* 센터링 컨테이너 */}\n <div className={merge(\n \"flex h-full justify-center p-4\",\n centered ? \"items-center\" : \"items-start pt-16\"\n )}>\n {/* 모달 컨테이너 */}\n {/* CSS 변수 기반 배경색 (Tailwind v4 dark: + bg-* 충돌 우회) */}\n <div\n ref={combinedRef}\n className={merge(\n \"relative bg-[var(--modal-bg)] rounded-lg shadow-2xl border border-[var(--modal-border)] transform transition-all duration-300 ease-out\",\n sizeClasses[size]\n )}\n style={{\n animation: \"modalSlideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1)\"\n }}\n >\n \n {/* 헤더 */}\n {title && (\n <div className=\"relative z-10 px-6 pt-6 pb-4 border-b border-border/50\">\n {/* 타이틀과 닫기 버튼 - 같은 줄, 양쪽 끝 */}\n <div className=\"flex items-center justify-between gap-4 mb-2\">\n <h2 id={titleId} className=\"text-xl font-semibold text-foreground flex-1 min-w-0\">{title}</h2>\n {/* 닫기 버튼 - 타이틀과 같은 계층의 오른쪽 끝 */}\n {_closable && (\n <button\n onClick={onClose}\n className=\"flex-shrink-0 p-2 text-muted-foreground hover:text-foreground transition-all duration-200 rounded-full hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 z-20\"\n aria-label=\"닫기\"\n >\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </div>\n {/* 설명 - 아래 줄 */}\n {description && (\n <p id={descriptionId} className=\"text-sm text-muted-foreground\">{description}</p>\n )}\n </div>\n )}\n\n {/* 타이틀이 없을 때만 별도 닫기 버튼 */}\n {!title && _closable && (\n <button\n onClick={onClose}\n className=\"absolute top-4 right-4 p-2 text-muted-foreground hover:text-foreground transition-all duration-200 rounded-full hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 z-20\"\n aria-label=\"닫기\"\n >\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n \n {/* 모달 내용 */}\n <div className={`relative z-10 ${title ? 'px-6 mb-6' : 'p-6'}`}>\n {children}\n </div>\n </div>\n </div>\n </div>\n )\n\n // 브라우저에서만 createPortal 사용 (SSR 호환)\n if (mounted && typeof document !== 'undefined') {\n return createPortal(modalContent, document.body)\n }\n\n // SSR fallback - 그냥 렌더링 (첫 렌더링에는 보이지 않음)\n return null\n})\n\nModal.displayName = \"Modal\" "]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {b}from'./chunk-U6CTBZ2U.mjs';import x from'react';import {cva}from'class-variance-authority';import {jsx}from'react/jsx-runtime';var o=cva("w-full",{variants:{size:{sm:"max-w-2xl",md:"max-w-4xl",lg:"max-w-6xl",xl:"max-w-7xl",full:"max-w-full"},padding:{none:"",sm:"px-3 sm:px-4 py-6 sm:py-8",md:"px-4 sm:px-6 py-8 sm:py-12",lg:"px-4 sm:px-6 lg:px-8 py-10 sm:py-16",xl:"px-6 sm:px-8 lg:px-12 py-12 sm:py-20"}},defaultVariants:{size:"lg",padding:"md"}}),i=x.forwardRef(({className:m,size:a="lg",padding:n="md",centered:p=true,fluid:e=false,...s},t)=>jsx("div",{ref:t,className:b(o({size:e?void 0:a,padding:n}),e&&"max-w-full",p&&"mx-auto",m),...s}));i.displayName="Container";export{i as a};//# sourceMappingURL=chunk-CVWWS25A.mjs.map
|
|
3
|
+
//# sourceMappingURL=chunk-CVWWS25A.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Container.tsx"],"names":["containerVariants","cva","Container","React","className","size","padding","centered","fluid","props","ref","jsx","merge"],"mappings":"yIAMO,IAAMA,CAAAA,CAAoBC,IAC/B,QAAA,CACA,CACE,SAAU,CACR,IAAA,CAAM,CACJ,EAAA,CAAI,WAAA,CACJ,EAAA,CAAI,WAAA,CACJ,EAAA,CAAI,WAAA,CACJ,GAAI,WAAA,CACJ,IAAA,CAAM,YACR,CAAA,CACA,OAAA,CAAS,CACP,IAAA,CAAM,EAAA,CACN,EAAA,CAAI,2BAAA,CACJ,EAAA,CAAI,4BAAA,CACJ,GAAI,qCAAA,CACJ,EAAA,CAAI,sCACN,CACF,CAAA,CACA,eAAA,CAAiB,CACf,IAAA,CAAM,IAAA,CACN,OAAA,CAAS,IACX,CACF,CACF,EAuBMC,CAAAA,CAAYC,CAAAA,CAAM,WACtB,CAAC,CACC,UAAAC,CAAAA,CACA,IAAA,CAAAC,CAAAA,CAAO,IAAA,CACP,OAAA,CAAAC,CAAAA,CAAU,KACV,QAAA,CAAAC,CAAAA,CAAW,KACX,KAAA,CAAAC,CAAAA,CAAQ,MACR,GAAGC,CACL,CAAA,CAAGC,CAAAA,GAECC,GAAAA,CAAC,KAAA,CAAA,CACC,IAAKD,CAAAA,CACL,SAAA,CAAWE,EACTZ,CAAAA,CAAkB,CAAE,KAAMQ,CAAAA,CAAQ,MAAA,CAAYH,CAAAA,CAAM,OAAA,CAAAC,CAAQ,CAAC,EAC7DE,CAAAA,EAAS,YAAA,CACTD,CAAAA,EAAY,SAAA,CACZH,CACF,CAAA,CACC,GAAGK,CAAAA,CACN,CAGN,EACAP,CAAAA,CAAU,WAAA,CAAc,WAAA","file":"chunk-CVWWS25A.mjs","sourcesContent":["\"use client\"\n\nimport React from \"react\"\nimport { cva } from \"class-variance-authority\"\nimport { merge } from \"../lib/utils\"\n\nexport const containerVariants = cva(\n \"w-full\",\n {\n variants: {\n size: {\n sm: \"max-w-2xl\",\n md: \"max-w-4xl\",\n lg: \"max-w-6xl\",\n xl: \"max-w-7xl\",\n full: \"max-w-full\",\n },\n padding: {\n none: \"\",\n sm: \"px-3 sm:px-4 py-6 sm:py-8\",\n md: \"px-4 sm:px-6 py-8 sm:py-12\",\n lg: \"px-4 sm:px-6 lg:px-8 py-10 sm:py-16\",\n xl: \"px-6 sm:px-8 lg:px-12 py-12 sm:py-20\",\n },\n },\n defaultVariants: {\n size: \"lg\",\n padding: \"md\",\n },\n }\n)\n\n/**\n * Container 컴포넌트의 props\n */\nexport interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\"\n padding?: \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\"\n centered?: boolean\n fluid?: boolean\n}\n\n/**\n * Container 컴포넌트\n *\n * 콘텐츠를 감싸는 컨테이너 컴포넌트입니다.\n * 반응형 최대 너비와 패딩을 제공하여 일관된 레이아웃을 구성합니다.\n *\n * @example\n * <Container><h1>제목</h1></Container>\n * <Container size=\"sm\" padding=\"none\"><div>콘텐츠</div></Container>\n * <Container fluid padding=\"xl\"><div>전체 너비</div></Container>\n */\nconst Container = React.forwardRef<HTMLDivElement, ContainerProps>(\n ({\n className,\n size = \"lg\",\n padding = \"md\",\n centered = true,\n fluid = false,\n ...props\n }, ref) => {\n return (\n <div\n ref={ref}\n className={merge(\n containerVariants({ size: fluid ? undefined : size, padding }),\n fluid && \"max-w-full\",\n centered && \"mx-auto\",\n className\n )}\n {...props}\n />\n )\n }\n)\nContainer.displayName = \"Container\"\n\nexport { Container } "]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {b}from'./chunk-U6CTBZ2U.mjs';import b$1,{createContext,useContext,useState,useCallback}from'react';import {jsxs,jsx}from'react/jsx-runtime';var f=createContext(void 0);function I(){let t=useContext(f);if(!t)throw new Error("useToast must be used within a ToastProvider");return t}var w=()=>{},k=()=>{},y=()=>{};function R(){let t=useContext(f);return t||{toasts:[],addToast:w,removeToast:k,clearToasts:y}}function S({children:t,maxToasts:n=5,position:a="top-right"}){let[r,s]=useState([]),i=useCallback(o=>{s(u=>u.filter(g=>g.id!==o));},[]),l=useCallback(o=>{let u=Math.random().toString(36).substr(2,9),g={...o,id:u};s(v=>[...v,g].slice(-n)),o.duration!==0&&setTimeout(()=>{i(u);},o.duration||5e3);},[n,i]),m=useCallback(()=>{s([]);},[]);return jsxs(f.Provider,{value:{toasts:r,addToast:l,removeToast:i,clearToasts:m},children:[t,jsx(C,{toasts:r,removeToast:i,position:a})]})}function C({toasts:t,removeToast:n,position:a}){let r={"top-right":"top-4 right-4","top-left":"top-4 left-4","bottom-right":"bottom-4 right-4","bottom-left":"bottom-4 left-4","top-center":"top-4 left-1/2 transform -translate-x-1/2","bottom-center":"bottom-4 left-1/2 transform -translate-x-1/2"};return t.length===0?null:jsx("div",{className:b("fixed z-50 space-y-3 max-w-sm",r[a]),children:t.map(s=>jsx(L,{toast:s,onRemove:n},s.id))})}function L({toast:t,onRemove:n}){let[a,r]=useState(false);b$1.useEffect(()=>{r(true);},[]);let s=()=>{r(false),setTimeout(()=>n(t.id),300);},i=o=>{switch(o){case "success":return "bg-[var(--toast-success-bg)] border-green-300 dark:border-green-700 text-green-800 dark:text-green-200 shadow-lg shadow-green-100/50 dark:shadow-none";case "error":return "bg-[var(--toast-error-bg)] border-red-300 dark:border-red-700 text-red-800 dark:text-red-200 shadow-lg shadow-red-100/50 dark:shadow-none";case "warning":return "bg-[var(--toast-warning-bg)] border-yellow-300 dark:border-yellow-700 text-yellow-800 dark:text-yellow-200 shadow-lg shadow-yellow-100/50 dark:shadow-none";case "info":return "bg-[var(--toast-info-bg)] border-indigo-300 dark:border-indigo-700 text-cyan-800 dark:text-cyan-200 shadow-lg shadow-indigo-100/50 dark:shadow-none"}},l=o=>{switch(o){case "success":return "text-green-500 dark:text-green-400";case "error":return "text-red-500 dark:text-red-400";case "warning":return "text-yellow-500 dark:text-yellow-400";case "info":return "text-cyan-500 dark:text-cyan-400"}},m=o=>{switch(o){case "success":return jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})});case "error":return jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})});case "warning":return jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"})});case "info":return jsx("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"})})}};return jsxs("div",{className:b("flex items-start p-4 rounded-xl border backdrop-blur-sm transition-all duration-300 transform",i(t.type),a?"translate-x-0 opacity-100 scale-100":"translate-x-full opacity-0 scale-95"),style:{animation:a?"slideInRight 0.3s cubic-bezier(0.16, 1, 0.3, 1)":void 0},children:[jsxs("div",{className:b("flex-shrink-0 mr-3",l(t.type)),children:[" ",m(t.type)]}),jsxs("div",{className:"flex-1 min-w-0",children:[t.title&&jsxs("h4",{className:"text-sm font-semibold mb-1",children:[" ",t.title]}),jsx("p",{className:"text-sm leading-relaxed",children:t.message}),t.action&&jsx("button",{onClick:t.action.onClick,className:"mt-3 text-sm font-medium underline hover:no-underline transition-all duration-200",children:t.action.label})]}),jsxs("div",{className:"flex-shrink-0 ml-4",children:[" ",jsx("button",{onClick:s,className:b("inline-flex rounded-md p-1.5 transition-colors duration-200 ease-in-out hover:bg-black/5 dark:hover:bg-white/10 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-offset-2",l(t.type)),"aria-label":"\uB2EB\uAE30",children:jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]})]})}export{I as a,R as b,S as c};//# sourceMappingURL=chunk-DYNBM24D.mjs.map
|
|
3
|
+
//# sourceMappingURL=chunk-DYNBM24D.mjs.map
|