sellmate-design-system-react 8.1.0 → 9.0.0-beta.10

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 (76) hide show
  1. package/AGENTS.md +279 -124
  2. package/README.md +101 -8
  3. package/dist/components/SChipFilter/README.md +70 -0
  4. package/dist/components/SChipFilter/SChipFilter.d.ts +167 -0
  5. package/dist/components/SChipFilter/index.d.ts +1 -0
  6. package/dist/components/SCircleProgress/README.md +3 -0
  7. package/dist/components/SCircleProgress/SCircleProgress.d.ts +7 -1
  8. package/dist/components/SDatePicker/README.md +43 -3
  9. package/dist/components/SDatePicker/SDatePicker.d.ts +36 -3
  10. package/dist/components/SDatePicker/index.d.ts +1 -1
  11. package/dist/components/SDateRangePicker/README.md +21 -0
  12. package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +16 -0
  13. package/dist/components/SDateRangePicker/index.d.ts +1 -1
  14. package/dist/components/SField/README.md +3 -0
  15. package/dist/components/SField/SField.d.ts +12 -0
  16. package/dist/components/SGhostButton/README.md +6 -0
  17. package/dist/components/SGnb/gnb.config.d.ts +2 -0
  18. package/dist/components/SGuide/README.md +1 -1
  19. package/dist/components/SGuide/SGuide.d.ts +1 -1
  20. package/dist/components/SIcon/README.md +6 -0
  21. package/dist/components/SIcon/icons.gen.d.ts +1 -0
  22. package/dist/components/SImage/README.md +44 -0
  23. package/dist/components/SImage/SImage.d.ts +41 -0
  24. package/dist/components/SImage/index.d.ts +1 -0
  25. package/dist/components/SKeyValueTable/README.md +1 -0
  26. package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +8 -1
  27. package/dist/components/SListItem/README.md +2 -2
  28. package/dist/components/SListItem/SListItem.d.ts +2 -2
  29. package/dist/components/SPage/README.md +23 -0
  30. package/dist/components/SPage/SPage.d.ts +27 -1
  31. package/dist/components/SPage/SPageHeader.d.ts +32 -0
  32. package/dist/components/SPage/index.d.ts +2 -1
  33. package/dist/components/SPage/page.config.d.ts +8 -0
  34. package/dist/components/SPortal/README.md +1 -1
  35. package/dist/components/SPortal/SPortal.d.ts +7 -1
  36. package/dist/components/SRadio/README.md +2 -0
  37. package/dist/components/SRadioButton/README.md +13 -0
  38. package/dist/components/SSearchInput/README.md +48 -0
  39. package/dist/components/SSearchInput/SSearchInput.d.ts +46 -0
  40. package/dist/components/SSearchInput/index.d.ts +1 -0
  41. package/dist/components/SSectionHeaderCard/README.md +10 -23
  42. package/dist/components/SSectionHeaderCard/SSectionHeaderCard.d.ts +21 -14
  43. package/dist/components/SSectionHeaderCard/index.d.ts +1 -1
  44. package/dist/components/SSelect/README.md +4 -2
  45. package/dist/components/SSelect/SSelect.d.ts +2 -2
  46. package/dist/components/STable/README.md +1 -0
  47. package/dist/components/STable/STable.d.ts +20 -0
  48. package/dist/components/STabs/README.md +0 -1
  49. package/dist/components/STabs/STabs.d.ts +2 -4
  50. package/dist/components/STabs/index.d.ts +1 -1
  51. package/dist/components/STabs/tabs.config.d.ts +3 -4
  52. package/dist/components/STag/README.md +2 -0
  53. package/dist/components/STextLink/README.md +2 -0
  54. package/dist/components/STimePicker/timepicker.config.d.ts +5 -0
  55. package/dist/components/SToggle/README.md +1 -0
  56. package/dist/components/SToggle/SToggle.d.ts +4 -1
  57. package/dist/components/STooltip/README.md +2 -0
  58. package/dist/components/STree/README.md +3 -0
  59. package/dist/components/STree/STree.d.ts +8 -0
  60. package/dist/components/STree/index.d.ts +1 -1
  61. package/dist/index.cjs +2988 -393
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.ts +3 -0
  64. package/dist/index.js +2981 -394
  65. package/dist/index.js.map +1 -1
  66. package/dist/lib/floating-width.d.ts +22 -0
  67. package/dist/lib/truncated-value-tooltip.d.ts +18 -0
  68. package/dist/llms-full.txt +545 -152
  69. package/dist/llms.txt +283 -127
  70. package/dist/styles.css +397 -36
  71. package/dist/theme.css +34 -11
  72. package/eslint/index.mjs +5 -0
  73. package/eslint/rules/field-width-grade.d.mts +41 -0
  74. package/eslint/rules/field-width-grade.mjs +294 -0
  75. package/eslint/scale.gen.mjs +1 -1
  76. package/package.json +7 -4
package/dist/index.cjs CHANGED
@@ -7,7 +7,7 @@ var react = require('react');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
8
  var reactDom = require('react-dom');
9
9
  var RCheckbox = require('@radix-ui/react-checkbox');
10
- var RPopover2 = require('@radix-ui/react-popover');
10
+ var RPopover10 = require('@radix-ui/react-popover');
11
11
  var client = require('react-dom/client');
12
12
  var RTooltip = require('@radix-ui/react-tooltip');
13
13
  var RDropdown = require('@radix-ui/react-dropdown-menu');
@@ -34,7 +34,7 @@ function _interopNamespace(e) {
34
34
  }
35
35
 
36
36
  var RCheckbox__namespace = /*#__PURE__*/_interopNamespace(RCheckbox);
37
- var RPopover2__namespace = /*#__PURE__*/_interopNamespace(RPopover2);
37
+ var RPopover10__namespace = /*#__PURE__*/_interopNamespace(RPopover10);
38
38
  var RTooltip__namespace = /*#__PURE__*/_interopNamespace(RTooltip);
39
39
  var RDropdown__namespace = /*#__PURE__*/_interopNamespace(RDropdown);
40
40
  var RSwitch__namespace = /*#__PURE__*/_interopNamespace(RSwitch);
@@ -2593,6 +2593,27 @@ var Icon_sidebar = (p) => {
2593
2593
  }
2594
2594
  );
2595
2595
  };
