sellmate-design-system-react 0.1.11 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/components/SActionModal/SActionModal.cjs +9 -3
  2. package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
  3. package/dist/components/SActionModal/SActionModal.js +9 -3
  4. package/dist/components/SActionModal/SActionModal.js.map +1 -1
  5. package/dist/components/SBarcodeInput/SBarcodeInput.cjs +9 -3
  6. package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
  7. package/dist/components/SBarcodeInput/SBarcodeInput.js +9 -3
  8. package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
  9. package/dist/components/SCalendar/SCalendar.cjs +9 -3
  10. package/dist/components/SCalendar/SCalendar.cjs.map +1 -1
  11. package/dist/components/SCalendar/SCalendar.js +9 -3
  12. package/dist/components/SCalendar/SCalendar.js.map +1 -1
  13. package/dist/components/SChip/SChip.cjs +9 -3
  14. package/dist/components/SChip/SChip.cjs.map +1 -1
  15. package/dist/components/SChip/SChip.js +9 -3
  16. package/dist/components/SChip/SChip.js.map +1 -1
  17. package/dist/components/SChipInput/SChipInput.cjs +25 -10
  18. package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
  19. package/dist/components/SChipInput/SChipInput.js +25 -10
  20. package/dist/components/SChipInput/SChipInput.js.map +1 -1
  21. package/dist/components/SCircleProgress/SCircleProgress.cjs +6 -2
  22. package/dist/components/SCircleProgress/SCircleProgress.cjs.map +1 -1
  23. package/dist/components/SCircleProgress/SCircleProgress.js +6 -2
  24. package/dist/components/SCircleProgress/SCircleProgress.js.map +1 -1
  25. package/dist/components/SConfirmModal/SConfirmModal.cjs +9 -3
  26. package/dist/components/SConfirmModal/SConfirmModal.cjs.map +1 -1
  27. package/dist/components/SConfirmModal/SConfirmModal.js +9 -3
  28. package/dist/components/SConfirmModal/SConfirmModal.js.map +1 -1
  29. package/dist/components/SDatePicker/SDatePicker.cjs +9 -3
  30. package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
  31. package/dist/components/SDatePicker/SDatePicker.js +9 -3
  32. package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
  33. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +9 -3
  34. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  35. package/dist/components/SDateRangePicker/SDateRangePicker.js +9 -3
  36. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  37. package/dist/components/SDropdownButton/SDropdownButton.cjs +1 -1
  38. package/dist/components/SDropdownButton/SDropdownButton.cjs.map +1 -1
  39. package/dist/components/SDropdownButton/SDropdownButton.js +1 -1
  40. package/dist/components/SDropdownButton/SDropdownButton.js.map +1 -1
  41. package/dist/components/SField/SField.cjs +9 -3
  42. package/dist/components/SField/SField.cjs.map +1 -1
  43. package/dist/components/SField/SField.js +9 -3
  44. package/dist/components/SField/SField.js.map +1 -1
  45. package/dist/components/SFilePicker/SFilePicker.cjs +9 -3
  46. package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
  47. package/dist/components/SFilePicker/SFilePicker.js +9 -3
  48. package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
  49. package/dist/components/SGhostButton/SGhostButton.cjs +9 -3
  50. package/dist/components/SGhostButton/SGhostButton.cjs.map +1 -1
  51. package/dist/components/SGhostButton/SGhostButton.d.ts +7 -1
  52. package/dist/components/SGhostButton/SGhostButton.js +9 -3
  53. package/dist/components/SGhostButton/SGhostButton.js.map +1 -1
  54. package/dist/components/SGnb/SGnb.cjs +3478 -0
  55. package/dist/components/SGnb/SGnb.cjs.map +1 -0
  56. package/dist/components/SGnb/SGnb.d.ts +39 -0
  57. package/dist/components/SGnb/SGnb.js +3456 -0
  58. package/dist/components/SGnb/SGnb.js.map +1 -0
  59. package/dist/components/SGnb/gnb.config.d.ts +87 -0
  60. package/dist/components/SGnb/index.d.ts +2 -0
  61. package/dist/components/SGuide/SGuide.cjs +9 -3
  62. package/dist/components/SGuide/SGuide.cjs.map +1 -1
  63. package/dist/components/SGuide/SGuide.js +9 -3
  64. package/dist/components/SGuide/SGuide.js.map +1 -1
  65. package/dist/components/SInput/SInput.cjs +9 -3
  66. package/dist/components/SInput/SInput.cjs.map +1 -1
  67. package/dist/components/SInput/SInput.js +9 -3
  68. package/dist/components/SInput/SInput.js.map +1 -1
  69. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +20 -6
  70. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  71. package/dist/components/SKeyValueTable/SKeyValueTable.js +20 -6
  72. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  73. package/dist/components/SLayout/SLayout.cjs +97 -0
  74. package/dist/components/SLayout/SLayout.cjs.map +1 -0
  75. package/dist/components/SLayout/SLayout.d.ts +47 -0
  76. package/dist/components/SLayout/SLayout.js +93 -0
  77. package/dist/components/SLayout/SLayout.js.map +1 -0
  78. package/dist/components/SLayout/index.d.ts +1 -0
  79. package/dist/components/SLoadingContainer/SLoadingContainer.cjs +6 -2
  80. package/dist/components/SLoadingContainer/SLoadingContainer.cjs.map +1 -1
  81. package/dist/components/SLoadingContainer/SLoadingContainer.js +6 -2
  82. package/dist/components/SLoadingContainer/SLoadingContainer.js.map +1 -1
  83. package/dist/components/SLoadingContainer/loading.cjs +6 -2
  84. package/dist/components/SLoadingContainer/loading.cjs.map +1 -1
  85. package/dist/components/SLoadingContainer/loading.js +6 -2
  86. package/dist/components/SLoadingContainer/loading.js.map +1 -1
  87. package/dist/components/SLoadingModal/SLoadingModal.cjs +15 -5
  88. package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
  89. package/dist/components/SLoadingModal/SLoadingModal.js +15 -5
  90. package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
  91. package/dist/components/SModalContainer/SModalContainer.cjs +9 -3
  92. package/dist/components/SModalContainer/SModalContainer.cjs.map +1 -1
  93. package/dist/components/SModalContainer/SModalContainer.d.ts +10 -0
  94. package/dist/components/SModalContainer/SModalContainer.js +9 -3
  95. package/dist/components/SModalContainer/SModalContainer.js.map +1 -1
  96. package/dist/components/SNumberInput/SNumberInput.cjs +9 -3
  97. package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
  98. package/dist/components/SNumberInput/SNumberInput.js +9 -3
  99. package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
  100. package/dist/components/SPage/SPage.cjs +104 -0
  101. package/dist/components/SPage/SPage.cjs.map +1 -0
  102. package/dist/components/SPage/SPage.d.ts +13 -0
  103. package/dist/components/SPage/SPage.js +102 -0
  104. package/dist/components/SPage/SPage.js.map +1 -0
  105. package/dist/components/SPage/index.d.ts +1 -0
  106. package/dist/components/SPopover/SPopover.cjs +9 -3
  107. package/dist/components/SPopover/SPopover.cjs.map +1 -1
  108. package/dist/components/SPopover/SPopover.js +9 -3
  109. package/dist/components/SPopover/SPopover.js.map +1 -1
  110. package/dist/components/SPortal/SPortal.cjs +1 -1
  111. package/dist/components/SPortal/SPortal.cjs.map +1 -1
  112. package/dist/components/SPortal/SPortal.js +1 -1
  113. package/dist/components/SPortal/SPortal.js.map +1 -1
  114. package/dist/components/SSelect/SSelect.cjs +20 -6
  115. package/dist/components/SSelect/SSelect.cjs.map +1 -1
  116. package/dist/components/SSelect/SSelect.js +20 -6
  117. package/dist/components/SSelect/SSelect.js.map +1 -1
  118. package/dist/components/STable/STable.cjs +28 -10
  119. package/dist/components/STable/STable.cjs.map +1 -1
  120. package/dist/components/STable/STable.js +28 -10
  121. package/dist/components/STable/STable.js.map +1 -1
  122. package/dist/components/STextarea/STextarea.cjs +9 -3
  123. package/dist/components/STextarea/STextarea.cjs.map +1 -1
  124. package/dist/components/STextarea/STextarea.js +9 -3
  125. package/dist/components/STextarea/STextarea.js.map +1 -1
  126. package/dist/components/SToast/SToast.cjs +9 -3
  127. package/dist/components/SToast/SToast.cjs.map +1 -1
  128. package/dist/components/SToast/SToast.js +9 -3
  129. package/dist/components/SToast/SToast.js.map +1 -1
  130. package/dist/components/SToast/SToastContainer.cjs +9 -3
  131. package/dist/components/SToast/SToastContainer.cjs.map +1 -1
  132. package/dist/components/SToast/SToastContainer.js +9 -3
  133. package/dist/components/SToast/SToastContainer.js.map +1 -1
  134. package/dist/components/STooltip/STooltip.cjs +9 -3
  135. package/dist/components/STooltip/STooltip.cjs.map +1 -1
  136. package/dist/components/STooltip/STooltip.js +9 -3
  137. package/dist/components/STooltip/STooltip.js.map +1 -1
  138. package/dist/index.cjs +453 -17
  139. package/dist/index.cjs.map +1 -1
  140. package/dist/index.d.ts +3 -0
  141. package/dist/index.js +444 -19
  142. package/dist/index.js.map +1 -1
  143. package/dist/styles.css +530 -26
  144. package/dist/theme.css +70 -14
  145. package/package.json +1 -1
