sellmate-design-system-react 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/SActionModal/SActionModal.cjs +11 -3
- package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
- package/dist/components/SActionModal/SActionModal.js +11 -3
- package/dist/components/SActionModal/SActionModal.js.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs +226 -209
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.js +226 -209
- package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
- package/dist/components/SCalendar/SCalendar.cjs +9 -1
- package/dist/components/SCalendar/SCalendar.cjs.map +1 -1
- package/dist/components/SCalendar/SCalendar.js +9 -1
- package/dist/components/SCalendar/SCalendar.js.map +1 -1
- package/dist/components/SChip/SChip.cjs +9 -1
- package/dist/components/SChip/SChip.cjs.map +1 -1
- package/dist/components/SChip/SChip.d.ts +1 -1
- package/dist/components/SChip/SChip.js +9 -1
- package/dist/components/SChip/SChip.js.map +1 -1
- package/dist/components/SChipInput/SChipInput.cjs +260 -235
- package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
- package/dist/components/SChipInput/SChipInput.js +259 -235
- package/dist/components/SChipInput/SChipInput.js.map +1 -1
- package/dist/components/SCircleProgress/SCircleProgress.cjs +56 -58
- package/dist/components/SCircleProgress/SCircleProgress.cjs.map +1 -1
- package/dist/components/SCircleProgress/SCircleProgress.js +56 -58
- package/dist/components/SCircleProgress/SCircleProgress.js.map +1 -1
- package/dist/components/SConfirmModal/SConfirmModal.cjs +9 -1
- package/dist/components/SConfirmModal/SConfirmModal.cjs.map +1 -1
- package/dist/components/SConfirmModal/SConfirmModal.js +9 -1
- package/dist/components/SConfirmModal/SConfirmModal.js.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.cjs +247 -221
- package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.js +247 -221
- package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs +308 -256
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.js +308 -256
- package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
- package/dist/components/SDropdownButton/SDropdownButton.cjs +51 -4
- package/dist/components/SDropdownButton/SDropdownButton.cjs.map +1 -1
- package/dist/components/SDropdownButton/SDropdownButton.js +52 -5
- package/dist/components/SDropdownButton/SDropdownButton.js.map +1 -1
- package/dist/components/SField/SField.cjs +226 -21
- package/dist/components/SField/SField.cjs.map +1 -1
- package/dist/components/SField/SField.d.ts +11 -0
- package/dist/components/SField/SField.js +226 -21
- package/dist/components/SField/SField.js.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.cjs +306 -278
- package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.d.ts +4 -2
- package/dist/components/SFilePicker/SFilePicker.js +306 -278
- package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
- package/dist/components/SGhostButton/SGhostButton.cjs +8 -0
- package/dist/components/SGhostButton/SGhostButton.cjs.map +1 -1
- package/dist/components/SGhostButton/SGhostButton.js +8 -0
- package/dist/components/SGhostButton/SGhostButton.js.map +1 -1
- package/dist/components/SGuide/SGuide.cjs +12 -1
- package/dist/components/SGuide/SGuide.cjs.map +1 -1
- package/dist/components/SGuide/SGuide.js +12 -1
- package/dist/components/SGuide/SGuide.js.map +1 -1
- package/dist/components/SInput/SInput.cjs +226 -209
- package/dist/components/SInput/SInput.cjs.map +1 -1
- package/dist/components/SInput/SInput.js +226 -209
- package/dist/components/SInput/SInput.js.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs +640 -453
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +3 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.js +640 -453
- package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
- package/dist/components/SLoadingContainer/SLoadingContainer.cjs +64 -58
- package/dist/components/SLoadingContainer/SLoadingContainer.cjs.map +1 -1
- package/dist/components/SLoadingContainer/SLoadingContainer.js +64 -58
- package/dist/components/SLoadingContainer/SLoadingContainer.js.map +1 -1
- package/dist/components/SLoadingContainer/loading.cjs +64 -58
- package/dist/components/SLoadingContainer/loading.cjs.map +1 -1
- package/dist/components/SLoadingContainer/loading.js +64 -58
- package/dist/components/SLoadingContainer/loading.js.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.cjs +68 -61
- package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.js +68 -61
- package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
- package/dist/components/SModalContainer/SModalContainer.cjs +9 -1
- package/dist/components/SModalContainer/SModalContainer.cjs.map +1 -1
- package/dist/components/SModalContainer/SModalContainer.js +9 -1
- package/dist/components/SModalContainer/SModalContainer.js.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.cjs +334 -50
- package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.d.ts +2 -0
- package/dist/components/SNumberInput/SNumberInput.js +334 -50
- package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
- package/dist/components/SPagination/SPagination.cjs +1 -1
- package/dist/components/SPagination/SPagination.cjs.map +1 -1
- package/dist/components/SPagination/SPagination.js +1 -1
- package/dist/components/SPagination/SPagination.js.map +1 -1
- package/dist/components/SPopover/SPopover.cjs +20 -3
- package/dist/components/SPopover/SPopover.cjs.map +1 -1
- package/dist/components/SPopover/SPopover.js +20 -4
- package/dist/components/SPopover/SPopover.js.map +1 -1
- package/dist/components/SPopup/SPopup.cjs +2 -2
- package/dist/components/SPopup/SPopup.cjs.map +1 -1
- package/dist/components/SPopup/SPopup.js +2 -2
- package/dist/components/SPopup/SPopup.js.map +1 -1
- package/dist/components/SPortal/SPortal.cjs +8 -0
- package/dist/components/SPortal/SPortal.cjs.map +1 -1
- package/dist/components/SPortal/SPortal.js +8 -0
- package/dist/components/SPortal/SPortal.js.map +1 -1
- package/dist/components/SSelect/SSelect.cjs +76 -32
- package/dist/components/SSelect/SSelect.cjs.map +1 -1
- package/dist/components/SSelect/SSelect.js +76 -32
- package/dist/components/SSelect/SSelect.js.map +1 -1
- package/dist/components/STable/STable.cjs +133 -91
- package/dist/components/STable/STable.cjs.map +1 -1
- package/dist/components/STable/STable.js +133 -91
- package/dist/components/STable/STable.js.map +1 -1
- package/dist/components/STextLink/STextLink.cjs +137 -3
- package/dist/components/STextLink/STextLink.cjs.map +1 -1
- package/dist/components/STextLink/STextLink.d.ts +1 -1
- package/dist/components/STextLink/STextLink.js +118 -4
- package/dist/components/STextLink/STextLink.js.map +1 -1
- package/dist/components/STextarea/STextarea.cjs +226 -21
- package/dist/components/STextarea/STextarea.cjs.map +1 -1
- package/dist/components/STextarea/STextarea.js +226 -21
- package/dist/components/STextarea/STextarea.js.map +1 -1
- package/dist/components/SToast/SToast.cjs +8 -0
- package/dist/components/SToast/SToast.cjs.map +1 -1
- package/dist/components/SToast/SToast.js +8 -0
- package/dist/components/SToast/SToast.js.map +1 -1
- package/dist/components/SToast/SToastContainer.cjs +11 -3
- package/dist/components/SToast/SToastContainer.cjs.map +1 -1
- package/dist/components/SToast/SToastContainer.js +11 -3
- package/dist/components/SToast/SToastContainer.js.map +1 -1
- package/dist/components/STooltip/STooltip.cjs +217 -14
- package/dist/components/STooltip/STooltip.cjs.map +1 -1
- package/dist/components/STooltip/STooltip.js +217 -14
- package/dist/components/STooltip/STooltip.js.map +1 -1
- package/dist/index.cjs +532 -300
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +531 -300
- package/dist/index.js.map +1 -1
- package/dist/lib/floating-anim.cjs +8 -0
- package/dist/lib/floating-anim.cjs.map +1 -1
- package/dist/lib/floating-anim.d.ts +5 -1
- package/dist/lib/floating-anim.js +8 -0
- package/dist/lib/floating-anim.js.map +1 -1
- package/dist/styles.css +175 -66
- package/dist/theme.css +49 -22
- package/package.json +1 -1
|
@@ -41,12 +41,20 @@ function cn(...inputs) {
|
|
|
41
41
|
// src/lib/floating-anim.ts
|
|
42
42
|
var FLOATING_SLIDE_ANIM = [
|
|
43
43
|
"data-[state=open]:data-[side=top]:animate-floating-in-top",
|
|
44
|
+
"data-[state=delayed-open]:data-[side=top]:animate-floating-in-top",
|
|
45
|
+
"data-[state=instant-open]:data-[side=top]:animate-floating-in-top",
|
|
44
46
|
"data-[state=closed]:data-[side=top]:animate-floating-out-top",
|
|
45
47
|
"data-[state=open]:data-[side=bottom]:animate-floating-in-bottom",
|
|
48
|
+
"data-[state=delayed-open]:data-[side=bottom]:animate-floating-in-bottom",
|
|
49
|
+
"data-[state=instant-open]:data-[side=bottom]:animate-floating-in-bottom",
|
|
46
50
|
"data-[state=closed]:data-[side=bottom]:animate-floating-out-bottom",
|
|
47
51
|
"data-[state=open]:data-[side=left]:animate-floating-in-left",
|
|
52
|
+
"data-[state=delayed-open]:data-[side=left]:animate-floating-in-left",
|
|
53
|
+
"data-[state=instant-open]:data-[side=left]:animate-floating-in-left",
|
|
48
54
|
"data-[state=closed]:data-[side=left]:animate-floating-out-left",
|
|
49
55
|
"data-[state=open]:data-[side=right]:animate-floating-in-right",
|
|
56
|
+
"data-[state=delayed-open]:data-[side=right]:animate-floating-in-right",
|
|
57
|
+
"data-[state=instant-open]:data-[side=right]:animate-floating-in-right",
|
|
50
58
|
"data-[state=closed]:data-[side=right]:animate-floating-out-right"
|
|
51
59
|
].join(" ");
|
|
52
60
|
var PortalContainerContext = react.createContext(null);
|
|
@@ -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) 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 */\nexport const FLOATING_SLIDE_ANIM = [\n 'data-[state=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=closed]:data-[side=bottom]:animate-floating-out-bottom',\n 'data-[state=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=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);\n\nexport const PortalContainerProvider = PortalContainerContext.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","'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);\n\nexport const PortalContainerProvider = PortalContainerContext.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","'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"]}
|
|
@@ -19,12 +19,20 @@ function cn(...inputs) {
|
|
|
19
19
|
// src/lib/floating-anim.ts
|
|
20
20
|
var FLOATING_SLIDE_ANIM = [
|
|
21
21
|
"data-[state=open]:data-[side=top]:animate-floating-in-top",
|
|
22
|
+
"data-[state=delayed-open]:data-[side=top]:animate-floating-in-top",
|
|
23
|
+
"data-[state=instant-open]:data-[side=top]:animate-floating-in-top",
|
|
22
24
|
"data-[state=closed]:data-[side=top]:animate-floating-out-top",
|
|
23
25
|
"data-[state=open]:data-[side=bottom]:animate-floating-in-bottom",
|
|
26
|
+
"data-[state=delayed-open]:data-[side=bottom]:animate-floating-in-bottom",
|
|
27
|
+
"data-[state=instant-open]:data-[side=bottom]:animate-floating-in-bottom",
|
|
24
28
|
"data-[state=closed]:data-[side=bottom]:animate-floating-out-bottom",
|
|
25
29
|
"data-[state=open]:data-[side=left]:animate-floating-in-left",
|
|
30
|
+
"data-[state=delayed-open]:data-[side=left]:animate-floating-in-left",
|
|
31
|
+
"data-[state=instant-open]:data-[side=left]:animate-floating-in-left",
|
|
26
32
|
"data-[state=closed]:data-[side=left]:animate-floating-out-left",
|
|
27
33
|
"data-[state=open]:data-[side=right]:animate-floating-in-right",
|
|
34
|
+
"data-[state=delayed-open]:data-[side=right]:animate-floating-in-right",
|
|
35
|
+
"data-[state=instant-open]:data-[side=right]:animate-floating-in-right",
|
|
28
36
|
"data-[state=closed]:data-[side=right]:animate-floating-out-right"
|
|
29
37
|
].join(" ");
|
|
30
38
|
var PortalContainerContext = createContext(null);
|
|
@@ -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) 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 */\nexport const FLOATING_SLIDE_ANIM = [\n 'data-[state=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=closed]:data-[side=bottom]:animate-floating-out-bottom',\n 'data-[state=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=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);\n\nexport const PortalContainerProvider = PortalContainerContext.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","'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);\n\nexport const PortalContainerProvider = PortalContainerContext.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","'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"]}
|
|
@@ -2883,12 +2883,20 @@ var SIcon = react.forwardRef(function SIcon2({ name, size = 24, color, rotate, l
|
|
|
2883
2883
|
// src/lib/floating-anim.ts
|
|
2884
2884
|
var FLOATING_SLIDE_ANIM = [
|
|
2885
2885
|
"data-[state=open]:data-[side=top]:animate-floating-in-top",
|
|
2886
|
+
"data-[state=delayed-open]:data-[side=top]:animate-floating-in-top",
|
|
2887
|
+
"data-[state=instant-open]:data-[side=top]:animate-floating-in-top",
|
|
2886
2888
|
"data-[state=closed]:data-[side=top]:animate-floating-out-top",
|
|
2887
2889
|
"data-[state=open]:data-[side=bottom]:animate-floating-in-bottom",
|
|
2890
|
+
"data-[state=delayed-open]:data-[side=bottom]:animate-floating-in-bottom",
|
|
2891
|
+
"data-[state=instant-open]:data-[side=bottom]:animate-floating-in-bottom",
|
|
2888
2892
|
"data-[state=closed]:data-[side=bottom]:animate-floating-out-bottom",
|
|
2889
2893
|
"data-[state=open]:data-[side=left]:animate-floating-in-left",
|
|
2894
|
+
"data-[state=delayed-open]:data-[side=left]:animate-floating-in-left",
|
|
2895
|
+
"data-[state=instant-open]:data-[side=left]:animate-floating-in-left",
|
|
2890
2896
|
"data-[state=closed]:data-[side=left]:animate-floating-out-left",
|
|
2891
2897
|
"data-[state=open]:data-[side=right]:animate-floating-in-right",
|
|
2898
|
+
"data-[state=delayed-open]:data-[side=right]:animate-floating-in-right",
|
|
2899
|
+
"data-[state=instant-open]:data-[side=right]:animate-floating-in-right",
|
|
2892
2900
|
"data-[state=closed]:data-[side=right]:animate-floating-out-right"
|
|
2893
2901
|
].join(" ");
|
|
2894
2902
|
|
|
@@ -3083,7 +3091,8 @@ var HIDE_DELAY_MS = 100;
|
|
|
3083
3091
|
function useBody(content, message = []) {
|
|
3084
3092
|
if (content != null) return content;
|
|
3085
3093
|
if (message.length === 0) return null;
|
|
3086
|
-
|
|
3094
|
+
const singleLine = message.length === 1;
|
|
3095
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: singleLine ? "leading-[24px]" : void 0, children: line }, i)) });
|
|
3087
3096
|
}
|
|
3088
3097
|
var STooltip = react.forwardRef(function STooltip2({
|
|
3089
3098
|
content,
|
|
@@ -3141,7 +3150,7 @@ var STooltip = react.forwardRef(function STooltip2({
|
|
|
3141
3150
|
if (disabled || body == null) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: triggerNode });
|
|
3142
3151
|
const isHover = trigger === "hover";
|
|
3143
3152
|
const contentClass = cn(
|
|
3144
|
-
"z-50 max-w-xs rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px]",
|
|
3153
|
+
"z-50 max-w-xs rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
|
|
3145
3154
|
FLOATING_SLIDE_ANIM,
|
|
3146
3155
|
className
|
|
3147
3156
|
);
|
|
@@ -3186,11 +3195,17 @@ var STooltip = react.forwardRef(function STooltip2({
|
|
|
3186
3195
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-[12px]", children: [
|
|
3187
3196
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: body }),
|
|
3188
3197
|
useClose && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3189
|
-
|
|
3198
|
+
SGhostButton,
|
|
3190
3199
|
{
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3200
|
+
icon: "close",
|
|
3201
|
+
size: "xs",
|
|
3202
|
+
intent: tooltipType === "default" ? "inverse" : "default",
|
|
3203
|
+
ariaLabel: "\uB2EB\uAE30",
|
|
3204
|
+
className: "shrink-0",
|
|
3205
|
+
onClick: () => {
|
|
3206
|
+
cancelHide();
|
|
3207
|
+
setOpen(false);
|
|
3208
|
+
}
|
|
3194
3209
|
}
|
|
3195
3210
|
)
|
|
3196
3211
|
] }),
|
|
@@ -3228,8 +3243,8 @@ function useFormField(field) {
|
|
|
3228
3243
|
}, [ctx, name]);
|
|
3229
3244
|
}
|
|
3230
3245
|
var FIELD_SIZE = {
|
|
3231
|
-
sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon:
|
|
3232
|
-
md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon:
|
|
3246
|
+
sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
|
|
3247
|
+
md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
|
|
3233
3248
|
};
|
|
3234
3249
|
var ALIGN = {
|
|
3235
3250
|
start: "flex-start",
|
|
@@ -3244,6 +3259,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3244
3259
|
labelTooltip = "",
|
|
3245
3260
|
labelTooltipProps,
|
|
3246
3261
|
labelWidth,
|
|
3262
|
+
labelHeight,
|
|
3247
3263
|
size = "sm",
|
|
3248
3264
|
addonLabel = "",
|
|
3249
3265
|
addonAlign = "start",
|
|
@@ -3258,6 +3274,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3258
3274
|
readOnly = false,
|
|
3259
3275
|
width,
|
|
3260
3276
|
multiline = false,
|
|
3277
|
+
borderless = false,
|
|
3261
3278
|
children,
|
|
3262
3279
|
className,
|
|
3263
3280
|
style,
|
|
@@ -3291,8 +3308,8 @@ var SField = react.forwardRef(function SField2({
|
|
|
3291
3308
|
const resolvedSuccess = status === "pass";
|
|
3292
3309
|
const showError = resolvedError && errorMessage !== "";
|
|
3293
3310
|
const interactive = !disabled && !readOnly;
|
|
3294
|
-
const borderColor = disabled ? "var(--color-grey-45)" : interactive && focused ? "var(--sys-color-field-border-focus)" : resolvedSuccess ? "var(--sys-color-field-border-success)" : resolvedError ? "var(--sys-color-field-border-danger)" : "var(--sys-color-field-border-default)";
|
|
3295
|
-
const bgColor = disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
|
|
3311
|
+
const borderColor = borderless ? "transparent" : disabled ? "var(--color-grey-45)" : interactive && focused ? "var(--sys-color-field-border-focus)" : resolvedSuccess ? "var(--sys-color-field-border-success)" : resolvedError ? "var(--sys-color-field-border-danger)" : "var(--sys-color-field-border-default)";
|
|
3312
|
+
const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
|
|
3296
3313
|
const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
|
|
3297
3314
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3298
3315
|
"div",
|
|
@@ -3309,7 +3326,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3309
3326
|
{
|
|
3310
3327
|
className: "flex shrink-0 items-center font-bold text-(--sys-color-field-text-default)",
|
|
3311
3328
|
style: {
|
|
3312
|
-
height: s.height,
|
|
3329
|
+
height: cssLen(labelHeight) ?? s.height,
|
|
3313
3330
|
marginRight: s.fieldGap,
|
|
3314
3331
|
width: cssLen(labelWidth),
|
|
3315
3332
|
fontSize: s.fontSize,
|
|
@@ -3335,14 +3352,14 @@ var SField = react.forwardRef(function SField2({
|
|
|
3335
3352
|
{
|
|
3336
3353
|
className: cn(
|
|
3337
3354
|
"relative box-border flex min-w-0 items-stretch overflow-hidden border border-solid border-(--field-bc)",
|
|
3338
|
-
interactive && "hover:border-(--sys-color-field-border-focus) hover:shadow-[0px_0px_4px_0px_#0071ff66] focus-within:border-(--sys-color-field-border-focus) focus-within:shadow-[0px_0px_4px_0px_#0071ff66]",
|
|
3355
|
+
interactive && !borderless && "hover:border-(--sys-color-field-border-focus) hover:shadow-[0px_0px_4px_0px_#0071ff66] focus-within:border-(--sys-color-field-border-focus) focus-within:shadow-[0px_0px_4px_0px_#0071ff66]",
|
|
3339
3356
|
disabled && "cursor-not-allowed"
|
|
3340
3357
|
),
|
|
3341
3358
|
style: {
|
|
3342
3359
|
// @ts-expect-error CSS 변수
|
|
3343
3360
|
"--field-bc": borderColor,
|
|
3344
3361
|
// focused/hovered prop이 명시된 경우 강조를 즉시 반영
|
|
3345
|
-
...interactive && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
|
|
3362
|
+
...interactive && !borderless && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
|
|
3346
3363
|
...multiline ? { minHeight: s.height } : { height: s.height },
|
|
3347
3364
|
"width": cssLen(width),
|
|
3348
3365
|
"borderRadius": s.radius,
|
|
@@ -3387,15 +3404,28 @@ function flattenOptions(options, depth = 1) {
|
|
|
3387
3404
|
const out = [];
|
|
3388
3405
|
for (const o of options) {
|
|
3389
3406
|
const hasChildren = Array.isArray(o.children) && o.children.length > 0;
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3407
|
+
if (hasChildren) {
|
|
3408
|
+
const childFlat = flattenOptions(o.children, depth + 1);
|
|
3409
|
+
const allChildrenDisabled = childFlat.every((c) => c.disabled);
|
|
3410
|
+
out.push({
|
|
3411
|
+
value: String(o.value),
|
|
3412
|
+
label: o.label,
|
|
3413
|
+
disabled: !!o.disabled || allChildrenDisabled,
|
|
3414
|
+
depth,
|
|
3415
|
+
isGroup: true,
|
|
3416
|
+
raw: o.value
|
|
3417
|
+
});
|
|
3418
|
+
out.push(...childFlat);
|
|
3419
|
+
} else {
|
|
3420
|
+
out.push({
|
|
3421
|
+
value: String(o.value),
|
|
3422
|
+
label: o.label,
|
|
3423
|
+
disabled: !!o.disabled,
|
|
3424
|
+
depth,
|
|
3425
|
+
isGroup: false,
|
|
3426
|
+
raw: o.value
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3399
3429
|
}
|
|
3400
3430
|
return out;
|
|
3401
3431
|
}
|
|
@@ -3519,6 +3549,9 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3519
3549
|
);
|
|
3520
3550
|
const flat = react.useMemo(() => flattenOptions(options), [options]);
|
|
3521
3551
|
const selectable = react.useMemo(() => selectableOptions(flat), [flat]);
|
|
3552
|
+
const hasLeaf = react.useMemo(() => flat.some((o) => !o.isGroup), [flat]);
|
|
3553
|
+
const allOptionsDisabled = hasLeaf && selectable.length === 0;
|
|
3554
|
+
const effectiveDisabled = disabled || allOptionsDisabled;
|
|
3522
3555
|
const showSearch = useSearch && selectable.length >= SEARCH_THRESHOLD;
|
|
3523
3556
|
const labelByValue = react.useMemo(() => new Map(flat.map((o) => [o.value, o.label])), [flat]);
|
|
3524
3557
|
const rawByValue = react.useMemo(() => new Map(flat.map((o) => [o.value, o.raw])), [flat]);
|
|
@@ -3569,6 +3602,7 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3569
3602
|
const selectStore = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3570
3603
|
Ariakit__namespace.SelectProvider,
|
|
3571
3604
|
{
|
|
3605
|
+
animated: portalContainer == null ? 150 : false,
|
|
3572
3606
|
value: multi ? selected : selected[0] ?? "",
|
|
3573
3607
|
setValue: (v) => emit(Array.isArray(v) ? v : v === "" ? [] : [v]),
|
|
3574
3608
|
open,
|
|
@@ -3584,10 +3618,10 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3584
3618
|
Ariakit__namespace.Select,
|
|
3585
3619
|
{
|
|
3586
3620
|
ref: triggerRef,
|
|
3587
|
-
disabled,
|
|
3621
|
+
disabled: effectiveDisabled,
|
|
3588
3622
|
className: "flex h-full w-full cursor-pointer items-center justify-between gap-[8px] bg-transparent px-[12px] text-left text-[12px] leading-[20px] outline-none disabled:cursor-not-allowed",
|
|
3589
3623
|
style: {
|
|
3590
|
-
color:
|
|
3624
|
+
color: effectiveDisabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
|
|
3591
3625
|
},
|
|
3592
3626
|
children: [
|
|
3593
3627
|
isHtmlLabel(triggerText) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3602,7 +3636,7 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3602
3636
|
{
|
|
3603
3637
|
name: "chevronDown",
|
|
3604
3638
|
size: 12,
|
|
3605
|
-
color:
|
|
3639
|
+
color: effectiveDisabled ? "var(--cmp-select-icon-disabled)" : "var(--cmp-select-icon-default)",
|
|
3606
3640
|
className: cn("shrink-0 transition-transform duration-200", open && "rotate-180")
|
|
3607
3641
|
}
|
|
3608
3642
|
)
|
|
@@ -3616,7 +3650,7 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3616
3650
|
portalElement: portalContainer,
|
|
3617
3651
|
gutter: 4,
|
|
3618
3652
|
sameWidth: !dropdownWidth,
|
|
3619
|
-
className: "z-50 overflow-auto rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)]
|
|
3653
|
+
className: "z-50 overflow-auto rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)] data-[enter]:animate-floating-in-bottom data-[leave]:animate-floating-out-bottom",
|
|
3620
3654
|
style: {
|
|
3621
3655
|
maxHeight: dropdownHeight,
|
|
3622
3656
|
...dropdownWidth ? { width: dropdownWidth } : {},
|
|
@@ -3657,7 +3691,7 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3657
3691
|
),
|
|
3658
3692
|
hasItems ? /* @__PURE__ */ jsxRuntime.jsx(ItemList, { items: filtered, multi, selected, useSearch: showSearch }) : (
|
|
3659
3693
|
// 원본 sd-select-listbox__empty (표시할 항목이 없을 때)
|
|
3660
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-[12px] py-[12px] text-
|
|
3694
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-[12px] py-[12px] text-left text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-disabled)]", children: "\uD45C\uC2DC\uD560 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." })
|
|
3661
3695
|
)
|
|
3662
3696
|
]
|
|
3663
3697
|
}
|
|
@@ -3680,7 +3714,7 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3680
3714
|
error: error || ruleError !== "",
|
|
3681
3715
|
status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
|
|
3682
3716
|
errorMessage: ruleError !== "" ? ruleError : errorMessage,
|
|
3683
|
-
disabled,
|
|
3717
|
+
disabled: effectiveDisabled,
|
|
3684
3718
|
width,
|
|
3685
3719
|
className,
|
|
3686
3720
|
style,
|
|
@@ -3715,7 +3749,11 @@ function ItemList({
|
|
|
3715
3749
|
{
|
|
3716
3750
|
className: cn(
|
|
3717
3751
|
"select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
|
|
3718
|
-
isDepth1 ? "border-t border-solid border-[color:var(--cmp-listItem-depth1-border-color)] bg-[var(--cmp-listItem-depth1-bg-default)] font-bold" : "bg-[var(--cmp-listItem-depth2-middle-bg)] font-medium"
|
|
3752
|
+
isDepth1 ? "border-t border-solid border-[color:var(--cmp-listItem-depth1-border-color)] bg-[var(--cmp-listItem-depth1-bg-default)] font-bold" : "bg-[var(--cmp-listItem-depth2-middle-bg)] font-medium",
|
|
3753
|
+
// 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
|
|
3754
|
+
// 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
|
|
3755
|
+
o.disabled && "text-[color:var(--cmp-listItem-content-disabled)]",
|
|
3756
|
+
o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
|
|
3719
3757
|
),
|
|
3720
3758
|
style: { paddingLeft: padLeft },
|
|
3721
3759
|
children: o.label
|
|
@@ -3742,15 +3780,21 @@ function ItemList({
|
|
|
3742
3780
|
"span",
|
|
3743
3781
|
{
|
|
3744
3782
|
className: cn(
|
|
3745
|
-
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid
|
|
3746
|
-
|
|
3783
|
+
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
|
|
3784
|
+
o.disabled ? (
|
|
3785
|
+
// 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
|
|
3786
|
+
"border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
|
|
3787
|
+
) : cn(
|
|
3788
|
+
"group-data-[active-item]:border-[color:var(--cmp-checkbox-checked-border-inverse)]",
|
|
3789
|
+
isSelected ? "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)] group-data-[active-item]:bg-transparent" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-transparent"
|
|
3790
|
+
)
|
|
3747
3791
|
),
|
|
3748
3792
|
children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3749
3793
|
SIcon,
|
|
3750
3794
|
{
|
|
3751
3795
|
name: "checkboxCheck",
|
|
3752
3796
|
size: 12,
|
|
3753
|
-
color: "var(--cmp-checkbox-checked-icon-default)"
|
|
3797
|
+
color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
|
|
3754
3798
|
}
|
|
3755
3799
|
)
|
|
3756
3800
|
}
|