2596
+ var Icon_star = (p) => {
2597
+ const { size = 24, ...rest } = p;
2598
+ return /* @__PURE__ */ jsxRuntime.jsx(
2599
+ "svg",
2600
+ {
2601
+ width: size,
2602
+ height: size,
2603
+ viewBox: "0 0 24 24",
2604
+ fill: "none",
2605
+ xmlns: "http://www.w3.org/2000/svg",
2606
+ ...rest,
2607
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2608
+ "path",
2609
+ {
2610
+ d: "M12.3358 3.45335C12.0755 2.84888 11.2185 2.84888 10.9582 3.45335L8.78136 8.50811C8.67278 8.76024 8.43512 8.93291 8.16178 8.95826L2.68175 9.46652C2.02642 9.5273 1.76157 10.3424 2.25602 10.7768L6.39071 14.409C6.59695 14.5902 6.68773 14.8696 6.62737 15.1374L5.41733 20.5063C5.27263 21.1483 5.966 21.6521 6.5319 21.3161L11.2641 18.5062C11.5001 18.366 11.7939 18.366 12.0299 18.5062L16.7621 21.3161C17.328 21.6521 18.0214 21.1483 17.8767 20.5063L16.6666 15.1374C16.6063 14.8696 16.6971 14.5902 16.9033 14.409L21.038 10.7768C21.5324 10.3424 21.2676 9.5273 20.6123 9.46652L15.1322 8.95826C14.8589 8.93291 14.6212 8.76024 14.5126 8.50811L12.3358 3.45335Z",
2611
+ fill: "currentColor"
2612
+ }
2613
+ )
2614
+ }
2615
+ );
2616
+ };
2596
2617
  var Icon_store = (p) => {
2597
2618
  const { size = 24, ...rest } = p;
2598
2619
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -3087,6 +3108,7 @@ var ICONS = {
3087
3108
  settingOutline: Icon_settingOutline,
3088
3109
  shield: Icon_shield,
3089
3110
  sidebar: Icon_sidebar,
3111
+ star: Icon_star,
3090
3112
  store: Icon_store,
3091
3113
  synchronize: Icon_synchronize,
3092
3114
  thumbsDown: Icon_thumbsDown,
@@ -3119,6 +3141,209 @@ var SIcon = /* @__PURE__ */ react.forwardRef(function SIcon2({ name, size = 24,
3119
3141
  }
3120
3142
  );
3121
3143
  });
3144
+ var VIEWBOX = 80;
3145
+ var RADIUS = 34;
3146
+ var STROKE = 12;
3147
+ var CIRCUMFERENCE = 2 * Math.PI * RADIUS;
3148
+ var COLOR_MAP = {
3149
+ primary: { arc: "var(--cmp-progress-color-active)", track: "var(--cmp-progress-color-track)" },
3150
+ // inverse: 어두운 배경용 — arc 흰색, track 은 흰색 40%(회색 링)
3151
+ inverse: { arc: "var(--color-white)", track: "rgba(255,255,255,0.4)" },
3152
+ error: { arc: "var(--color-red-75)", track: "var(--cmp-progress-color-track)" },
3153
+ complete: { arc: "#01BB4B", track: "var(--cmp-progress-color-track)" },
3154
+ // neutral: 콘텐츠가 아직 없는 자리를 채우는 용도 — arc 도 회색이라 진행률보다 "대기"로 읽힌다
3155
+ neutral: { arc: "var(--color-grey-35)", track: "var(--cmp-progress-color-track)" }
3156
+ };
3157
+ var DEFAULT_SIZE = 80;
3158
+ var clamp = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
3159
+ var SCircleProgress = /* @__PURE__ */ react.forwardRef(
3160
+ function SCircleProgress2({
3161
+ value = 0,
3162
+ type = "primary",
3163
+ indeterminate = false,
3164
+ size = DEFAULT_SIZE,
3165
+ label,
3166
+ innerValue = false,
3167
+ className,
3168
+ style
3169
+ }, ref) {
3170
+ const v = clamp(value);
3171
+ const dimension = typeof size === "number" ? `${size}px` : size;
3172
+ const colors = COLOR_MAP[type];
3173
+ const c = VIEWBOX / 2;
3174
+ const percentColor = v === 0 ? "var(--sys-color-fg-tertiary, #888888)" : colors.arc;
3175
+ const filled = v / 100 * CIRCUMFERENCE;
3176
+ const showPercent = !indeterminate;
3177
+ const percentNode = /* @__PURE__ */ jsxRuntime.jsxs(
3178
+ "span",
3179
+ {
3180
+ className: cn(
3181
+ "text-center text-[16px] font-bold",
3182
+ innerValue ? (
3183
+ // leading-[normal]: 26px leading 은 폰트 content(19px) 위아래로 홀수(7px)만큼 남아
3184
+ // 3px/4px 로 쪼개지고 글자가 링 중심보다 위로 뜬다. (leading-normal 은 1.5 라 동일 문제)
3185
+ "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 leading-[normal]"
3186
+ ) : "leading-[26px]"
3187
+ ),
3188
+ style: { color: percentColor },
3189
+ children: [
3190
+ Math.round(v),
3191
+ "%"
3192
+ ]
3193
+ }
3194
+ );
3195
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3196
+ "div",
3197
+ {
3198
+ ref,
3199
+ className: cn(
3200
+ "inline-flex flex-col items-center gap-[var(--cmp-progress-circular-gap)]",
3201
+ className
3202
+ ),
3203
+ style,
3204
+ children: [
3205
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(innerValue && "relative"), children: [
3206
+ /* @__PURE__ */ jsxRuntime.jsxs(
3207
+ "svg",
3208
+ {
3209
+ viewBox: `0 0 ${VIEWBOX} ${VIEWBOX}`,
3210
+ xmlns: "http://www.w3.org/2000/svg",
3211
+ className: cn(indeterminate && "origin-center animate-circle-spin"),
3212
+ style: { width: dimension, height: dimension },
3213
+ children: [
3214
+ /* @__PURE__ */ jsxRuntime.jsx(
3215
+ "circle",
3216
+ {
3217
+ cx: c,
3218
+ cy: c,
3219
+ r: RADIUS,
3220
+ fill: "none",
3221
+ strokeWidth: STROKE,
3222
+ style: { stroke: colors.track }
3223
+ }
3224
+ ),
3225
+ /* @__PURE__ */ jsxRuntime.jsx(
3226
+ "circle",
3227
+ {
3228
+ cx: c,
3229
+ cy: c,
3230
+ r: RADIUS,
3231
+ fill: "none",
3232
+ strokeWidth: STROKE,
3233
+ strokeLinecap: "round",
3234
+ className: cn(
3235
+ "origin-center",
3236
+ indeterminate ? "animate-circle-arc" : "-rotate-90 transition-[stroke-dasharray] duration-[400ms] ease-linear"
3237
+ ),
3238
+ style: {
3239
+ stroke: colors.arc,
3240
+ ...indeterminate ? {} : { strokeDasharray: `${filled} ${CIRCUMFERENCE}`, strokeDashoffset: 0 }
3241
+ }
3242
+ }
3243
+ )
3244
+ ]
3245
+ }
3246
+ ),
3247
+ showPercent && innerValue && percentNode
3248
+ ] }),
3249
+ showPercent && !innerValue && percentNode,
3250
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-center text-[12px] font-normal leading-[20px] text-[color:var(--cmp-progress-color-label)]", children: label })
3251
+ ]
3252
+ }
3253
+ );
3254
+ }
3255
+ );
3256
+ var IMAGE_FITS = ["cover", "contain", "fill", "none", "scale-down"];
3257
+ var EMPTY_ICON_SIZE = "60cqh";
3258
+ var SPINNER_SIZE = "45cqh";
3259
+ var SImage = /* @__PURE__ */ react.forwardRef(function SImage2({
3260
+ src,
3261
+ alt = "",
3262
+ ratio = 1,
3263
+ fit = "cover",
3264
+ position = "50% 50%",
3265
+ loading: loading2 = "lazy",
3266
+ loadingShowDelay = 0,
3267
+ noSpinner = false,
3268
+ loadingSlot,
3269
+ errorSlot,
3270
+ imgProps,
3271
+ onLoad,
3272
+ onError,
3273
+ className,
3274
+ style,
3275
+ ...rest
3276
+ }, ref) {
3277
+ const [status, setStatus] = react.useState("loading");
3278
+ const [spinnerVisible, setSpinnerVisible] = react.useState(loadingShowDelay <= 0);
3279
+ const [trackedSrc, setTrackedSrc] = react.useState(src);
3280
+ const imgRef = react.useRef(null);
3281
+ if (src !== trackedSrc) {
3282
+ setTrackedSrc(src);
3283
+ setStatus("loading");
3284
+ }
3285
+ react.useEffect(() => {
3286
+ if (!src) return;
3287
+ const node = imgRef.current;
3288
+ if (node?.complete && node.naturalWidth > 0) {
3289
+ setStatus((current) => current === "loading" ? "loaded" : current);
3290
+ }
3291
+ }, [src]);
3292
+ react.useEffect(() => {
3293
+ if (status !== "loading" || loadingShowDelay <= 0) {
3294
+ setSpinnerVisible(status === "loading");
3295
+ return;
3296
+ }
3297
+ setSpinnerVisible(false);
3298
+ const timer = setTimeout(() => setSpinnerVisible(true), loadingShowDelay);
3299
+ return () => clearTimeout(timer);
3300
+ }, [status, loadingShowDelay, src]);
3301
+ const isEmpty = !src;
3302
+ const showFallback = isEmpty || status === "error";
3303
+ const showSpinner = !isEmpty && status === "loading" && !noSpinner && spinnerVisible;
3304
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3305
+ "div",
3306
+ {
3307
+ ref,
3308
+ className: cn("relative overflow-hidden bg-(--cmp-image-bg)", className),
3309
+ style: { containerType: "size", aspectRatio: ratio, ...style },
3310
+ ...rest,
3311
+ children: [
3312
+ !isEmpty && /* @__PURE__ */ jsxRuntime.jsx(
3313
+ "img",
3314
+ {
3315
+ ...imgProps,
3316
+ ref: imgRef,
3317
+ src,
3318
+ alt,
3319
+ loading: loading2,
3320
+ onLoad: () => {
3321
+ setStatus("loaded");
3322
+ onLoad?.(src);
3323
+ },
3324
+ onError: (event) => {
3325
+ setStatus("error");
3326
+ onError?.(event);
3327
+ },
3328
+ className: cn(
3329
+ "h-full w-full",
3330
+ // 로딩 중에도 자리를 지켜야 브라우저가 계속 받아온다 — 숨기되 없애지 않는다
3331
+ status === "loaded" ? "visible" : "invisible",
3332
+ imgProps?.className
3333
+ ),
3334
+ style: {
3335
+ objectFit: fit,
3336
+ objectPosition: position,
3337
+ ...imgProps?.style
3338
+ }
3339
+ }
3340
+ ),
3341
+ showFallback && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 flex items-center justify-center", children: errorSlot ?? /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "image", size: EMPTY_ICON_SIZE, color: "var(--cmp-image-icon)" }) }),
3342
+ showSpinner && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 flex items-center justify-center", children: loadingSlot ?? /* @__PURE__ */ jsxRuntime.jsx(SCircleProgress, { indeterminate: true, type: "neutral", size: SPINNER_SIZE }) })
3343
+ ]
3344
+ }
3345
+ );
3346
+ });
3122
3347
  var BADGE_COLORS = [
3123
3348
  "red",
3124
3349
  "orange",
@@ -3220,12 +3445,13 @@ var SDraggableItemView = /* @__PURE__ */ react.forwardRef(
3220
3445
  !disabled && !selected && !dragging && "hover:bg-(--cmp-listDraggableItem-bg-hover)",
3221
3446
  hasSupportingText && supportingTextPosition === "bottom" ? dense ? "min-h-[52px]" : "min-h-[60px]" : dense ? "h-[32px]" : "h-[40px]",
3222
3447
  paddingYClass,
3223
- bordered ? cn(
3448
+ // 항목 사이 구분선은 리스트가 그린다.
3449
+ bordered && cn(
3224
3450
  "rounded-(--cmp-listDraggableItem-radius-bordered) border border-(--cmp-listDraggableItem-border-default)",
3225
3451
  !disabled && !selected && !dragging && "hover:border-(--cmp-listDraggableItem-bordered-border-hover)",
3226
3452
  !disabled && selected && !dragging && "border-(--cmp-listDraggableItem-bordered-border-selected) bg-(--cmp-listDraggableItem-bordered-bg-selected)",
3227
3453
  !disabled && selected && dragging && "border-[length:var(--cmp-listDraggableItem-bordered-dragging-border-width)] border-(--cmp-listDraggableItem-bordered-dragging-border-color) bg-(--cmp-listDraggableItem-bordered-dragging-bg)"
3228
- ) : "border-b border-(--cmp-listDraggableItem-border-default)",
3454
+ ),
3229
3455
  disabled && "cursor-not-allowed",
3230
3456
  className
3231
3457
  ),
@@ -3435,6 +3661,7 @@ var SListImpl = /* @__PURE__ */ react.forwardRef(function SList({ className, chi
3435
3661
  className: cn(
3436
3662
  useGap && "flex flex-col gap-(--cmp-list-group-gap)",
3437
3663
  usePadding && "p-(--cmp-list-group-padding)",
3664
+ "[&>*]:border-b [&>*]:border-(--cmp-listItem-border-default)",
3438
3665
  className
3439
3666
  ),
3440
3667
  ...rest,
@@ -3806,7 +4033,7 @@ var SExpansionItem = /* @__PURE__ */ react.forwardRef(
3806
4033
  ref,
3807
4034
  className: cn(
3808
4035
  "group/expansion-item relative overflow-hidden bg-(--cmp-listExpansionItem-title-bg-default)",
3809
- !bordered && "border-b border-(--cmp-listExpansionItem-border-default)",
4036
+ // 항목 사이 구분선은 리스트가 그린다.
3810
4037
  // 보더 색은 시맨틱 토큰(border-default → hover strong → 펼침 accentLight)을 쓴다.
3811
4038
  // TODO: expansionItem/bordered/radius 토큰 생성 후 rounded 고정값도 토큰으로 교체한다.
3812
4039
  bordered && "rounded-[8px] border border-border-default hover:border-border-strong [&:has(>button:hover)]:border-border-strong",
@@ -3916,7 +4143,7 @@ var SListItemImpl = /* @__PURE__ */ react.forwardRef(function SListItem({
3916
4143
  trailing,
3917
4144
  bordered = false,
3918
4145
  selected = false,
3919
- clickable = true,
4146
+ clickable = false,
3920
4147
  interaction,
3921
4148
  accentStripe = false,
3922
4149
  dense = false,
@@ -3969,10 +4196,10 @@ var SListItemImpl = /* @__PURE__ */ react.forwardRef(function SListItem({
3969
4196
  "gap-(--cmp-listItem-gap)",
3970
4197
  "pr-(--cmp-listItem-paddingX)",
3971
4198
  depth === 2 ? "pl-[36px]" : depth === 3 ? "pl-[56px]" : "pl-[16px]",
3972
- bordered ? cn(
4199
+ bordered && cn(
3973
4200
  "overflow-hidden rounded-(--cmp-listItem-radius-bordered) border border-(--cmp-listItem-border-default)",
3974
4201
  isSelected ? "border-(--cmp-listItem-bordered-border-selected)" : isInteractive && "hover:border-(--cmp-listItem-bordered-border-hover)"
3975
- ) : "border-b border-(--cmp-listItem-border-default)",
4202
+ ),
3976
4203
  className
3977
4204
  ),
3978
4205
  ...rest,
@@ -4051,6 +4278,8 @@ var SExpansionListImpl = /* @__PURE__ */ react.forwardRef(function SExpansionLis
4051
4278
  const depth2 = child.props.depth ?? 1;
4052
4279
  const itemKey2 = String(child.key ?? index);
4053
4280
  return react.cloneElement(child, {
4281
+ // 선택을 리스트가 관리하는 이상 아이템은 클릭 가능해야 한다 — SListItem 의 기본값(false)을 뒤집는다
4282
+ clickable: child.props.clickable ?? true,
4054
4283
  selected: selectedKeys[depth2] === itemKey2,
4055
4284
  onClick: (event) => {
4056
4285
  setSelectedKeys((current) => ({ ...current, [depth2]: itemKey2 }));
@@ -4356,8 +4585,8 @@ function SGhostButton({
4356
4585
  }
4357
4586
  );
4358
4587
  if (!hasTooltip) return btn;
4359
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpen, children: [
4360
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
4588
+ return /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open, onOpenChange: setOpen, children: [
4589
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
4361
4590
  "span",
4362
4591
  {
4363
4592
  className: cn("inline-flex", anchorClassName),
@@ -4369,8 +4598,8 @@ function SGhostButton({
4369
4598
  children: btn
4370
4599
  }
4371
4600
  ) }),
4372
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
4373
- RPopover2__namespace.Content,
4601
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
4602
+ RPopover10__namespace.Content,
4374
4603
  {
4375
4604
  side: "top",
4376
4605
  sideOffset: 4,
@@ -4384,6 +4613,18 @@ function SGhostButton({
4384
4613
  ) })
4385
4614
  ] });
4386
4615
  }
4616
+ var TREE_LABEL_TYPOGRAPHY = {
4617
+ sm: {
4618
+ default: "typo-body-sm-medium",
4619
+ selected: "typo-body-sm-bold",
4620
+ rowHeight: "h-[28px]"
4621
+ },
4622
+ md: {
4623
+ default: "typo-body-md-medium",
4624
+ selected: "typo-body-md-bold",
4625
+ rowHeight: "h-[32px]"
4626
+ }
4627
+ };
4387
4628
  function collectIds(nodes) {
4388
4629
  return nodes.flatMap((node) => [node.id, ...collectIds(node.children ?? [])]);
4389
4630
  }
@@ -4394,18 +4635,19 @@ function collectSelectableIds(node) {
4394
4635
  function toSelectedSet(value) {
4395
4636
  return new Set(value ?? []);
4396
4637
  }
4397
- function getSelectionState(node, selectedSet) {
4398
- if (selectedSet.has(node.id)) {
4399
- return { selected: true, indeterminate: false };
4638
+ var TREE_ALL_NODE = { id: "__all__", label: "\uC804\uCCB4" };
4639
+ function getSelectionState(node, selectedSet, cascadeSelection) {
4640
+ if (!cascadeSelection) {
4641
+ return { selected: selectedSet.has(node.id), indeterminate: false };
4400
4642
  }
4401
4643
  const selectableChildren = (node.children ?? []).filter(
4402
4644
  (child) => collectSelectableIds(child).length > 0
4403
4645
  );
4404
4646
  if (selectableChildren.length === 0) {
4405
- return { selected: false, indeterminate: false };
4647
+ return { selected: selectedSet.has(node.id), indeterminate: false };
4406
4648
  }
4407
4649
  const childStates = selectableChildren.map(
4408
- (child) => getSelectionState(child, selectedSet)
4650
+ (child) => getSelectionState(child, selectedSet, cascadeSelection)
4409
4651
  );
4410
4652
  const selectedCount = childStates.filter((state2) => state2.selected).length;
4411
4653
  return {
@@ -4415,6 +4657,7 @@ function getSelectionState(node, selectedSet) {
4415
4657
  }
4416
4658
  function TreeConnector({
4417
4659
  guideline,
4660
+ size,
4418
4661
  depth,
4419
4662
  hasChildren,
4420
4663
  isLast,
@@ -4422,6 +4665,8 @@ function TreeConnector({
4422
4665
  }) {
4423
4666
  if (depth <= 1) return null;
4424
4667
  const ancestorConnectors = ancestorLast.slice(0, -1);
4668
+ const branchTop = size === "sm" ? "top-[13px]" : "top-[15px]";
4669
+ const nodeHeight = size === "sm" ? "h-[15px]" : "h-[17px]";
4425
4670
  return /* @__PURE__ */ jsxRuntime.jsxs("span", { "aria-hidden": "true", className: "flex self-stretch", children: [
4426
4671
  ancestorConnectors.map((last, index) => /* @__PURE__ */ jsxRuntime.jsx(
4427
4672
  "span",
@@ -4435,13 +4680,22 @@ function TreeConnector({
4435
4680
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative block w-[20px] shrink-0", children: [
4436
4681
  guideline && !isLast && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute bottom-0 left-[6px] top-[-1px] w-px bg-(--cmp-tree-connector-line-color)" }),
4437
4682
  guideline && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4438
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-[6px] top-[-1px] h-[17px] w-px bg-(--cmp-tree-connector-line-color)" }),
4683
+ /* @__PURE__ */ jsxRuntime.jsx(
4684
+ "span",
4685
+ {
4686
+ className: cn(
4687
+ "absolute left-[6px] top-[-1px] w-px bg-(--cmp-tree-connector-line-color)",
4688
+ nodeHeight
4689
+ )
4690
+ }
4691
+ ),
4439
4692
  /* @__PURE__ */ jsxRuntime.jsx(
4440
4693
  "span",
4441
4694
  {
4442
4695
  "data-tree-connector-branch": hasChildren ? "parent" : "leaf",
4443
4696
  className: cn(
4444
- "absolute left-[6px] top-[15px] h-px bg-(--cmp-tree-connector-line-color)",
4697
+ "absolute left-[6px] h-px bg-(--cmp-tree-connector-line-color)",
4698
+ branchTop,
4445
4699
  hasChildren ? "w-[14px]" : "w-[calc(14px+var(--cmp-tree-item-arrow-icon))]"
4446
4700
  )
4447
4701
  }
@@ -4450,6 +4704,44 @@ function TreeConnector({
4450
4704
  ] })
4451
4705
  ] });
4452
4706
  }
4707
+ function STreeAllItem({
4708
+ selected,
4709
+ indeterminate,
4710
+ border,
4711
+ size,
4712
+ onSelect
4713
+ }) {
4714
+ const checkboxValue = indeterminate ? null : selected;
4715
+ const typography = TREE_LABEL_TYPOGRAPHY[size];
4716
+ const labelTypo = selected || indeterminate ? typography.selected : typography.default;
4717
+ return /* @__PURE__ */ jsxRuntime.jsx(
4718
+ "div",
4719
+ {
4720
+ role: "treeitem",
4721
+ "aria-level": 1,
4722
+ "aria-selected": selected,
4723
+ onClick: onSelect,
4724
+ className: cn(
4725
+ "group/tree-item box-border flex w-full cursor-pointer items-center overflow-hidden bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left hover:bg-(--cmp-tree-item-bg-hover)",
4726
+ typography.rowHeight,
4727
+ border && "border-b border-(--cmp-tree-item-border-default)"
4728
+ ),
4729
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "box-border flex min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap)", children: [
4730
+ /* @__PURE__ */ jsxRuntime.jsx("span", { onClick: (event) => event.stopPropagation(), className: "flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(SCheckbox, { value: checkboxValue, onValueChange: () => onSelect() }) }),
4731
+ /* @__PURE__ */ jsxRuntime.jsx(
4732
+ "span",
4733
+ {
4734
+ className: cn(
4735
+ "block min-w-0 flex-1 truncate py-(--cmp-tree-item-paddingY) text-(--cmp-tree-item-text-default)",
4736
+ labelTypo
4737
+ ),
4738
+ children: "\uC804\uCCB4"
4739
+ }
4740
+ )
4741
+ ] })
4742
+ }
4743
+ );
4744
+ }
4453
4745
  var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4454
4746
  node,
4455
4747
  depth = 1,
@@ -4461,6 +4753,7 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4461
4753
  border = true,
4462
4754
  indeterminate = false,
4463
4755
  selectable = true,
4756
+ size = "sm",
4464
4757
  leading,
4465
4758
  trailing,
4466
4759
  onToggle,
@@ -4472,6 +4765,7 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4472
4765
  const renderState = {
4473
4766
  node,
4474
4767
  depth,
4768
+ size,
4475
4769
  expanded,
4476
4770
  selected,
4477
4771
  indeterminate,
@@ -4481,6 +4775,8 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4481
4775
  const leadingNode = typeof leading === "function" ? leading(renderState) : leading;
4482
4776
  const trailingNode = typeof trailing === "function" ? trailing(renderState) : trailing;
4483
4777
  const checkboxValue = indeterminate ? null : selected;
4778
+ const typography = TREE_LABEL_TYPOGRAPHY[size];
4779
+ const labelTypo = selected || indeterminate ? typography.selected : typography.default;
4484
4780
  const handleSelect = (event) => {
4485
4781
  if (node.disabled) return;
4486
4782
  onTreeSelect?.(node, event);
@@ -4500,7 +4796,8 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4500
4796
  "aria-disabled": node.disabled || void 0,
4501
4797
  onClick: handleRowClick,
4502
4798
  className: cn(
4503
- "group/tree-item flex min-h-[32px] w-full items-center gap-(--cmp-tree-item-rowGap) bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left",
4799
+ "group/tree-item box-border flex w-full items-center gap-(--cmp-tree-item-rowGap) overflow-hidden bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left",
4800
+ typography.rowHeight,
4504
4801
  border && "border-b border-(--cmp-tree-item-border-default)",
4505
4802
  !node.disabled && "hover:bg-(--cmp-tree-item-bg-hover)",
4506
4803
  node.disabled && "cursor-not-allowed opacity-60",
@@ -4513,13 +4810,14 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4513
4810
  TreeConnector,
4514
4811
  {
4515
4812
  guideline,
4813
+ size,
4516
4814
  depth,
4517
4815
  hasChildren,
4518
4816
  isLast,
4519
4817
  ancestorLast
4520
4818
  }
4521
4819
  ),
4522
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap) py-(--cmp-tree-item-paddingY)", children: [
4820
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "box-border flex min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap)", children: [
4523
4821
  hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
4524
4822
  SGhostButton,
4525
4823
  {
@@ -4555,25 +4853,17 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4555
4853
  /* @__PURE__ */ jsxRuntime.jsx(
4556
4854
  "span",
4557
4855
  {
4856
+ onClick: (event) => {
4857
+ if (!selectable) return;
4858
+ event.stopPropagation();
4859
+ if (!node.disabled && selectable) onTreeSelect?.(node, event);
4860
+ },
4558
4861
  className: cn(
4559
- "min-w-0 flex-1 text-[14px] leading-[24px]",
4560
- selected || indeterminate ? "font-bold text-(--cmp-tree-item-text-selected)" : "font-medium text-(--cmp-tree-item-text-default)"
4862
+ "block min-w-0 flex-1 truncate py-(--cmp-tree-item-paddingY) text-(--cmp-tree-item-text-default)",
4863
+ labelTypo,
4864
+ node.disabled ? "cursor-not-allowed" : selectable ? "cursor-pointer" : void 0
4561
4865
  ),
4562
- children: /* @__PURE__ */ jsxRuntime.jsx(
4563
- "span",
4564
- {
4565
- onClick: (event) => {
4566
- if (!selectable) return;
4567
- event.stopPropagation();
4568
- if (!node.disabled && selectable) onTreeSelect?.(node, event);
4569
- },
4570
- className: cn(
4571
- "inline-block max-w-full truncate",
4572
- node.disabled ? "cursor-not-allowed" : selectable ? "cursor-pointer" : void 0
4573
- ),
4574
- children: node.label
4575
- }
4576
- )
4866
+ children: node.label
4577
4867
  }
4578
4868
  ),
4579
4869
  trailingNode
@@ -4590,6 +4880,8 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4590
4880
  expandedIds,
4591
4881
  defaultExpandedIds,
4592
4882
  selectable = true,
4883
+ size = "sm",
4884
+ useAll = false,
4593
4885
  cascadeSelection = true,
4594
4886
  guideline = true,
4595
4887
  border = true,
@@ -4608,6 +4900,11 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4608
4900
  const currentExpandedIds = expandedIds ?? internalExpandedIds;
4609
4901
  const selectedSet = react.useMemo(() => toSelectedSet(currentValue), [currentValue]);
4610
4902
  const expandedSet = react.useMemo(() => new Set(currentExpandedIds), [currentExpandedIds]);
4903
+ const selectableIds = react.useMemo(() => items.flatMap(collectSelectableIds), [items]);
4904
+ const allSelected = selectableIds.length > 0 && selectableIds.every((id) => selectedSet.has(id));
4905
+ const someSelected = selectableIds.some((id) => selectedSet.has(id));
4906
+ const allIndeterminate = someSelected && !allSelected;
4907
+ const showAllItem = selectable && useAll;
4611
4908
  const updateExpanded = (node) => {
4612
4909
  const next = new Set(currentExpandedIds);
4613
4910
  if (next.has(node.id)) next.delete(node.id);
@@ -4619,7 +4916,7 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4619
4916
  const updateSelected = (node) => {
4620
4917
  if (node.disabled) return;
4621
4918
  const targetIds = cascadeSelection ? collectSelectableIds(node) : [node.id];
4622
- const everySelected = getSelectionState(node, selectedSet).selected;
4919
+ const everySelected = getSelectionState(node, selectedSet, cascadeSelection).selected;
4623
4920
  const next = new Set(selectedSet);
4624
4921
  targetIds.forEach((id) => {
4625
4922
  if (everySelected) next.delete(id);
@@ -4629,10 +4926,22 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4629
4926
  if (value === void 0) setInternalValue(orderedIds);
4630
4927
  onValueChange?.(orderedIds, node);
4631
4928
  };
4929
+ const updateAllSelected = (event) => {
4930
+ event?.stopPropagation();
4931
+ if (selectableIds.length === 0) return;
4932
+ const next = new Set(selectedSet);
4933
+ selectableIds.forEach((id) => {
4934
+ if (allSelected) next.delete(id);
4935
+ else next.add(id);
4936
+ });
4937
+ const orderedIds = collectIds(items).filter((id) => next.has(id));
4938
+ if (value === void 0) setInternalValue(orderedIds);
4939
+ onValueChange?.(orderedIds, TREE_ALL_NODE);
4940
+ };
4632
4941
  const renderNodes = (nodes, depth = 1, ancestorLast = []) => nodes.map((node, index) => {
4633
4942
  const isLast = index === nodes.length - 1;
4634
4943
  const isExpanded = expandedSet.has(node.id);
4635
- const { selected, indeterminate } = getSelectionState(node, selectedSet);
4944
+ const { selected, indeterminate } = getSelectionState(node, selectedSet, cascadeSelection);
4636
4945
  return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
4637
4946
  /* @__PURE__ */ jsxRuntime.jsx(
4638
4947
  STreeItem2,
@@ -4645,6 +4954,7 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4645
4954
  selected,
4646
4955
  indeterminate,
4647
4956
  selectable,
4957
+ size,
4648
4958
  guideline,
4649
4959
  border,
4650
4960
  leading,
@@ -4666,14 +4976,26 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4666
4976
  ) : null
4667
4977
  ] }, node.id);
4668
4978
  });
4669
- return /* @__PURE__ */ jsxRuntime.jsx(
4979
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4670
4980
  "div",
4671
4981
  {
4672
4982
  ref,
4673
4983
  role: "tree",
4674
4984
  className: cn("flex w-full flex-col items-start bg-(--cmp-tree-item-bg-default)", className),
4675
4985
  ...rest,
4676
- children: renderNodes(items)
4986
+ children: [
4987
+ showAllItem && /* @__PURE__ */ jsxRuntime.jsx(
4988
+ STreeAllItem,
4989
+ {
4990
+ selected: allSelected,
4991
+ indeterminate: allIndeterminate,
4992
+ border,
4993
+ size,
4994
+ onSelect: updateAllSelected
4995
+ }
4996
+ ),
4997
+ renderNodes(items)
4998
+ ]
4677
4999
  }
4678
5000
  );
4679
5001
  });
@@ -4841,7 +5163,7 @@ var STooltipImpl = /* @__PURE__ */ react.forwardRef(function STooltip({
4841
5163
  ...restContentProps
4842
5164
  } = contentProps ?? {};
4843
5165
  const contentClass = cn(
4844
- "z-50 w-fit whitespace-nowrap rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px] shadow-normal-sm",
5166
+ "z-50 w-fit max-w-[var(--radix-popover-content-available-width)] rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px] shadow-normal-sm",
4845
5167
  FLOATING_SLIDE_ANIM,
4846
5168
  className,
4847
5169
  contentPropsClassName
@@ -4853,14 +5175,14 @@ var STooltipImpl = /* @__PURE__ */ react.forwardRef(function STooltip({
4853
5175
  ...style,
4854
5176
  ...contentPropsStyle
4855
5177
  };
4856
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpenState, children: [
5178
+ return /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open, onOpenChange: setOpenState, children: [
4857
5179
  isNone ? (
4858
5180
  // 트리거 없음: children 을 래퍼 없이 그대로 앵커로 쓴다 (소비자 레이아웃 보존).
4859
5181
  // mouse/focus 핸들러가 없으므로 useClose 없이도 자동 닫힘이 발생하지 않는다.
4860
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: triggerNode })
5182
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Anchor, { asChild: true, children: triggerNode })
4861
5183
  ) : isHover ? (
4862
5184
  // hover: Anchor(위치 기준) + 직접 mouse 핸들러 — 클릭으로 토글되지 않도록 Trigger 미사용
4863
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
5185
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
4864
5186
  "span",
4865
5187
  {
4866
5188
  className: "inline-flex",
@@ -4871,9 +5193,9 @@ var STooltipImpl = /* @__PURE__ */ react.forwardRef(function STooltip({
4871
5193
  children: triggerNode
4872
5194
  }
4873
5195
  ) })
4874
- ) : /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: triggerNode }),
4875
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
4876
- RPopover2__namespace.Content,
5196
+ ) : /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: triggerNode }),
5197
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
5198
+ RPopover10__namespace.Content,
4877
5199
  {
4878
5200
  side: placement,
4879
5201
  sideOffset,
@@ -4907,7 +5229,7 @@ var STooltipImpl = /* @__PURE__ */ react.forwardRef(function STooltip({
4907
5229
  }
4908
5230
  )
4909
5231
  ] }),
4910
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
5232
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
4911
5233
  ]
4912
5234
  }
4913
5235
  ) })
@@ -4919,6 +5241,10 @@ var BODY_PADDING_CLASS = {
4919
5241
  wide: "p-(--sys-space-panel-padding-wide)",
4920
5242
  none: ""
4921
5243
  };
5244
+ var BODY_BACKGROUND_CLASS = {
5245
+ frame: "",
5246
+ neutral: "bg-(--sys-color-bg-neutralLight)"
5247
+ };
4922
5248
  var topBorderColorClass = {
4923
5249
  default: "bg-(--cmp-sectionHeaderCard-topBorder-color-default)",
4924
5250
  accent: "bg-(--cmp-sectionHeaderCard-topBorder-color-accent)"
@@ -4928,8 +5254,8 @@ var titleSizeClass = {
4928
5254
  sm: "typo-heading-sm"
4929
5255
  };
4930
5256
  var SSectionHeaderCardBodyImpl = /* @__PURE__ */ react.forwardRef(
4931
- function SSectionHeaderCardBody({ padding = "default", className, children, ...rest }, ref) {
4932
- return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn(BODY_PADDING_CLASS[padding], className), ...rest, children });
5257
+ function SSectionHeaderCardBody({ padding = "default", background = "frame", children }, ref) {
5258
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn(BODY_PADDING_CLASS[padding], BODY_BACKGROUND_CLASS[background]), children });
4933
5259
  }
4934
5260
  );
4935
5261
  var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
@@ -4942,8 +5268,7 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
4942
5268
  subtitle,
4943
5269
  slot,
4944
5270
  thickness = false,
4945
- className,
4946
- ...rest
5271
+ className
4947
5272
  }, ref) {
4948
5273
  const hasHelp = helpText != null && helpText.length > 0;
4949
5274
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -4964,7 +5289,6 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
4964
5289
  "box-border flex min-h-(--cmp-sectionHeaderCard-header-minHeight) w-full shrink-0 items-center gap-(--cmp-sectionHeaderCard-header-gap) overflow-hidden border-b border-(--cmp-sectionHeaderCard-header-border) bg-(--cmp-sectionHeaderCard-header-bg) px-(--cmp-sectionHeaderCard-header-paddingX) py-(--cmp-sectionHeaderCard-header-paddingY)",
4965
5290
  className
4966
5291
  ),
4967
- ...rest,
4968
5292
  children: [
4969
5293
  marker && /* @__PURE__ */ jsxRuntime.jsx(SBadge, { color: "darkblue", className: "shrink-0" }),
4970
5294
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 shrink-0 items-center gap-[8px]", children: [
@@ -5008,8 +5332,24 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
5008
5332
  }
5009
5333
  );
5010
5334
  var SSectionHeaderCardRootImpl = /* @__PURE__ */ react.forwardRef(
5011
- function SSectionHeaderCard({ className, children, ...rest }, ref) {
5012
- return /* @__PURE__ */ jsxRuntime.jsx(
5335
+ function SSectionHeaderCard({
5336
+ title,
5337
+ titleSize,
5338
+ marker,
5339
+ required,
5340
+ helpText,
5341
+ subtitle,
5342
+ slot,
5343
+ thickness,
5344
+ headerClassName,
5345
+ padding,
5346
+ background,
5347
+ className,
5348
+ children,
5349
+ ...rest
5350
+ }, ref) {
5351
+ const hasHeader = title != null;
5352
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5013
5353
  "div",
5014
5354
  {
5015
5355
  ref,
@@ -5018,24 +5358,28 @@ var SSectionHeaderCardRootImpl = /* @__PURE__ */ react.forwardRef(
5018
5358
  className
5019
5359
  ),
5020
5360
  ...rest,
5021
- children
5361
+ children: [
5362
+ hasHeader && /* @__PURE__ */ jsxRuntime.jsx(
5363
+ SSectionHeaderCardHeaderImpl,
5364
+ {
5365
+ title,
5366
+ titleSize,
5367
+ marker,
5368
+ required,
5369
+ helpText,
5370
+ subtitle,
5371
+ slot,
5372
+ thickness,
5373
+ className: headerClassName
5374
+ }
5375
+ ),
5376
+ /* @__PURE__ */ jsxRuntime.jsx(SSectionHeaderCardBodyImpl, { padding, background, children })
5377
+ ]
5022
5378
  }
5023
5379
  );
5024
5380
  }
5025
5381
  );
5026
- var SSectionHeaderCardRoot = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5027
- var SSectionHeaderCardHeader2 = /* @__PURE__ */ withDisplayName(
5028
- SSectionHeaderCardHeaderImpl,
5029
- "SSectionHeaderCard.Header"
5030
- );
5031
- var SSectionHeaderCardBody2 = /* @__PURE__ */ withDisplayName(
5032
- SSectionHeaderCardBodyImpl,
5033
- "SSectionHeaderCard.Body"
5034
- );
5035
- var SSectionHeaderCard2 = /* @__PURE__ */ Object.assign(SSectionHeaderCardRoot, {
5036
- Header: SSectionHeaderCardHeader2,
5037
- Body: SSectionHeaderCardBody2
5038
- });
5382
+ var SSectionHeaderCard2 = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5039
5383
  var SDivider = /* @__PURE__ */ react.forwardRef(function SDivider2({ vertical = false, className, style, ...rest }, ref) {
5040
5384
  return /* @__PURE__ */ jsxRuntime.jsx(
5041
5385
  "hr",
@@ -5556,14 +5900,14 @@ var SCallout = /* @__PURE__ */ react.forwardRef(function SCallout2({ type = "def
5556
5900
  );
5557
5901
  });
5558
5902
  var FILL_COLOR = {
5559
- primary: "var(--cmp-progress-active-color)",
5560
- error: "var(--cmp-progress-error-color)",
5561
- complete: "var(--cmp-progress-success-color)"
5903
+ primary: "var(--cmp-progress-color-active)",
5904
+ error: "var(--cmp-progress-color-error)",
5905
+ complete: "var(--cmp-progress-color-success)"
5562
5906
  };
5563
- var clamp = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
5907
+ var clamp2 = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
5564
5908
  var SLinearProgress = /* @__PURE__ */ react.forwardRef(
5565
5909
  function SLinearProgress2({ value = 0, type = "primary", indeterminate = false, label, className, style }, ref) {
5566
- const v = clamp(value);
5910
+ const v = clamp2(value);
5567
5911
  const fill = FILL_COLOR[type];
5568
5912
  const valueColor = v === 0 ? "var(--sys-color-fg-tertiary, #888888)" : fill;
5569
5913
  const valueText = `${Math.round(v)}%`;
@@ -5610,115 +5954,6 @@ var SLinearProgress = /* @__PURE__ */ react.forwardRef(
5610
5954
  );
5611
5955
  }
5612
5956
  );
5613
- var VIEWBOX = 80;
5614
- var RADIUS = 34;
5615
- var STROKE = 12;
5616
- var CIRCUMFERENCE = 2 * Math.PI * RADIUS;
5617
- var COLOR_MAP = {
5618
- primary: { arc: "var(--cmp-progress-active-color)", track: "var(--cmp-progress-color-track)" },
5619
- // inverse: 어두운 배경용 — arc 흰색, track 은 흰색 40%(회색 링)
5620
- inverse: { arc: "var(--color-white)", track: "rgba(255,255,255,0.4)" },
5621
- error: { arc: "var(--color-red-75)", track: "var(--cmp-progress-color-track)" },
5622
- complete: { arc: "#01BB4B", track: "var(--cmp-progress-color-track)" }
5623
- };
5624
- var clamp2 = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
5625
- var SCircleProgress = /* @__PURE__ */ react.forwardRef(
5626
- function SCircleProgress2({
5627
- value = 0,
5628
- type = "primary",
5629
- indeterminate = false,
5630
- label,
5631
- innerValue = false,
5632
- className,
5633
- style
5634
- }, ref) {
5635
- const v = clamp2(value);
5636
- const colors = COLOR_MAP[type];
5637
- const c = VIEWBOX / 2;
5638
- const percentColor = v === 0 ? "var(--sys-color-fg-tertiary, #888888)" : colors.arc;
5639
- const filled = v / 100 * CIRCUMFERENCE;
5640
- const showPercent = !indeterminate;
5641
- const percentNode = /* @__PURE__ */ jsxRuntime.jsxs(
5642
- "span",
5643
- {
5644
- className: cn(
5645
- "text-center text-[16px] font-bold",
5646
- innerValue ? (
5647
- // leading-[normal]: 26px leading 은 폰트 content(19px) 위아래로 홀수(7px)만큼 남아
5648
- // 3px/4px 로 쪼개지고 글자가 링 중심보다 위로 뜬다. (leading-normal 은 1.5 라 동일 문제)
5649
- "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 leading-[normal]"
5650
- ) : "leading-[26px]"
5651
- ),
5652
- style: { color: percentColor },
5653
- children: [
5654
- Math.round(v),
5655
- "%"
5656
- ]
5657
- }
5658
- );
5659
- return /* @__PURE__ */ jsxRuntime.jsxs(
5660
- "div",
5661
- {
5662
- ref,
5663
- className: cn(
5664
- "inline-flex flex-col items-center gap-[var(--cmp-progress-circular-gap)]",
5665
- className
5666
- ),
5667
- style,
5668
- children: [
5669
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(innerValue && "relative"), children: [
5670
- /* @__PURE__ */ jsxRuntime.jsxs(
5671
- "svg",
5672
- {
5673
- viewBox: `0 0 ${VIEWBOX} ${VIEWBOX}`,
5674
- xmlns: "http://www.w3.org/2000/svg",
5675
- className: cn(
5676
- "h-[80px] w-[80px]",
5677
- indeterminate && "origin-center animate-circle-spin"
5678
- ),
5679
- children: [
5680
- /* @__PURE__ */ jsxRuntime.jsx(
5681
- "circle",
5682
- {
5683
- cx: c,
5684
- cy: c,
5685
- r: RADIUS,
5686
- fill: "none",
5687
- strokeWidth: STROKE,
5688
- style: { stroke: colors.track }
5689
- }
5690
- ),
5691
- /* @__PURE__ */ jsxRuntime.jsx(
5692
- "circle",
5693
- {
5694
- cx: c,
5695
- cy: c,
5696
- r: RADIUS,
5697
- fill: "none",
5698
- strokeWidth: STROKE,
5699
- strokeLinecap: "round",
5700
- className: cn(
5701
- "origin-center",
5702
- indeterminate ? "animate-circle-arc" : "-rotate-90 transition-[stroke-dasharray] duration-[400ms] ease-linear"
5703
- ),
5704
- style: {
5705
- stroke: colors.arc,
5706
- ...indeterminate ? {} : { strokeDasharray: `${filled} ${CIRCUMFERENCE}`, strokeDashoffset: 0 }
5707
- }
5708
- }
5709
- )
5710
- ]
5711
- }
5712
- ),
5713
- showPercent && innerValue && percentNode
5714
- ] }),
5715
- showPercent && !innerValue && percentNode,
5716
- label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-center text-[12px] font-normal leading-[20px] text-[color:var(--cmp-progress-color-label)]", children: label })
5717
- ]
5718
- }
5719
- );
5720
- }
5721
- );
5722
5957
 
5723
5958
  // src/lib/global-overlay.ts
5724
5959
  var GLOBAL_OVERLAY_ATTR = "data-s-global-overlay";
@@ -5915,6 +6150,15 @@ var STextLink = /* @__PURE__ */ react.forwardRef(function STextLink2({
5915
6150
  /* @__PURE__ */ jsxRuntime.jsx(RTooltip__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(RTooltip__namespace.Content, { side: "top", sideOffset: 4, className: cn("z-50", FLOATING_SLIDE_ANIM), children: /* @__PURE__ */ jsxRuntime.jsx(STag, { shape: "square", size: "xs", color: "grey", label: text }) }) })
5916
6151
  ] }) });
5917
6152
  });
6153
+
6154
+ // src/lib/floating-width.ts
6155
+ var POPOVER_AVAILABLE_WIDTH = "var(--radix-popover-content-available-width)";
6156
+ var DROPDOWN_AVAILABLE_WIDTH = "var(--radix-dropdown-menu-content-available-width)";
6157
+ function clampToAvailableWidth(value, availableWidth) {
6158
+ if (value == null || value === "") return availableWidth;
6159
+ const css = typeof value === "number" ? `${value}px` : value;
6160
+ return `min(${css}, ${availableWidth})`;
6161
+ }
5918
6162
  var SIZE2 = {
5919
6163
  xs: { height: 24, paddingX: 8, gap: 6, arrow: 12, fontSize: 12, minWidth: 64, splitMinWidth: 72 },
5920
6164
  sm: {
@@ -5951,6 +6195,7 @@ function splitDivider(color, outline) {
5951
6195
  };
5952
6196
  return (outline ? outlined : solid)[color];
5953
6197
  }
6198
+ var VIEWPORT_PADDING = 12;
5954
6199
  var itemClass = cn(
5955
6200
  "flex min-h-[28px] cursor-pointer select-none items-center gap-[8px] rounded-[2px] px-[12px] py-[4px] text-[12px] leading-[20px] outline-none",
5956
6201
  "text-[color:var(--sys-color-fg-secondary)]",
@@ -6032,12 +6277,16 @@ var SDropdownButtonImpl = /* @__PURE__ */ react.forwardRef(
6032
6277
  sideOffset: 4,
6033
6278
  align: "start",
6034
6279
  alignOffset: split ? splitAlignOffset : 0,
6280
+ collisionPadding: VIEWPORT_PADDING,
6035
6281
  className: cn(
6036
6282
  "z-50 overflow-hidden rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] shadow-normal-sm",
6037
- !split && "min-w-[var(--radix-dropdown-menu-trigger-width)]",
6283
+ // 창보다 넓은 메뉴는 창 폭에 맞춰 줄인다. 트리거 너비를 따르는 하한도 같은 상한으로
6284
+ // 감싸야 한다 — min-width 가 max-width 를 이기기 때문이다.
6285
+ "max-w-[var(--radix-dropdown-menu-content-available-width)]",
6286
+ !split && "min-w-[min(var(--radix-dropdown-menu-trigger-width),var(--radix-dropdown-menu-content-available-width))]",
6038
6287
  FLOATING_SLIDE_ANIM
6039
6288
  ),
6040
- style: split ? { minWidth: splitWidth } : void 0,
6289
+ style: split ? { minWidth: clampToAvailableWidth(splitWidth, DROPDOWN_AVAILABLE_WIDTH) } : void 0,
6041
6290
  children: items.map((it) => /* @__PURE__ */ jsxRuntime.jsxs(
6042
6291
  RDropdown__namespace.Item,
6043
6292
  {
@@ -6049,7 +6298,7 @@ var SDropdownButtonImpl = /* @__PURE__ */ react.forwardRef(
6049
6298
  /* @__PURE__ */ jsxRuntime.jsx(
6050
6299
  "span",
6051
6300
  {
6052
- className: "min-w-0 whitespace-nowrap",
6301
+ className: "min-w-0 break-words",
6053
6302
  dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(it.label) }
6054
6303
  }
6055
6304
  )
@@ -6190,10 +6439,15 @@ function SSwitch({
6190
6439
  }
6191
6440
  );
6192
6441
  }
6442
+ var SIZE_CLASSES = {
6443
+ xs: "h-[24px] px-[8px] text-[12px] font-normal leading-[20px]",
6444
+ sm: "h-[28px] px-[12px] text-[14px] font-medium leading-[24px]"
6445
+ };
6193
6446
  function SToggle({
6194
6447
  value = false,
6195
6448
  label = "",
6196
6449
  disabled = false,
6450
+ size = "sm",
6197
6451
  onValueChange,
6198
6452
  className,
6199
6453
  style
@@ -6209,7 +6463,8 @@ function SToggle({
6209
6463
  "aria-pressed": value,
6210
6464
  onClick: () => !disabled && onValueChange?.(!value),
6211
6465
  className: cn(
6212
- "box-border inline-flex h-[28px] items-center justify-center whitespace-nowrap rounded-full border border-solid px-[12px] text-[14px] font-medium leading-[24px] transition-colors",
6466
+ "box-border inline-flex items-center justify-center whitespace-nowrap rounded-full border border-solid transition-colors",
6467
+ SIZE_CLASSES[size],
6213
6468
  disabled ? "cursor-not-allowed" : "cursor-pointer",
6214
6469
  // 미선택(off) hover 시 파란 채움 + 흰 글자
6215
6470
  !disabled && !value && "hover:!bg-[var(--cmp-toggle-bg-hover)] hover:!text-[color:var(--cmp-toggle-content-hover)] hover:!border-[color:var(--cmp-toggle-bg-hover)]",
@@ -6261,16 +6516,20 @@ function SPopover({
6261
6516
  const descColor = type === "default" ? "var(--cmp-popover-default-description)" : "var(--cmp-popover-light-description)";
6262
6517
  const leftLinkLabelClass = type === "default" ? "text-[color:var(--cmp-popover-default-subAction)]" : "text-[color:var(--cmp-popover-light-subAction)]";
6263
6518
  const triggerNode = trigger ?? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex cursor-pointer", "aria-label": ariaLabel, children: /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: iconSize, color }) });
6264
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open: actualOpen, onOpenChange: setOpen, children: [
6265
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: triggerNode }),
6266
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
6267
- RPopover2__namespace.Content,
6519
+ return /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open: actualOpen, onOpenChange: setOpen, children: [
6520
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: triggerNode }),
6521
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
6522
+ RPopover10__namespace.Content,
6268
6523
  {
6269
6524
  side: placement,
6270
6525
  sideOffset: 8,
6271
6526
  className: cn(
6272
6527
  // 닫기 버튼이 항상 있으므로 우측은 close clearance(36px) 로 고정
6273
- "relative z-50 flex min-w-[160px] flex-col gap-[var(--cmp-popover-gap)] rounded-[var(--cmp-popover-radius)] py-[var(--cmp-popover-paddingY)] px-[var(--cmp-popover-paddingX)] text-[12px] leading-[20px] shadow-normal-sm",
6528
+ "relative z-50 flex flex-col gap-[var(--cmp-popover-gap)] rounded-[var(--cmp-popover-radius)] py-[var(--cmp-popover-paddingY)] px-[var(--cmp-popover-paddingX)] text-[12px] leading-[20px] shadow-normal-sm",
6529
+ // 창보다 넓어지면 창 폭에 맞춘다. 최소 너비도 같은 상한으로 감싼다 —
6530
+ // min-width 가 max-width 를 이기므로 감싸지 않으면 상한이 무력화된다.
6531
+ "max-w-[var(--radix-popover-content-available-width)]",
6532
+ "min-w-[min(160px,var(--radix-popover-content-available-width))]",
6274
6533
  FLOATING_SLIDE_ANIM,
6275
6534
  menuClassName
6276
6535
  ),
@@ -6326,7 +6585,7 @@ function SPopover({
6326
6585
  ]
6327
6586
  }
6328
6587
  ),
6329
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
6588
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
6330
6589
  ]
6331
6590
  }
6332
6591
  ) })
@@ -6355,8 +6614,9 @@ function SPortal({
6355
6614
  ariaLabel
6356
6615
  }) {
6357
6616
  const portalContainer = usePortalContainer();
6617
+ const { minWidth: styleMinWidth, maxWidth: styleMaxWidth, ...restStyle } = style ?? {};
6358
6618
  return /* @__PURE__ */ jsxRuntime.jsxs(
6359
- RPopover2__namespace.Root,
6619
+ RPopover10__namespace.Root,
6360
6620
  {
6361
6621
  open,
6362
6622
  onOpenChange: (next) => {
@@ -6364,9 +6624,9 @@ function SPortal({
6364
6624
  if (!next) onClose?.();
6365
6625
  },
6366
6626
  children: [
6367
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { virtualRef: anchorRef }),
6368
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
6369
- RPopover2__namespace.Content,
6627
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Anchor, { virtualRef: anchorRef }),
6628
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
6629
+ RPopover10__namespace.Content,
6370
6630
  {
6371
6631
  side: placement,
6372
6632
  align,
@@ -6393,11 +6653,18 @@ function SPortal({
6393
6653
  className: cn(
6394
6654
  "z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) shadow-normal-sm",
6395
6655
  maxHeight != null && "overflow-y-auto",
6396
- fit && "min-w-(--radix-popover-trigger-width)",
6656
+ // 앵커가 창보다 넓을 수 있는 자리(가로 스크롤 안의 트리거 등)에서도 하한이 상한을
6657
+ // 이기지 않도록, 앵커 너비 역시 가용 폭으로 가둔다.
6658
+ fit && "min-w-[min(var(--radix-popover-trigger-width),var(--radix-popover-content-available-width))]",
6397
6659
  FLOATING_SLIDE_ANIM,
6398
6660
  className
6399
6661
  ),
6400
- style: { maxHeight, ...style },
6662
+ style: {
6663
+ maxHeight,
6664
+ ...restStyle,
6665
+ ...styleMinWidth != null ? { minWidth: clampToAvailableWidth(styleMinWidth, POPOVER_AVAILABLE_WIDTH) } : {},
6666
+ maxWidth: clampToAvailableWidth(styleMaxWidth, POPOVER_AVAILABLE_WIDTH)
6667
+ },
6401
6668
  children
6402
6669
  }
6403
6670
  ) })
@@ -6557,16 +6824,18 @@ function SGuide({
6557
6824
  );
6558
6825
  if (isNotion)
6559
6826
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className, style, children: trigger });
6560
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpen, children: [
6561
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: trigger }),
6562
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
6563
- RPopover2__namespace.Content,
6827
+ return /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open, onOpenChange: setOpen, children: [
6828
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: trigger }),
6829
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
6830
+ RPopover10__namespace.Content,
6564
6831
  {
6565
6832
  side: "bottom",
6566
6833
  align: "start",
6567
6834
  sideOffset: 4,
6568
6835
  className: cn(
6569
6836
  "relative z-50 rounded-[var(--cmp-guide-contents-radius)] bg-[var(--color-white)]",
6837
+ // popupWidth(기본 426px)가 창보다 넓으면 창 폭에 맞춘다 — max-width 가 width 를 이긴다.
6838
+ "max-w-[var(--radix-popover-content-available-width)]",
6570
6839
  FLOATING_SLIDE_ANIM
6571
6840
  ),
6572
6841
  style: {
@@ -6741,7 +7010,7 @@ function SModalContainer({
6741
7010
  "div",
6742
7011
  {
6743
7012
  className: cn(
6744
- "relative flex max-h-[calc(100dvh-48px)] max-w-[calc(100dvw-48px)] flex-col overflow-hidden rounded-[var(--cmp-modal-radius)] bg-[var(--cmp-modal-bg)] shadow-normal-md",
7013
+ "relative flex max-h-[85dvh] max-w-[calc(100dvw-48px)] flex-col overflow-hidden rounded-[var(--cmp-modal-radius)] bg-[var(--cmp-modal-bg)] shadow-normal-md",
6745
7014
  shaking && "animate-modal-shake",
6746
7015
  className
6747
7016
  ),
@@ -6998,7 +7267,7 @@ function SActionModal({
6998
7267
  showClose: true,
6999
7268
  width,
7000
7269
  height,
7001
- className: "w-fit min-w-[min(480px,calc(100dvw-48px))] min-h-[min(320px,calc(100dvh-48px))]",
7270
+ className: "w-fit min-w-[min(480px,calc(100dvw-48px))] min-h-[min(320px,85dvh)]",
7002
7271
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-auto flex-col", children: [
7003
7272
  /* @__PURE__ */ jsxRuntime.jsx(SOverlayHeader, { title: modalTitle, description, reserveCloseSpace: true }),
7004
7273
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 overflow-auto px-[var(--cmp-modal-action-body-paddingX)] pb-[var(--cmp-modal-action-body-paddingBottom)]", children }),
@@ -7891,15 +8160,21 @@ var GNB_MENU = {
7891
8160
  depth2PaddingLeft: box("item-depth2-paddingLeft"),
7892
8161
  depth3PaddingLeft: box("item-depth3-paddingLeft"),
7893
8162
  depth1Icon: box("item-depth1-icon"),
8163
+ // box 는 depth1 전용 타이포 토큰이 없어 depth1 도 selected 타이포를 따른다
7894
8164
  depth1FontSize: 14,
7895
8165
  // token: gnb.box.item.typography.selected.fontSize
7896
8166
  depth1LineHeight: 24,
7897
8167
  // token: gnb.box.item.typography.selected.lineHeight
7898
- depth1FontWeight: 700,
8168
+ depth1FontWeight: 600,
8169
+ // token: gnb.box.item.typography.selected.fontWeight
7899
8170
  itemFontSize: 14,
7900
8171
  // token: gnb.box.item.typography.default.fontSize
7901
- itemLineHeight: 24
8172
+ itemLineHeight: 24,
7902
8173
  // token: gnb.box.item.typography.default.lineHeight
8174
+ itemFontWeight: 500,
8175
+ // token: gnb.box.item.typography.default.fontWeight
8176
+ itemFontWeightSelected: 600
8177
+ // token: gnb.box.item.typography.selected.fontWeight
7903
8178
  },
7904
8179
  belt: {
7905
8180
  bodyPaddingY: gnb("body-padding"),
@@ -7921,10 +8196,15 @@ var GNB_MENU = {
7921
8196
  depth1LineHeight: 22,
7922
8197
  // token: gnb.belt.item.depth1.typography.lineHeight
7923
8198
  depth1FontWeight: 700,
8199
+ // token: gnb.belt.item.depth1.typography.fontWeight
7924
8200
  itemFontSize: 12,
7925
8201
  // token: gnb.belt.item.typography.default.fontSize
7926
- itemLineHeight: 20
8202
+ itemLineHeight: 20,
7927
8203
  // token: gnb.belt.item.typography.default.lineHeight
8204
+ itemFontWeight: 500,
8205
+ // token: gnb.belt.item.typography.default.fontWeight
8206
+ itemFontWeightSelected: 700
8207
+ // token: gnb.belt.item.typography.selected.fontWeight
7928
8208
  }
7929
8209
  };
7930
8210
  var GNB_COLORS = {
@@ -8222,7 +8502,7 @@ var SGnb = /* @__PURE__ */ react.forwardRef(function SGnb2({
8222
8502
  fontFamily: "inherit",
8223
8503
  fontSize: depth === 1 ? M.depth1FontSize : M.itemFontSize,
8224
8504
  lineHeight: `${depth === 1 ? M.depth1LineHeight : M.itemLineHeight}px`,
8225
- fontWeight: depth === 1 || isSelected ? M.depth1FontWeight : 400,
8505
+ fontWeight: depth === 1 ? M.depth1FontWeight : isSelected ? M.itemFontWeightSelected : M.itemFontWeight,
8226
8506
  opacity: item.disabled ? 0.4 : 1,
8227
8507
  cursor: item.disabled ? "not-allowed" : "pointer"
8228
8508
  };
@@ -8639,10 +8919,10 @@ var SGnb = /* @__PURE__ */ react.forwardRef(function SGnb2({
8639
8919
  }
8640
8920
  ),
8641
8921
  header === "fix" && menuResizeHandle,
8642
- /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open: labelTip !== null, onOpenChange: (open) => !open && setLabelTip(null), children: [
8643
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { virtualRef: labelTipAnchorRef }),
8644
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
8645
- RPopover2__namespace.Content,
8922
+ /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open: labelTip !== null, onOpenChange: (open) => !open && setLabelTip(null), children: [
8923
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Anchor, { virtualRef: labelTipAnchorRef }),
8924
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
8925
+ RPopover10__namespace.Content,
8646
8926
  {
8647
8927
  side: "right",
8648
8928
  sideOffset: 4,
@@ -8660,41 +8940,310 @@ var SGnb = /* @__PURE__ */ react.forwardRef(function SGnb2({
8660
8940
  }
8661
8941
  );
8662
8942
  });
8943
+ var SPageHeader = /* @__PURE__ */ withDisplayName(
8944
+ react.forwardRef(function SPageHeader2({ title, subText, fix = false, onBack, slot, scrolled = false, className, style, ...rest }, ref) {
8945
+ const hasBack = onBack != null;
8946
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8947
+ "header",
8948
+ {
8949
+ ref,
8950
+ className: cn(
8951
+ "relative z-10 box-border flex shrink-0 items-center gap-(--cmp-pageHeader-gap) px-(--cmp-pageHeader-paddingX)",
8952
+ fix ? "h-(--cmp-pageHeader-bar-height) border-b border-(--cmp-pageHeader-bar-border) bg-(--cmp-pageHeader-bar-bg) py-(--cmp-pageHeader-bar-paddingY)" : cn(
8953
+ // 배경은 항상 투명 — 페이지 배경을 따라갈지는 fix 가 정하지 scrolled 가 정하지 않는다.
8954
+ // 스크롤되면 그림자만 더해 콘텐츠 위에 뜬 것처럼 보이게 한다.
8955
+ "bg-transparent pt-(--cmp-pageHeader-plain-paddingTop) pb-(--cmp-pageHeader-plain-paddingBottom)",
8956
+ scrolled && "shadow-floating"
8957
+ ),
8958
+ className
8959
+ ),
8960
+ style,
8961
+ ...rest,
8962
+ children: [
8963
+ hasBack && /* @__PURE__ */ jsxRuntime.jsx(
8964
+ SGhostButton,
8965
+ {
8966
+ icon: "chevronLeft",
8967
+ size: "xs",
8968
+ ariaLabel: "\uB4A4\uB85C\uAC00\uAE30",
8969
+ onClick: onBack,
8970
+ className: "shrink-0"
8971
+ }
8972
+ ),
8973
+ /* @__PURE__ */ jsxRuntime.jsxs(
8974
+ "div",
8975
+ {
8976
+ className: cn(
8977
+ "flex min-w-0 flex-1 items-center",
8978
+ fix ? "flex-row gap-(--cmp-pageHeader-gap)" : "flex-col items-start gap-(--cmp-pageHeader-plain-title-gap)",
8979
+ !hasBack && "pl-(--cmp-pageHeader-title-paddingLeft)"
8980
+ ),
8981
+ children: [
8982
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "typo-heading-lg min-w-0 truncate text-(--cmp-pageHeader-title-text)", children: title }),
8983
+ subText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "typo-body-md-default min-w-0 truncate text-(--cmp-pageHeader-subtitle-text)", children: subText })
8984
+ ]
8985
+ }
8986
+ ),
8987
+ slot && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex shrink-0 items-center", children: slot })
8988
+ ]
8989
+ }
8990
+ );
8991
+ }),
8992
+ "SPageHeader"
8993
+ );
8663
8994
  var MIN_CONTENT_WIDTH = `calc(var(${SLAYOUT_MIN_WIDTH_VAR}, 0px) - 100cqw + 100%)`;
8664
8995
  var BACKGROUND_CLASS = {
8665
- frame: `bg-[var(--sys-color-bg-frame)] ${SCROLLBAR_TRACK_BORDER_CLASS} ${SCROLLBAR_TRACK_BG_CLASS}`,
8666
- neutral: `bg-[var(--sys-color-bg-neutralLight)] ${SCROLLBAR_TRACK_BORDER_CLASS} ${SCROLLBAR_TRACK_BG_WHITE_CLASS}`,
8667
- screen: `bg-[var(--sys-color-bg-screen)] ${SCROLLBAR_TRACK_BORDER_CLASS} ${SCROLLBAR_TRACK_BG_CLASS}`
8996
+ frame: "bg-[var(--sys-color-bg-frame)]",
8997
+ neutral: "bg-[var(--sys-color-bg-neutralLight)]",
8998
+ screen: "bg-[var(--sys-color-bg-screen)]"
8999
+ };
9000
+ var NATIVE_SCROLLBAR_BACKGROUND_CLASS = {
9001
+ frame: `${SCROLLBAR_TRACK_BORDER_CLASS} ${SCROLLBAR_TRACK_BG_CLASS}`,
9002
+ neutral: `${SCROLLBAR_TRACK_BORDER_CLASS} ${SCROLLBAR_TRACK_BG_WHITE_CLASS}`,
9003
+ screen: `${SCROLLBAR_TRACK_BORDER_CLASS} ${SCROLLBAR_TRACK_BG_CLASS}`
9004
+ };
9005
+ var SCROLLBAR_SIZE = 12;
9006
+ var SCROLLBAR_MIN_THUMB_SIZE = 32;
9007
+ var getMetrics = (viewportSize, contentSize, scrollOffset) => {
9008
+ if (contentSize <= viewportSize) {
9009
+ return { visible: false, thumbSize: 0, thumbOffset: 0 };
9010
+ }
9011
+ const trackSize = viewportSize;
9012
+ const maxScrollOffset = contentSize - viewportSize;
9013
+ const thumbSize = Math.min(
9014
+ trackSize,
9015
+ Math.max(SCROLLBAR_MIN_THUMB_SIZE, viewportSize / contentSize * trackSize)
9016
+ );
9017
+ const maxThumbOffset = trackSize - thumbSize;
9018
+ const thumbOffset = maxScrollOffset > 0 ? scrollOffset / maxScrollOffset * maxThumbOffset : 0;
9019
+ return { visible: true, thumbSize, thumbOffset };
8668
9020
  };
8669
- var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({ background = "frame", scrollEndSpacing = true, className, style, children, ...rest }, ref) {
9021
+ var setForwardedRef = (ref, node) => {
9022
+ if (typeof ref === "function") {
9023
+ ref(node);
9024
+ return;
9025
+ }
9026
+ if (ref) ref.current = node;
9027
+ };
9028
+ function SPageOverlayScrollbars({ scrollRef }) {
9029
+ const [vertical, setVertical] = react.useState({
9030
+ visible: false,
9031
+ thumbSize: 0,
9032
+ thumbOffset: 0
9033
+ });
9034
+ const [horizontal, setHorizontal] = react.useState({
9035
+ visible: false,
9036
+ thumbSize: 0,
9037
+ thumbOffset: 0
9038
+ });
9039
+ const update = react.useCallback(() => {
9040
+ const el = scrollRef.current;
9041
+ if (!el) return;
9042
+ setVertical(getMetrics(el.clientHeight, el.scrollHeight, el.scrollTop));
9043
+ setHorizontal(getMetrics(el.clientWidth, el.scrollWidth, el.scrollLeft));
9044
+ }, [scrollRef]);
9045
+ react.useEffect(() => {
9046
+ const el = scrollRef.current;
9047
+ if (!el) return;
9048
+ update();
9049
+ el.addEventListener("scroll", update, { passive: true });
9050
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(update);
9051
+ observer?.observe(el);
9052
+ if (el.firstElementChild) observer?.observe(el.firstElementChild);
9053
+ return () => {
9054
+ el.removeEventListener("scroll", update);
9055
+ observer?.disconnect();
9056
+ };
9057
+ }, [scrollRef, update]);
9058
+ const dragVertical = (startEvent) => {
9059
+ const el = scrollRef.current;
9060
+ if (!el || !vertical.visible) return;
9061
+ startEvent.preventDefault();
9062
+ startEvent.currentTarget.setPointerCapture(startEvent.pointerId);
9063
+ const startPointer = startEvent.clientY;
9064
+ const startScrollTop = el.scrollTop;
9065
+ const maxThumbOffset = el.clientHeight - vertical.thumbSize;
9066
+ const maxScrollTop = el.scrollHeight - el.clientHeight;
9067
+ if (maxThumbOffset <= 0 || maxScrollTop <= 0) return;
9068
+ const onPointerMove = (moveEvent) => {
9069
+ const delta = moveEvent.clientY - startPointer;
9070
+ el.scrollTop = startScrollTop + delta / maxThumbOffset * maxScrollTop;
9071
+ };
9072
+ const onPointerUp = () => {
9073
+ window.removeEventListener("pointermove", onPointerMove);
9074
+ window.removeEventListener("pointerup", onPointerUp);
9075
+ };
9076
+ window.addEventListener("pointermove", onPointerMove);
9077
+ window.addEventListener("pointerup", onPointerUp, { once: true });
9078
+ };
9079
+ const dragHorizontal = (startEvent) => {
9080
+ const el = scrollRef.current;
9081
+ if (!el || !horizontal.visible) return;
9082
+ startEvent.preventDefault();
9083
+ startEvent.currentTarget.setPointerCapture(startEvent.pointerId);
9084
+ const startPointer = startEvent.clientX;
9085
+ const startScrollLeft = el.scrollLeft;
9086
+ const maxThumbOffset = el.clientWidth - horizontal.thumbSize;
9087
+ const maxScrollLeft = el.scrollWidth - el.clientWidth;
9088
+ if (maxThumbOffset <= 0 || maxScrollLeft <= 0) return;
9089
+ const onPointerMove = (moveEvent) => {
9090
+ const delta = moveEvent.clientX - startPointer;
9091
+ el.scrollLeft = startScrollLeft + delta / maxThumbOffset * maxScrollLeft;
9092
+ };
9093
+ const onPointerUp = () => {
9094
+ window.removeEventListener("pointermove", onPointerMove);
9095
+ window.removeEventListener("pointerup", onPointerUp);
9096
+ };
9097
+ window.addEventListener("pointermove", onPointerMove);
9098
+ window.addEventListener("pointerup", onPointerUp, { once: true });
9099
+ };
9100
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9101
+ vertical.visible && /* @__PURE__ */ jsxRuntime.jsx(
9102
+ "div",
9103
+ {
9104
+ "aria-hidden": "true",
9105
+ className: "pointer-events-none absolute bottom-0 right-0 top-0 z-[1] w-[12px]",
9106
+ style: { bottom: horizontal.visible ? SCROLLBAR_SIZE : 0 },
9107
+ children: /* @__PURE__ */ jsxRuntime.jsx(
9108
+ "div",
9109
+ {
9110
+ className: "pointer-events-auto absolute right-[2px] w-[8px] cursor-default touch-none rounded-full bg-[var(--color-grey-45)]",
9111
+ style: {
9112
+ height: vertical.thumbSize,
9113
+ transform: `translateY(${vertical.thumbOffset}px)`
9114
+ },
9115
+ onPointerDown: dragVertical
9116
+ }
9117
+ )
9118
+ }
9119
+ ),
9120
+ horizontal.visible && /* @__PURE__ */ jsxRuntime.jsx(
9121
+ "div",
9122
+ {
9123
+ "aria-hidden": "true",
9124
+ className: "pointer-events-none absolute bottom-0 left-0 right-0 z-[1] h-[12px]",
9125
+ style: { right: vertical.visible ? SCROLLBAR_SIZE : 0 },
9126
+ children: /* @__PURE__ */ jsxRuntime.jsx(
9127
+ "div",
9128
+ {
9129
+ className: "pointer-events-auto absolute bottom-[2px] h-[8px] cursor-default touch-none rounded-full bg-[var(--color-grey-45)]",
9130
+ style: {
9131
+ width: horizontal.thumbSize,
9132
+ transform: `translateX(${horizontal.thumbOffset}px)`
9133
+ },
9134
+ onPointerDown: dragHorizontal
9135
+ }
9136
+ )
9137
+ }
9138
+ )
9139
+ ] });
9140
+ }
9141
+ var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
9142
+ background = "frame",
9143
+ scrollEndSpacing = true,
9144
+ overlayScrollbar = false,
9145
+ contentHeight = "auto",
9146
+ header,
9147
+ className,
9148
+ style,
9149
+ children,
9150
+ onScroll,
9151
+ ...rest
9152
+ }, ref) {
8670
9153
  const layout = react.useContext(SLayoutContext);
8671
9154
  const gridStyle = layout?.header === "full" ? { gridArea: "page" } : void 0;
8672
- return /* @__PURE__ */ jsxRuntime.jsx(
9155
+ const scrollRef = react.useRef(null);
9156
+ const setMainRef = react.useCallback(
9157
+ (node) => {
9158
+ scrollRef.current = node;
9159
+ setForwardedRef(ref, node);
9160
+ },
9161
+ [ref]
9162
+ );
9163
+ const [scrolled, setScrolled] = react.useState(false);
9164
+ const handleScroll = (e) => {
9165
+ onScroll?.(e);
9166
+ if (header != null && !header.fix) setScrolled(e.currentTarget.scrollTop > 0);
9167
+ };
9168
+ const fill = contentHeight === "fill";
9169
+ const main = /* @__PURE__ */ jsxRuntime.jsx(
8673
9170
  "main",
8674
9171
  {
8675
- ref,
9172
+ ref: setMainRef,
8676
9173
  className: cn(
8677
9174
  "box-border min-h-0 min-w-0 flex-1 overflow-auto p-[var(--cmp-pageBody-padding-default)]",
8678
- scrollEndSpacing && "pb-[var(--cmp-pageBody-padding-scrollEnd)]",
8679
- SCROLLBAR_CLASS,
9175
+ overlayScrollbar && "h-full",
9176
+ !fill && scrollEndSpacing && "pb-[var(--cmp-pageBody-padding-scrollEnd)]",
9177
+ overlayScrollbar ? SCROLLBAR_HIDDEN_CLASS : `${SCROLLBAR_CLASS} ${NATIVE_SCROLLBAR_BACKGROUND_CLASS[background]}`,
8680
9178
  BACKGROUND_CLASS[background],
8681
9179
  className
8682
9180
  ),
8683
- style: { ...gridStyle, ...style },
9181
+ style: header == null && !overlayScrollbar ? { ...gridStyle, ...style } : style,
9182
+ onScroll: handleScroll,
8684
9183
  ...rest,
8685
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { minWidth: MIN_CONTENT_WIDTH }, children })
9184
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(fill && "h-full"), style: { minWidth: MIN_CONTENT_WIDTH }, children })
8686
9185
  }
8687
9186
  );
9187
+ if (!overlayScrollbar) {
9188
+ if (header == null) return main;
9189
+ return (
9190
+ // fix 가 아닌 헤더는 자기 배경이 투명이라(SPageHeader.tsx) 이 배경이 그대로 비친다.
9191
+ /* @__PURE__ */ jsxRuntime.jsxs(
9192
+ "div",
9193
+ {
9194
+ className: cn("flex min-h-0 min-w-0 flex-1 flex-col", BACKGROUND_CLASS[background]),
9195
+ style: gridStyle,
9196
+ children: [
9197
+ /* @__PURE__ */ jsxRuntime.jsx(SPageHeader, { ...header, scrolled }),
9198
+ main
9199
+ ]
9200
+ }
9201
+ )
9202
+ );
9203
+ }
9204
+ if (header == null) {
9205
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9206
+ "div",
9207
+ {
9208
+ className: cn(
9209
+ "relative min-h-0 min-w-0 flex-1 overflow-hidden",
9210
+ BACKGROUND_CLASS[background]
9211
+ ),
9212
+ style: gridStyle,
9213
+ children: [
9214
+ main,
9215
+ /* @__PURE__ */ jsxRuntime.jsx(SPageOverlayScrollbars, { scrollRef })
9216
+ ]
9217
+ }
9218
+ );
9219
+ }
9220
+ return (
9221
+ // fix 가 아닌 헤더는 자기 배경이 투명이라(SPageHeader.tsx) 이 배경이 그대로 비친다 —
9222
+ // 그래서 SPage 의 background 를 main 뿐 아니라 여기에도 칠한다(스크롤바 관련 클래스는
9223
+ // 스크롤 요소가 아닌 여기서는 그냥 무시된다). fix 헤더는 자체 흰 배경이 덮으므로 무관하다.
9224
+ /* @__PURE__ */ jsxRuntime.jsxs(
9225
+ "div",
9226
+ {
9227
+ className: cn("flex min-h-0 min-w-0 flex-1 flex-col", BACKGROUND_CLASS[background]),
9228
+ style: gridStyle,
9229
+ children: [
9230
+ /* @__PURE__ */ jsxRuntime.jsx(SPageHeader, { ...header, scrolled }),
9231
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative min-h-0 min-w-0 flex-1 overflow-hidden", children: [
9232
+ main,
9233
+ /* @__PURE__ */ jsxRuntime.jsx(SPageOverlayScrollbars, { scrollRef })
9234
+ ] })
9235
+ ]
9236
+ }
9237
+ )
9238
+ );
8688
9239
  });
8689
9240
 
8690
9241
  // src/components/SPage/page.config.ts
8691
9242
  var PAGE_BACKGROUNDS = ["frame", "neutral", "screen"];
9243
+ var PAGE_CONTENT_HEIGHTS = ["auto", "fill"];
8692
9244
 
8693
9245
  // src/components/STabs/tabs.config.ts
8694
- var TAB_MAIN_SIZE = {
8695
- md: { height: 36, paddingX: 24 },
8696
- lg: { height: 44, paddingX: 32 }
8697
- };
9246
+ var TAB_MAIN_SIZE = { height: 36, paddingX: 24 };
8698
9247
  var TAB_TYPOGRAPHY = { fontSize: 12, lineHeight: 20 };
8699
9248
  var TAB_MAIN_COLORS = {
8700
9249
  selected: {
@@ -8716,13 +9265,11 @@ function STabs({
8716
9265
  value,
8717
9266
  tabs,
8718
9267
  onValueChange,
8719
- size = "md",
8720
9268
  isSub = false,
8721
9269
  vertical = false,
8722
9270
  className,
8723
9271
  style
8724
9272
  }) {
8725
- const s = TAB_MAIN_SIZE[size];
8726
9273
  const blue = TAB_MAIN_COLORS.selected.border;
8727
9274
  const isVertical = isSub && vertical;
8728
9275
  const tabRefs = react.useRef([]);
@@ -8763,8 +9310,8 @@ function STabs({
8763
9310
  const border = selected ? TAB_MAIN_COLORS.selected.border : TAB_MAIN_COLORS.default.border;
8764
9311
  const selectedBg = TAB_MAIN_COLORS.selected.bg;
8765
9312
  tabStyle = {
8766
- height: s.height,
8767
- padding: `0 ${s.paddingX}px`,
9313
+ height: TAB_MAIN_SIZE.height,
9314
+ padding: `0 ${TAB_MAIN_SIZE.paddingX}px`,
8768
9315
  fontSize: TAB_TYPOGRAPHY.fontSize,
8769
9316
  lineHeight: `${TAB_TYPOGRAPHY.lineHeight}px`,
8770
9317
  background: selected ? TAB_MAIN_COLORS.selected.bg : TAB_MAIN_COLORS.default.bg,
@@ -9506,6 +10053,7 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9506
10053
  readOnly = false,
9507
10054
  width,
9508
10055
  minWidth,
10056
+ maxWidth,
9509
10057
  multiline = false,
9510
10058
  borderless = false,
9511
10059
  children,
@@ -9541,7 +10089,10 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9541
10089
  const resolvedSuccess = status === "pass";
9542
10090
  const showError = resolvedError && errorMessage !== "";
9543
10091
  const interactive = !disabled && !readOnly;
9544
- const hug = cssLen(width) !== void 0;
10092
+ const isPercentWidth = typeof width === "string" && width.trim().endsWith("%");
10093
+ const hug = cssLen(width) !== void 0 && !isPercentWidth;
10094
+ const addonWidth = effectiveAddonLabel !== "" ? cssLen(labelWidth) : void 0;
10095
+ const resolvedMaxWidth = cssLen(maxWidth) === void 0 || effectiveAddonLabel === "" ? cssLen(maxWidth) : addonWidth ? `calc(${cssLen(maxWidth)} + ${addonWidth})` : void 0;
9545
10096
  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)";
9546
10097
  const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
9547
10098
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
@@ -9597,8 +10148,12 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9597
10148
  /* @__PURE__ */ jsxRuntime.jsxs(
9598
10149
  "div",
9599
10150
  {
9600
- className: cn("flex min-w-0 flex-col", !hug && "flex-1"),
9601
- style: { width: cssLen(width), minWidth: cssLen(minWidth) },
10151
+ className: cn("flex min-w-0 flex-col", !hug && !isPercentWidth && "flex-1"),
10152
+ style: {
10153
+ width: cssLen(width),
10154
+ minWidth: cssLen(minWidth),
10155
+ maxWidth: resolvedMaxWidth
10156
+ },
9602
10157
  children: [
9603
10158
  /* @__PURE__ */ jsxRuntime.jsxs(
9604
10159
  "div",
@@ -10353,6 +10908,7 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10353
10908
  height,
10354
10909
  stickyColumn,
10355
10910
  radius = "default",
10911
+ bordered = true,
10356
10912
  noDataLabel = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
10357
10913
  noDataSlot,
10358
10914
  isLoading = false,
@@ -10611,7 +11167,7 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10611
11167
  };
10612
11168
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[var(--cmp-table-radius)] rounded-t-none" : "rounded-[var(--cmp-table-radius)]";
10613
11169
  const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-body-paddingY-default)";
10614
- const cellPadX = "var(--cmp-table-body-paddingX-wide)";
11170
+ const cellPadXOf = (col) => col.contentType === "control" ? "var(--cmp-table-body-paddingX-default)" : "var(--cmp-table-body-paddingX-wide)";
10615
11171
  const rowH = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-body-default-height)";
10616
11172
  const totalColSpan = visibleCols.length + (selectable ? 1 : 0);
10617
11173
  const stickyInfo = (i) => {
@@ -10665,7 +11221,8 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10665
11221
  // 만으로는 스태킹 컨텍스트가 생기지 않아, 모달 안에 들어가면 30·31이 모달 Content
10666
11222
  // 레벨까지 올라가 같은 컨텍스트의 floating portal 밴드(z-2)를 덮는다.
10667
11223
  // 자손은 이미 overflow-hidden으로 클리핑되므로 테이블 내부 순서는 달라지지 않는다.
10668
- "bg-white relative isolate flex flex-col overflow-hidden border border-solid border-[color:var(--cmp-table-border-color)]",
11224
+ "bg-white relative isolate flex flex-col overflow-hidden",
11225
+ bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
10669
11226
  // height가 없으면 루트가 받은 높이를 끝까지 채운다. 호스트가 className으로
10670
11227
  // `flex-1 min-h-0` 등을 줘 루트 높이가 확정된 경우, 행이 적어도 테두리 박스가
10671
11228
  // 남은 영역을 그대로 차지한다. basis는 auto로 둬 높이가 확정되지 않은
@@ -10835,9 +11392,10 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10835
11392
  stickyShadowClass(i),
10836
11393
  typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
10837
11394
  );
11395
+ const tdPadX = cellPadXOf(col);
10838
11396
  const tdStyle = {
10839
- paddingLeft: cellPadX,
10840
- paddingRight: cellPadX,
11397
+ paddingLeft: tdPadX,
11398
+ paddingRight: tdPadX,
10841
11399
  paddingTop: cellPadY,
10842
11400
  paddingBottom: cellPadY,
10843
11401
  textAlign: col.align ?? "left",
@@ -10890,19 +11448,29 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10890
11448
  // SPagination은 전체 폭 기준 중앙, rows-per-page SSelect는 우측에 absolute 배치해 중앙 정렬을 유지한다.
10891
11449
  // 루트 높이가 확정된 경우(테이블이 남은 영역을 채우는 배치) 페이지네이션 바가
10892
11450
  // 눌리지 않도록 shrink-0으로 48px를 보장한다.
10893
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative -mt-px flex h-[48px] shrink-0 items-center justify-center rounded-[var(--cmp-table-radius)] border border-solid border-[color:var(--cmp-table-border-color)] bg-[var(--color-grey-05)]", children: [
10894
- /* @__PURE__ */ jsxRuntime.jsx(SPagination, { currentPage: displayPage, lastPage, onPageChange: changePage }),
10895
- useRowsPerPageSelect && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-[10px] top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsxRuntime.jsx(
10896
- SSelect2,
10897
- {
10898
- options: rowsPerPageOption,
10899
- value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
10900
- emitValue: true,
10901
- onValueChange: (v) => changeRowsPerPage(Number(v)),
10902
- width: 128
10903
- }
10904
- ) })
10905
- ] })
11451
+ /* @__PURE__ */ jsxRuntime.jsxs(
11452
+ "div",
11453
+ {
11454
+ className: cn(
11455
+ "relative flex h-[48px] shrink-0 items-center justify-center rounded-[var(--cmp-table-radius)] bg-[var(--color-grey-05)]",
11456
+ // 테두리를 끄면 겹칠 대상이 없어진다 — -mt-px 를 남기면 1px 어긋남만 생긴다.
11457
+ bordered && "-mt-px border border-solid border-[color:var(--cmp-table-border-color)]"
11458
+ ),
11459
+ children: [
11460
+ /* @__PURE__ */ jsxRuntime.jsx(SPagination, { currentPage: displayPage, lastPage, onPageChange: changePage }),
11461
+ useRowsPerPageSelect && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-[10px] top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsxRuntime.jsx(
11462
+ SSelect2,
11463
+ {
11464
+ options: rowsPerPageOption,
11465
+ value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
11466
+ emitValue: true,
11467
+ onValueChange: (v) => changeRowsPerPage(Number(v)),
11468
+ width: 128
11469
+ }
11470
+ ) })
11471
+ ]
11472
+ }
11473
+ )
10906
11474
  ]
10907
11475
  }
10908
11476
  );
@@ -10965,27 +11533,88 @@ var STableBar = /* @__PURE__ */ react.forwardRef(function STableBar2({ total, se
10965
11533
 
10966
11534
  // src/lib/autofill.ts
10967
11535
  var AUTOFILL_RESET_CLASS = "autofill:[-webkit-text-fill-color:currentColor] autofill:[box-shadow:0_0_0_1000px_var(--sys-color-field-bg-default)_inset] autofill:[transition:background-color_5000s_ease-in-out_0s] [&:-webkit-autofill]:[-webkit-text-fill-color:currentColor] [&:-webkit-autofill]:[box-shadow:0_0_0_1000px_var(--sys-color-field-bg-default)_inset] [&:-webkit-autofill]:[transition:background-color_5000s_ease-in-out_0s]";
10968
- var FONT = {
10969
- sm: { fontSize: 12, lineHeight: 20, paddingX: "var(--cmp-textinput-sm-paddingX)" },
10970
- md: { fontSize: 14, lineHeight: 24, paddingX: "var(--cmp-textinput-md-paddingX)" }
10971
- };
10972
- var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
10973
- value,
10974
- onValueChange,
10975
- onChange,
10976
- type = "text",
10977
- size = "sm",
10978
- icon,
10979
- iconColor,
10980
- prefix,
10981
- suffix,
10982
- clearable = false,
10983
- rules,
10984
- status,
10985
- focused,
10986
- hovered,
10987
- inputClass,
10988
- inputStyle,
11536
+ var HIDE_DELAY_MS2 = 100;
11537
+ function mergeRefs(...refs) {
11538
+ return (node) => {
11539
+ for (const ref of refs) {
11540
+ if (!ref) continue;
11541
+ if (typeof ref === "function") ref(node);
11542
+ else ref.current = node;
11543
+ }
11544
+ };
11545
+ }
11546
+ function useTruncatedValueTooltip({
11547
+ enabled,
11548
+ text
11549
+ }) {
11550
+ const portalContainer = usePortalContainer();
11551
+ const elRef = react.useRef(null);
11552
+ const [open, setOpen] = react.useState(false);
11553
+ const hideTimer = react.useRef(void 0);
11554
+ const cancelHide = react.useCallback(() => {
11555
+ if (hideTimer.current) {
11556
+ clearTimeout(hideTimer.current);
11557
+ hideTimer.current = void 0;
11558
+ }
11559
+ }, []);
11560
+ const startHide = react.useCallback(() => {
11561
+ cancelHide();
11562
+ hideTimer.current = setTimeout(() => setOpen(false), HIDE_DELAY_MS2);
11563
+ }, [cancelHide]);
11564
+ const onMouseEnter = react.useCallback(() => {
11565
+ cancelHide();
11566
+ const el = elRef.current;
11567
+ if (!el) return;
11568
+ if (el.scrollWidth - el.clientWidth > 1) setOpen(true);
11569
+ }, [cancelHide]);
11570
+ react.useEffect(() => {
11571
+ if (!enabled || text === "") setOpen(false);
11572
+ }, [enabled, text]);
11573
+ react.useEffect(() => cancelHide, [cancelHide]);
11574
+ const active = enabled && text !== "";
11575
+ return {
11576
+ valueRef: elRef,
11577
+ onMouseEnter: active ? onMouseEnter : cancelHide,
11578
+ onMouseLeave: active ? startHide : cancelHide,
11579
+ tooltip: active ? /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open, onOpenChange: setOpen, children: [
11580
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Anchor, { virtualRef: elRef }),
11581
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
11582
+ RPopover10__namespace.Content,
11583
+ {
11584
+ side: "top",
11585
+ sideOffset: 4,
11586
+ "aria-hidden": true,
11587
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
11588
+ onOpenAutoFocus: (e) => e.preventDefault(),
11589
+ onMouseEnter: cancelHide,
11590
+ onMouseLeave: startHide,
11591
+ children: /* @__PURE__ */ jsxRuntime.jsx(STag, { shape: "square", size: "xs", color: "grey", label: text })
11592
+ }
11593
+ ) })
11594
+ ] }) : null
11595
+ };
11596
+ }
11597
+ var FONT = {
11598
+ sm: { fontSize: 12, lineHeight: 20, paddingX: "var(--cmp-textinput-sm-paddingX)" },
11599
+ md: { fontSize: 14, lineHeight: 24, paddingX: "var(--cmp-textinput-md-paddingX)" }
11600
+ };
11601
+ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
11602
+ value,
11603
+ onValueChange,
11604
+ onChange,
11605
+ type = "text",
11606
+ size = "sm",
11607
+ icon,
11608
+ iconColor,
11609
+ prefix,
11610
+ suffix,
11611
+ clearable = false,
11612
+ rules,
11613
+ status,
11614
+ focused,
11615
+ hovered,
11616
+ inputClass,
11617
+ inputStyle,
10989
11618
  label,
10990
11619
  labelWidth,
10991
11620
  labelTooltip,
@@ -11022,6 +11651,10 @@ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
11022
11651
  const resolvedError = error || ruleError !== "";
11023
11652
  const resolvedErrorMessage = ruleError !== "" ? ruleError : errorMessage;
11024
11653
  const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
11654
+ const valueTooltip = useTruncatedValueTooltip({
11655
+ enabled: readOnly && !disabled,
11656
+ text: String(value ?? "")
11657
+ });
11025
11658
  return /* @__PURE__ */ jsxRuntime.jsx(
11026
11659
  SField,
11027
11660
  {
@@ -11058,11 +11691,11 @@ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
11058
11691
  lineHeight: `${f.lineHeight}px`
11059
11692
  },
11060
11693
  children: [
11061
- prefix != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: prefix }),
11694
+ prefix != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex flex-shrink-0 items-center", children: prefix }),
11062
11695
  /* @__PURE__ */ jsxRuntime.jsx(
11063
11696
  "input",
11064
11697
  {
11065
- ref,
11698
+ ref: mergeRefs(ref, valueTooltip.valueRef),
11066
11699
  type: inputType,
11067
11700
  value: value ?? "",
11068
11701
  disabled,
@@ -11087,10 +11720,19 @@ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
11087
11720
  color: disabled ? "var(--cmp-textinput-text-disabled)" : "var(--sys-color-field-text-default)",
11088
11721
  ...inputStyle
11089
11722
  },
11090
- ...rest
11723
+ ...rest,
11724
+ onMouseEnter: (e) => {
11725
+ rest.onMouseEnter?.(e);
11726
+ valueTooltip.onMouseEnter();
11727
+ },
11728
+ onMouseLeave: (e) => {
11729
+ rest.onMouseLeave?.(e);
11730
+ valueTooltip.onMouseLeave();
11731
+ }
11091
11732
  }
11092
11733
  ),
11093
- suffix != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: suffix }),
11734
+ valueTooltip.tooltip,
11735
+ suffix != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex flex-shrink-0 items-center", children: suffix }),
11094
11736
  clearable && hasValue && !disabled && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