@@ -120,7 +120,7 @@ function SPortal({
120
120
  onCloseAutoFocus,
121
121
  onClick: autoClose ? () => onClose?.() : void 0,
122
122
  className: cn(
123
- "z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) py-4 shadow-normal-sm",
123
+ "z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) shadow-normal-sm",
124
124
  maxHeight != null && "overflow-y-auto",
125
125
  fit && "min-w-(--radix-popover-trigger-width)",
126
126
  FLOATING_SLIDE_ANIM,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/cn.ts","../../../src/lib/floating-anim.ts","../../../src/lib/portal-container.tsx","../../../src/components/SPortal/SPortal.tsx"],"names":["extendTailwindMerge","clsx","createContext","useContext","jsxs","RPopover","jsx"],"mappings":"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","file":"SPortal.cjs","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n/**\n * theme.css 의 --text-* 는 기본 Tailwind 폰트사이즈 스케일(xs/sm/base...)이 아닌\n * 숫자 스케일(10~32)이라, 확장 없이는 tailwind-merge 가 이를 텍스트 컬러 그룹으로\n * 오인해 text-12 같은 클래스를 text-(--color-var) 뒤에서 충돌 제거해버린다.\n */\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [{ text: ['10', '11', '12', '13', '14', '16', '18', '20', '24', '28', '32'] }],\n },\n },\n});\n\n/**\n * className 병합 유틸.\n * clsx로 조건부 클래스를 합치고, tailwind-merge로 Tailwind 클래스 충돌을 해소한다.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * Radix Floating(SPopover/STooltip/RTooltip) Content 의 placement 방향별 슬라이드+페이드.\n * Radix 가 붙이는 data-side(실제 렌더 방향)·data-state 에 맞춰 열림/닫힘 애니메이션을 적용한다.\n * (sd-floating-portal 의 sd-floating-slide-* 포팅 — 키프레임은 theme.css `--animate-floating-*`)\n *\n * 예: side=top 이면 아래(4px)에서 위로 올라오며 나타나고, 닫힐 때 다시 내려가며 사라진다.\n *\n * @radix-ui/react-popover · react-dropdown-menu 의 data-state 는 open/closed 지만,\n * @radix-ui/react-tooltip 은 open 대신 delayed-open/instant-open 을 쓴다 — 두 케이스 모두 커버한다.\n * Tailwind JIT는 소스에 리터럴로 등장하는 클래스명만 스캔하므로 템플릿으로 조합하지 않고 전부 나열한다.\n */\nexport const FLOATING_SLIDE_ANIM = [\n 'data-[state=open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=delayed-open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=instant-open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=closed]:data-[side=top]:animate-floating-out-top',\n 'data-[state=open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=delayed-open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=instant-open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=closed]:data-[side=bottom]:animate-floating-out-bottom',\n 'data-[state=open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=delayed-open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=instant-open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=closed]:data-[side=left]:animate-floating-out-left',\n 'data-[state=open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=delayed-open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=instant-open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=closed]:data-[side=right]:animate-floating-out-right',\n].join(' ');\n","'use client';\n\nimport { createContext, useContext } from 'react';\n\n/**\n * Floating 컴포넌트(STooltip/SPopover/SSelect listbox/SDropdownButton/SDatePicker 등)가\n * 자신의 Portal 을 어디로 렌더할지 결정하는 컨텍스트.\n *\n * 기본값(null)이면 각 컴포넌트는 body 로 portal 한다.\n * SModalContainer 는 모달이 열려있는 동안 이 값을 \"모달 스태킹 밴드(z-1001) 안의 전용 노드\"로\n * 채워서, 모달 안에서 열린 floating 이 모달 딤(z-1000) 뒤에 가려지지 않고 위로 뜨게 한다.\n * 반대로 모달 밖(베이스 화면)에서 열린 floating 은 여전히 body(z-50)로 나가므로,\n * 나중에 뜬 모달이 정상적으로 그 위를 덮는다. (소속에 따라 상대적으로 결정)\n */\nconst PortalContainerContext = createContext<HTMLElement | null>(null);\nconst InsideModalContext = createContext(false);\n\nexport const PortalContainerProvider = PortalContainerContext.Provider;\nexport const InsideModalProvider = InsideModalContext.Provider;\n\n/**\n * 현재 소속된 Portal 컨테이너를 반환한다.\n * Radix `*.Portal` 의 `container`, Ariakit 의 `portalElement` 에 그대로 넘기면 되며,\n * 컨테이너가 없을 때(모달 밖)는 `undefined` 를 반환해 각 라이브러리 기본값(body)을 쓴다.\n */\nexport function usePortalContainer(): HTMLElement | undefined {\n return useContext(PortalContainerContext) ?? undefined;\n}\n\n/**\n * 현재 floating 이 모달 내부 컨텍스트에서 렌더링되는지 반환한다.\n * portal target ref 가 아직 준비되지 않은 첫 렌더에서도 true를 유지해\n * 모달 내부 전용 우회 로직이 깜빡이지 않도록 한다.\n */\nexport function useInsideModal(): boolean {\n return useContext(InsideModalContext);\n}\n","'use client';\n\nimport { type CSSProperties, type ReactNode, type RefObject } from 'react';\nimport * as RPopover from '@radix-ui/react-popover';\nimport { cn } from '../../lib/cn';\nimport { FLOATING_SLIDE_ANIM } from '../../lib/floating-anim';\nimport { usePortalContainer } from '../../lib/portal-container';\n\nexport type SPortalPlacement = 'top' | 'bottom' | 'left' | 'right';\nexport type SPortalAlign = 'start' | 'center' | 'end';\n\n/** Radix Anchor 가 위치 계산에 쓰는 최소 인터페이스 (HTMLElement 가 충족한다). */\ntype Measurable = { getBoundingClientRect(): DOMRect };\n\nexport interface SPortalProps {\n /** 표시 여부 (제어). onClose 와 함께 사용한다. */\n open: boolean;\n /** 외부 클릭 · ESC 등으로 닫힘이 요청될 때 (sd-portal `sdClose` 대응). */\n onClose?: () => void;\n /** 열림/닫힘 변경 전체 콜백 (선택). */\n onOpenChange?: (open: boolean) => void;\n /**\n * 위치 기준이 되는 앵커 엘리먼트 ref (sd-portal `parentRef` 대응).\n * 트리거 버튼 등에 그대로 걸면 그 엘리먼트에 붙어서 뜬다.\n */\n anchorRef: RefObject<HTMLElement | null>;\n /** 표시 방향 (공간이 부족하면 자동으로 반대 방향으로 전환). */\n placement?: SPortalPlacement;\n /** 교차축 정렬 (start·center·end). */\n align?: SPortalAlign;\n /** 앵커로부터의 간격 px (sd-portal offset[1]). */\n offset?: number;\n /** 정렬축 오프셋 px (sd-portal offset[0]). */\n alignOffset?: number;\n /** 앵커 너비에 맞춰 최소 너비를 잡는다. */\n fit?: boolean;\n /** 콘텐츠 최대 높이 px. 넘치면 세로 스크롤. */\n maxHeight?: number;\n /** 콘텐츠 내부를 클릭하면 자동으로 닫는다. */\n autoClose?: boolean;\n /** 외부 클릭 · ESC 로 닫히지 않게 한다. */\n persistent?: boolean;\n /** 뷰포트 경계 여백 px (충돌 회피 패딩). */\n viewportPadding?: number;\n /** 열릴 때 포커스 이동 처리 (기본은 Radix가 콘텐츠로 포커스를 옮김). e.preventDefault()로 막을 수 있다. */\n onOpenAutoFocus?: (event: Event) => void;\n /** 닫힐 때 포커스 복귀 처리 (기본은 Radix가 앵커로 포커스를 되돌림). e.preventDefault()로 막을 수 있다. */\n onCloseAutoFocus?: (event: Event) => void;\n /** 본문 내용 (콘텐츠 자유). */\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n ariaLabel?: string;\n}\n\n/**\n * SPortal — sd-portal/sd-floating-portal 의 사용성을 Radix Popover 로 옮긴 범용 앵커드 플로팅 컨테이너.\n *\n * 트리거를 감싸지 않고 `anchorRef` 로 임의의 엘리먼트에 붙는다(=sd-portal `parentRef`).\n * open/onClose 로 외부에서 제어하며, flip·collision·스크롤 재배치·외부클릭·ESC·모달 포털은\n * Radix 가 처리한다. 콘텐츠는 children 으로 완전히 자유롭다.\n */\nexport function SPortal({\n open,\n onClose,\n onOpenChange,\n anchorRef,\n placement = 'bottom',\n align = 'start',\n offset = 4,\n alignOffset = 0,\n fit = false,\n maxHeight,\n autoClose = false,\n persistent = false,\n viewportPadding = 0,\n onOpenAutoFocus,\n onCloseAutoFocus,\n children,\n className,\n style,\n ariaLabel,\n}: SPortalProps) {\n const portalContainer = usePortalContainer();\n\n return (\n <RPopover.Root\n open={open}\n onOpenChange={next => {\n onOpenChange?.(next);\n if (!next) onClose?.();\n }}\n >\n <RPopover.Anchor virtualRef={anchorRef as RefObject<Measurable>} />\n <RPopover.Portal container={portalContainer}>\n <RPopover.Content\n side={placement}\n align={align}\n sideOffset={offset}\n alignOffset={alignOffset}\n collisionPadding={viewportPadding}\n aria-label={ariaLabel}\n // persistent: 외부 클릭·ESC 로 닫히지 않음\n // 아니면: 앵커(트리거) 위 클릭은 소비자가 토글하므로 여기서 닫지 않는다(중복 토글 방지)\n onPointerDownOutside={e => {\n if (persistent || anchorRef.current?.contains(e.target as Node)) {\n e.preventDefault();\n }\n }}\n // 앵커(예: input)에 이미 포커스가 있는 상태로 열리는 경우(클릭 즉시 오픈), Radix는 포커스가\n // 레이어 밖에 있다고 판단해 열리자마자 닫아버린다 — 앵커 내부 포커스는 outside로 취급하지 않는다.\n onFocusOutside={e => {\n if (persistent || anchorRef.current?.contains(e.target as Node)) {\n e.preventDefault();\n }\n }}\n onEscapeKeyDown={persistent ? e => e.preventDefault() : undefined}\n onOpenAutoFocus={onOpenAutoFocus}\n onCloseAutoFocus={onCloseAutoFocus}\n onClick={autoClose ? () => onClose?.() : undefined}\n className={cn(\n 'z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) py-4 shadow-normal-sm',\n maxHeight != null && 'overflow-y-auto',\n fit && 'min-w-(--radix-popover-trigger-width)',\n FLOATING_SLIDE_ANIM,\n className,\n )}\n style={{ maxHeight, ...style }}\n >\n {children}\n </RPopover.Content>\n </RPopover.Portal>\n </RPopover.Root>\n );\n}\n\nSPortal.displayName = 'SPortal';\n"]}
1
+ {"version":3,"sources":["../../../src/lib/cn.ts","../../../src/lib/floating-anim.ts","../../../src/lib/portal-container.tsx","../../../src/components/SPortal/SPortal.tsx"],"names":["extendTailwindMerge","clsx","createContext","useContext","jsxs","RPopover","jsx"],"mappings":"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","file":"SPortal.cjs","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n/**\n * theme.css 의 --text-* 는 기본 Tailwind 폰트사이즈 스케일(xs/sm/base...)이 아닌\n * 숫자 스케일(10~32)이라, 확장 없이는 tailwind-merge 가 이를 텍스트 컬러 그룹으로\n * 오인해 text-12 같은 클래스를 text-(--color-var) 뒤에서 충돌 제거해버린다.\n */\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [{ text: ['10', '11', '12', '13', '14', '16', '18', '20', '24', '28', '32'] }],\n },\n },\n});\n\n/**\n * className 병합 유틸.\n * clsx로 조건부 클래스를 합치고, tailwind-merge로 Tailwind 클래스 충돌을 해소한다.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * Radix Floating(SPopover/STooltip/RTooltip) Content 의 placement 방향별 슬라이드+페이드.\n * Radix 가 붙이는 data-side(실제 렌더 방향)·data-state 에 맞춰 열림/닫힘 애니메이션을 적용한다.\n * (sd-floating-portal 의 sd-floating-slide-* 포팅 — 키프레임은 theme.css `--animate-floating-*`)\n *\n * 예: side=top 이면 아래(4px)에서 위로 올라오며 나타나고, 닫힐 때 다시 내려가며 사라진다.\n *\n * @radix-ui/react-popover · react-dropdown-menu 의 data-state 는 open/closed 지만,\n * @radix-ui/react-tooltip 은 open 대신 delayed-open/instant-open 을 쓴다 — 두 케이스 모두 커버한다.\n * Tailwind JIT는 소스에 리터럴로 등장하는 클래스명만 스캔하므로 템플릿으로 조합하지 않고 전부 나열한다.\n */\nexport const FLOATING_SLIDE_ANIM = [\n 'data-[state=open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=delayed-open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=instant-open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=closed]:data-[side=top]:animate-floating-out-top',\n 'data-[state=open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=delayed-open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=instant-open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=closed]:data-[side=bottom]:animate-floating-out-bottom',\n 'data-[state=open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=delayed-open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=instant-open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=closed]:data-[side=left]:animate-floating-out-left',\n 'data-[state=open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=delayed-open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=instant-open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=closed]:data-[side=right]:animate-floating-out-right',\n].join(' ');\n","'use client';\n\nimport { createContext, useContext } from 'react';\n\n/**\n * Floating 컴포넌트(STooltip/SPopover/SSelect listbox/SDropdownButton/SDatePicker 등)가\n * 자신의 Portal 을 어디로 렌더할지 결정하는 컨텍스트.\n *\n * 기본값(null)이면 각 컴포넌트는 body 로 portal 한다.\n * SModalContainer 는 모달이 열려있는 동안 이 값을 \"모달 스태킹 밴드(z-1001) 안의 전용 노드\"로\n * 채워서, 모달 안에서 열린 floating 이 모달 딤(z-1000) 뒤에 가려지지 않고 위로 뜨게 한다.\n * 반대로 모달 밖(베이스 화면)에서 열린 floating 은 여전히 body(z-50)로 나가므로,\n * 나중에 뜬 모달이 정상적으로 그 위를 덮는다. (소속에 따라 상대적으로 결정)\n */\nconst PortalContainerContext = createContext<HTMLElement | null>(null);\nconst InsideModalContext = createContext(false);\n\nexport const PortalContainerProvider = PortalContainerContext.Provider;\nexport const InsideModalProvider = InsideModalContext.Provider;\n\n/**\n * 현재 소속된 Portal 컨테이너를 반환한다.\n * Radix `*.Portal` 의 `container`, Ariakit 의 `portalElement` 에 그대로 넘기면 되며,\n * 컨테이너가 없을 때(모달 밖)는 `undefined` 를 반환해 각 라이브러리 기본값(body)을 쓴다.\n */\nexport function usePortalContainer(): HTMLElement | undefined {\n return useContext(PortalContainerContext) ?? undefined;\n}\n\n/**\n * 현재 floating 이 모달 내부 컨텍스트에서 렌더링되는지 반환한다.\n * portal target ref 가 아직 준비되지 않은 첫 렌더에서도 true를 유지해\n * 모달 내부 전용 우회 로직이 깜빡이지 않도록 한다.\n */\nexport function useInsideModal(): boolean {\n return useContext(InsideModalContext);\n}\n","'use client';\n\nimport { type CSSProperties, type ReactNode, type RefObject } from 'react';\nimport * as RPopover from '@radix-ui/react-popover';\nimport { cn } from '../../lib/cn';\nimport { FLOATING_SLIDE_ANIM } from '../../lib/floating-anim';\nimport { usePortalContainer } from '../../lib/portal-container';\n\nexport type SPortalPlacement = 'top' | 'bottom' | 'left' | 'right';\nexport type SPortalAlign = 'start' | 'center' | 'end';\n\n/** Radix Anchor 가 위치 계산에 쓰는 최소 인터페이스 (HTMLElement 가 충족한다). */\ntype Measurable = { getBoundingClientRect(): DOMRect };\n\nexport interface SPortalProps {\n /** 표시 여부 (제어). onClose 와 함께 사용한다. */\n open: boolean;\n /** 외부 클릭 · ESC 등으로 닫힘이 요청될 때 (sd-portal `sdClose` 대응). */\n onClose?: () => void;\n /** 열림/닫힘 변경 전체 콜백 (선택). */\n onOpenChange?: (open: boolean) => void;\n /**\n * 위치 기준이 되는 앵커 엘리먼트 ref (sd-portal `parentRef` 대응).\n * 트리거 버튼 등에 그대로 걸면 그 엘리먼트에 붙어서 뜬다.\n */\n anchorRef: RefObject<HTMLElement | null>;\n /** 표시 방향 (공간이 부족하면 자동으로 반대 방향으로 전환). */\n placement?: SPortalPlacement;\n /** 교차축 정렬 (start·center·end). */\n align?: SPortalAlign;\n /** 앵커로부터의 간격 px (sd-portal offset[1]). */\n offset?: number;\n /** 정렬축 오프셋 px (sd-portal offset[0]). */\n alignOffset?: number;\n /** 앵커 너비에 맞춰 최소 너비를 잡는다. */\n fit?: boolean;\n /** 콘텐츠 최대 높이 px. 넘치면 세로 스크롤. */\n maxHeight?: number;\n /** 콘텐츠 내부를 클릭하면 자동으로 닫는다. */\n autoClose?: boolean;\n /** 외부 클릭 · ESC 로 닫히지 않게 한다. */\n persistent?: boolean;\n /** 뷰포트 경계 여백 px (충돌 회피 패딩). */\n viewportPadding?: number;\n /** 열릴 때 포커스 이동 처리 (기본은 Radix가 콘텐츠로 포커스를 옮김). e.preventDefault()로 막을 수 있다. */\n onOpenAutoFocus?: (event: Event) => void;\n /** 닫힐 때 포커스 복귀 처리 (기본은 Radix가 앵커로 포커스를 되돌림). e.preventDefault()로 막을 수 있다. */\n onCloseAutoFocus?: (event: Event) => void;\n /** 본문 내용 (콘텐츠 자유). */\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n ariaLabel?: string;\n}\n\n/**\n * SPortal — sd-portal/sd-floating-portal 의 사용성을 Radix Popover 로 옮긴 범용 앵커드 플로팅 컨테이너.\n *\n * 트리거를 감싸지 않고 `anchorRef` 로 임의의 엘리먼트에 붙는다(=sd-portal `parentRef`).\n * open/onClose 로 외부에서 제어하며, flip·collision·스크롤 재배치·외부클릭·ESC·모달 포털은\n * Radix 가 처리한다. 콘텐츠는 children 으로 완전히 자유롭다.\n */\nexport function SPortal({\n open,\n onClose,\n onOpenChange,\n anchorRef,\n placement = 'bottom',\n align = 'start',\n offset = 4,\n alignOffset = 0,\n fit = false,\n maxHeight,\n autoClose = false,\n persistent = false,\n viewportPadding = 0,\n onOpenAutoFocus,\n onCloseAutoFocus,\n children,\n className,\n style,\n ariaLabel,\n}: SPortalProps) {\n const portalContainer = usePortalContainer();\n\n return (\n <RPopover.Root\n open={open}\n onOpenChange={next => {\n onOpenChange?.(next);\n if (!next) onClose?.();\n }}\n >\n <RPopover.Anchor virtualRef={anchorRef as RefObject<Measurable>} />\n <RPopover.Portal container={portalContainer}>\n <RPopover.Content\n side={placement}\n align={align}\n sideOffset={offset}\n alignOffset={alignOffset}\n collisionPadding={viewportPadding}\n aria-label={ariaLabel}\n // persistent: 외부 클릭·ESC 로 닫히지 않음\n // 아니면: 앵커(트리거) 위 클릭은 소비자가 토글하므로 여기서 닫지 않는다(중복 토글 방지)\n onPointerDownOutside={e => {\n if (persistent || anchorRef.current?.contains(e.target as Node)) {\n e.preventDefault();\n }\n }}\n // 앵커(예: input)에 이미 포커스가 있는 상태로 열리는 경우(클릭 즉시 오픈), Radix는 포커스가\n // 레이어 밖에 있다고 판단해 열리자마자 닫아버린다 — 앵커 내부 포커스는 outside로 취급하지 않는다.\n onFocusOutside={e => {\n if (persistent || anchorRef.current?.contains(e.target as Node)) {\n e.preventDefault();\n }\n }}\n onEscapeKeyDown={persistent ? e => e.preventDefault() : undefined}\n onOpenAutoFocus={onOpenAutoFocus}\n onCloseAutoFocus={onCloseAutoFocus}\n onClick={autoClose ? () => onClose?.() : undefined}\n className={cn(\n 'z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) shadow-normal-sm',\n maxHeight != null && 'overflow-y-auto',\n fit && 'min-w-(--radix-popover-trigger-width)',\n FLOATING_SLIDE_ANIM,\n className,\n )}\n style={{ maxHeight, ...style }}\n >\n {children}\n </RPopover.Content>\n </RPopover.Portal>\n </RPopover.Root>\n );\n}\n\nSPortal.displayName = 'SPortal';\n"]}
@@ -98,7 +98,7 @@ function SPortal({
98
98
  onCloseAutoFocus,
99
99
  onClick: autoClose ? () => onClose?.() : void 0,
100
100
  className: cn(
101
- "z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) py-4 shadow-normal-sm",
101
+ "z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) shadow-normal-sm",
102
102
  maxHeight != null && "overflow-y-auto",
103
103
  fit && "min-w-(--radix-popover-trigger-width)",
104
104
  FLOATING_SLIDE_ANIM,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/cn.ts","../../../src/lib/floating-anim.ts","../../../src/lib/portal-container.tsx","../../../src/components/SPortal/SPortal.tsx"],"names":[],"mappings":"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","file":"SPortal.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n/**\n * theme.css 의 --text-* 는 기본 Tailwind 폰트사이즈 스케일(xs/sm/base...)이 아닌\n * 숫자 스케일(10~32)이라, 확장 없이는 tailwind-merge 가 이를 텍스트 컬러 그룹으로\n * 오인해 text-12 같은 클래스를 text-(--color-var) 뒤에서 충돌 제거해버린다.\n */\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [{ text: ['10', '11', '12', '13', '14', '16', '18', '20', '24', '28', '32'] }],\n },\n },\n});\n\n/**\n * className 병합 유틸.\n * clsx로 조건부 클래스를 합치고, tailwind-merge로 Tailwind 클래스 충돌을 해소한다.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * Radix Floating(SPopover/STooltip/RTooltip) Content 의 placement 방향별 슬라이드+페이드.\n * Radix 가 붙이는 data-side(실제 렌더 방향)·data-state 에 맞춰 열림/닫힘 애니메이션을 적용한다.\n * (sd-floating-portal 의 sd-floating-slide-* 포팅 — 키프레임은 theme.css `--animate-floating-*`)\n *\n * 예: side=top 이면 아래(4px)에서 위로 올라오며 나타나고, 닫힐 때 다시 내려가며 사라진다.\n *\n * @radix-ui/react-popover · react-dropdown-menu 의 data-state 는 open/closed 지만,\n * @radix-ui/react-tooltip 은 open 대신 delayed-open/instant-open 을 쓴다 — 두 케이스 모두 커버한다.\n * Tailwind JIT는 소스에 리터럴로 등장하는 클래스명만 스캔하므로 템플릿으로 조합하지 않고 전부 나열한다.\n */\nexport const FLOATING_SLIDE_ANIM = [\n 'data-[state=open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=delayed-open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=instant-open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=closed]:data-[side=top]:animate-floating-out-top',\n 'data-[state=open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=delayed-open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=instant-open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=closed]:data-[side=bottom]:animate-floating-out-bottom',\n 'data-[state=open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=delayed-open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=instant-open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=closed]:data-[side=left]:animate-floating-out-left',\n 'data-[state=open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=delayed-open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=instant-open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=closed]:data-[side=right]:animate-floating-out-right',\n].join(' ');\n","'use client';\n\nimport { createContext, useContext } from 'react';\n\n/**\n * Floating 컴포넌트(STooltip/SPopover/SSelect listbox/SDropdownButton/SDatePicker 등)가\n * 자신의 Portal 을 어디로 렌더할지 결정하는 컨텍스트.\n *\n * 기본값(null)이면 각 컴포넌트는 body 로 portal 한다.\n * SModalContainer 는 모달이 열려있는 동안 이 값을 \"모달 스태킹 밴드(z-1001) 안의 전용 노드\"로\n * 채워서, 모달 안에서 열린 floating 이 모달 딤(z-1000) 뒤에 가려지지 않고 위로 뜨게 한다.\n * 반대로 모달 밖(베이스 화면)에서 열린 floating 은 여전히 body(z-50)로 나가므로,\n * 나중에 뜬 모달이 정상적으로 그 위를 덮는다. (소속에 따라 상대적으로 결정)\n */\nconst PortalContainerContext = createContext<HTMLElement | null>(null);\nconst InsideModalContext = createContext(false);\n\nexport const PortalContainerProvider = PortalContainerContext.Provider;\nexport const InsideModalProvider = InsideModalContext.Provider;\n\n/**\n * 현재 소속된 Portal 컨테이너를 반환한다.\n * Radix `*.Portal` 의 `container`, Ariakit 의 `portalElement` 에 그대로 넘기면 되며,\n * 컨테이너가 없을 때(모달 밖)는 `undefined` 를 반환해 각 라이브러리 기본값(body)을 쓴다.\n */\nexport function usePortalContainer(): HTMLElement | undefined {\n return useContext(PortalContainerContext) ?? undefined;\n}\n\n/**\n * 현재 floating 이 모달 내부 컨텍스트에서 렌더링되는지 반환한다.\n * portal target ref 가 아직 준비되지 않은 첫 렌더에서도 true를 유지해\n * 모달 내부 전용 우회 로직이 깜빡이지 않도록 한다.\n */\nexport function useInsideModal(): boolean {\n return useContext(InsideModalContext);\n}\n","'use client';\n\nimport { type CSSProperties, type ReactNode, type RefObject } from 'react';\nimport * as RPopover from '@radix-ui/react-popover';\nimport { cn } from '../../lib/cn';\nimport { FLOATING_SLIDE_ANIM } from '../../lib/floating-anim';\nimport { usePortalContainer } from '../../lib/portal-container';\n\nexport type SPortalPlacement = 'top' | 'bottom' | 'left' | 'right';\nexport type SPortalAlign = 'start' | 'center' | 'end';\n\n/** Radix Anchor 가 위치 계산에 쓰는 최소 인터페이스 (HTMLElement 가 충족한다). */\ntype Measurable = { getBoundingClientRect(): DOMRect };\n\nexport interface SPortalProps {\n /** 표시 여부 (제어). onClose 와 함께 사용한다. */\n open: boolean;\n /** 외부 클릭 · ESC 등으로 닫힘이 요청될 때 (sd-portal `sdClose` 대응). */\n onClose?: () => void;\n /** 열림/닫힘 변경 전체 콜백 (선택). */\n onOpenChange?: (open: boolean) => void;\n /**\n * 위치 기준이 되는 앵커 엘리먼트 ref (sd-portal `parentRef` 대응).\n * 트리거 버튼 등에 그대로 걸면 그 엘리먼트에 붙어서 뜬다.\n */\n anchorRef: RefObject<HTMLElement | null>;\n /** 표시 방향 (공간이 부족하면 자동으로 반대 방향으로 전환). */\n placement?: SPortalPlacement;\n /** 교차축 정렬 (start·center·end). */\n align?: SPortalAlign;\n /** 앵커로부터의 간격 px (sd-portal offset[1]). */\n offset?: number;\n /** 정렬축 오프셋 px (sd-portal offset[0]). */\n alignOffset?: number;\n /** 앵커 너비에 맞춰 최소 너비를 잡는다. */\n fit?: boolean;\n /** 콘텐츠 최대 높이 px. 넘치면 세로 스크롤. */\n maxHeight?: number;\n /** 콘텐츠 내부를 클릭하면 자동으로 닫는다. */\n autoClose?: boolean;\n /** 외부 클릭 · ESC 로 닫히지 않게 한다. */\n persistent?: boolean;\n /** 뷰포트 경계 여백 px (충돌 회피 패딩). */\n viewportPadding?: number;\n /** 열릴 때 포커스 이동 처리 (기본은 Radix가 콘텐츠로 포커스를 옮김). e.preventDefault()로 막을 수 있다. */\n onOpenAutoFocus?: (event: Event) => void;\n /** 닫힐 때 포커스 복귀 처리 (기본은 Radix가 앵커로 포커스를 되돌림). e.preventDefault()로 막을 수 있다. */\n onCloseAutoFocus?: (event: Event) => void;\n /** 본문 내용 (콘텐츠 자유). */\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n ariaLabel?: string;\n}\n\n/**\n * SPortal — sd-portal/sd-floating-portal 의 사용성을 Radix Popover 로 옮긴 범용 앵커드 플로팅 컨테이너.\n *\n * 트리거를 감싸지 않고 `anchorRef` 로 임의의 엘리먼트에 붙는다(=sd-portal `parentRef`).\n * open/onClose 로 외부에서 제어하며, flip·collision·스크롤 재배치·외부클릭·ESC·모달 포털은\n * Radix 가 처리한다. 콘텐츠는 children 으로 완전히 자유롭다.\n */\nexport function SPortal({\n open,\n onClose,\n onOpenChange,\n anchorRef,\n placement = 'bottom',\n align = 'start',\n offset = 4,\n alignOffset = 0,\n fit = false,\n maxHeight,\n autoClose = false,\n persistent = false,\n viewportPadding = 0,\n onOpenAutoFocus,\n onCloseAutoFocus,\n children,\n className,\n style,\n ariaLabel,\n}: SPortalProps) {\n const portalContainer = usePortalContainer();\n\n return (\n <RPopover.Root\n open={open}\n onOpenChange={next => {\n onOpenChange?.(next);\n if (!next) onClose?.();\n }}\n >\n <RPopover.Anchor virtualRef={anchorRef as RefObject<Measurable>} />\n <RPopover.Portal container={portalContainer}>\n <RPopover.Content\n side={placement}\n align={align}\n sideOffset={offset}\n alignOffset={alignOffset}\n collisionPadding={viewportPadding}\n aria-label={ariaLabel}\n // persistent: 외부 클릭·ESC 로 닫히지 않음\n // 아니면: 앵커(트리거) 위 클릭은 소비자가 토글하므로 여기서 닫지 않는다(중복 토글 방지)\n onPointerDownOutside={e => {\n if (persistent || anchorRef.current?.contains(e.target as Node)) {\n e.preventDefault();\n }\n }}\n // 앵커(예: input)에 이미 포커스가 있는 상태로 열리는 경우(클릭 즉시 오픈), Radix는 포커스가\n // 레이어 밖에 있다고 판단해 열리자마자 닫아버린다 — 앵커 내부 포커스는 outside로 취급하지 않는다.\n onFocusOutside={e => {\n if (persistent || anchorRef.current?.contains(e.target as Node)) {\n e.preventDefault();\n }\n }}\n onEscapeKeyDown={persistent ? e => e.preventDefault() : undefined}\n onOpenAutoFocus={onOpenAutoFocus}\n onCloseAutoFocus={onCloseAutoFocus}\n onClick={autoClose ? () => onClose?.() : undefined}\n className={cn(\n 'z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) py-4 shadow-normal-sm',\n maxHeight != null && 'overflow-y-auto',\n fit && 'min-w-(--radix-popover-trigger-width)',\n FLOATING_SLIDE_ANIM,\n className,\n )}\n style={{ maxHeight, ...style }}\n >\n {children}\n </RPopover.Content>\n </RPopover.Portal>\n </RPopover.Root>\n );\n}\n\nSPortal.displayName = 'SPortal';\n"]}
1
+ {"version":3,"sources":["../../../src/lib/cn.ts","../../../src/lib/floating-anim.ts","../../../src/lib/portal-container.tsx","../../../src/components/SPortal/SPortal.tsx"],"names":[],"mappings":"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","file":"SPortal.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n/**\n * theme.css 의 --text-* 는 기본 Tailwind 폰트사이즈 스케일(xs/sm/base...)이 아닌\n * 숫자 스케일(10~32)이라, 확장 없이는 tailwind-merge 가 이를 텍스트 컬러 그룹으로\n * 오인해 text-12 같은 클래스를 text-(--color-var) 뒤에서 충돌 제거해버린다.\n */\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [{ text: ['10', '11', '12', '13', '14', '16', '18', '20', '24', '28', '32'] }],\n },\n },\n});\n\n/**\n * className 병합 유틸.\n * clsx로 조건부 클래스를 합치고, tailwind-merge로 Tailwind 클래스 충돌을 해소한다.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * Radix Floating(SPopover/STooltip/RTooltip) Content 의 placement 방향별 슬라이드+페이드.\n * Radix 가 붙이는 data-side(실제 렌더 방향)·data-state 에 맞춰 열림/닫힘 애니메이션을 적용한다.\n * (sd-floating-portal 의 sd-floating-slide-* 포팅 — 키프레임은 theme.css `--animate-floating-*`)\n *\n * 예: side=top 이면 아래(4px)에서 위로 올라오며 나타나고, 닫힐 때 다시 내려가며 사라진다.\n *\n * @radix-ui/react-popover · react-dropdown-menu 의 data-state 는 open/closed 지만,\n * @radix-ui/react-tooltip 은 open 대신 delayed-open/instant-open 을 쓴다 — 두 케이스 모두 커버한다.\n * Tailwind JIT는 소스에 리터럴로 등장하는 클래스명만 스캔하므로 템플릿으로 조합하지 않고 전부 나열한다.\n */\nexport const FLOATING_SLIDE_ANIM = [\n 'data-[state=open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=delayed-open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=instant-open]:data-[side=top]:animate-floating-in-top',\n 'data-[state=closed]:data-[side=top]:animate-floating-out-top',\n 'data-[state=open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=delayed-open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=instant-open]:data-[side=bottom]:animate-floating-in-bottom',\n 'data-[state=closed]:data-[side=bottom]:animate-floating-out-bottom',\n 'data-[state=open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=delayed-open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=instant-open]:data-[side=left]:animate-floating-in-left',\n 'data-[state=closed]:data-[side=left]:animate-floating-out-left',\n 'data-[state=open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=delayed-open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=instant-open]:data-[side=right]:animate-floating-in-right',\n 'data-[state=closed]:data-[side=right]:animate-floating-out-right',\n].join(' ');\n","'use client';\n\nimport { createContext, useContext } from 'react';\n\n/**\n * Floating 컴포넌트(STooltip/SPopover/SSelect listbox/SDropdownButton/SDatePicker 등)가\n * 자신의 Portal 을 어디로 렌더할지 결정하는 컨텍스트.\n *\n * 기본값(null)이면 각 컴포넌트는 body 로 portal 한다.\n * SModalContainer 는 모달이 열려있는 동안 이 값을 \"모달 스태킹 밴드(z-1001) 안의 전용 노드\"로\n * 채워서, 모달 안에서 열린 floating 이 모달 딤(z-1000) 뒤에 가려지지 않고 위로 뜨게 한다.\n * 반대로 모달 밖(베이스 화면)에서 열린 floating 은 여전히 body(z-50)로 나가므로,\n * 나중에 뜬 모달이 정상적으로 그 위를 덮는다. (소속에 따라 상대적으로 결정)\n */\nconst PortalContainerContext = createContext<HTMLElement | null>(null);\nconst InsideModalContext = createContext(false);\n\nexport const PortalContainerProvider = PortalContainerContext.Provider;\nexport const InsideModalProvider = InsideModalContext.Provider;\n\n/**\n * 현재 소속된 Portal 컨테이너를 반환한다.\n * Radix `*.Portal` 의 `container`, Ariakit 의 `portalElement` 에 그대로 넘기면 되며,\n * 컨테이너가 없을 때(모달 밖)는 `undefined` 를 반환해 각 라이브러리 기본값(body)을 쓴다.\n */\nexport function usePortalContainer(): HTMLElement | undefined {\n return useContext(PortalContainerContext) ?? undefined;\n}\n\n/**\n * 현재 floating 이 모달 내부 컨텍스트에서 렌더링되는지 반환한다.\n * portal target ref 가 아직 준비되지 않은 첫 렌더에서도 true를 유지해\n * 모달 내부 전용 우회 로직이 깜빡이지 않도록 한다.\n */\nexport function useInsideModal(): boolean {\n return useContext(InsideModalContext);\n}\n","'use client';\n\nimport { type CSSProperties, type ReactNode, type RefObject } from 'react';\nimport * as RPopover from '@radix-ui/react-popover';\nimport { cn } from '../../lib/cn';\nimport { FLOATING_SLIDE_ANIM } from '../../lib/floating-anim';\nimport { usePortalContainer } from '../../lib/portal-container';\n\nexport type SPortalPlacement = 'top' | 'bottom' | 'left' | 'right';\nexport type SPortalAlign = 'start' | 'center' | 'end';\n\n/** Radix Anchor 가 위치 계산에 쓰는 최소 인터페이스 (HTMLElement 가 충족한다). */\ntype Measurable = { getBoundingClientRect(): DOMRect };\n\nexport interface SPortalProps {\n /** 표시 여부 (제어). onClose 와 함께 사용한다. */\n open: boolean;\n /** 외부 클릭 · ESC 등으로 닫힘이 요청될 때 (sd-portal `sdClose` 대응). */\n onClose?: () => void;\n /** 열림/닫힘 변경 전체 콜백 (선택). */\n onOpenChange?: (open: boolean) => void;\n /**\n * 위치 기준이 되는 앵커 엘리먼트 ref (sd-portal `parentRef` 대응).\n * 트리거 버튼 등에 그대로 걸면 그 엘리먼트에 붙어서 뜬다.\n */\n anchorRef: RefObject<HTMLElement | null>;\n /** 표시 방향 (공간이 부족하면 자동으로 반대 방향으로 전환). */\n placement?: SPortalPlacement;\n /** 교차축 정렬 (start·center·end). */\n align?: SPortalAlign;\n /** 앵커로부터의 간격 px (sd-portal offset[1]). */\n offset?: number;\n /** 정렬축 오프셋 px (sd-portal offset[0]). */\n alignOffset?: number;\n /** 앵커 너비에 맞춰 최소 너비를 잡는다. */\n fit?: boolean;\n /** 콘텐츠 최대 높이 px. 넘치면 세로 스크롤. */\n maxHeight?: number;\n /** 콘텐츠 내부를 클릭하면 자동으로 닫는다. */\n autoClose?: boolean;\n /** 외부 클릭 · ESC 로 닫히지 않게 한다. */\n persistent?: boolean;\n /** 뷰포트 경계 여백 px (충돌 회피 패딩). */\n viewportPadding?: number;\n /** 열릴 때 포커스 이동 처리 (기본은 Radix가 콘텐츠로 포커스를 옮김). e.preventDefault()로 막을 수 있다. */\n onOpenAutoFocus?: (event: Event) => void;\n /** 닫힐 때 포커스 복귀 처리 (기본은 Radix가 앵커로 포커스를 되돌림). e.preventDefault()로 막을 수 있다. */\n onCloseAutoFocus?: (event: Event) => void;\n /** 본문 내용 (콘텐츠 자유). */\n children?: ReactNode;\n className?: string;\n style?: CSSProperties;\n ariaLabel?: string;\n}\n\n/**\n * SPortal — sd-portal/sd-floating-portal 의 사용성을 Radix Popover 로 옮긴 범용 앵커드 플로팅 컨테이너.\n *\n * 트리거를 감싸지 않고 `anchorRef` 로 임의의 엘리먼트에 붙는다(=sd-portal `parentRef`).\n * open/onClose 로 외부에서 제어하며, flip·collision·스크롤 재배치·외부클릭·ESC·모달 포털은\n * Radix 가 처리한다. 콘텐츠는 children 으로 완전히 자유롭다.\n */\nexport function SPortal({\n open,\n onClose,\n onOpenChange,\n anchorRef,\n placement = 'bottom',\n align = 'start',\n offset = 4,\n alignOffset = 0,\n fit = false,\n maxHeight,\n autoClose = false,\n persistent = false,\n viewportPadding = 0,\n onOpenAutoFocus,\n onCloseAutoFocus,\n children,\n className,\n style,\n ariaLabel,\n}: SPortalProps) {\n const portalContainer = usePortalContainer();\n\n return (\n <RPopover.Root\n open={open}\n onOpenChange={next => {\n onOpenChange?.(next);\n if (!next) onClose?.();\n }}\n >\n <RPopover.Anchor virtualRef={anchorRef as RefObject<Measurable>} />\n <RPopover.Portal container={portalContainer}>\n <RPopover.Content\n side={placement}\n align={align}\n sideOffset={offset}\n alignOffset={alignOffset}\n collisionPadding={viewportPadding}\n aria-label={ariaLabel}\n // persistent: 외부 클릭·ESC 로 닫히지 않음\n // 아니면: 앵커(트리거) 위 클릭은 소비자가 토글하므로 여기서 닫지 않는다(중복 토글 방지)\n onPointerDownOutside={e => {\n if (persistent || anchorRef.current?.contains(e.target as Node)) {\n e.preventDefault();\n }\n }}\n // 앵커(예: input)에 이미 포커스가 있는 상태로 열리는 경우(클릭 즉시 오픈), Radix는 포커스가\n // 레이어 밖에 있다고 판단해 열리자마자 닫아버린다 — 앵커 내부 포커스는 outside로 취급하지 않는다.\n onFocusOutside={e => {\n if (persistent || anchorRef.current?.contains(e.target as Node)) {\n e.preventDefault();\n }\n }}\n onEscapeKeyDown={persistent ? e => e.preventDefault() : undefined}\n onOpenAutoFocus={onOpenAutoFocus}\n onCloseAutoFocus={onCloseAutoFocus}\n onClick={autoClose ? () => onClose?.() : undefined}\n className={cn(\n 'z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) shadow-normal-sm',\n maxHeight != null && 'overflow-y-auto',\n fit && 'min-w-(--radix-popover-trigger-width)',\n FLOATING_SLIDE_ANIM,\n className,\n )}\n style={{ maxHeight, ...style }}\n >\n {children}\n </RPopover.Content>\n </RPopover.Portal>\n </RPopover.Root>\n );\n}\n\nSPortal.displayName = 'SPortal';\n"]}
@@ -3091,9 +3091,12 @@ function SGhostButton({
3091
3091
  size = "sm",
3092
3092
  intent = "default",
3093
3093
  ariaLabel = "",
3094
+ ariaPressed,
3094
3095
  tooltipText,
3095
3096
  disabled = false,
3096
3097
  onClick,
3098
+ anchorClassName,
3099
+ anchorStyle,
3097
3100
  className,
3098
3101
  style
3099
3102
  }) {
@@ -3116,6 +3119,8 @@ function SGhostButton({
3116
3119
  };
3117
3120
  const s = SIZE[size];
3118
3121
  const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
3122
+ const text = (tooltipText ?? "").trim();
3123
+ const hasTooltip = text !== "";
3119
3124
  const btn = /* @__PURE__ */ jsxRuntime.jsxs(
3120
3125
  "button",
3121
3126
  {
@@ -3123,6 +3128,7 @@ function SGhostButton({
3123
3128
  disabled,
3124
3129
  "aria-disabled": disabled ? "true" : void 0,
3125
3130
  "aria-label": ariaLabel.trim() || void 0,
3131
+ "aria-pressed": ariaPressed,
3126
3132
  tabIndex: disabled ? -1 : void 0,
3127
3133
  onClick: (e) => {
3128
3134
  if (disabled) {
@@ -3156,13 +3162,13 @@ function SGhostButton({
3156
3162
  ]
3157
3163
  }
3158
3164
  );
3159
- const text = (tooltipText ?? "").trim();
3160
- if (text === "") return btn;
3165
+ if (!hasTooltip) return btn;
3161
3166
  return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpen, children: [
3162
3167
  /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3163
3168
  "span",
3164
3169
  {
3165
- className: "inline-flex",
3170
+ className: cn("inline-flex", anchorClassName),
3171
+ style: anchorStyle,
3166
3172
  onMouseEnter: openNow,
3167
3173
  onMouseLeave: startHide,
3168
3174
  onFocus: openNow,
@@ -3535,7 +3541,7 @@ function SPortal({
3535
3541
  onCloseAutoFocus,
3536
3542
  onClick: autoClose ? () => onClose?.() : void 0,
3537
3543
  className: cn(
3538
- "z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) py-4 shadow-normal-sm",
3544
+ "z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) shadow-normal-sm",
3539
3545
  maxHeight != null && "overflow-y-auto",
3540
3546
  fit && "min-w-(--radix-popover-trigger-width)",
3541
3547
  FLOATING_SLIDE_ANIM,
@@ -3885,7 +3891,7 @@ var SSelect = react.forwardRef(function SSelect2({
3885
3891
  event.preventDefault();
3886
3892
  triggerRef.current?.focus();
3887
3893
  },
3888
- className: "rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] py-[4px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
3894
+ className: "rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
3889
3895
  style: {
3890
3896
  ...dropdownWidth ? { width: dropdownWidth } : {},
3891
3897
  maxWidth: maxDropdownWidth
@@ -3936,6 +3942,10 @@ var SSelect = react.forwardRef(function SSelect2({
3936
3942
  onSelectAll: handleSelectAll,
3937
3943
  onSelect: handleSelect,
3938
3944
  onHoverValue: (value2) => {
3945
+ if (value2 == null) {
3946
+ setFocusedIndex(-1);
3947
+ return;
3948
+ }
3939
3949
  const index = navigableItems.findIndex(
3940
3950
  (item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
3941
3951
  );
@@ -3984,6 +3994,7 @@ function ItemList({
3984
3994
  ),
3985
3995
  style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: 12 },
3986
3996
  onMouseEnter: () => onHoverValue("__select_all__"),
3997
+ onMouseLeave: () => onHoverValue(null),
3987
3998
  onClick: onSelectAll,
3988
3999
  children: [
3989
4000
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4042,10 +4053,13 @@ function ItemList({
4042
4053
  itemBase,
4043
4054
  isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
4044
4055
  focusedValue === o.value && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]",
4045
- activeClass
4056
+ // 비활성 항목은 hover/focus 스타일 대상에서 제외한다.
4057
+ // (:hover 는 disabled 버튼에도 매칭되므로 클래스 자체를 붙이지 않아야 한다)
4058
+ !o.disabled && activeClass
4046
4059
  ),
4047
4060
  style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
4048
4061
  onMouseEnter: () => onHoverValue(o.value),
4062
+ onMouseLeave: () => onHoverValue(null),
4049
4063
  onClick: () => onSelect(o.value),
4050
4064
  children: [
4051
4065
  multi && /* @__PURE__ */ jsxRuntime.jsx(