11095
11737
  SGhostButton,
11096
11738
  {
@@ -11323,8 +11965,10 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
11323
11965
  const [display, setDisplay] = react.useState(() => formatWithCommas(numValue));
11324
11966
  const [ruleError, setRuleError] = react.useState("");
11325
11967
  const innerRef = react.useRef(null);
11968
+ const valueTooltip = useTruncatedValueTooltip({ enabled: readOnly && !disabled, text: display });
11326
11969
  const setRefs2 = (el) => {
11327
11970
  innerRef.current = el;
11971
+ valueTooltip.valueRef.current = el;
11328
11972
  if (typeof ref === "function") ref(el);
11329
11973
  else if (ref) ref.current = el;
11330
11974
  };
@@ -11538,9 +12182,12 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
11538
12182
  textAlign: useButton ? "center" : "right",
11539
12183
  color: disabled ? "var(--cmp-numberInput-text-disabled)" : "var(--cmp-numberInput-text-default)",
11540
12184
  ...inputStyle
11541
- }
12185
+ },
12186
+ onMouseEnter: valueTooltip.onMouseEnter,
12187
+ onMouseLeave: valueTooltip.onMouseLeave
11542
12188
  }
11543
12189
  ),
12190
+ valueTooltip.tooltip,
11544
12191
  inputSuffix !== "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", style: { color: affixColor }, children: inputSuffix }),
11545
12192
  useButton && stepper("inc")
11546
12193
  ]
@@ -11919,12 +12566,275 @@ function SCalendar({
11919
12566
  }
11920
12567
  );
11921
12568
  }
12569
+ var DATEPICKER_MODE_LIST_HEIGHT = 240;
12570
+ var DATEPICKER_MODE_ITEM_HEIGHT = 28;
12571
+ var YEAR_LIST_EDGE_BUFFER = 24;
12572
+ var YEAR_LIST_EXPAND_SIZE = 80;
12573
+ var MONTHS = Array.from({ length: 12 }, (_, i) => i + 1);
12574
+ var getTodayParts = () => {
12575
+ const today = /* @__PURE__ */ new Date();
12576
+ return {
12577
+ year: today.getFullYear(),
12578
+ month: today.getMonth() + 1
12579
+ };
12580
+ };
12581
+ var parseYear = (value) => {
12582
+ const year = Number(value?.slice(0, 4));
12583
+ return Number.isFinite(year) && year > 0 ? year : void 0;
12584
+ };
12585
+ var parseMonth = (value) => {
12586
+ const month = Number(value?.slice(5, 7));
12587
+ return Number.isFinite(month) && month >= 1 && month <= 12 ? month : void 0;
12588
+ };
12589
+ var formatMonthValue = (year, month) => `${year}-${String(month).padStart(2, "0")}`;
12590
+ var isYearDisabled = (year, selectable) => {
12591
+ if (!selectable) return false;
12592
+ const [start, end] = selectable;
12593
+ if (start !== "" && year < Number(start.slice(0, 4))) return true;
12594
+ if (end !== "" && year > Number(end.slice(0, 4))) return true;
12595
+ return false;
12596
+ };
12597
+ var isMonthDisabled = (year, month, selectable) => {
12598
+ if (!selectable) return false;
12599
+ const value = formatMonthValue(year, month);
12600
+ const [start, end] = selectable;
12601
+ if (start !== "" && value < start.slice(0, 7)) return true;
12602
+ if (end !== "" && value > end.slice(0, 7)) return true;
12603
+ return false;
12604
+ };
12605
+ function YearWheel({ value, anchorYear, selectable, onCommit }) {
12606
+ const listRef = react.useRef(null);
12607
+ const syncingScrollRef = react.useRef(false);
12608
+ const anchorYearRef = react.useRef(anchorYear);
12609
+ const [range, setRange] = react.useState(() => ({
12610
+ start: anchorYear - YEAR_LIST_EXPAND_SIZE,
12611
+ end: anchorYear + YEAR_LIST_EXPAND_SIZE
12612
+ }));
12613
+ const rangeStartRef = react.useRef(range.start);
12614
+ rangeStartRef.current = range.start;
12615
+ const years = Array.from({ length: range.end - range.start + 1 }, (_, i) => range.start + i);
12616
+ react.useLayoutEffect(() => {
12617
+ const list = listRef.current;
12618
+ if (!list) return;
12619
+ const index = anchorYearRef.current - rangeStartRef.current;
12620
+ syncingScrollRef.current = true;
12621
+ list.scrollTop = index * DATEPICKER_MODE_ITEM_HEIGHT;
12622
+ requestAnimationFrame(() => {
12623
+ syncingScrollRef.current = false;
12624
+ });
12625
+ }, []);
12626
+ const handleScroll = () => {
12627
+ const list = listRef.current;
12628
+ if (!list) return;
12629
+ if (syncingScrollRef.current) return;
12630
+ if (list.scrollTop < DATEPICKER_MODE_ITEM_HEIGHT * YEAR_LIST_EDGE_BUFFER) {
12631
+ const previousHeight = list.scrollHeight;
12632
+ setRange((current) => ({
12633
+ start: current.start - YEAR_LIST_EXPAND_SIZE,
12634
+ end: current.end
12635
+ }));
12636
+ requestAnimationFrame(() => {
12637
+ if (!listRef.current) return;
12638
+ syncingScrollRef.current = true;
12639
+ listRef.current.scrollTop += listRef.current.scrollHeight - previousHeight;
12640
+ requestAnimationFrame(() => {
12641
+ syncingScrollRef.current = false;
12642
+ });
12643
+ });
12644
+ return;
12645
+ }
12646
+ if (list.scrollHeight - list.scrollTop - list.clientHeight < DATEPICKER_MODE_ITEM_HEIGHT * YEAR_LIST_EDGE_BUFFER) {
12647
+ setRange((current) => ({
12648
+ start: current.start,
12649
+ end: current.end + YEAR_LIST_EXPAND_SIZE
12650
+ }));
12651
+ }
12652
+ };
12653
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full overflow-hidden bg-[var(--cmp-datepicker-mode-bg)]", children: /* @__PURE__ */ jsxRuntime.jsx(
12654
+ "div",
12655
+ {
12656
+ ref: listRef,
12657
+ role: "listbox",
12658
+ "aria-label": "year",
12659
+ className: "h-full overflow-y-auto overscroll-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
12660
+ style: {
12661
+ paddingTop: `calc((${DATEPICKER_MODE_LIST_HEIGHT}px - ${DATEPICKER_MODE_ITEM_HEIGHT}px) / 2)`,
12662
+ paddingBottom: `calc((${DATEPICKER_MODE_LIST_HEIGHT}px - ${DATEPICKER_MODE_ITEM_HEIGHT}px) / 2)`
12663
+ },
12664
+ onScroll: handleScroll,
12665
+ children: years.map((year) => {
12666
+ const selected = year === value;
12667
+ const disabled = isYearDisabled(year, selectable);
12668
+ return /* @__PURE__ */ jsxRuntime.jsx(
12669
+ "button",
12670
+ {
12671
+ type: "button",
12672
+ role: "option",
12673
+ "aria-selected": selected,
12674
+ disabled,
12675
+ className: cn(
12676
+ "flex w-full items-center justify-center gap-[var(--cmp-datepicker-mode-item-gap)] border-0 px-[var(--cmp-datepicker-mode-item-paddingX)] py-[var(--cmp-datepicker-mode-item-paddingY)] text-center font-base text-12 leading-[20px]",
12677
+ disabled && "cursor-not-allowed bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-disabled)]",
12678
+ !disabled && !selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-default)] hover:bg-[var(--cmp-datepicker-mode-item-bg-hover)] hover:text-[color:var(--cmp-datepicker-mode-item-content-hover)]",
12679
+ !disabled && selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-bold text-[color:var(--cmp-datepicker-mode-item-content-selected)]"
12680
+ ),
12681
+ onClick: () => !disabled && onCommit(year),
12682
+ children: year
12683
+ },
12684
+ year
12685
+ );
12686
+ })
12687
+ }
12688
+ ) });
12689
+ }
12690
+ function SDatePickerYearListbox({
12691
+ value,
12692
+ anchorYear,
12693
+ selectable,
12694
+ onValueChange,
12695
+ className,
12696
+ style
12697
+ }) {
12698
+ const today = getTodayParts();
12699
+ const resolvedAnchorYear = anchorYear ?? today.year;
12700
+ const [year, setYear] = react.useState(() => parseYear(value) ?? resolvedAnchorYear);
12701
+ react.useEffect(() => {
12702
+ setYear(parseYear(value) ?? resolvedAnchorYear);
12703
+ }, [value, resolvedAnchorYear]);
12704
+ const commitYear = (nextYear) => {
12705
+ setYear(nextYear);
12706
+ onValueChange?.(String(nextYear));
12707
+ };
12708
+ return /* @__PURE__ */ jsxRuntime.jsx(
12709
+ "div",
12710
+ {
12711
+ className: cn(
12712
+ "inline-flex h-[240px] w-[var(--cmp-datepicker-mode-year-width)] overflow-hidden rounded-[var(--cmp-datepicker-mode-radius)] bg-[var(--cmp-datepicker-mode-bg)] shadow-normal-sm",
12713
+ className
12714
+ ),
12715
+ style,
12716
+ children: /* @__PURE__ */ jsxRuntime.jsx(
12717
+ YearWheel,
12718
+ {
12719
+ value: year,
12720
+ anchorYear: resolvedAnchorYear,
12721
+ selectable,
12722
+ onCommit: commitYear
12723
+ }
12724
+ )
12725
+ }
12726
+ );
12727
+ }
12728
+ function MonthWheel({ year, value, selectable, onCommit }) {
12729
+ const listRef = react.useRef(null);
12730
+ const initialValueRef = react.useRef(value);
12731
+ react.useLayoutEffect(() => {
12732
+ const list = listRef.current;
12733
+ if (!list) return;
12734
+ list.scrollTop = Math.max(
12735
+ 0,
12736
+ (initialValueRef.current - 1) * DATEPICKER_MODE_ITEM_HEIGHT - (list.clientHeight - DATEPICKER_MODE_ITEM_HEIGHT) / 2
12737
+ );
12738
+ }, []);
12739
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full overflow-hidden bg-[var(--cmp-datepicker-mode-bg)]", children: /* @__PURE__ */ jsxRuntime.jsx(
12740
+ "div",
12741
+ {
12742
+ ref: listRef,
12743
+ role: "listbox",
12744
+ "aria-label": "month",
12745
+ className: "h-full overflow-y-auto overscroll-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
12746
+ children: MONTHS.map((month) => {
12747
+ const selected = month === value;
12748
+ const disabled = isMonthDisabled(year, month, selectable);
12749
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12750
+ "button",
12751
+ {
12752
+ type: "button",
12753
+ role: "option",
12754
+ "aria-selected": selected,
12755
+ disabled,
12756
+ className: cn(
12757
+ "flex w-full items-center justify-center gap-[var(--cmp-datepicker-mode-item-gap)] border-0 px-[var(--cmp-datepicker-mode-item-paddingX)] py-[var(--cmp-datepicker-mode-item-paddingY)] text-center font-base text-12 leading-[20px]",
12758
+ disabled && "cursor-not-allowed bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-disabled)]",
12759
+ !disabled && !selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-default)] hover:bg-[var(--cmp-datepicker-mode-item-bg-hover)] hover:text-[color:var(--cmp-datepicker-mode-item-content-hover)]",
12760
+ !disabled && selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-bold text-[color:var(--cmp-datepicker-mode-item-content-selected)]"
12761
+ ),
12762
+ onClick: () => !disabled && onCommit(month),
12763
+ children: [
12764
+ month,
12765
+ "\uC6D4"
12766
+ ]
12767
+ },
12768
+ month
12769
+ );
12770
+ })
12771
+ }
12772
+ ) });
12773
+ }
12774
+ function SDatePickerMonthListbox({
12775
+ value,
12776
+ anchorYear,
12777
+ selectable,
12778
+ onValueChange,
12779
+ onMonthSelect,
12780
+ className,
12781
+ style
12782
+ }) {
12783
+ const today = getTodayParts();
12784
+ const resolvedAnchorYear = anchorYear ?? today.year;
12785
+ const [year, setYear] = react.useState(() => parseYear(value) ?? resolvedAnchorYear);
12786
+ const [month, setMonth] = react.useState(() => parseMonth(value) ?? today.month);
12787
+ react.useEffect(() => {
12788
+ setYear(parseYear(value) ?? resolvedAnchorYear);
12789
+ setMonth(parseMonth(value) ?? today.month);
12790
+ }, [value, resolvedAnchorYear, today.month]);
12791
+ const commitYear = (nextYear) => {
12792
+ setYear(nextYear);
12793
+ onValueChange?.(formatMonthValue(nextYear, month));
12794
+ };
12795
+ const commitMonth = (nextMonth) => {
12796
+ setMonth(nextMonth);
12797
+ const nextValue = formatMonthValue(year, nextMonth);
12798
+ onValueChange?.(nextValue);
12799
+ onMonthSelect?.(nextValue);
12800
+ };
12801
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12802
+ "div",
12803
+ {
12804
+ className: cn(
12805
+ "inline-flex h-[240px] gap-[var(--cmp-datepicker-mode-yearMonth-gap)] overflow-hidden rounded-[var(--cmp-datepicker-mode-radius)] bg-[var(--cmp-datepicker-mode-bg)] shadow-normal-sm",
12806
+ className
12807
+ ),
12808
+ style,
12809
+ children: [
12810
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsxRuntime.jsx(
12811
+ YearWheel,
12812
+ {
12813
+ value: year,
12814
+ anchorYear: resolvedAnchorYear,
12815
+ selectable,
12816
+ onCommit: commitYear
12817
+ }
12818
+ ) }),
12819
+ /* @__PURE__ */ jsxRuntime.jsx(
12820
+ "div",
12821
+ {
12822
+ "aria-hidden": true,
12823
+ className: "w-[var(--border-width-1)] self-stretch bg-[var(--sys-color-divider-default)]"
12824
+ }
12825
+ ),
12826
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsxRuntime.jsx(MonthWheel, { year, value: month, selectable, onCommit: commitMonth }) })
12827
+ ]
12828
+ }
12829
+ );
12830
+ }
11922
12831
  function SDatePicker({
11923
12832
  value,
11924
12833
  onValueChange,
11925
12834
  onViewChange,
12835
+ mode = "date",
11926
12836
  size = "sm",
11927
- placeholder = "YYYY-MM-DD",
12837
+ placeholder,
11928
12838
  selectable,
11929
12839
  disabled = false,
11930
12840
  width,
@@ -11950,6 +12860,7 @@ function SDatePicker({
11950
12860
  const [focused, setFocused] = react.useState(false);
11951
12861
  const [ruleError, setRuleError] = react.useState("");
11952
12862
  const hasValue = value != null && value !== "";
12863
+ const resolvedPlaceholder = placeholder ?? (mode === "year" ? "YYYY" : mode === "month" ? "YYYY-MM" : "YYYY-MM-DD");
11953
12864
  useFormField({
11954
12865
  name,
11955
12866
  validate: () => {
@@ -11961,12 +12872,39 @@ function SDatePicker({
11961
12872
  });
11962
12873
  const resolvedError = error || ruleError !== "";
11963
12874
  const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
11964
- const dims = size === "md" ? { padX: 16, gap: 12, icon: 20, minW: 104, fontSize: 14, lineHeight: 24, radius: 6 } : { padX: 12, gap: 8, icon: 16, minW: 80, fontSize: 12, lineHeight: 20, radius: 4 };
12875
+ const dims = size === "md" ? {
12876
+ padX: 16,
12877
+ gap: 12,
12878
+ icon: 20,
12879
+ minW: 104,
12880
+ fieldMinW: 168,
12881
+ // 상한 등급 lg — 값 길이가 YYYY-MM-DD 로 정해져 있어, width="100%" 를 받아도
12882
+ // 행 전체로 늘어나지 않게 막는다 (SKeyValueTable 이 모든 컨트롤에 100% 를 넘긴다)
12883
+ fieldMaxW: 240,
12884
+ fontSize: 14,
12885
+ lineHeight: 24,
12886
+ radius: 6
12887
+ } : {
12888
+ padX: 12,
12889
+ gap: 8,
12890
+ icon: 16,
12891
+ minW: 80,
12892
+ fieldMinW: 128,
12893
+ // 상한 등급 md
12894
+ fieldMaxW: 160,
12895
+ fontSize: 12,
12896
+ lineHeight: 20,
12897
+ radius: 4
12898
+ };
11965
12899
  const select = (date) => {
11966
12900
  onValueChange?.(date);
11967
12901
  if (rules && rules.length > 0) setRuleError(runRules(date, rules));
11968
12902
  setOpen(false);
11969
12903
  };
12904
+ const selectListboxValue = (nextValue) => {
12905
+ onValueChange?.(nextValue);
12906
+ if (rules && rules.length > 0) setRuleError(runRules(nextValue, rules));
12907
+ };
11970
12908
  return /* @__PURE__ */ jsxRuntime.jsx(
11971
12909
  SField,
11972
12910
  {
@@ -11985,11 +12923,13 @@ function SDatePicker({
11985
12923
  focused: focused || open,
11986
12924
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
11987
12925
  width,
12926
+ minWidth: dims.fieldMinW,
12927
+ maxWidth: dims.fieldMaxW,
11988
12928
  disabled,
11989
12929
  className,
11990
12930
  style,
11991
- children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: (o) => !disabled && setOpen(o), children: [
11992
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
12931
+ children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open, onOpenChange: (o) => !disabled && setOpen(o), children: [
12932
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
11993
12933
  "button",
11994
12934
  {
11995
12935
  type: "button",
@@ -12019,20 +12959,23 @@ function SDatePicker({
12019
12959
  lineHeight: `${dims.lineHeight}px`,
12020
12960
  color: disabled ? "var(--cmp-datepicker-text-disabled)" : hasValue ? "var(--cmp-datepicker-text-default)" : "var(--cmp-field-hint-color)"
12021
12961
  },
12022
- children: hasValue ? value : placeholder
12962
+ children: hasValue ? value : resolvedPlaceholder
12023
12963
  }
12024
12964
  )
12025
12965
  ]
12026
12966
  }
12027
12967
  ) }),
12028
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
12029
- RPopover2__namespace.Content,
12968
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
12969
+ RPopover10__namespace.Content,
12030
12970
  {
12031
12971
  side: "bottom",
12032
- align: "start",
12972
+ align: mode === "date" ? "start" : "center",
12033
12973
  sideOffset: 4,
12034
- className: cn("z-50", FLOATING_SLIDE_ANIM),
12035
- children: /* @__PURE__ */ jsxRuntime.jsx(
12974
+ className: cn(
12975
+ "z-50 max-w-[var(--radix-popover-content-available-width)]",
12976
+ FLOATING_SLIDE_ANIM
12977
+ ),
12978
+ children: mode === "date" ? /* @__PURE__ */ jsxRuntime.jsx(
12036
12979
  SCalendar,
12037
12980
  {
12038
12981
  value: value ?? void 0,
@@ -12041,6 +12984,24 @@ function SDatePicker({
12041
12984
  onValueChange: select,
12042
12985
  onViewChange
12043
12986
  }
12987
+ ) : mode === "year" ? /* @__PURE__ */ jsxRuntime.jsx(
12988
+ SDatePickerYearListbox,
12989
+ {
12990
+ value,
12991
+ selectable,
12992
+ onValueChange: (nextValue) => {
12993
+ selectListboxValue(nextValue);
12994
+ setOpen(false);
12995
+ }
12996
+ }
12997
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
12998
+ SDatePickerMonthListbox,
12999
+ {
13000
+ value,
13001
+ selectable,
13002
+ onValueChange: selectListboxValue,
13003
+ onMonthSelect: () => setOpen(false)
13004
+ }
12044
13005
  )
12045
13006
  }
12046
13007
  ) })
@@ -12179,78 +13140,81 @@ function RangeCalendar({
12179
13140
  }
12180
13141
  };
12181
13142
  const GRID_COLS = "grid grid-cols-[repeat(7,var(--cmp-datepicker-calendar-day-width))]";
12182
- const renderPanel = (gy, gm, isLeft) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-[var(--cmp-datepicker-calendar-gap)]", children: [
12183
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-[var(--cmp-datepicker-calendar-gap)]", children: [
12184
- /* @__PURE__ */ jsxRuntime.jsx(
12185
- SGhostButton,
12186
- {
12187
- icon: "chevronLeft",
12188
- size: "xxs",
12189
- ariaLabel: "prevMonth",
12190
- onClick: goPrevMonth,
12191
- className: cn(!isLeft && "invisible pointer-events-none")
12192
- }
12193
- ),
12194
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex min-w-[40px] flex-1 items-center justify-center text-center text-[14px] font-medium leading-[24px]", children: [
12195
- gy,
12196
- ".",
12197
- String(gm).padStart(2, "0")
13143
+ const renderPanel = (gy, gm, isLeft) => (
13144
+ // 좁은 창에서 눌려 격자가 깨지지 않도록, 줄이는 대신 부모가 가로 스크롤하게 둔다.
13145
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 flex-col gap-[var(--cmp-datepicker-calendar-gap)]", children: [
13146
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-[var(--cmp-datepicker-calendar-gap)]", children: [
13147
+ /* @__PURE__ */ jsxRuntime.jsx(
13148
+ SGhostButton,
13149
+ {
13150
+ icon: "chevronLeft",
13151
+ size: "xxs",
13152
+ ariaLabel: "prevMonth",
13153
+ onClick: goPrevMonth,
13154
+ className: cn(!isLeft && "invisible pointer-events-none")
13155
+ }
13156
+ ),
13157
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex min-w-[40px] flex-1 items-center justify-center text-center text-[14px] font-medium leading-[24px]", children: [
13158
+ gy,
13159
+ ".",
13160
+ String(gm).padStart(2, "0")
13161
+ ] }),
13162
+ /* @__PURE__ */ jsxRuntime.jsx(
13163
+ SGhostButton,
13164
+ {
13165
+ icon: "chevronRight",
13166
+ size: "xxs",
13167
+ ariaLabel: "nextMonth",
13168
+ onClick: goNextMonth,
13169
+ className: cn(isLeft && "invisible pointer-events-none")
13170
+ }
13171
+ )
12198
13172
  ] }),
12199
- /* @__PURE__ */ jsxRuntime.jsx(
12200
- SGhostButton,
12201
- {
12202
- icon: "chevronRight",
12203
- size: "xxs",
12204
- ariaLabel: "nextMonth",
12205
- onClick: goNextMonth,
12206
- className: cn(isLeft && "invisible pointer-events-none")
12207
- }
12208
- )
12209
- ] }),
12210
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: GRID_COLS, children: WEEK_LABELS.map((l) => /* @__PURE__ */ jsxRuntime.jsx(
12211
- "span",
12212
- {
12213
- className: "flex h-[20px] items-center justify-center text-[12px] font-normal text-[color:var(--cmp-datepicker-calendar-week-color)]",
12214
- children: l
12215
- },
12216
- l
12217
- )) }),
12218
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(GRID_COLS, "gap-y-[var(--cmp-datepicker-calendar-grid-rowGap)]"), children: monthCells(gy, gm).map((cell) => {
12219
- const isEdge = cell.inCurrentMonth && (cell.date === rStart || complete && cell.date === rEnd);
12220
- const between = cell.inCurrentMonth && inRange(cell.date);
12221
- const disabled = cell.inCurrentMonth && isDisabled(cell.date);
12222
- const isToday = cell.inCurrentMonth && today === cell.date;
12223
- let cellBg;
12224
- if (between) cellBg = RANGE_BG;
12225
- else if (cell.inCurrentMonth && hasRange && cell.date === rStart)
12226
- cellBg = `linear-gradient(to right, transparent 50%, ${RANGE_BG} 50%)`;
12227
- else if (cell.inCurrentMonth && hasRange && cell.date === rEnd) cellBg = RANGE_BG;
12228
- return /* @__PURE__ */ jsxRuntime.jsx(
12229
- "div",
13173
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: GRID_COLS, children: WEEK_LABELS.map((l) => /* @__PURE__ */ jsxRuntime.jsx(
13174
+ "span",
12230
13175
  {
12231
- className: "flex h-[var(--cmp-datepicker-calendar-range-height)] w-[var(--cmp-datepicker-calendar-day-width)] items-center justify-center",
12232
- style: { background: cellBg },
12233
- children: /* @__PURE__ */ jsxRuntime.jsx(
12234
- "button",
12235
- {
12236
- type: "button",
12237
- disabled: !cell.inCurrentMonth || disabled,
12238
- onMouseEnter: () => setHover(cell.date),
12239
- onClick: () => cell.inCurrentMonth && click(cell.date),
12240
- className: cn(
12241
- "relative z-[1] flex h-[var(--cmp-datepicker-calendar-day-circle-size)] w-[var(--cmp-datepicker-calendar-day-circle-size)] items-center justify-center rounded-[var(--cmp-datepicker-calendar-day-circle-radius)] border border-solid border-transparent bg-transparent text-[14px]",
12242
- !cell.inCurrentMonth ? "invisible pointer-events-none" : disabled ? "cursor-not-allowed" : "cursor-pointer",
12243
- isEdge ? "bg-[var(--cmp-datepicker-calendar-day-select-bg)] text-[color:var(--cmp-datepicker-calendar-day-select-text)]" : disabled ? "text-[color:var(--cmp-datepicker-calendar-day-disabled-text)]" : "text-[color:var(--cmp-datepicker-calendar-day-default-text)] hover:border-[color:var(--cmp-datepicker-calendar-day-hover-border)] hover:text-[color:var(--cmp-datepicker-calendar-day-hover-text)]",
12244
- isToday && !disabled && "font-bold"
12245
- ),
12246
- children: cell.inCurrentMonth ? cell.day : ""
12247
- }
12248
- )
13176
+ className: "flex h-[20px] items-center justify-center text-[12px] font-normal text-[color:var(--cmp-datepicker-calendar-week-color)]",
13177
+ children: l
12249
13178
  },
12250
- cell.date
12251
- );
12252
- }) })
12253
- ] });
13179
+ l
13180
+ )) }),
13181
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(GRID_COLS, "gap-y-[var(--cmp-datepicker-calendar-grid-rowGap)]"), children: monthCells(gy, gm).map((cell) => {
13182
+ const isEdge = cell.inCurrentMonth && (cell.date === rStart || complete && cell.date === rEnd);
13183
+ const between = cell.inCurrentMonth && inRange(cell.date);
13184
+ const disabled = cell.inCurrentMonth && isDisabled(cell.date);
13185
+ const isToday = cell.inCurrentMonth && today === cell.date;
13186
+ let cellBg;
13187
+ if (between) cellBg = RANGE_BG;
13188
+ else if (cell.inCurrentMonth && hasRange && cell.date === rStart)
13189
+ cellBg = `linear-gradient(to right, transparent 50%, ${RANGE_BG} 50%)`;
13190
+ else if (cell.inCurrentMonth && hasRange && cell.date === rEnd) cellBg = RANGE_BG;
13191
+ return /* @__PURE__ */ jsxRuntime.jsx(
13192
+ "div",
13193
+ {
13194
+ className: "flex h-[var(--cmp-datepicker-calendar-range-height)] w-[var(--cmp-datepicker-calendar-day-width)] items-center justify-center",
13195
+ style: { background: cellBg },
13196
+ children: /* @__PURE__ */ jsxRuntime.jsx(
13197
+ "button",
13198
+ {
13199
+ type: "button",
13200
+ disabled: !cell.inCurrentMonth || disabled,
13201
+ onMouseEnter: () => setHover(cell.date),
13202
+ onClick: () => cell.inCurrentMonth && click(cell.date),
13203
+ className: cn(
13204
+ "relative z-[1] flex h-[var(--cmp-datepicker-calendar-day-circle-size)] w-[var(--cmp-datepicker-calendar-day-circle-size)] items-center justify-center rounded-[var(--cmp-datepicker-calendar-day-circle-radius)] border border-solid border-transparent bg-transparent text-[14px]",
13205
+ !cell.inCurrentMonth ? "invisible pointer-events-none" : disabled ? "cursor-not-allowed" : "cursor-pointer",
13206
+ isEdge ? "bg-[var(--cmp-datepicker-calendar-day-select-bg)] text-[color:var(--cmp-datepicker-calendar-day-select-text)]" : disabled ? "text-[color:var(--cmp-datepicker-calendar-day-disabled-text)]" : "text-[color:var(--cmp-datepicker-calendar-day-default-text)] hover:border-[color:var(--cmp-datepicker-calendar-day-hover-border)] hover:text-[color:var(--cmp-datepicker-calendar-day-hover-text)]",
13207
+ isToday && !disabled && "font-bold"
13208
+ ),
13209
+ children: cell.inCurrentMonth ? cell.day : ""
13210
+ }
13211
+ )
13212
+ },
13213
+ cell.date
13214
+ );
13215
+ }) })
13216
+ ] })
13217
+ );
12254
13218
  const renderTimeSection = (which, date, suffix) => {
12255
13219
  const active = !!date;
12256
13220
  const [yy = "", mm = "", dd = ""] = date ? date.split("-") : [];
@@ -12318,8 +13282,11 @@ function RangeCalendar({
12318
13282
  "div",
12319
13283
  {
12320
13284
  className: cn(
12321
- "inline-flex flex-col gap-[var(--cmp-datepicker-calendar-gap)] rounded-[var(--cmp-datepicker-calendar-radius)] bg-[var(--cmp-datepicker-calendar-bg)] p-[var(--cmp-datepicker-calendar-paddingXY)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.2)] select-none",
12322
- useTimePicker ? "h-auto" : "h-[364px]"
13285
+ "inline-flex max-w-full flex-col gap-[var(--cmp-datepicker-calendar-gap)] rounded-[var(--cmp-datepicker-calendar-radius)] bg-[var(--cmp-datepicker-calendar-bg)] p-[var(--cmp-datepicker-calendar-paddingXY)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.2)] select-none",
13286
+ // 높이는 고정이 아니라 하한이다 — 5주/6주 달을 오갈 때의 높이 흔들림만 막으면 되고,
13287
+ // 창이 좁아 패널이 가로 스크롤될 때는 스크롤바 높이만큼 늘어날 수 있어야 한다.
13288
+ // (단일 달 SCalendar 도 min-h 를 쓴다)
13289
+ useTimePicker ? "h-auto" : "min-h-[364px]"
12323
13290
  ),
12324
13291
  children: [
12325
13292
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center", children: [
@@ -12338,11 +13305,20 @@ function RangeCalendar({
12338
13305
  }
12339
13306
  )
12340
13307
  ] }),
12341
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-[var(--cmp-datepicker-calendar-range-panelGap)]", children: [
12342
- renderPanel(y, m, true),
12343
- /* @__PURE__ */ jsxRuntime.jsx(SDivider, { vertical: true }),
12344
- renderPanel(nextMonthYM.year, nextMonthYM.month, false)
12345
- ] }),
13308
+ /* @__PURE__ */ jsxRuntime.jsxs(
13309
+ "div",
13310
+ {
13311
+ className: cn(
13312
+ "flex max-w-full gap-[var(--cmp-datepicker-calendar-range-panelGap)] overflow-x-auto",
13313
+ SCROLLBAR_CLASS
13314
+ ),
13315
+ children: [
13316
+ renderPanel(y, m, true),
13317
+ /* @__PURE__ */ jsxRuntime.jsx(SDivider, { vertical: true }),
13318
+ renderPanel(nextMonthYM.year, nextMonthYM.month, false)
13319
+ ]
13320
+ }
13321
+ ),
12346
13322
  useTimePicker && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
12347
13323
  /* @__PURE__ */ jsxRuntime.jsx(SDivider, {}),
12348
13324
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-[var(--cmp-datepicker-calendar-time-gap)]", children: [
@@ -12401,7 +13377,30 @@ function SDateRangePicker({
12401
13377
  });
12402
13378
  const resolvedError = error || ruleError !== "";
12403
13379
  const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
12404
- const dims = size === "md" ? { padX: 16, gap: 12, icon: 20, minW: 104, fontSize: 14, lineHeight: 24, radius: 6 } : { padX: 12, gap: 8, icon: 16, minW: 80, fontSize: 12, lineHeight: 20, radius: 4 };
13380
+ const dims = size === "md" ? {
13381
+ padX: 16,
13382
+ gap: 12,
13383
+ icon: 20,
13384
+ minW: 104,
13385
+ fieldMinW: 260,
13386
+ // 상한 등급 xl — 값이 `YYYY-MM-DD ~ YYYY-MM-DD` 라 단일 날짜의 두 배가 넘는다.
13387
+ // 최소너비(260)가 이미 lg(240)를 넘으므로 상한도 한 등급 위여야 한다
13388
+ fieldMaxW: 480,
13389
+ fontSize: 14,
13390
+ lineHeight: 24,
13391
+ radius: 6
13392
+ } : {
13393
+ padX: 12,
13394
+ gap: 8,
13395
+ icon: 16,
13396
+ minW: 80,
13397
+ fieldMinW: 216,
13398
+ // 상한 등급 lg — 최소너비 216 이 md(160)를 넘어 md 로는 담기지 않는다
13399
+ fieldMaxW: 240,
13400
+ fontSize: 12,
13401
+ lineHeight: 20,
13402
+ radius: 4
13403
+ };
12405
13404
  const select = (r) => {
12406
13405
  setPendingStart(null);
12407
13406
  onValueChange?.(r);
@@ -12427,11 +13426,13 @@ function SDateRangePicker({
12427
13426
  focused: open,
12428
13427
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12429
13428
  width,
13429
+ minWidth: dims.fieldMinW,
13430
+ maxWidth: dims.fieldMaxW,
12430
13431
  disabled,
12431
13432
  className,
12432
13433
  style,
12433
13434
  children: /* @__PURE__ */ jsxRuntime.jsxs(
12434
- RPopover2__namespace.Root,
13435
+ RPopover10__namespace.Root,
12435
13436
  {
12436
13437
  open,
12437
13438
  onOpenChange: (o) => {
@@ -12440,7 +13441,7 @@ function SDateRangePicker({
12440
13441
  if (!o) setPendingStart(null);
12441
13442
  },
12442
13443
  children: [
12443
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
13444
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
12444
13445
  "button",
12445
13446
  {
12446
13447
  type: "button",
@@ -12481,13 +13482,16 @@ function SDateRangePicker({
12481
13482
  ]
12482
13483
  }
12483
13484
  ) }),
12484
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
12485
- RPopover2__namespace.Content,
13485
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
13486
+ RPopover10__namespace.Content,
12486
13487
  {
12487
13488
  side: "bottom",
12488
13489
  align: "start",
12489
13490
  sideOffset: 4,
12490
- className: cn("z-50", FLOATING_SLIDE_ANIM),
13491
+ className: cn(
13492
+ "z-50 max-w-[var(--radix-popover-content-available-width)]",
13493
+ FLOATING_SLIDE_ANIM
13494
+ ),
12491
13495
  children: /* @__PURE__ */ jsxRuntime.jsx(
12492
13496
  RangeCalendar,
12493
13497
  {
@@ -12888,6 +13892,7 @@ function SKeyValueTable({
12888
13892
  values = {},
12889
13893
  search = false,
12890
13894
  radius = "default",
13895
+ bordered = true,
12891
13896
  onChange,
12892
13897
  onSearch,
12893
13898
  className,
@@ -12952,7 +13957,8 @@ function SKeyValueTable({
12952
13957
  });
12953
13958
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[8px] rounded-t-none" : "rounded-[8px]";
12954
13959
  const frameClass = cn(
12955
- "overflow-hidden border border-solid border-[color:var(--cmp-table-border-color)]",
13960
+ "overflow-hidden",
13961
+ bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
12956
13962
  radiusClass
12957
13963
  );
12958
13964
  const table = /* @__PURE__ */ jsxRuntime.jsxs(
@@ -13475,7 +14481,7 @@ function SRadioButton({
13475
14481
  disabled: od,
13476
14482
  onClick: () => !od && !selected && onValueChange?.(o.value),
13477
14483
  className: cn(
13478
- "relative box-border inline-flex items-center justify-center whitespace-nowrap border border-solid text-[12px] font-medium leading-[20px] transition-colors",
14484
+ "relative box-border grow inline-flex items-center justify-center whitespace-nowrap border border-solid text-[12px] font-medium leading-[20px] transition-colors",
13479
14485
  i === 0 ? "rounded-l-[var(--cmp-radioButton-radius)]" : "-ml-px",
13480
14486
  i === options.length - 1 ? "rounded-r-[var(--cmp-radioButton-radius)]" : "",
13481
14487
  selected ? "z-10" : "",
@@ -13497,6 +14503,1564 @@ function SRadioButton({
13497
14503
  }
13498
14504
  );
13499
14505
  }
14506
+ var FONT2 = {
14507
+ sm: {
14508
+ fontSize: 12,
14509
+ lineHeight: 20,
14510
+ paddingX: "var(--cmp-textinput-sm-paddingX)",
14511
+ gap: "var(--cmp-textinput-sm-gap)"
14512
+ },
14513
+ md: {
14514
+ fontSize: 14,
14515
+ lineHeight: 24,
14516
+ paddingX: "var(--cmp-textinput-md-paddingX)",
14517
+ gap: "var(--cmp-textinput-md-gap)"
14518
+ }
14519
+ };
14520
+ var SSearchInput = /* @__PURE__ */ react.forwardRef(function SSearchInput2({
14521
+ value,
14522
+ onValueChange,
14523
+ onChange,
14524
+ onSearch,
14525
+ size = "sm",
14526
+ placeholder = "\uACB0\uACFC \uB0B4 \uAC80\uC0C9",
14527
+ clearable = false,
14528
+ disabled = false,
14529
+ width,
14530
+ focused,
14531
+ hovered,
14532
+ inputClass,
14533
+ inputStyle,
14534
+ className,
14535
+ style,
14536
+ onKeyDown,
14537
+ ...rest
14538
+ }, ref) {
14539
+ const f = FONT2[size];
14540
+ const hasValue = value !== void 0 && value !== null && String(value) !== "";
14541
+ return /* @__PURE__ */ jsxRuntime.jsx(
14542
+ SField,
14543
+ {
14544
+ size,
14545
+ width,
14546
+ focused,
14547
+ hovered,
14548
+ disabled,
14549
+ className,
14550
+ style,
14551
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
14552
+ "div",
14553
+ {
14554
+ className: "flex h-full w-full items-center",
14555
+ style: {
14556
+ gap: f.gap,
14557
+ padding: `0 ${f.paddingX}`,
14558
+ fontSize: f.fontSize,
14559
+ lineHeight: `${f.lineHeight}px`
14560
+ },
14561
+ children: [
14562
+ /* @__PURE__ */ jsxRuntime.jsx(
14563
+ SIcon,
14564
+ {
14565
+ name: "search",
14566
+ size: 16,
14567
+ color: disabled ? "var(--sys-color-disabled-icon)" : "var(--cmp-textinput-icon-default)",
14568
+ className: "flex-shrink-0"
14569
+ }
14570
+ ),
14571
+ /* @__PURE__ */ jsxRuntime.jsx(
14572
+ "input",
14573
+ {
14574
+ ref,
14575
+ type: "search",
14576
+ value: value ?? "",
14577
+ disabled,
14578
+ placeholder,
14579
+ onChange: (e) => {
14580
+ onChange?.(e);
14581
+ onValueChange?.(e.target.value);
14582
+ },
14583
+ onKeyDown: (e) => {
14584
+ onKeyDown?.(e);
14585
+ if (e.key === "Enter" && !e.nativeEvent.isComposing) {
14586
+ onSearch?.(e.currentTarget.value);
14587
+ }
14588
+ },
14589
+ className: cn(
14590
+ // type="search" 의 브라우저 기본 취소 버튼은 clearable 과 겹치므로 지운다
14591
+ "h-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-none placeholder:text-[color:var(--sys-color-field-text-placeholder)] disabled:cursor-not-allowed [&::-webkit-search-cancel-button]:hidden",
14592
+ AUTOFILL_RESET_CLASS,
14593
+ inputClass
14594
+ ),
14595
+ style: {
14596
+ // fontSize/lineHeight 는 컨테이너에서 상속 (원본 font-size: inherit)
14597
+ color: disabled ? "var(--cmp-textinput-text-disabled)" : "var(--sys-color-field-text-default)",
14598
+ ...inputStyle
14599
+ },
14600
+ ...rest
14601
+ }
14602
+ ),
14603
+ clearable && hasValue && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
14604
+ SGhostButton,
14605
+ {
14606
+ icon: "close",
14607
+ size: "xxs",
14608
+ ariaLabel: "\uC9C0\uC6B0\uAE30",
14609
+ className: "flex-shrink-0",
14610
+ onClick: () => onValueChange?.("")
14611
+ }
14612
+ )
14613
+ ]
14614
+ }
14615
+ )
14616
+ }
14617
+ );
14618
+ });
14619
+ var CHIPFILTER_LISTBOX_WIDTH = 240;
14620
+ var CHIPFILTER_DATE_PRESET_LISTBOX_WIDTH = 246;
14621
+ var CHIPFILTER = {
14622
+ barBg: "var(--cmp-chipFilter-bar-bg)",
14623
+ barBorder: "var(--cmp-chipFilter-bar-border)",
14624
+ barPaddingX: "var(--cmp-chipFilter-bar-paddingX)",
14625
+ barPaddingY: "var(--cmp-chipFilter-bar-paddingY)",
14626
+ barGap: "var(--cmp-chipFilter-bar-gap)",
14627
+ dividerHeight: "var(--cmp-chipFilter-divider-height)",
14628
+ chipHeight: "var(--cmp-chipFilter-chip-height)",
14629
+ chipRadius: "var(--cmp-chipFilter-chip-radius)",
14630
+ chipGap: "var(--cmp-chipFilter-chip-gap)",
14631
+ chipDefaultBg: "var(--cmp-chipFilter-chip-default-bg)",
14632
+ chipDefaultBorder: "var(--cmp-chipFilter-chip-default-border)",
14633
+ chipDefaultText: "var(--cmp-chipFilter-chip-default-text)",
14634
+ chipActiveBorder: "var(--cmp-chipFilter-chip-active-border)",
14635
+ chipWarningBorder: "var(--cmp-chipFilter-chip-warning-border)",
14636
+ chipAppliedBg: "var(--cmp-chipFilter-chip-applied-bg)",
14637
+ chipAppliedBorder: "var(--cmp-chipFilter-chip-applied-border)",
14638
+ chipAppliedLabel: "var(--cmp-chipFilter-chip-applied-text-label)",
14639
+ chipAppliedValue: "var(--color-brilliantblue-80)",
14640
+ chipDisabledBg: "var(--cmp-chipFilter-chip-disabled-bg)",
14641
+ chipDisabledBorder: "var(--cmp-chipFilter-chip-disabled-border)",
14642
+ chipDisabledText: "var(--cmp-chipFilter-chip-disabled-text)",
14643
+ addBorder: "var(--cmp-chipFilter-addButton-border-default)",
14644
+ addBorderDisabled: "var(--cmp-chipFilter-addButton-border-disabled)",
14645
+ addText: "var(--cmp-chipFilter-addButton-text-default)",
14646
+ addTextDisabled: "var(--cmp-chipFilter-addButton-text-disabled)",
14647
+ listboxBg: "var(--cmp-chipFilter-listbox-bg)",
14648
+ listboxBorder: "var(--cmp-chipFilter-listbox-border-color)",
14649
+ listboxRadius: "var(--cmp-chipFilter-listbox-radius)",
14650
+ listboxLabel: "var(--cmp-chipFilter-listbox-top-label-color)",
14651
+ listboxBottomDividerHeight: "var(--cmp-chipFilter-listbox-bottom-divider-height)",
14652
+ itemPaddingLeft: "var(--cmp-chipFilter-item-paddingLeft)",
14653
+ itemPaddingRight: "var(--cmp-chipFilter-item-paddingRight)",
14654
+ itemPaddingY: "var(--cmp-chipFilter-item-paddingY)",
14655
+ itemMultiPaddingX: "var(--cmp-chipFilter-item-multi-paddingX)",
14656
+ itemTextDefault: "var(--cmp-chipFilter-item-text-default)",
14657
+ itemTextSelected: "var(--cmp-chipFilter-item-text-selected)"
14658
+ };
14659
+ var DEFAULT_PERIOD_PRESETS = [
14660
+ { value: "day", label: "\uC77C\uBCC4" },
14661
+ { value: "month", label: "\uC6D4\uBCC4" },
14662
+ { value: "quarter", label: "\uBD84\uAE30\uBCC4" },
14663
+ { value: "half", label: "\uBC18\uAE30\uBCC4" },
14664
+ { value: "year", label: "\uC5F0\uB3C4\uBCC4" },
14665
+ { value: "custom", label: "\uC0AC\uC6A9\uC790 \uC9C0\uC815", custom: true }
14666
+ ];
14667
+ var isKeywordValue = (value) => typeof value === "object" && value != null && !Array.isArray(value) && "keywords" in value;
14668
+ var isPeriodValue = (value) => typeof value === "object" && value != null && !Array.isArray(value) && "unit" in value;
14669
+ var keywordEntries = (value) => {
14670
+ if (isKeywordValue(value)) return value.keywords;
14671
+ if (Array.isArray(value)) {
14672
+ return value.filter((v) => typeof v === "string");
14673
+ }
14674
+ return [];
14675
+ };
14676
+ var isEmptyValue = (value) => {
14677
+ if (value == null) return true;
14678
+ if (Array.isArray(value)) return value.length === 0 || value.every((v) => v == null || v === "");
14679
+ if (isKeywordValue(value)) return value.keywords.length === 0;
14680
+ return value === "";
14681
+ };
14682
+ var normalizeValues = (value) => value ?? {};
14683
+ var toArrayValue = (value) => Array.isArray(value) ? value.filter(
14684
+ (v) => typeof v === "string" || typeof v === "number"
14685
+ ) : [];
14686
+ var todayIso = () => {
14687
+ const d = /* @__PURE__ */ new Date();
14688
+ const pad = (n) => String(n).padStart(2, "0");
14689
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
14690
+ };
14691
+ var isInlineDateField = (field) => field.type === "date" && field.radioButton === true && (field.presets?.length ?? 0) > 0;
14692
+ var getFieldPresets = (field) => {
14693
+ if (field.type === "period") return field.presets ?? DEFAULT_PERIOD_PRESETS;
14694
+ return field.presets ?? [];
14695
+ };
14696
+ var getDefaultPeriodInputValue = (unit) => {
14697
+ const today = todayIso();
14698
+ if (unit === "day") return today;
14699
+ if (unit === "month") return today.slice(0, 7);
14700
+ if (unit === "quarter") return 1;
14701
+ if (unit === "half") return "first";
14702
+ if (unit === "year") return today.slice(0, 4);
14703
+ return [today, today];
14704
+ };
14705
+ var createPeriodValue = (unit) => ({
14706
+ unit,
14707
+ value: getDefaultPeriodInputValue(unit)
14708
+ });
14709
+ var resolveFieldDefault = (field) => {
14710
+ if (field.defaultValue !== void 0) return field.defaultValue;
14711
+ if (isInlineDateField(field)) return todayIso();
14712
+ if (!field.required) return void 0;
14713
+ if (field.type === "single") return field.options?.[0]?.value;
14714
+ if (field.type === "date") return todayIso();
14715
+ if (field.type === "period") {
14716
+ const unit = getFieldPresets(field).find((preset) => !preset.custom)?.value;
14717
+ return unit ? createPeriodValue(unit) : void 0;
14718
+ }
14719
+ return void 0;
14720
+ };
14721
+ var valuesEqual = (a, b) => {
14722
+ if (isKeywordValue(a) || isKeywordValue(b)) {
14723
+ return isKeywordValue(a) && isKeywordValue(b) && a.mode === b.mode && valuesEqual(a.keywords, b.keywords);
14724
+ }
14725
+ if (Array.isArray(a) || Array.isArray(b)) {
14726
+ return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => v === b[i]);
14727
+ }
14728
+ return a === b;
14729
+ };
14730
+ var valuesMapEqual = (a, b) => {
14731
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
14732
+ for (const key of keys) {
14733
+ if (!valuesEqual(a[key], b[key])) return false;
14734
+ }
14735
+ return true;
14736
+ };
14737
+ var isAtDefaultValue = (field, value) => {
14738
+ const def = resolveFieldDefault(field);
14739
+ return def !== void 0 && valuesEqual(value, def);
14740
+ };
14741
+ var isInlineField = (field) => isInlineDateField(field) || field.type === "custom";
14742
+ var isFixedField = (field) => field.fix === true || field.required === true;
14743
+ var isAddableField = (field) => !isFixedField(field);
14744
+ var isGroupedFields = (fields) => fields.length > 0 && "fields" in fields[0];
14745
+ var isGroupSatisfied = (group, values) => {
14746
+ if (!group.rule) return true;
14747
+ const keys = group.fields.map((f) => f.key);
14748
+ if (group.rule.type === "requireKey") return !isEmptyValue(values[group.rule.key]);
14749
+ if (group.rule.type === "requireAll") return keys.every((k) => !isEmptyValue(values[k]));
14750
+ if (group.rule.type === "requireAny") return keys.some((k) => !isEmptyValue(values[k]));
14751
+ return true;
14752
+ };
14753
+ var koreanParticle = (word, consonant, vowel) => {
14754
+ const code = word.charCodeAt(word.length - 1);
14755
+ if (code < 44032 || code > 55203) return consonant;
14756
+ return (code - 44032) % 28 === 0 ? vowel : consonant;
14757
+ };
14758
+ var defaultGroupTooltipMessage = (rule, fields) => {
14759
+ if (rule.type === "requireAll") return "\uD56D\uBAA9\uC744 \uBAA8\uB450 \uC120\uD0DD\uD574 \uC8FC\uC138\uC694.";
14760
+ if (rule.type === "requireAny") {
14761
+ const name = rule.dataGroupName;
14762
+ if (!name) return "\uCD5C\uC18C 1\uAC1C\uC758 \uD544\uD130\uB97C \uC120\uD0DD\uD574\uC8FC\uC138\uC694.";
14763
+ return `\uCD5C\uC18C 1\uAC1C\uC758 ${name}${koreanParticle(name, "\uC744", "\uB97C")} \uC120\uD0DD\uD574\uC8FC\uC138\uC694.`;
14764
+ }
14765
+ const label = fields.find((f) => f.key === rule.key)?.label ?? "\uD56D\uBAA9";
14766
+ return `${label}${koreanParticle(label, "\uC744", "\uB97C")} \uC120\uD0DD\uD574 \uC8FC\uC138\uC694.`;
14767
+ };
14768
+ var matchPresetId = (presets, value) => {
14769
+ if (isEmptyValue(value)) return null;
14770
+ if (isPeriodValue(value)) return value.unit;
14771
+ for (const preset of presets) {
14772
+ if (preset.custom) continue;
14773
+ const resolved = preset.resolve ? preset.resolve() : preset.value;
14774
+ if (Array.isArray(resolved) && Array.isArray(value)) {
14775
+ if (resolved[0] === value[0] && resolved[1] === value[1]) return preset.value;
14776
+ } else if (!Array.isArray(resolved) && !Array.isArray(value) && resolved === value) {
14777
+ return preset.value;
14778
+ }
14779
+ }
14780
+ return presets.find((preset) => preset.custom)?.value ?? null;
14781
+ };
14782
+ var optionLabel = (field, value) => field.options?.find((option) => option.value === value)?.label ?? String(value);
14783
+ var formatValueList = (labels) => {
14784
+ if (labels.length <= 1) return labels[0] ?? "";
14785
+ return `${labels[0]} \uC678 ${labels.length - 1}\uAC1C`;
14786
+ };
14787
+ var displayValue = (field, value) => {
14788
+ if (isEmptyValue(value)) return "";
14789
+ if (field.type === "multi") {
14790
+ const values = toArrayValue(value);
14791
+ const selectableValues = (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value);
14792
+ if (selectableValues.length > 0 && selectableValues.every((selectableValue) => values.includes(selectableValue))) {
14793
+ return "\uC804\uCCB4";
14794
+ }
14795
+ return formatValueList(values.map((v) => optionLabel(field, v)));
14796
+ }
14797
+ if (field.type === "single") return optionLabel(field, value);
14798
+ if (field.type === "date")
14799
+ return Array.isArray(value) ? `${value[0]} ~ ${value[1]}` : String(value);
14800
+ if (field.type === "period") {
14801
+ if (!isPeriodValue(value)) {
14802
+ const preset2 = getFieldPresets(field).find((p) => p.value === value);
14803
+ return preset2?.label ?? String(value);
14804
+ }
14805
+ const preset = getFieldPresets(field).find((p) => p.value === value.unit);
14806
+ const label = preset?.label ?? value.unit;
14807
+ if (value.unit === "quarter") return `${label}(${value.value ?? 1}\uBD84\uAE30)`;
14808
+ if (value.unit === "half") return `${label}(${value.value === "second" ? "\uD558\uBC18\uAE30" : "\uC0C1\uBC18\uAE30"})`;
14809
+ if (Array.isArray(value.value)) return `${value.value[0]} ~ ${value.value[1]}`;
14810
+ return value.value != null && value.value !== "" ? String(value.value) : label;
14811
+ }
14812
+ if (field.type === "keyword") return formatValueList(keywordEntries(value));
14813
+ return String(value);
14814
+ };
14815
+ var chipDisplayValue = (field, value) => {
14816
+ const text = displayValue(field, value);
14817
+ if (field.type !== "date" || isEmptyValue(value)) return text;
14818
+ const presets = getFieldPresets(field);
14819
+ if (presets.length === 0) return text;
14820
+ const preset = presets.find((p) => p.value === matchPresetId(presets, value));
14821
+ return preset && !preset.custom ? `${preset.label}(${text})` : text;
14822
+ };
14823
+ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14824
+ fields: fieldsProp = [],
14825
+ value,
14826
+ onValueChange,
14827
+ onFilterChange,
14828
+ onSearch,
14829
+ activeKeys,
14830
+ onActiveKeysChange,
14831
+ onReset,
14832
+ onAddFilter,
14833
+ label = "\uAC80\uC0C9 \uD544\uD130",
14834
+ showLabel = false,
14835
+ showReset = true,
14836
+ disabled = false,
14837
+ className,
14838
+ style
14839
+ }, ref) {
14840
+ const usingGroupedFields = isGroupedFields(fieldsProp);
14841
+ const groups = react.useMemo(
14842
+ () => usingGroupedFields ? fieldsProp : [{ fields: fieldsProp }],
14843
+ [usingGroupedFields, fieldsProp]
14844
+ );
14845
+ const fields = react.useMemo(() => groups.flatMap((group) => group.fields), [groups]);
14846
+ const values = normalizeValues(value);
14847
+ const portalContainer = usePortalContainer();
14848
+ const [internalActiveKeys, setInternalActiveKeys] = react.useState(
14849
+ () => fields.filter((field) => isFixedField(field) || field.defaultActive !== false).map((field) => field.key)
14850
+ );
14851
+ const [openKey, setOpenKey] = react.useState(null);
14852
+ const [addOpen, setAddOpen] = react.useState(false);
14853
+ const triggerRefs = react.useRef(/* @__PURE__ */ new Map());
14854
+ const initializedDefaultsRef = react.useRef(/* @__PURE__ */ new Set());
14855
+ const prevOpenKeyRef = react.useRef(null);
14856
+ const valuesRef = react.useRef(values);
14857
+ valuesRef.current = values;
14858
+ const lastSearchedValuesRef = react.useRef(values);
14859
+ const invalidGroups = react.useMemo(() => {
14860
+ const invalid = /* @__PURE__ */ new Set();
14861
+ groups.forEach((group, groupIndex) => {
14862
+ if (!isGroupSatisfied(group, values)) invalid.add(groupIndex);
14863
+ });
14864
+ return invalid;
14865
+ }, [groups, values]);
14866
+ const isGroupsValid = react.useCallback(
14867
+ (current) => groups.every((group) => isGroupSatisfied(group, current)),
14868
+ [groups]
14869
+ );
14870
+ const runSearch = react.useCallback(
14871
+ (next) => {
14872
+ if (valuesMapEqual(lastSearchedValuesRef.current, next)) return;
14873
+ if (!isGroupsValid(next)) return;
14874
+ lastSearchedValuesRef.current = next;
14875
+ onSearch?.(next);
14876
+ },
14877
+ [isGroupsValid, onSearch]
14878
+ );
14879
+ react.useEffect(() => {
14880
+ const prevKey = prevOpenKeyRef.current;
14881
+ if (prevKey != null && prevKey !== openKey) {
14882
+ runSearch(valuesRef.current);
14883
+ }
14884
+ prevOpenKeyRef.current = openKey;
14885
+ }, [openKey, runSearch]);
14886
+ react.useEffect(() => {
14887
+ if (activeKeys) return;
14888
+ setInternalActiveKeys((current) => {
14889
+ const known = new Set(fields.map((field) => field.key));
14890
+ const kept = current.filter((key) => known.has(key));
14891
+ const additions = fields.filter(
14892
+ (field) => (isFixedField(field) || field.defaultActive !== false) && !kept.includes(field.key)
14893
+ ).map((field) => field.key);
14894
+ return [...kept, ...additions];
14895
+ });
14896
+ }, [activeKeys, fields]);
14897
+ react.useEffect(() => {
14898
+ const patch = {};
14899
+ let changed = false;
14900
+ fields.forEach((field) => {
14901
+ if (initializedDefaultsRef.current.has(field.key)) return;
14902
+ if (!isEmptyValue(values[field.key])) {
14903
+ initializedDefaultsRef.current.add(field.key);
14904
+ return;
14905
+ }
14906
+ const def = resolveFieldDefault(field);
14907
+ if (def === void 0) return;
14908
+ patch[field.key] = def;
14909
+ initializedDefaultsRef.current.add(field.key);
14910
+ changed = true;
14911
+ });
14912
+ if (changed) onValueChange?.({ ...values, ...patch });
14913
+ }, [fields, values, onValueChange]);
14914
+ const fixedKeys = fields.filter(isFixedField).map((field) => field.key);
14915
+ const resolvedActiveKeys = Array.from(
14916
+ /* @__PURE__ */ new Set([...activeKeys ?? internalActiveKeys, ...fixedKeys])
14917
+ );
14918
+ const activeFields = fields.filter((field) => resolvedActiveKeys.includes(field.key));
14919
+ const inactiveFields = fields.filter(
14920
+ (field) => isAddableField(field) && !resolvedActiveKeys.includes(field.key)
14921
+ );
14922
+ const groupIndexByKey = /* @__PURE__ */ new Map();
14923
+ groups.forEach((group, groupIndex) => {
14924
+ group.fields.forEach((field) => groupIndexByKey.set(field.key, groupIndex));
14925
+ });
14926
+ const hasValue = fields.some((field) => !isEmptyValue(values[field.key]));
14927
+ const hasAddButton = fields.some(isAddableField);
14928
+ const hasRightActions = hasAddButton || showReset;
14929
+ const currentOpenField = fields.find((field) => field.key === openKey);
14930
+ const editorPopoverStyle = react.useMemo(() => {
14931
+ if (currentOpenField?.type === "keyword") {
14932
+ return { width: CHIPFILTER_LISTBOX_WIDTH };
14933
+ }
14934
+ if ((currentOpenField?.type === "date" || currentOpenField?.type === "period") && getFieldPresets(currentOpenField).length > 0) {
14935
+ return { width: CHIPFILTER_DATE_PRESET_LISTBOX_WIDTH };
14936
+ }
14937
+ return { width: CHIPFILTER_LISTBOX_WIDTH };
14938
+ }, [currentOpenField]);
14939
+ const setActive = (next) => {
14940
+ if (!activeKeys) setInternalActiveKeys(next);
14941
+ onActiveKeysChange?.(next);
14942
+ };
14943
+ const emitValue = (key, nextValue) => {
14944
+ const next = { ...values, [key]: nextValue };
14945
+ onValueChange?.(next);
14946
+ onFilterChange?.({ key, value: nextValue, values: next });
14947
+ return next;
14948
+ };
14949
+ const emitValueAndSearch = (key, nextValue) => {
14950
+ runSearch(emitValue(key, nextValue));
14951
+ };
14952
+ const clearValue = (key) => {
14953
+ const field = fields.find((f) => f.key === key);
14954
+ const def = field ? resolveFieldDefault(field) : void 0;
14955
+ runSearch(emitValue(key, def !== void 0 ? def : null));
14956
+ };
14957
+ const removeField = (key) => {
14958
+ const nextValues = emitValue(key, null);
14959
+ setActive(resolvedActiveKeys.filter((activeKey) => activeKey !== key));
14960
+ if (openKey === key) setOpenKey(null);
14961
+ runSearch(nextValues);
14962
+ };
14963
+ const reset = react.useCallback(() => {
14964
+ const next = fields.reduce((acc, field) => {
14965
+ const def = resolveFieldDefault(field);
14966
+ acc[field.key] = def !== void 0 ? def : null;
14967
+ return acc;
14968
+ }, {});
14969
+ onValueChange?.(next);
14970
+ runSearch(next);
14971
+ onReset?.();
14972
+ }, [fields, onReset, onValueChange, runSearch]);
14973
+ const open = react.useCallback(
14974
+ (key) => {
14975
+ const field = fields.find((f) => f.key === key);
14976
+ if (!field || isInlineField(field)) return;
14977
+ setOpenKey(key);
14978
+ },
14979
+ [fields]
14980
+ );
14981
+ const validate = react.useCallback(() => invalidGroups.size === 0, [invalidGroups]);
14982
+ react.useImperativeHandle(ref, () => ({ open, reset, validate }), [open, reset, validate]);
14983
+ const addField = (key) => {
14984
+ const next = [...resolvedActiveKeys, key];
14985
+ setActive(next);
14986
+ onAddFilter?.(key);
14987
+ setAddOpen(false);
14988
+ const field = fields.find((f) => f.key === key);
14989
+ if (field && !isInlineField(field)) requestAnimationFrame(() => setOpenKey(key));
14990
+ };
14991
+ const rootStyle = react.useMemo(
14992
+ () => ({
14993
+ "--chipfilter-bar-bg": CHIPFILTER.barBg,
14994
+ "--chipfilter-bar-border": CHIPFILTER.barBorder,
14995
+ "--chipfilter-bar-padding-x": CHIPFILTER.barPaddingX,
14996
+ "--chipfilter-bar-padding-y": CHIPFILTER.barPaddingY,
14997
+ "--chipfilter-bar-gap": CHIPFILTER.barGap,
14998
+ "--chipfilter-divider-height": CHIPFILTER.dividerHeight,
14999
+ ...style
15000
+ }),
15001
+ [style]
15002
+ );
15003
+ return /* @__PURE__ */ jsxRuntime.jsxs(
15004
+ "div",
15005
+ {
15006
+ className: cn(
15007
+ "flex w-full items-start gap-[var(--chipfilter-bar-gap)] border-b border-solid bg-[var(--chipfilter-bar-bg)] px-[var(--chipfilter-bar-padding-x)] py-[var(--chipfilter-bar-padding-y)]",
15008
+ className
15009
+ ),
15010
+ style: { ...rootStyle, borderColor: "var(--chipfilter-bar-border)" },
15011
+ children: [
15012
+ showLabel && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15013
+ /* @__PURE__ */ jsxRuntime.jsx(STag, { className: "shrink-0", shape: "square", size: "sm", color: "darkblue", label }),
15014
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, { top: true })
15015
+ ] }),
15016
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-[var(--cmp-chipFilter-chip-group)]", children: [
15017
+ (() => {
15018
+ const nodes = [];
15019
+ let runBuffer = [];
15020
+ let runGroupIndex = null;
15021
+ const flushRun = () => {
15022
+ if (runBuffer.length === 0) return;
15023
+ const group = runGroupIndex != null ? groups[runGroupIndex] : void 0;
15024
+ const message = usingGroupedFields && group?.rule ? group.tooltipMessage ?? defaultGroupTooltipMessage(group.rule, group.fields) : void 0;
15025
+ if (message) {
15026
+ nodes.push(
15027
+ /* @__PURE__ */ jsxRuntime.jsx(
15028
+ GroupTooltip,
15029
+ {
15030
+ message,
15031
+ open: invalidGroups.has(runGroupIndex),
15032
+ children: runBuffer
15033
+ },
15034
+ `group-${runGroupIndex}`
15035
+ )
15036
+ );
15037
+ } else {
15038
+ nodes.push(...runBuffer);
15039
+ }
15040
+ runBuffer = [];
15041
+ };
15042
+ activeFields.forEach((field, index) => {
15043
+ const inline = isInlineField(field);
15044
+ const groupIndex = groupIndexByKey.get(field.key) ?? 0;
15045
+ const warning = field.warning || invalidGroups.has(groupIndex);
15046
+ const prevField = index > 0 ? activeFields[index - 1] : null;
15047
+ const prevGroupIndex = prevField ? groupIndexByKey.get(prevField.key) ?? 0 : null;
15048
+ const needsLeadingDivider = index === 0 ? false : inline || prevField != null && isInlineField(prevField) || groupIndex !== prevGroupIndex;
15049
+ if (groupIndex !== runGroupIndex) {
15050
+ flushRun();
15051
+ runGroupIndex = groupIndex;
15052
+ }
15053
+ const addable = isAddableField(field);
15054
+ const showClear = addable || !isAtDefaultValue(field, values[field.key]);
15055
+ const handleClear = () => {
15056
+ if (addable) {
15057
+ removeField(field.key);
15058
+ return;
15059
+ }
15060
+ clearValue(field.key);
15061
+ };
15062
+ const node = isInlineDateField(field) ? /* @__PURE__ */ jsxRuntime.jsx(
15063
+ InlineDatePresetFilter,
15064
+ {
15065
+ field,
15066
+ value: values[field.key],
15067
+ disabled: disabled || field.disabled,
15068
+ warning,
15069
+ showClear,
15070
+ onValueChange: (next) => emitValueAndSearch(field.key, next),
15071
+ onClear: handleClear
15072
+ },
15073
+ field.key
15074
+ ) : field.type === "custom" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex shrink-0 items-center gap-[4px]", children: [
15075
+ field.render?.({
15076
+ value: values[field.key],
15077
+ disabled: disabled || field.disabled,
15078
+ warning,
15079
+ onValueChange: (next) => emitValueAndSearch(field.key, next)
15080
+ }),
15081
+ addable && !disabled && !field.disabled && showClear && /* @__PURE__ */ jsxRuntime.jsx(
15082
+ "button",
15083
+ {
15084
+ type: "button",
15085
+ "aria-label": `${field.label} \uD544\uD130 \uC81C\uAC70`,
15086
+ className: "inline-flex size-[16px] shrink-0 items-center justify-center rounded-[var(--cmp-ghostButton-radius-sm)] border-none bg-transparent p-0",
15087
+ onClick: handleClear,
15088
+ children: /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "close", size: 12, color: "var(--cmp-ghostButton-default-content)" })
15089
+ }
15090
+ )
15091
+ ] }, field.key) : /* @__PURE__ */ jsxRuntime.jsx(
15092
+ FilterChip,
15093
+ {
15094
+ field,
15095
+ valueText: chipDisplayValue(field, values[field.key]),
15096
+ active: openKey === field.key,
15097
+ applied: !isEmptyValue(values[field.key]),
15098
+ disabled: disabled || field.disabled,
15099
+ warning,
15100
+ showClear,
15101
+ showClearWhenEmpty: addable,
15102
+ refCallback: (node2) => triggerRefs.current.set(field.key, node2),
15103
+ onOpen: () => setOpenKey(openKey === field.key ? null : field.key),
15104
+ onClear: handleClear
15105
+ },
15106
+ field.key
15107
+ );
15108
+ runBuffer.push(
15109
+ needsLeadingDivider ? /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
15110
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, { inset: true }),
15111
+ node
15112
+ ] }, `${field.key}-wrap`) : node
15113
+ );
15114
+ });
15115
+ flushRun();
15116
+ return nodes;
15117
+ })(),
15118
+ (() => {
15119
+ const lastField = activeFields[activeFields.length - 1];
15120
+ const needsTrailingDivider = lastField != null && isInlineField(lastField) && !hasRightActions;
15121
+ return needsTrailingDivider ? /* @__PURE__ */ jsxRuntime.jsx(Divider, { inset: true }) : null;
15122
+ })(),
15123
+ hasRightActions && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15124
+ /* @__PURE__ */ jsxRuntime.jsx(Divider, { inset: true }),
15125
+ hasAddButton && /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open: addOpen, onOpenChange: setAddOpen, children: [
15126
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
15127
+ "button",
15128
+ {
15129
+ type: "button",
15130
+ disabled: disabled || inactiveFields.length === 0,
15131
+ className: "inline-flex shrink-0 cursor-pointer items-center justify-center gap-[var(--cmp-chipFilter-addButton-gap)] whitespace-nowrap border border-solid bg-[var(--cmp-chipFilter-addButton-bg)] px-[var(--cmp-chipFilter-addButton-paddingX)] text-[12px] font-medium leading-[20px] outline-none disabled:cursor-not-allowed",
15132
+ style: {
15133
+ minHeight: "var(--cmp-chipFilter-addButton-height)" ,
15134
+ borderRadius: "var(--cmp-chipFilter-addButton-radius)" ,
15135
+ borderColor: disabled || inactiveFields.length === 0 ? CHIPFILTER.addBorderDisabled : CHIPFILTER.addBorder,
15136
+ color: disabled || inactiveFields.length === 0 ? CHIPFILTER.addTextDisabled : CHIPFILTER.addText
15137
+ },
15138
+ children: [
15139
+ /* @__PURE__ */ jsxRuntime.jsx(
15140
+ SIcon,
15141
+ {
15142
+ name: "add",
15143
+ size: 12,
15144
+ color: disabled || inactiveFields.length === 0 ? CHIPFILTER.addTextDisabled : CHIPFILTER.addText
15145
+ }
15146
+ ),
15147
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\uD544\uD130 \uCD94\uAC00" })
15148
+ ]
15149
+ }
15150
+ ) }),
15151
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
15152
+ RPopover10__namespace.Content,
15153
+ {
15154
+ side: "bottom",
15155
+ align: "start",
15156
+ sideOffset: 4,
15157
+ className: cn("z-50 min-w-[180px]", FLOATING_SLIDE_ANIM),
15158
+ children: /* @__PURE__ */ jsxRuntime.jsx(ListBox, { children: inactiveFields.length > 0 ? inactiveFields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(MenuItem, { onClick: () => addField(field.key), children: field.label }, field.key)) : /* @__PURE__ */ jsxRuntime.jsx(EmptyItem, { children: "\uCD94\uAC00\uD560 \uD544\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }) })
15159
+ }
15160
+ ) })
15161
+ ] }),
15162
+ showReset && /* @__PURE__ */ jsxRuntime.jsxs(
15163
+ "button",
15164
+ {
15165
+ type: "button",
15166
+ disabled: disabled || !hasValue,
15167
+ onClick: reset,
15168
+ className: cn(
15169
+ "inline-flex shrink-0 cursor-pointer items-center gap-[4px] border-none bg-transparent p-0 text-[12px] font-medium leading-[20px] disabled:cursor-not-allowed",
15170
+ // 부모 flex gap(6px)은 칩 사이 간격 기준이라, 필터 추가 버튼과의 12px 간격을 맞추려면
15171
+ // 부족한 만큼(12px - gap)을 추가로 띄운다. 필터 추가가 없을 땐 divider와 이미 12px이라 불필요.
15172
+ hasAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
15173
+ ),
15174
+ style: {
15175
+ color: disabled || !hasValue ? "var(--cmp-textLink-content-disabled)" : "var(--cmp-textLink-content-default)"
15176
+ },
15177
+ children: [
15178
+ /* @__PURE__ */ jsxRuntime.jsx(
15179
+ SIcon,
15180
+ {
15181
+ name: "reset",
15182
+ size: 16,
15183
+ color: disabled || !hasValue ? "var(--cmp-textLink-icon-disabled)" : "var(--cmp-textLink-icon-default)"
15184
+ }
15185
+ ),
15186
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\uAC80\uC0C9 \uCD08\uAE30\uD654" })
15187
+ ]
15188
+ }
15189
+ )
15190
+ ] })
15191
+ ] }),
15192
+ /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open: openKey != null, onOpenChange: (next) => !next && setOpenKey(null), children: [
15193
+ /* @__PURE__ */ jsxRuntime.jsx(
15194
+ RPopover10__namespace.Anchor,
15195
+ {
15196
+ virtualRef: {
15197
+ current: openKey ? triggerRefs.current.get(openKey) : null
15198
+ }
15199
+ }
15200
+ ),
15201
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
15202
+ RPopover10__namespace.Content,
15203
+ {
15204
+ side: "bottom",
15205
+ align: "start",
15206
+ sideOffset: 4,
15207
+ onOpenAutoFocus: (event) => event.preventDefault(),
15208
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
15209
+ style: editorPopoverStyle,
15210
+ children: currentOpenField && /* @__PURE__ */ jsxRuntime.jsx(
15211
+ EditorPanel,
15212
+ {
15213
+ field: currentOpenField,
15214
+ value: values[currentOpenField.key],
15215
+ disabled: disabled || currentOpenField.disabled,
15216
+ onValueChange: (next) => emitValue(currentOpenField.key, next),
15217
+ onClose: () => setOpenKey(null)
15218
+ }
15219
+ )
15220
+ }
15221
+ ) })
15222
+ ] })
15223
+ ]
15224
+ }
15225
+ );
15226
+ });
15227
+ function Divider({ inset, top } = {}) {
15228
+ return /* @__PURE__ */ jsxRuntime.jsx(
15229
+ "span",
15230
+ {
15231
+ "aria-hidden": true,
15232
+ className: cn(
15233
+ "inline-flex w-px shrink-0 bg-[var(--chipfilter-bar-border)]",
15234
+ top ? "mt-[2px] self-start" : "my-auto",
15235
+ inset && "mx-[calc(12px-var(--cmp-chipFilter-chip-group))]"
15236
+ ),
15237
+ style: { height: "var(--chipfilter-divider-height)" }
15238
+ }
15239
+ );
15240
+ }
15241
+ function GroupTooltip({
15242
+ message,
15243
+ open,
15244
+ children
15245
+ }) {
15246
+ return /* @__PURE__ */ jsxRuntime.jsx(STooltip2, { trigger: "none", open, placement: "top", tooltipType: "warning", message: [message], children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-[var(--cmp-chipFilter-chip-group)]", children }) });
15247
+ }
15248
+ function FilterChip({
15249
+ field,
15250
+ valueText,
15251
+ active,
15252
+ applied,
15253
+ disabled,
15254
+ warning,
15255
+ showClear,
15256
+ showClearWhenEmpty,
15257
+ refCallback,
15258
+ onOpen,
15259
+ onClear
15260
+ }) {
15261
+ const border = disabled ? CHIPFILTER.chipDisabledBorder : warning ? CHIPFILTER.chipWarningBorder : applied ? active ? "var(--color-blue-strong)" : CHIPFILTER.chipAppliedBorder : active ? CHIPFILTER.chipActiveBorder : CHIPFILTER.chipDefaultBorder;
15262
+ const background = disabled ? CHIPFILTER.chipDisabledBg : applied ? CHIPFILTER.chipAppliedBg : CHIPFILTER.chipDefaultBg;
15263
+ const textColor = disabled ? CHIPFILTER.chipDisabledText : CHIPFILTER.chipDefaultText;
15264
+ return /* @__PURE__ */ jsxRuntime.jsxs(
15265
+ "button",
15266
+ {
15267
+ ref: refCallback,
15268
+ type: "button",
15269
+ disabled,
15270
+ "aria-expanded": active,
15271
+ onPointerDown: (event) => event.stopPropagation(),
15272
+ onClick: onOpen,
15273
+ className: "group/chip-filter inline-flex max-w-full shrink-0 cursor-pointer items-center justify-center whitespace-nowrap border border-solid px-[var(--cmp-chipFilter-chip-paddingX)] text-[12px] leading-[20px] outline-none disabled:cursor-not-allowed",
15274
+ style: {
15275
+ minHeight: CHIPFILTER.chipHeight,
15276
+ gap: CHIPFILTER.chipGap,
15277
+ borderRadius: CHIPFILTER.chipRadius,
15278
+ borderColor: border,
15279
+ background,
15280
+ color: textColor
15281
+ },
15282
+ children: [
15283
+ field.required && /* @__PURE__ */ jsxRuntime.jsx(
15284
+ SIcon,
15285
+ {
15286
+ name: "asterisk",
15287
+ size: 12,
15288
+ color: disabled ? CHIPFILTER.chipDisabledText : "var(--cmp-tag-darkblue-content)"
15289
+ }
15290
+ ),
15291
+ applied ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex min-w-0 items-center gap-[4px] truncate", children: [
15292
+ /* @__PURE__ */ jsxRuntime.jsx(
15293
+ "span",
15294
+ {
15295
+ className: "font-bold",
15296
+ style: { color: disabled ? CHIPFILTER.chipDisabledText : CHIPFILTER.chipAppliedLabel },
15297
+ children: field.label
15298
+ }
15299
+ ),
15300
+ /* @__PURE__ */ jsxRuntime.jsx(
15301
+ "span",
15302
+ {
15303
+ style: { color: disabled ? CHIPFILTER.chipDisabledText : CHIPFILTER.chipAppliedLabel },
15304
+ children: ":"
15305
+ }
15306
+ ),
15307
+ /* @__PURE__ */ jsxRuntime.jsx(
15308
+ "span",
15309
+ {
15310
+ className: "min-w-0 truncate font-bold",
15311
+ style: { color: disabled ? CHIPFILTER.chipDisabledText : CHIPFILTER.chipAppliedValue },
15312
+ children: valueText
15313
+ }
15314
+ )
15315
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15316
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("min-w-0 truncate", active && "font-bold"), children: field.label }),
15317
+ /* @__PURE__ */ jsxRuntime.jsx(
15318
+ SIcon,
15319
+ {
15320
+ name: "caretDown",
15321
+ size: 12,
15322
+ color: disabled ? CHIPFILTER.chipDisabledText : CHIPFILTER.chipDefaultText
15323
+ }
15324
+ )
15325
+ ] }),
15326
+ !disabled && showClear && (applied || showClearWhenEmpty) && /* @__PURE__ */ jsxRuntime.jsx(
15327
+ "span",
15328
+ {
15329
+ role: "button",
15330
+ tabIndex: 0,
15331
+ "aria-label": `${field.label} \uD544\uD130 \uC9C0\uC6B0\uAE30`,
15332
+ className: "inline-flex size-[16px] items-center justify-center rounded-[var(--cmp-ghostButton-radius-sm)]",
15333
+ onClick: (event) => {
15334
+ event.preventDefault();
15335
+ event.stopPropagation();
15336
+ onClear();
15337
+ },
15338
+ onKeyDown: (event) => {
15339
+ if (event.key !== "Enter" && event.key !== " ") return;
15340
+ event.preventDefault();
15341
+ event.stopPropagation();
15342
+ onClear();
15343
+ },
15344
+ children: /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "close", size: 12, color: "var(--cmp-ghostButton-default-content)" })
15345
+ }
15346
+ )
15347
+ ]
15348
+ }
15349
+ );
15350
+ }
15351
+ function EditorPanel({
15352
+ field,
15353
+ value,
15354
+ disabled,
15355
+ onValueChange,
15356
+ onClose
15357
+ }) {
15358
+ const wrapsInPaddedTop = field.type === "date" || field.type === "period";
15359
+ const isMulti = field.type === "multi";
15360
+ const selectableValues = isMulti ? (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value) : [];
15361
+ const selectedValues = isMulti ? toArrayValue(value) : [];
15362
+ const allSelected = isMulti && selectableValues.length > 0 && selectableValues.every((v) => selectedValues.includes(v));
15363
+ const toggleSelectAll = () => {
15364
+ onValueChange(
15365
+ allSelected ? selectedValues.filter((v) => !selectableValues.includes(v)) : [...selectedValues, ...selectableValues.filter((v) => !selectedValues.includes(v))]
15366
+ );
15367
+ };
15368
+ return /* @__PURE__ */ jsxRuntime.jsxs(ListBox, { children: [
15369
+ wrapsInPaddedTop ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-[var(--cmp-chipFilter-listbox-top-gap)] px-[var(--cmp-chipFilter-listbox-top-paddingX)] py-[var(--cmp-chipFilter-listbox-top-paddingY)]", children: /* @__PURE__ */ jsxRuntime.jsx(
15370
+ FilterEditor,
15371
+ {
15372
+ field,
15373
+ value,
15374
+ disabled,
15375
+ onValueChange
15376
+ }
15377
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(
15378
+ FilterEditor,
15379
+ {
15380
+ field,
15381
+ value,
15382
+ disabled,
15383
+ onValueChange
15384
+ }
15385
+ ),
15386
+ /* @__PURE__ */ jsxRuntime.jsxs(
15387
+ "div",
15388
+ {
15389
+ className: "flex items-center justify-end gap-[var(--cmp-chipFilter-listbox-bottom-gap)] border-t border-solid px-[var(--cmp-chipFilter-listbox-bottom-paddingX)] py-[var(--cmp-chipFilter-listbox-bottom-paddingY)]",
15390
+ style: { borderColor: CHIPFILTER.listboxBorder },
15391
+ children: [
15392
+ isMulti && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15393
+ /* @__PURE__ */ jsxRuntime.jsx(
15394
+ STextLink,
15395
+ {
15396
+ size: "sm",
15397
+ label: allSelected ? "\uC120\uD0DD \uD574\uC81C" : "\uC804\uCCB4 \uC120\uD0DD",
15398
+ labelClass: "font-bold",
15399
+ disabled,
15400
+ onClick: toggleSelectAll,
15401
+ style: { color: "var(--sys-color-fg-secondary)" }
15402
+ }
15403
+ ),
15404
+ /* @__PURE__ */ jsxRuntime.jsx(
15405
+ "span",
15406
+ {
15407
+ "aria-hidden": true,
15408
+ className: "inline-flex w-px shrink-0",
15409
+ style: {
15410
+ height: CHIPFILTER.listboxBottomDividerHeight,
15411
+ background: CHIPFILTER.listboxBorder
15412
+ }
15413
+ }
15414
+ )
15415
+ ] }),
15416
+ /* @__PURE__ */ jsxRuntime.jsx(
15417
+ STextLink,
15418
+ {
15419
+ size: "sm",
15420
+ label: "\uAC80\uC0C9",
15421
+ labelClass: "font-bold",
15422
+ onClick: onClose,
15423
+ style: { color: CHIPFILTER.chipAppliedValue }
15424
+ }
15425
+ )
15426
+ ]
15427
+ }
15428
+ )
15429
+ ] });
15430
+ }
15431
+ function FilterEditor({
15432
+ field,
15433
+ value,
15434
+ disabled,
15435
+ onValueChange
15436
+ }) {
15437
+ if (field.type === "period") {
15438
+ return /* @__PURE__ */ jsxRuntime.jsx(
15439
+ PeriodPresetEditor,
15440
+ {
15441
+ field,
15442
+ value,
15443
+ disabled,
15444
+ onValueChange
15445
+ }
15446
+ );
15447
+ }
15448
+ if (field.type === "date") {
15449
+ return /* @__PURE__ */ jsxRuntime.jsx(
15450
+ DatePresetEditor,
15451
+ {
15452
+ field,
15453
+ value,
15454
+ disabled,
15455
+ onValueChange
15456
+ }
15457
+ );
15458
+ }
15459
+ if (field.type === "keyword") {
15460
+ return /* @__PURE__ */ jsxRuntime.jsx(
15461
+ KeywordEditor,
15462
+ {
15463
+ field,
15464
+ value: isKeywordValue(value) ? value : keywordEntries(value),
15465
+ disabled,
15466
+ onValueChange
15467
+ }
15468
+ );
15469
+ }
15470
+ return /* @__PURE__ */ jsxRuntime.jsx(
15471
+ OptionList,
15472
+ {
15473
+ field,
15474
+ value: field.type === "multi" ? toArrayValue(value) : value,
15475
+ multiple: field.type === "multi",
15476
+ disabled,
15477
+ onValueChange
15478
+ }
15479
+ );
15480
+ }
15481
+ function DatePresetEditor({
15482
+ field,
15483
+ value,
15484
+ disabled,
15485
+ onValueChange
15486
+ }) {
15487
+ const presets = getFieldPresets(field);
15488
+ const [selectedId, setSelectedId] = react.useState(() => matchPresetId(presets, value));
15489
+ const syncedInitialValueRef = react.useRef(!isEmptyValue(value));
15490
+ react.useEffect(() => {
15491
+ if (syncedInitialValueRef.current || isEmptyValue(value)) return;
15492
+ syncedInitialValueRef.current = true;
15493
+ setSelectedId(matchPresetId(presets, value));
15494
+ }, [value, presets]);
15495
+ const selectedPreset = presets.find((preset) => preset.value === selectedId);
15496
+ const selectPreset = (preset) => {
15497
+ setSelectedId(preset.value);
15498
+ if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : preset.value);
15499
+ };
15500
+ if (field.type === "date" && presets.length === 0) {
15501
+ return /* @__PURE__ */ jsxRuntime.jsx(
15502
+ SDatePicker,
15503
+ {
15504
+ value: typeof value === "string" ? value : null,
15505
+ onValueChange,
15506
+ selectable: field.selectable,
15507
+ disabled,
15508
+ placeholder: field.placeholder
15509
+ }
15510
+ );
15511
+ }
15512
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-[var(--cmp-chipFilter-listbox-top-gap)]", children: [
15513
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-[8px]", children: presets.map((preset) => /* @__PURE__ */ jsxRuntime.jsx(
15514
+ SRadio,
15515
+ {
15516
+ val: preset.value,
15517
+ value: selectedId,
15518
+ label: preset.label,
15519
+ disabled,
15520
+ onValueChange: () => selectPreset(preset)
15521
+ },
15522
+ preset.value
15523
+ )) }),
15524
+ selectedPreset?.custom && /* @__PURE__ */ jsxRuntime.jsx(
15525
+ SDateRangePicker,
15526
+ {
15527
+ value: Array.isArray(value) && value.length === 2 ? value : null,
15528
+ onValueChange,
15529
+ selectable: field.selectable,
15530
+ maxRange: field.maxRange,
15531
+ disabled
15532
+ }
15533
+ )
15534
+ ] });
15535
+ }
15536
+ function PeriodPresetEditor({
15537
+ field,
15538
+ value,
15539
+ disabled,
15540
+ onValueChange
15541
+ }) {
15542
+ const presets = getFieldPresets(field);
15543
+ const firstUnit = presets.find((preset) => !preset.custom)?.value ?? "day";
15544
+ const [selectedId, setSelectedId] = react.useState(() => matchPresetId(presets, value) ?? firstUnit);
15545
+ const periodValue = isPeriodValue(value) ? value : createPeriodValue(selectedId ?? firstUnit);
15546
+ const selectedUnit = selectedId ?? periodValue.unit;
15547
+ react.useEffect(() => {
15548
+ if (isEmptyValue(value)) return;
15549
+ setSelectedId(matchPresetId(presets, value) ?? firstUnit);
15550
+ }, [firstUnit, presets, value]);
15551
+ const selectPreset = (preset) => {
15552
+ const nextUnit = preset.value;
15553
+ setSelectedId(nextUnit);
15554
+ onValueChange(createPeriodValue(nextUnit));
15555
+ };
15556
+ const updateInputValue = (nextValue) => {
15557
+ onValueChange({ unit: selectedUnit, value: nextValue });
15558
+ };
15559
+ const renderInput = () => {
15560
+ if (selectedUnit === "day") {
15561
+ return /* @__PURE__ */ jsxRuntime.jsx(
15562
+ SDatePicker,
15563
+ {
15564
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15565
+ onValueChange: updateInputValue,
15566
+ selectable: field.selectable,
15567
+ disabled,
15568
+ width: "100%"
15569
+ }
15570
+ );
15571
+ }
15572
+ if (selectedUnit === "month") {
15573
+ return /* @__PURE__ */ jsxRuntime.jsx(
15574
+ SDatePicker,
15575
+ {
15576
+ mode: "month",
15577
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15578
+ onValueChange: updateInputValue,
15579
+ selectable: field.selectable,
15580
+ disabled,
15581
+ width: "100%"
15582
+ }
15583
+ );
15584
+ }
15585
+ if (selectedUnit === "quarter") {
15586
+ return /* @__PURE__ */ jsxRuntime.jsx(
15587
+ SSelect2,
15588
+ {
15589
+ value: typeof periodValue.value === "number" ? periodValue.value : 1,
15590
+ onValueChange: updateInputValue,
15591
+ emitValue: true,
15592
+ disabled,
15593
+ width: "100%",
15594
+ options: [
15595
+ { value: 1, label: "1\uBD84\uAE30" },
15596
+ { value: 2, label: "2\uBD84\uAE30" },
15597
+ { value: 3, label: "3\uBD84\uAE30" },
15598
+ { value: 4, label: "4\uBD84\uAE30" }
15599
+ ]
15600
+ }
15601
+ );
15602
+ }
15603
+ if (selectedUnit === "half") {
15604
+ return /* @__PURE__ */ jsxRuntime.jsx(
15605
+ SSelect2,
15606
+ {
15607
+ value: typeof periodValue.value === "string" ? periodValue.value : "first",
15608
+ onValueChange: updateInputValue,
15609
+ emitValue: true,
15610
+ disabled,
15611
+ width: "100%",
15612
+ options: [
15613
+ { value: "first", label: "\uC0C1\uBC18\uAE30" },
15614
+ { value: "second", label: "\uD558\uBC18\uAE30" }
15615
+ ]
15616
+ }
15617
+ );
15618
+ }
15619
+ if (selectedUnit === "year") {
15620
+ return /* @__PURE__ */ jsxRuntime.jsx(
15621
+ SDatePicker,
15622
+ {
15623
+ mode: "year",
15624
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15625
+ onValueChange: updateInputValue,
15626
+ selectable: field.selectable,
15627
+ disabled,
15628
+ width: "100%"
15629
+ }
15630
+ );
15631
+ }
15632
+ return /* @__PURE__ */ jsxRuntime.jsx(
15633
+ SDateRangePicker,
15634
+ {
15635
+ value: Array.isArray(periodValue.value) && periodValue.value.length === 2 ? periodValue.value : null,
15636
+ onValueChange: updateInputValue,
15637
+ selectable: field.selectable,
15638
+ maxRange: field.maxRange,
15639
+ disabled,
15640
+ width: "100%"
15641
+ }
15642
+ );
15643
+ };
15644
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-[var(--cmp-chipFilter-listbox-top-gap)]", children: [
15645
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-[8px]", children: presets.map((preset) => /* @__PURE__ */ jsxRuntime.jsx(
15646
+ SRadio,
15647
+ {
15648
+ val: preset.value,
15649
+ value: selectedId,
15650
+ label: preset.label,
15651
+ disabled,
15652
+ onValueChange: () => selectPreset(preset)
15653
+ },
15654
+ preset.value
15655
+ )) }),
15656
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, className: "h-px w-full", style: { background: CHIPFILTER.listboxBorder } }),
15657
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full [&>*]:w-full", children: renderInput() })
15658
+ ] });
15659
+ }
15660
+ function InlineDatePresetFilter({
15661
+ field,
15662
+ value,
15663
+ disabled,
15664
+ warning,
15665
+ showClear,
15666
+ onValueChange,
15667
+ onClear
15668
+ }) {
15669
+ const presets = (field.presets ?? []).filter((preset) => !preset.custom);
15670
+ const [selectedId, setSelectedId] = react.useState(() => matchPresetId(presets, value));
15671
+ const syncedInitialValueRef = react.useRef(!isEmptyValue(value));
15672
+ react.useEffect(() => {
15673
+ if (syncedInitialValueRef.current || isEmptyValue(value)) return;
15674
+ syncedInitialValueRef.current = true;
15675
+ setSelectedId(matchPresetId(presets, value));
15676
+ }, [value, presets]);
15677
+ const applied = !isEmptyValue(value);
15678
+ const rangeValue = Array.isArray(value) ? value : typeof value === "string" && value !== "" ? [value, value] : null;
15679
+ const [pickerOpen, setPickerOpen] = react.useState(false);
15680
+ const portalContainer = usePortalContainer();
15681
+ const selectPreset = (id) => {
15682
+ const preset = presets.find((p) => p.value === id);
15683
+ if (!preset) return;
15684
+ setSelectedId(preset.value);
15685
+ onValueChange(preset.resolve ? preset.resolve() : null);
15686
+ };
15687
+ const badge = applied && /* @__PURE__ */ jsxRuntime.jsxs(
15688
+ "span",
15689
+ {
15690
+ className: "inline-flex shrink-0 items-center gap-[4px] whitespace-nowrap border border-solid px-[var(--cmp-chipFilter-chip-paddingX)] text-[12px] leading-[20px]",
15691
+ style: {
15692
+ minHeight: CHIPFILTER.chipHeight,
15693
+ borderRadius: CHIPFILTER.chipRadius,
15694
+ borderColor: warning ? CHIPFILTER.chipWarningBorder : CHIPFILTER.chipAppliedBorder,
15695
+ background: CHIPFILTER.chipAppliedBg,
15696
+ color: CHIPFILTER.chipAppliedLabel,
15697
+ cursor: rangeValue ? "pointer" : void 0
15698
+ },
15699
+ children: [
15700
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-bold", children: [
15701
+ field.label,
15702
+ ":"
15703
+ ] }),
15704
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold", style: { color: CHIPFILTER.chipAppliedValue }, children: displayValue(field, value) }),
15705
+ !disabled && showClear && /* @__PURE__ */ jsxRuntime.jsx(
15706
+ "span",
15707
+ {
15708
+ role: "button",
15709
+ tabIndex: 0,
15710
+ "aria-label": `${field.label} \uD544\uD130 \uC9C0\uC6B0\uAE30`,
15711
+ className: "inline-flex size-[16px] items-center justify-center rounded-[var(--cmp-ghostButton-radius-sm)]",
15712
+ onClick: (event) => {
15713
+ event.stopPropagation();
15714
+ onClear();
15715
+ },
15716
+ onKeyDown: (event) => {
15717
+ if (event.key !== "Enter" && event.key !== " ") return;
15718
+ event.preventDefault();
15719
+ event.stopPropagation();
15720
+ onClear();
15721
+ },
15722
+ children: /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "close", size: 12, color: "var(--cmp-ghostButton-default-content)" })
15723
+ }
15724
+ )
15725
+ ]
15726
+ }
15727
+ );
15728
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-[8px]", children: [
15729
+ /* @__PURE__ */ jsxRuntime.jsx(
15730
+ SRadioButton,
15731
+ {
15732
+ size: "xs",
15733
+ options: presets.map((preset) => ({ value: preset.value, label: preset.label })),
15734
+ value: selectedId,
15735
+ disabled,
15736
+ onValueChange: (next) => selectPreset(String(next))
15737
+ }
15738
+ ),
15739
+ applied && (rangeValue ? (
15740
+ // 뱃지 모양은 그대로 두고, 눌렀을 때 캘린더가 열려 기간을 직접 다시 고를 수 있게 한다.
15741
+ // 직접 고르면 프리셋 선택은 해제된다.
15742
+ /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open: pickerOpen, onOpenChange: setPickerOpen, children: [
15743
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
15744
+ "button",
15745
+ {
15746
+ type: "button",
15747
+ disabled,
15748
+ className: "border-none bg-transparent p-0 outline-none disabled:cursor-not-allowed",
15749
+ children: badge
15750
+ }
15751
+ ) }),
15752
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
15753
+ RPopover10__namespace.Content,
15754
+ {
15755
+ side: "bottom",
15756
+ align: "start",
15757
+ sideOffset: 4,
15758
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
15759
+ children: /* @__PURE__ */ jsxRuntime.jsx(
15760
+ RangeCalendar,
15761
+ {
15762
+ value: rangeValue,
15763
+ selectable: field.selectable,
15764
+ maxRange: field.maxRange,
15765
+ onSelect: (range) => {
15766
+ setSelectedId(null);
15767
+ onValueChange(range);
15768
+ setPickerOpen(false);
15769
+ }
15770
+ }
15771
+ )
15772
+ }
15773
+ ) })
15774
+ ] })
15775
+ ) : badge)
15776
+ ] });
15777
+ }
15778
+ var MATCH_MODE_OPTIONS = [
15779
+ { value: "contains", label: "\uD3EC\uD568" },
15780
+ { value: "exact", label: "\uC77C\uCE58" }
15781
+ ];
15782
+ var EXCLUDE_MATCH_MODE_OPTION = {
15783
+ value: "excludes",
15784
+ label: "\uBBF8\uD3EC\uD568"
15785
+ };
15786
+ function KeywordEditor({
15787
+ field,
15788
+ value,
15789
+ disabled,
15790
+ onValueChange
15791
+ }) {
15792
+ const entries3 = keywordEntries(value);
15793
+ const [draft, setDraft] = react.useState("");
15794
+ const [mode, setMode] = react.useState(
15795
+ isKeywordValue(value) ? value.mode : "contains"
15796
+ );
15797
+ const emit3 = (nextEntries) => {
15798
+ onValueChange(field.matchModes ? { keywords: nextEntries, mode } : nextEntries);
15799
+ };
15800
+ const addEntry = (raw) => {
15801
+ const next = raw.trim();
15802
+ if (next === "" || entries3.includes(next)) return;
15803
+ emit3([...entries3, next]);
15804
+ setDraft("");
15805
+ };
15806
+ const removeEntry = (entry) => {
15807
+ emit3(entries3.filter((e) => e !== entry));
15808
+ };
15809
+ const onKeyDown = (event) => {
15810
+ if (event.key === "Enter") {
15811
+ if (event.nativeEvent.isComposing) return;
15812
+ event.preventDefault();
15813
+ addEntry(draft);
15814
+ }
15815
+ };
15816
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15817
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-[var(--cmp-chipFilter-listbox-top-gap)] px-[var(--cmp-chipFilter-listbox-top-paddingX)] py-[var(--cmp-chipFilter-listbox-top-paddingY)]", children: [
15818
+ field.matchModes && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
15819
+ /* @__PURE__ */ jsxRuntime.jsx(
15820
+ "span",
15821
+ {
15822
+ className: "shrink-0 whitespace-nowrap text-[12px] font-medium leading-[20px]",
15823
+ style: { color: CHIPFILTER.listboxLabel },
15824
+ children: "\uAC80\uC0C9 \uC870\uAC74 :"
15825
+ }
15826
+ ),
15827
+ /* @__PURE__ */ jsxRuntime.jsx(
15828
+ SRadioButton,
15829
+ {
15830
+ size: "xs",
15831
+ style: { width: 150 },
15832
+ options: field.excludeMode ? [...MATCH_MODE_OPTIONS, EXCLUDE_MATCH_MODE_OPTION] : MATCH_MODE_OPTIONS,
15833
+ value: mode,
15834
+ disabled,
15835
+ onValueChange: (next) => setMode(next)
15836
+ }
15837
+ )
15838
+ ] }),
15839
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-[6px] rounded-[4px] border border-solid border-[color:var(--sys-color-field-border-default)] bg-[var(--sys-color-field-bg-default)] px-[8px] py-[4px] focus-within:border-[color:var(--sys-color-field-border-focus)]", children: [
15840
+ /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "search", size: 16, color: "var(--sys-color-field-icon-default)" }),
15841
+ /* @__PURE__ */ jsxRuntime.jsx(
15842
+ "input",
15843
+ {
15844
+ value: draft,
15845
+ disabled,
15846
+ placeholder: field.placeholder ?? "\uC785\uB825 \uD6C4 Enter\uB85C \uCD94\uAC00",
15847
+ onChange: (event) => setDraft(event.target.value),
15848
+ onKeyDown,
15849
+ className: "min-w-0 flex-1 border-none bg-transparent text-[12px] leading-[20px] text-[color:var(--sys-color-field-text-default)] outline-none placeholder:text-[color:var(--sys-color-field-text-placeholder)] disabled:cursor-not-allowed"
15850
+ }
15851
+ )
15852
+ ] })
15853
+ ] }),
15854
+ entries3.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
15855
+ "div",
15856
+ {
15857
+ className: "max-h-[220px] overflow-auto border-t border-solid py-[var(--cmp-chipFilter-listbox-list-paddingY)]",
15858
+ style: { borderColor: CHIPFILTER.listboxBorder },
15859
+ children: entries3.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
15860
+ KeywordEntryRow,
15861
+ {
15862
+ label: optionLabel(field, entry),
15863
+ disabled,
15864
+ onRemove: () => removeEntry(entry)
15865
+ },
15866
+ entry
15867
+ ))
15868
+ }
15869
+ )
15870
+ ] });
15871
+ }
15872
+ function KeywordEntryRow({
15873
+ label,
15874
+ disabled,
15875
+ onRemove
15876
+ }) {
15877
+ return /* @__PURE__ */ jsxRuntime.jsxs(
15878
+ "div",
15879
+ {
15880
+ className: "group flex w-full items-center justify-between gap-[8px] text-[12px] leading-[20px]",
15881
+ style: {
15882
+ paddingTop: CHIPFILTER.itemPaddingY,
15883
+ paddingBottom: CHIPFILTER.itemPaddingY,
15884
+ paddingLeft: CHIPFILTER.itemPaddingLeft,
15885
+ paddingRight: CHIPFILTER.itemPaddingRight,
15886
+ color: disabled ? CHIPFILTER.chipDisabledText : CHIPFILTER.itemTextDefault
15887
+ },
15888
+ children: [
15889
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("min-w-0 truncate", !disabled && "group-hover:font-bold"), children: label }),
15890
+ !disabled && /* @__PURE__ */ jsxRuntime.jsx(
15891
+ "button",
15892
+ {
15893
+ type: "button",
15894
+ "aria-label": `${label} \uC0AD\uC81C`,
15895
+ className: "invisible inline-flex size-[16px] shrink-0 cursor-pointer items-center justify-center group-hover:visible",
15896
+ onClick: onRemove,
15897
+ children: /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "close", size: 12, color: "var(--cmp-ghostButton-default-content)" })
15898
+ }
15899
+ )
15900
+ ]
15901
+ }
15902
+ );
15903
+ }
15904
+ var OPTION_SEARCH_THRESHOLD = 11;
15905
+ function OptionList({
15906
+ field,
15907
+ value,
15908
+ multiple,
15909
+ disabled,
15910
+ onValueChange
15911
+ }) {
15912
+ const selected = multiple ? toArrayValue(value) : [];
15913
+ const singleValue = !multiple && (typeof value === "string" || typeof value === "number") ? value : null;
15914
+ const [search, setSearch] = react.useState("");
15915
+ const options = field.options ?? [];
15916
+ const showSearch = options.length >= OPTION_SEARCH_THRESHOLD;
15917
+ const filteredOptions = react.useMemo(() => {
15918
+ if (!showSearch || search.trim() === "") return options;
15919
+ const query = search.trim().toLowerCase();
15920
+ return options.filter((option) => option.label.toLowerCase().includes(query));
15921
+ }, [options, search, showSearch]);
15922
+ if (options.length === 0) return /* @__PURE__ */ jsxRuntime.jsx(EmptyItem, { children: "\uD45C\uC2DC\uD560 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." });
15923
+ return /* @__PURE__ */ jsxRuntime.jsxs(
15924
+ "div",
15925
+ {
15926
+ className: cn(
15927
+ "max-h-[240px] overflow-auto",
15928
+ !showSearch && "pt-[var(--cmp-chipFilter-listbox-list-paddingY)]",
15929
+ "pb-[var(--cmp-chipFilter-listbox-list-paddingY)]"
15930
+ ),
15931
+ children: [
15932
+ showSearch && /* @__PURE__ */ jsxRuntime.jsx(
15933
+ "div",
15934
+ {
15935
+ className: "sticky top-0 z-[1] border-b border-solid px-[8px] pb-[4px] pt-[var(--cmp-chipFilter-listbox-list-paddingY)]",
15936
+ style: { background: CHIPFILTER.listboxBg, borderColor: CHIPFILTER.listboxBorder },
15937
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-[6px] rounded-[4px] border border-solid border-[color:var(--sys-color-field-border-default)] bg-[var(--sys-color-field-bg-default)] px-[8px] py-[4px] focus-within:border-[color:var(--sys-color-field-border-focus)]", children: [
15938
+ /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "search", size: 16, color: "var(--sys-color-field-icon-default)" }),
15939
+ /* @__PURE__ */ jsxRuntime.jsx(
15940
+ "input",
15941
+ {
15942
+ value: search,
15943
+ disabled,
15944
+ placeholder: "\uAC80\uC0C9",
15945
+ onChange: (event) => setSearch(event.target.value),
15946
+ className: "min-w-0 flex-1 border-none bg-transparent text-[12px] leading-[20px] text-[color:var(--sys-color-field-text-default)] outline-none placeholder:text-[color:var(--sys-color-field-text-placeholder)] disabled:cursor-not-allowed"
15947
+ }
15948
+ ),
15949
+ search !== "" && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
15950
+ SGhostButton,
15951
+ {
15952
+ icon: "close",
15953
+ size: "xxs",
15954
+ ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
15955
+ className: "flex-shrink-0",
15956
+ onClick: () => setSearch("")
15957
+ }
15958
+ )
15959
+ ] })
15960
+ }
15961
+ ),
15962
+ showSearch && filteredOptions.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(EmptyItem, { children: "\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }),
15963
+ filteredOptions.map((option) => {
15964
+ const checked = multiple ? selected.includes(option.value) : singleValue === option.value;
15965
+ return /* @__PURE__ */ jsxRuntime.jsxs(
15966
+ MenuItem,
15967
+ {
15968
+ selected: checked,
15969
+ disabled: disabled || option.disabled,
15970
+ multi: multiple,
15971
+ onClick: () => {
15972
+ if (multiple) {
15973
+ const next = checked ? selected.filter((v) => v !== option.value) : [...selected, option.value];
15974
+ onValueChange(next);
15975
+ } else {
15976
+ onValueChange(option.value);
15977
+ }
15978
+ },
15979
+ children: [
15980
+ multiple && /* @__PURE__ */ jsxRuntime.jsx(OptionCheckbox, { checked }),
15981
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: option.label })
15982
+ ]
15983
+ },
15984
+ String(option.value)
15985
+ );
15986
+ })
15987
+ ]
15988
+ }
15989
+ );
15990
+ }
15991
+ function OptionCheckbox({ checked }) {
15992
+ return /* @__PURE__ */ jsxRuntime.jsx(
15993
+ "span",
15994
+ {
15995
+ className: cn(
15996
+ "inline-flex size-[16px] shrink-0 items-center justify-center rounded-[2px] border border-solid",
15997
+ checked ? "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-[var(--cmp-checkbox-unchecked-bg-default)]"
15998
+ ),
15999
+ children: checked && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "checkboxCheck", size: 12, color: "var(--cmp-checkbox-checked-icon-default)" })
16000
+ }
16001
+ );
16002
+ }
16003
+ function ListBox({ children }) {
16004
+ return /* @__PURE__ */ jsxRuntime.jsx(
16005
+ "div",
16006
+ {
16007
+ className: "overflow-hidden border border-solid shadow-[var(--shadow-overlay)]",
16008
+ style: {
16009
+ borderRadius: CHIPFILTER.listboxRadius,
16010
+ background: CHIPFILTER.listboxBg,
16011
+ borderColor: CHIPFILTER.listboxBorder
16012
+ },
16013
+ children
16014
+ }
16015
+ );
16016
+ }
16017
+ function MenuItem({
16018
+ children,
16019
+ selected,
16020
+ disabled,
16021
+ multi,
16022
+ onMouseEnter,
16023
+ onClick
16024
+ }) {
16025
+ return /* @__PURE__ */ jsxRuntime.jsx(
16026
+ "button",
16027
+ {
16028
+ type: "button",
16029
+ disabled,
16030
+ onMouseEnter,
16031
+ onClick,
16032
+ className: cn(
16033
+ "flex w-full min-w-0 cursor-pointer items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none disabled:cursor-not-allowed",
16034
+ selected ? "font-bold" : "font-normal",
16035
+ !disabled && "hover:font-bold"
16036
+ ),
16037
+ style: {
16038
+ paddingTop: CHIPFILTER.itemPaddingY,
16039
+ paddingBottom: CHIPFILTER.itemPaddingY,
16040
+ paddingLeft: multi ? CHIPFILTER.itemMultiPaddingX : CHIPFILTER.itemPaddingLeft,
16041
+ paddingRight: multi ? CHIPFILTER.itemMultiPaddingX : CHIPFILTER.itemPaddingRight,
16042
+ color: disabled ? CHIPFILTER.chipDisabledText : selected ? CHIPFILTER.itemTextSelected : CHIPFILTER.itemTextDefault
16043
+ },
16044
+ children
16045
+ }
16046
+ );
16047
+ }
16048
+ function EmptyItem({ children }) {
16049
+ return /* @__PURE__ */ jsxRuntime.jsx(
16050
+ "div",
16051
+ {
16052
+ className: "text-[12px] leading-[20px]",
16053
+ style: {
16054
+ paddingTop: CHIPFILTER.itemPaddingY,
16055
+ paddingBottom: CHIPFILTER.itemPaddingY,
16056
+ paddingLeft: CHIPFILTER.itemPaddingLeft,
16057
+ paddingRight: CHIPFILTER.itemPaddingRight,
16058
+ color: CHIPFILTER.chipDisabledText
16059
+ },
16060
+ children
16061
+ }
16062
+ );
16063
+ }
13500
16064
  var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
13501
16065
  values = [],
13502
16066
  onValueChange,
@@ -13935,7 +16499,7 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
13935
16499
  }
13936
16500
  );
13937
16501
  });
13938
- var FONT2 = {
16502
+ var FONT3 = {
13939
16503
  sm: {
13940
16504
  fontSize: 12,
13941
16505
  paddingX: "var(--cmp-textinput-sm-paddingX)",
@@ -13983,7 +16547,7 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
13983
16547
  className,
13984
16548
  style
13985
16549
  }, ref) {
13986
- const f = FONT2[size];
16550
+ const f = FONT3[size];
13987
16551
  const [ruleError, setRuleError] = react.useState("");
13988
16552
  const hasValue = value !== void 0 && value !== null && String(value) !== "";
13989
16553
  useFormField({
@@ -13997,6 +16561,10 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
13997
16561
  });
13998
16562
  const resolvedError = error || ruleError !== "";
13999
16563
  const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
16564
+ const valueTooltip = useTruncatedValueTooltip({
16565
+ enabled: readOnly && !disabled,
16566
+ text: String(value ?? "")
16567
+ });
14000
16568
  return /* @__PURE__ */ jsxRuntime.jsx(
14001
16569
  SField,
14002
16570
  {
@@ -14033,7 +16601,7 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
14033
16601
  /* @__PURE__ */ jsxRuntime.jsx(
14034
16602
  "input",
14035
16603
  {
14036
- ref,
16604
+ ref: mergeRefs(ref, valueTooltip.valueRef),
14037
16605
  name,
14038
16606
  type: "text",
14039
16607
  value: value ?? "",
@@ -14059,9 +16627,12 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
14059
16627
  fontSize: f.fontSize,
14060
16628
  color: disabled ? "var(--cmp-textinput-text-disabled)" : "var(--cmp-textinput-text-default)",
14061
16629
  ...inputStyle
14062
- }
16630
+ },
16631
+ onMouseEnter: valueTooltip.onMouseEnter,
16632
+ onMouseLeave: valueTooltip.onMouseLeave
14063
16633
  }
14064
16634
  ),
16635
+ valueTooltip.tooltip,
14065
16636
  suffix,
14066
16637
  clearable && hasValue && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
14067
16638
  SGhostButton,
@@ -14097,7 +16668,9 @@ var TIMEPICKER_SIZE_CONFIG = {
14097
16668
  fontSize: 12,
14098
16669
  lineHeight: 20,
14099
16670
  minWidth: 80,
14100
- fieldMinWidth: 128
16671
+ fieldMinWidth: 128,
16672
+ // 상한 등급 md
16673
+ fieldMaxWidth: 160
14101
16674
  },
14102
16675
  md: {
14103
16676
  height: V3("md-height"),
@@ -14109,7 +16682,9 @@ var TIMEPICKER_SIZE_CONFIG = {
14109
16682
  fontSize: 14,
14110
16683
  lineHeight: 24,
14111
16684
  minWidth: 96,
14112
- fieldMinWidth: 160
16685
+ fieldMinWidth: 160,
16686
+ // 상한 등급 lg
16687
+ fieldMaxWidth: 240
14113
16688
  }
14114
16689
  };
14115
16690
  function clampTimePart(value, min, max) {
@@ -14220,7 +16795,7 @@ function TimeSelector({
14220
16795
  const renderColumn = (type) => {
14221
16796
  const isHour = type === "hour";
14222
16797
  const val = isHour ? displayHour : minute;
14223
- const displayValue = editingPart === type ? inputDraft[type] ?? "" : String(val).padStart(2, "0");
16798
+ const displayValue2 = editingPart === type ? inputDraft[type] ?? "" : String(val).padStart(2, "0");
14224
16799
  return /* @__PURE__ */ jsxRuntime.jsxs(
14225
16800
  "div",
14226
16801
  {
@@ -14241,7 +16816,7 @@ function TimeSelector({
14241
16816
  {
14242
16817
  "aria-label": isHour ? "\uC2DC" : "\uBD84",
14243
16818
  inputMode: "numeric",
14244
- value: displayValue,
16819
+ value: displayValue2,
14245
16820
  placeholder: "-",
14246
16821
  onFocus: () => beginEdit(type, val),
14247
16822
  onBlur: () => endEdit(type),
@@ -14433,11 +17008,12 @@ function STimePicker({
14433
17008
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
14434
17009
  width,
14435
17010
  minWidth: dims.fieldMinWidth,
17011
+ maxWidth: dims.fieldMaxWidth,
14436
17012
  disabled,
14437
17013
  className,
14438
17014
  style,
14439
- children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: commitOpen, children: [
14440
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
17015
+ children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open, onOpenChange: commitOpen, children: [
17016
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
14441
17017
  "div",
14442
17018
  {
14443
17019
  ref: triggerRef,
@@ -14497,14 +17073,17 @@ function STimePicker({
14497
17073
  ]
14498
17074
  }
14499
17075
  ) }),
14500
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
14501
- RPopover2__namespace.Content,
17076
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
17077
+ RPopover10__namespace.Content,
14502
17078
  {
14503
17079
  side: "bottom",
14504
17080
  align: "start",
14505
17081
  sideOffset: 4,
14506
17082
  alignOffset: popoverAlignOffset,
14507
- className: cn("z-50", FLOATING_SLIDE_ANIM),
17083
+ className: cn(
17084
+ "z-50 max-w-[var(--radix-popover-content-available-width)]",
17085
+ FLOATING_SLIDE_ANIM
17086
+ ),
14508
17087
  children: /* @__PURE__ */ jsxRuntime.jsx(
14509
17088
  TimeSelector,
14510
17089
  {
@@ -14546,6 +17125,10 @@ var MIDDAY_FIELD_MIN_WIDTH = {
14546
17125
  sm: 178,
14547
17126
  md: 216
14548
17127
  };
17128
+ var MIDDAY_FIELD_MAX_WIDTH = {
17129
+ sm: 240,
17130
+ md: 240
17131
+ };
14549
17132
  function RangeTimeSelector({
14550
17133
  value,
14551
17134
  useMeridiem,
@@ -14663,7 +17246,7 @@ function RangeTimeSelector({
14663
17246
  const { minute } = getParts(side);
14664
17247
  const val = isHour ? getDisplayHour(side) : minute;
14665
17248
  const currentEditingKey = getEditingKey(side, kind);
14666
- const displayValue = editingKey === currentEditingKey ? inputDraft[currentEditingKey] ?? "" : String(val).padStart(2, "0");
17249
+ const displayValue2 = editingKey === currentEditingKey ? inputDraft[currentEditingKey] ?? "" : String(val).padStart(2, "0");
14667
17250
  return /* @__PURE__ */ jsxRuntime.jsxs(
14668
17251
  "div",
14669
17252
  {
@@ -14684,7 +17267,7 @@ function RangeTimeSelector({
14684
17267
  {
14685
17268
  "aria-label": `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"}`,
14686
17269
  inputMode: "numeric",
14687
- value: displayValue,
17270
+ value: displayValue2,
14688
17271
  placeholder: "-",
14689
17272
  onFocus: () => beginEdit(side, kind, val),
14690
17273
  onBlur: () => endEdit(side, kind),
@@ -14892,11 +17475,12 @@ function STimeRangePicker({
14892
17475
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
14893
17476
  width,
14894
17477
  minWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MIN_WIDTH[size] : dims.fieldMinWidth,
17478
+ maxWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MAX_WIDTH[size] : dims.fieldMaxWidth,
14895
17479
  disabled,
14896
17480
  className,
14897
17481
  style,
14898
- children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: commitOpen, children: [
14899
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
17482
+ children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open, onOpenChange: commitOpen, children: [
17483
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
14900
17484
  "div",
14901
17485
  {
14902
17486
  ref: triggerRef,
@@ -14956,14 +17540,17 @@ function STimeRangePicker({
14956
17540
  ]
14957
17541
  }
14958
17542
  ) }),
14959
- /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
14960
- RPopover2__namespace.Content,
17543
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
17544
+ RPopover10__namespace.Content,
14961
17545
  {
14962
17546
  side: "bottom",
14963
17547
  align: "start",
14964
17548
  sideOffset: 4,
14965
17549
  alignOffset: popoverAlignOffset,
14966
- className: cn("z-50", FLOATING_SLIDE_ANIM),
17550
+ className: cn(
17551
+ "z-50 max-w-[var(--radix-popover-content-available-width)]",
17552
+ FLOATING_SLIDE_ANIM
17553
+ ),
14967
17554
  children: /* @__PURE__ */ jsxRuntime.jsx(
14968
17555
  RangeTimeSelector,
14969
17556
  {
@@ -14995,7 +17582,10 @@ exports.GNB_MENU_WIDTH = GNB_MENU_WIDTH;
14995
17582
  exports.GNB_RAIL = GNB_RAIL;
14996
17583
  exports.GNB_RAIL_WIDTH = GNB_RAIL_WIDTH;
14997
17584
  exports.ICONS = ICONS;
17585
+ exports.IMAGE_FITS = IMAGE_FITS;
14998
17586
  exports.PAGE_BACKGROUNDS = PAGE_BACKGROUNDS;
17587
+ exports.PAGE_CONTENT_HEIGHTS = PAGE_CONTENT_HEIGHTS;
17588
+ exports.RangeCalendar = RangeCalendar;
14999
17589
  exports.SActionModal = SActionModal;
15000
17590
  exports.SBadge = SBadge;
15001
17591
  exports.SBarcodeInput = SBarcodeInput;
@@ -15006,10 +17596,13 @@ exports.SCallout = SCallout;
15006
17596
  exports.SCard = SCard;
15007
17597
  exports.SCheckbox = SCheckbox;
15008
17598
  exports.SChip = SChip2;
17599
+ exports.SChipFilter = SChipFilter;
15009
17600
  exports.SChipInput = SChipInput;
15010
17601
  exports.SCircleProgress = SCircleProgress;
15011
17602
  exports.SConfirmModal = SConfirmModal;
15012
17603
  exports.SDatePicker = SDatePicker;
17604
+ exports.SDatePickerMonthListbox = SDatePickerMonthListbox;
17605
+ exports.SDatePickerYearListbox = SDatePickerYearListbox;
15013
17606
  exports.SDateRangePicker = SDateRangePicker;
15014
17607
  exports.SDivider = SDivider;
15015
17608
  exports.SDraggableGroup = SDraggableGroup;
@@ -15027,6 +17620,7 @@ exports.SGhostButton = SGhostButton;
15027
17620
  exports.SGnb = SGnb;
15028
17621
  exports.SGuide = SGuide;
15029
17622
  exports.SIcon = SIcon;
17623
+ exports.SImage = SImage;
15030
17624
  exports.SInput = SInput;
15031
17625
  exports.SKeyValueTable = SKeyValueTable;
15032
17626
  exports.SLAYOUT_MIN_WIDTH = SLAYOUT_MIN_WIDTH;
@@ -15051,6 +17645,7 @@ exports.SRadioButton = SRadioButton;
15051
17645
  exports.SRadioGroup = SRadioGroup;
15052
17646
  exports.SSPLITTER_UNITS = SSPLITTER_UNITS;
15053
17647
  exports.SScrollArea = SScrollArea;
17648
+ exports.SSearchInput = SSearchInput;
15054
17649
  exports.SSectionHeaderCard = SSectionHeaderCard2;
15055
17650
  exports.SSelect = SSelect2;
15056
17651
  exports.SSplitter = SSplitter2;