sellmate-design-system-react 9.0.0-beta.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 (59) hide show
  1. package/AGENTS.md +153 -45
  2. package/README.md +63 -0
  3. package/dist/components/SChipFilter/README.md +2 -1
  4. package/dist/components/SChipFilter/SChipFilter.d.ts +13 -6
  5. package/dist/components/SCircleProgress/README.md +3 -0
  6. package/dist/components/SCircleProgress/SCircleProgress.d.ts +7 -1
  7. package/dist/components/SDatePicker/README.md +41 -3
  8. package/dist/components/SDatePicker/SDatePicker.d.ts +36 -3
  9. package/dist/components/SDatePicker/index.d.ts +1 -1
  10. package/dist/components/SField/README.md +3 -0
  11. package/dist/components/SField/SField.d.ts +12 -0
  12. package/dist/components/SGhostButton/README.md +2 -0
  13. package/dist/components/SGnb/gnb.config.d.ts +2 -0
  14. package/dist/components/SIcon/README.md +4 -0
  15. package/dist/components/SImage/README.md +44 -0
  16. package/dist/components/SImage/SImage.d.ts +41 -0
  17. package/dist/components/SImage/index.d.ts +1 -0
  18. package/dist/components/SKeyValueTable/README.md +1 -0
  19. package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +8 -1
  20. package/dist/components/SList/README.md +0 -1
  21. package/dist/components/SList/SList.d.ts +0 -2
  22. package/dist/components/SPage/README.md +2 -0
  23. package/dist/components/SPage/SPage.d.ts +17 -1
  24. package/dist/components/SPage/index.d.ts +1 -1
  25. package/dist/components/SPage/page.config.d.ts +8 -0
  26. package/dist/components/SSearchInput/README.md +48 -0
  27. package/dist/components/SSearchInput/SSearchInput.d.ts +46 -0
  28. package/dist/components/SSearchInput/index.d.ts +1 -0
  29. package/dist/components/SSectionHeaderCard/README.md +10 -23
  30. package/dist/components/SSectionHeaderCard/SSectionHeaderCard.d.ts +21 -14
  31. package/dist/components/SSectionHeaderCard/index.d.ts +1 -1
  32. package/dist/components/SSelect/README.md +2 -0
  33. package/dist/components/STable/README.md +1 -0
  34. package/dist/components/STable/STable.d.ts +20 -0
  35. package/dist/components/STabs/README.md +0 -1
  36. package/dist/components/STabs/STabs.d.ts +2 -4
  37. package/dist/components/STabs/index.d.ts +1 -1
  38. package/dist/components/STabs/tabs.config.d.ts +3 -4
  39. package/dist/components/STimePicker/timepicker.config.d.ts +5 -0
  40. package/dist/components/SToggle/README.md +1 -0
  41. package/dist/components/SToggle/SToggle.d.ts +4 -1
  42. package/dist/components/STree/README.md +3 -0
  43. package/dist/components/STree/STree.d.ts +8 -0
  44. package/dist/components/STree/index.d.ts +1 -1
  45. package/dist/index.cjs +1545 -359
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +1540 -360
  49. package/dist/index.js.map +1 -1
  50. package/dist/lib/truncated-value-tooltip.d.ts +18 -0
  51. package/dist/llms-full.txt +300 -69
  52. package/dist/llms.txt +157 -48
  53. package/dist/styles.css +201 -32
  54. package/dist/theme.css +34 -11
  55. package/eslint/index.mjs +5 -0
  56. package/eslint/rules/field-width-grade.d.mts +41 -0
  57. package/eslint/rules/field-width-grade.mjs +294 -0
  58. package/eslint/scale.gen.mjs +1 -1
  59. package/package.json +1 -1
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 RPopover9 = 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 RPopover9__namespace = /*#__PURE__*/_interopNamespace(RPopover9);
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);
@@ -3141,6 +3141,209 @@ var SIcon = /* @__PURE__ */ react.forwardRef(function SIcon2({ name, size = 24,
3141
3141
  }
3142
3142
  );
3143
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
+ });
3144
3347
  var BADGE_COLORS = [
3145
3348
  "red",
3146
3349
  "orange",
@@ -3242,7 +3445,7 @@ var SDraggableItemView = /* @__PURE__ */ react.forwardRef(
3242
3445
  !disabled && !selected && !dragging && "hover:bg-(--cmp-listDraggableItem-bg-hover)",
3243
3446
  hasSupportingText && supportingTextPosition === "bottom" ? dense ? "min-h-[52px]" : "min-h-[60px]" : dense ? "h-[32px]" : "h-[40px]",
3244
3447
  paddingYClass,
3245
- // 항목 사이 구분선은 리스트가 그린다 — SDraggableList 의 separator (SList.tsx)
3448
+ // 항목 사이 구분선은 리스트가 그린다.
3246
3449
  bordered && cn(
3247
3450
  "rounded-(--cmp-listDraggableItem-radius-bordered) border border-(--cmp-listDraggableItem-border-default)",
3248
3451
  !disabled && !selected && !dragging && "hover:border-(--cmp-listDraggableItem-bordered-border-hover)",
@@ -3450,7 +3653,7 @@ var SDraggableItemImpl = /* @__PURE__ */ react.forwardRef(function SDraggableIte
3450
3653
  ] });
3451
3654
  });
3452
3655
  var SDraggableItem2 = /* @__PURE__ */ withDisplayName(SDraggableItemImpl, "SDraggableItem");
3453
- var SListImpl = /* @__PURE__ */ react.forwardRef(function SList({ className, children, useGap = false, usePadding = false, separator = false, ...rest }, ref) {
3656
+ var SListImpl = /* @__PURE__ */ react.forwardRef(function SList({ className, children, useGap = false, usePadding = false, ...rest }, ref) {
3454
3657
  return /* @__PURE__ */ jsxRuntime.jsx(
3455
3658
  "div",
3456
3659
  {
@@ -3458,7 +3661,7 @@ var SListImpl = /* @__PURE__ */ react.forwardRef(function SList({ className, chi
3458
3661
  className: cn(
3459
3662
  useGap && "flex flex-col gap-(--cmp-list-group-gap)",
3460
3663
  usePadding && "p-(--cmp-list-group-padding)",
3461
- separator && "[&>*]:border-b [&>*]:border-(--cmp-listItem-border-default)",
3664
+ "[&>*]:border-b [&>*]:border-(--cmp-listItem-border-default)",
3462
3665
  className
3463
3666
  ),
3464
3667
  ...rest,
@@ -3830,7 +4033,7 @@ var SExpansionItem = /* @__PURE__ */ react.forwardRef(
3830
4033
  ref,
3831
4034
  className: cn(
3832
4035
  "group/expansion-item relative overflow-hidden bg-(--cmp-listExpansionItem-title-bg-default)",
3833
- // 항목 사이 구분선은 리스트가 그린다 — SList/SExpansionList 의 separator (SList.tsx)
4036
+ // 항목 사이 구분선은 리스트가 그린다.
3834
4037
  // 보더 색은 시맨틱 토큰(border-default → hover strong → 펼침 accentLight)을 쓴다.
3835
4038
  // TODO: expansionItem/bordered/radius 토큰 생성 후 rounded 고정값도 토큰으로 교체한다.
3836
4039
  bordered && "rounded-[8px] border border-border-default hover:border-border-strong [&:has(>button:hover)]:border-border-strong",
@@ -4382,8 +4585,8 @@ function SGhostButton({
4382
4585
  }
4383
4586
  );
4384
4587
  if (!hasTooltip) return btn;
4385
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open, onOpenChange: setOpen, children: [
4386
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__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(
4387
4590
  "span",
4388
4591
  {
4389
4592
  className: cn("inline-flex", anchorClassName),
@@ -4395,8 +4598,8 @@ function SGhostButton({
4395
4598
  children: btn
4396
4599
  }
4397
4600
  ) }),
4398
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
4399
- RPopover9__namespace.Content,
4601
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
4602
+ RPopover10__namespace.Content,
4400
4603
  {
4401
4604
  side: "top",
4402
4605
  sideOffset: 4,
@@ -4410,6 +4613,18 @@ function SGhostButton({
4410
4613
  ) })
4411
4614
  ] });
4412
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
+ };
4413
4628
  function collectIds(nodes) {
4414
4629
  return nodes.flatMap((node) => [node.id, ...collectIds(node.children ?? [])]);
4415
4630
  }
@@ -4420,18 +4635,19 @@ function collectSelectableIds(node) {
4420
4635
  function toSelectedSet(value) {
4421
4636
  return new Set(value ?? []);
4422
4637
  }
4423
- function getSelectionState(node, selectedSet) {
4424
- if (selectedSet.has(node.id)) {
4425
- 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 };
4426
4642
  }
4427
4643
  const selectableChildren = (node.children ?? []).filter(
4428
4644
  (child) => collectSelectableIds(child).length > 0
4429
4645
  );
4430
4646
  if (selectableChildren.length === 0) {
4431
- return { selected: false, indeterminate: false };
4647
+ return { selected: selectedSet.has(node.id), indeterminate: false };
4432
4648
  }
4433
4649
  const childStates = selectableChildren.map(
4434
- (child) => getSelectionState(child, selectedSet)
4650
+ (child) => getSelectionState(child, selectedSet, cascadeSelection)
4435
4651
  );
4436
4652
  const selectedCount = childStates.filter((state2) => state2.selected).length;
4437
4653
  return {
@@ -4441,6 +4657,7 @@ function getSelectionState(node, selectedSet) {
4441
4657
  }
4442
4658
  function TreeConnector({
4443
4659
  guideline,
4660
+ size,
4444
4661
  depth,
4445
4662
  hasChildren,
4446
4663
  isLast,
@@ -4448,6 +4665,8 @@ function TreeConnector({
4448
4665
  }) {
4449
4666
  if (depth <= 1) return null;
4450
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]";
4451
4670
  return /* @__PURE__ */ jsxRuntime.jsxs("span", { "aria-hidden": "true", className: "flex self-stretch", children: [
4452
4671
  ancestorConnectors.map((last, index) => /* @__PURE__ */ jsxRuntime.jsx(
4453
4672
  "span",
@@ -4461,13 +4680,22 @@ function TreeConnector({
4461
4680
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative block w-[20px] shrink-0", children: [
4462
4681
  guideline && !isLast && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute bottom-0 left-[6px] top-[-1px] w-px bg-(--cmp-tree-connector-line-color)" }),
4463
4682
  guideline && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4464
- /* @__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
+ ),
4465
4692
  /* @__PURE__ */ jsxRuntime.jsx(
4466
4693
  "span",
4467
4694
  {
4468
4695
  "data-tree-connector-branch": hasChildren ? "parent" : "leaf",
4469
4696
  className: cn(
4470
- "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,
4471
4699
  hasChildren ? "w-[14px]" : "w-[calc(14px+var(--cmp-tree-item-arrow-icon))]"
4472
4700
  )
4473
4701
  }
@@ -4476,6 +4704,44 @@ function TreeConnector({
4476
4704
  ] })
4477
4705
  ] });
4478
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
+ }
4479
4745
  var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4480
4746
  node,
4481
4747
  depth = 1,
@@ -4487,6 +4753,7 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4487
4753
  border = true,
4488
4754
  indeterminate = false,
4489
4755
  selectable = true,
4756
+ size = "sm",
4490
4757
  leading,
4491
4758
  trailing,
4492
4759
  onToggle,
@@ -4498,6 +4765,7 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4498
4765
  const renderState = {
4499
4766
  node,
4500
4767
  depth,
4768
+ size,
4501
4769
  expanded,
4502
4770
  selected,
4503
4771
  indeterminate,
@@ -4507,6 +4775,8 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4507
4775
  const leadingNode = typeof leading === "function" ? leading(renderState) : leading;
4508
4776
  const trailingNode = typeof trailing === "function" ? trailing(renderState) : trailing;
4509
4777
  const checkboxValue = indeterminate ? null : selected;
4778
+ const typography = TREE_LABEL_TYPOGRAPHY[size];
4779
+ const labelTypo = selected || indeterminate ? typography.selected : typography.default;
4510
4780
  const handleSelect = (event) => {
4511
4781
  if (node.disabled) return;
4512
4782
  onTreeSelect?.(node, event);
@@ -4526,7 +4796,8 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4526
4796
  "aria-disabled": node.disabled || void 0,
4527
4797
  onClick: handleRowClick,
4528
4798
  className: cn(
4529
- "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,
4530
4801
  border && "border-b border-(--cmp-tree-item-border-default)",
4531
4802
  !node.disabled && "hover:bg-(--cmp-tree-item-bg-hover)",
4532
4803
  node.disabled && "cursor-not-allowed opacity-60",
@@ -4539,13 +4810,14 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4539
4810
  TreeConnector,
4540
4811
  {
4541
4812
  guideline,
4813
+ size,
4542
4814
  depth,
4543
4815
  hasChildren,
4544
4816
  isLast,
4545
4817
  ancestorLast
4546
4818
  }
4547
4819
  ),
4548
- /* @__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: [
4549
4821
  hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
4550
4822
  SGhostButton,
4551
4823
  {
@@ -4581,25 +4853,17 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4581
4853
  /* @__PURE__ */ jsxRuntime.jsx(
4582
4854
  "span",
4583
4855
  {
4856
+ onClick: (event) => {
4857
+ if (!selectable) return;
4858
+ event.stopPropagation();
4859
+ if (!node.disabled && selectable) onTreeSelect?.(node, event);
4860
+ },
4584
4861
  className: cn(
4585
- "min-w-0 flex-1 text-[14px] leading-[24px]",
4586
- 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
4587
4865
  ),
4588
- children: /* @__PURE__ */ jsxRuntime.jsx(
4589
- "span",
4590
- {
4591
- onClick: (event) => {
4592
- if (!selectable) return;
4593
- event.stopPropagation();
4594
- if (!node.disabled && selectable) onTreeSelect?.(node, event);
4595
- },
4596
- className: cn(
4597
- "inline-block max-w-full truncate",
4598
- node.disabled ? "cursor-not-allowed" : selectable ? "cursor-pointer" : void 0
4599
- ),
4600
- children: node.label
4601
- }
4602
- )
4866
+ children: node.label
4603
4867
  }
4604
4868
  ),
4605
4869
  trailingNode
@@ -4616,6 +4880,8 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4616
4880
  expandedIds,
4617
4881
  defaultExpandedIds,
4618
4882
  selectable = true,
4883
+ size = "sm",
4884
+ useAll = false,
4619
4885
  cascadeSelection = true,
4620
4886
  guideline = true,
4621
4887
  border = true,
@@ -4634,6 +4900,11 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4634
4900
  const currentExpandedIds = expandedIds ?? internalExpandedIds;
4635
4901
  const selectedSet = react.useMemo(() => toSelectedSet(currentValue), [currentValue]);
4636
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;
4637
4908
  const updateExpanded = (node) => {
4638
4909
  const next = new Set(currentExpandedIds);
4639
4910
  if (next.has(node.id)) next.delete(node.id);
@@ -4645,7 +4916,7 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4645
4916
  const updateSelected = (node) => {
4646
4917
  if (node.disabled) return;
4647
4918
  const targetIds = cascadeSelection ? collectSelectableIds(node) : [node.id];
4648
- const everySelected = getSelectionState(node, selectedSet).selected;
4919
+ const everySelected = getSelectionState(node, selectedSet, cascadeSelection).selected;
4649
4920
  const next = new Set(selectedSet);
4650
4921
  targetIds.forEach((id) => {
4651
4922
  if (everySelected) next.delete(id);
@@ -4655,10 +4926,22 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4655
4926
  if (value === void 0) setInternalValue(orderedIds);
4656
4927
  onValueChange?.(orderedIds, node);
4657
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
+ };
4658
4941
  const renderNodes = (nodes, depth = 1, ancestorLast = []) => nodes.map((node, index) => {
4659
4942
  const isLast = index === nodes.length - 1;
4660
4943
  const isExpanded = expandedSet.has(node.id);
4661
- const { selected, indeterminate } = getSelectionState(node, selectedSet);
4944
+ const { selected, indeterminate } = getSelectionState(node, selectedSet, cascadeSelection);
4662
4945
  return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
4663
4946
  /* @__PURE__ */ jsxRuntime.jsx(
4664
4947
  STreeItem2,
@@ -4671,6 +4954,7 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4671
4954
  selected,
4672
4955
  indeterminate,
4673
4956
  selectable,
4957
+ size,
4674
4958
  guideline,
4675
4959
  border,
4676
4960
  leading,
@@ -4692,14 +4976,26 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4692
4976
  ) : null
4693
4977
  ] }, node.id);
4694
4978
  });
4695
- return /* @__PURE__ */ jsxRuntime.jsx(
4979
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4696
4980
  "div",
4697
4981
  {
4698
4982
  ref,
4699
4983
  role: "tree",
4700
4984
  className: cn("flex w-full flex-col items-start bg-(--cmp-tree-item-bg-default)", className),
4701
4985
  ...rest,
4702
- 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
+ ]
4703
4999
  }
4704
5000
  );
4705
5001
  });
@@ -4879,14 +5175,14 @@ var STooltipImpl = /* @__PURE__ */ react.forwardRef(function STooltip({
4879
5175
  ...style,
4880
5176
  ...contentPropsStyle
4881
5177
  };
4882
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open, onOpenChange: setOpenState, children: [
5178
+ return /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open, onOpenChange: setOpenState, children: [
4883
5179
  isNone ? (
4884
5180
  // 트리거 없음: children 을 래퍼 없이 그대로 앵커로 쓴다 (소비자 레이아웃 보존).
4885
5181
  // mouse/focus 핸들러가 없으므로 useClose 없이도 자동 닫힘이 발생하지 않는다.
4886
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Anchor, { asChild: true, children: triggerNode })
5182
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Anchor, { asChild: true, children: triggerNode })
4887
5183
  ) : isHover ? (
4888
5184
  // hover: Anchor(위치 기준) + 직접 mouse 핸들러 — 클릭으로 토글되지 않도록 Trigger 미사용
4889
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
5185
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
4890
5186
  "span",
4891
5187
  {
4892
5188
  className: "inline-flex",
@@ -4897,9 +5193,9 @@ var STooltipImpl = /* @__PURE__ */ react.forwardRef(function STooltip({
4897
5193
  children: triggerNode
4898
5194
  }
4899
5195
  ) })
4900
- ) : /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Trigger, { asChild: true, children: triggerNode }),
4901
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
4902
- RPopover9__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,
4903
5199
  {
4904
5200
  side: placement,
4905
5201
  sideOffset,
@@ -4933,7 +5229,7 @@ var STooltipImpl = /* @__PURE__ */ react.forwardRef(function STooltip({
4933
5229
  }
4934
5230
  )
4935
5231
  ] }),
4936
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
5232
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
4937
5233
  ]
4938
5234
  }
4939
5235
  ) })
@@ -4945,6 +5241,10 @@ var BODY_PADDING_CLASS = {
4945
5241
  wide: "p-(--sys-space-panel-padding-wide)",
4946
5242
  none: ""
4947
5243
  };
5244
+ var BODY_BACKGROUND_CLASS = {
5245
+ frame: "",
5246
+ neutral: "bg-(--sys-color-bg-neutralLight)"
5247
+ };
4948
5248
  var topBorderColorClass = {
4949
5249
  default: "bg-(--cmp-sectionHeaderCard-topBorder-color-default)",
4950
5250
  accent: "bg-(--cmp-sectionHeaderCard-topBorder-color-accent)"
@@ -4954,8 +5254,8 @@ var titleSizeClass = {
4954
5254
  sm: "typo-heading-sm"
4955
5255
  };
4956
5256
  var SSectionHeaderCardBodyImpl = /* @__PURE__ */ react.forwardRef(
4957
- function SSectionHeaderCardBody({ padding = "default", className, children, ...rest }, ref) {
4958
- 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 });
4959
5259
  }
4960
5260
  );
4961
5261
  var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
@@ -4968,8 +5268,7 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
4968
5268
  subtitle,
4969
5269
  slot,
4970
5270
  thickness = false,
4971
- className,
4972
- ...rest
5271
+ className
4973
5272
  }, ref) {
4974
5273
  const hasHelp = helpText != null && helpText.length > 0;
4975
5274
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -4990,7 +5289,6 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
4990
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)",
4991
5290
  className
4992
5291
  ),
4993
- ...rest,
4994
5292
  children: [
4995
5293
  marker && /* @__PURE__ */ jsxRuntime.jsx(SBadge, { color: "darkblue", className: "shrink-0" }),
4996
5294
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 shrink-0 items-center gap-[8px]", children: [
@@ -5034,8 +5332,24 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
5034
5332
  }
5035
5333
  );
5036
5334
  var SSectionHeaderCardRootImpl = /* @__PURE__ */ react.forwardRef(
5037
- function SSectionHeaderCard({ className, children, ...rest }, ref) {
5038
- 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(
5039
5353
  "div",
5040
5354
  {
5041
5355
  ref,
@@ -5044,24 +5358,28 @@ var SSectionHeaderCardRootImpl = /* @__PURE__ */ react.forwardRef(
5044
5358
  className
5045
5359
  ),
5046
5360
  ...rest,
5047
- 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
+ ]
5048
5378
  }
5049
5379
  );
5050
5380
  }
5051
5381
  );
5052
- var SSectionHeaderCardRoot = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5053
- var SSectionHeaderCardHeader2 = /* @__PURE__ */ withDisplayName(
5054
- SSectionHeaderCardHeaderImpl,
5055
- "SSectionHeaderCard.Header"
5056
- );
5057
- var SSectionHeaderCardBody2 = /* @__PURE__ */ withDisplayName(
5058
- SSectionHeaderCardBodyImpl,
5059
- "SSectionHeaderCard.Body"
5060
- );
5061
- var SSectionHeaderCard2 = /* @__PURE__ */ Object.assign(SSectionHeaderCardRoot, {
5062
- Header: SSectionHeaderCardHeader2,
5063
- Body: SSectionHeaderCardBody2
5064
- });
5382
+ var SSectionHeaderCard2 = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5065
5383
  var SDivider = /* @__PURE__ */ react.forwardRef(function SDivider2({ vertical = false, className, style, ...rest }, ref) {
5066
5384
  return /* @__PURE__ */ jsxRuntime.jsx(
5067
5385
  "hr",
@@ -5578,167 +5896,58 @@ var SCallout = /* @__PURE__ */ react.forwardRef(function SCallout2({ type = "def
5578
5896
  ),
5579
5897
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-[var(--cmp-callout-body-gap)] px-[var(--cmp-callout-body-paddingX)] py-[var(--cmp-callout-body-paddingY)] text-[12px] font-normal leading-[20px]", children: children ?? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col justify-center gap-[var(--cmp-callout-body-gap)] p-0", children: renderItems(message) }) })
5580
5898
  ]
5581
- }
5582
- );
5583
- });
5584
- var FILL_COLOR = {
5585
- primary: "var(--cmp-progress-active-color)",
5586
- error: "var(--cmp-progress-error-color)",
5587
- complete: "var(--cmp-progress-success-color)"
5588
- };
5589
- var clamp = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
5590
- var SLinearProgress = /* @__PURE__ */ react.forwardRef(
5591
- function SLinearProgress2({ value = 0, type = "primary", indeterminate = false, label, className, style }, ref) {
5592
- const v = clamp(value);
5593
- const fill = FILL_COLOR[type];
5594
- const valueColor = v === 0 ? "var(--sys-color-fg-tertiary, #888888)" : fill;
5595
- const valueText = `${Math.round(v)}%`;
5596
- return /* @__PURE__ */ jsxRuntime.jsxs(
5597
- "div",
5598
- {
5599
- ref,
5600
- className: cn("inline-flex w-full flex-col gap-[12px]", className),
5601
- style,
5602
- children: [
5603
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-[20px] w-full overflow-hidden rounded-full bg-[var(--cmp-progress-color-track)]", children: [
5604
- /* @__PURE__ */ jsxRuntime.jsx(
5605
- "div",
5606
- {
5607
- className: cn(
5608
- "absolute inset-y-0 left-0 h-full rounded-[inherit]",
5609
- indeterminate ? "w-full origin-left animate-linear-indeterminate" : "transition-[width] duration-[400ms] ease-linear"
5610
- ),
5611
- style: { background: fill, width: indeterminate ? void 0 : `${v}%` }
5612
- }
5613
- ),
5614
- !indeterminate && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5615
- /* @__PURE__ */ jsxRuntime.jsx(
5616
- "span",
5617
- {
5618
- className: "pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 select-none text-center text-[12px] font-bold leading-[20px]",
5619
- style: { color: valueColor },
5620
- children: valueText
5621
- }
5622
- ),
5623
- /* @__PURE__ */ jsxRuntime.jsx(
5624
- "span",
5625
- {
5626
- className: "pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 select-none text-center text-[12px] font-bold leading-[20px] text-[color:var(--cmp-progress-bar-text)]",
5627
- style: { clipPath: `inset(0 ${100 - v}% 0 0)` },
5628
- children: valueText
5629
- }
5630
- )
5631
- ] })
5632
- ] }),
5633
- label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-center text-[12px] font-normal leading-[20px] text-[color:var(--cmp-progress-color-label)]", children: label })
5634
- ]
5635
- }
5636
- );
5637
- }
5638
- );
5639
- var VIEWBOX = 80;
5640
- var RADIUS = 34;
5641
- var STROKE = 12;
5642
- var CIRCUMFERENCE = 2 * Math.PI * RADIUS;
5643
- var COLOR_MAP = {
5644
- primary: { arc: "var(--cmp-progress-active-color)", track: "var(--cmp-progress-color-track)" },
5645
- // inverse: 어두운 배경용 — arc 흰색, track 은 흰색 40%(회색 링)
5646
- inverse: { arc: "var(--color-white)", track: "rgba(255,255,255,0.4)" },
5647
- error: { arc: "var(--color-red-75)", track: "var(--cmp-progress-color-track)" },
5648
- complete: { arc: "#01BB4B", track: "var(--cmp-progress-color-track)" }
5649
- };
5650
- var clamp2 = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
5651
- var SCircleProgress = /* @__PURE__ */ react.forwardRef(
5652
- function SCircleProgress2({
5653
- value = 0,
5654
- type = "primary",
5655
- indeterminate = false,
5656
- label,
5657
- innerValue = false,
5658
- className,
5659
- style
5660
- }, ref) {
5661
- const v = clamp2(value);
5662
- const colors = COLOR_MAP[type];
5663
- const c = VIEWBOX / 2;
5664
- const percentColor = v === 0 ? "var(--sys-color-fg-tertiary, #888888)" : colors.arc;
5665
- const filled = v / 100 * CIRCUMFERENCE;
5666
- const showPercent = !indeterminate;
5667
- const percentNode = /* @__PURE__ */ jsxRuntime.jsxs(
5668
- "span",
5669
- {
5670
- className: cn(
5671
- "text-center text-[16px] font-bold",
5672
- innerValue ? (
5673
- // leading-[normal]: 26px leading 은 폰트 content(19px) 위아래로 홀수(7px)만큼 남아
5674
- // 3px/4px 로 쪼개지고 글자가 링 중심보다 위로 뜬다. (leading-normal 은 1.5 라 동일 문제)
5675
- "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 leading-[normal]"
5676
- ) : "leading-[26px]"
5677
- ),
5678
- style: { color: percentColor },
5679
- children: [
5680
- Math.round(v),
5681
- "%"
5682
- ]
5683
- }
5684
- );
5685
- return /* @__PURE__ */ jsxRuntime.jsxs(
5686
- "div",
5687
- {
5688
- ref,
5689
- className: cn(
5690
- "inline-flex flex-col items-center gap-[var(--cmp-progress-circular-gap)]",
5691
- className
5692
- ),
5693
- style,
5694
- children: [
5695
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(innerValue && "relative"), children: [
5696
- /* @__PURE__ */ jsxRuntime.jsxs(
5697
- "svg",
5698
- {
5699
- viewBox: `0 0 ${VIEWBOX} ${VIEWBOX}`,
5700
- xmlns: "http://www.w3.org/2000/svg",
5701
- className: cn(
5702
- "h-[80px] w-[80px]",
5703
- indeterminate && "origin-center animate-circle-spin"
5704
- ),
5705
- children: [
5706
- /* @__PURE__ */ jsxRuntime.jsx(
5707
- "circle",
5708
- {
5709
- cx: c,
5710
- cy: c,
5711
- r: RADIUS,
5712
- fill: "none",
5713
- strokeWidth: STROKE,
5714
- style: { stroke: colors.track }
5715
- }
5716
- ),
5717
- /* @__PURE__ */ jsxRuntime.jsx(
5718
- "circle",
5719
- {
5720
- cx: c,
5721
- cy: c,
5722
- r: RADIUS,
5723
- fill: "none",
5724
- strokeWidth: STROKE,
5725
- strokeLinecap: "round",
5726
- className: cn(
5727
- "origin-center",
5728
- indeterminate ? "animate-circle-arc" : "-rotate-90 transition-[stroke-dasharray] duration-[400ms] ease-linear"
5729
- ),
5730
- style: {
5731
- stroke: colors.arc,
5732
- ...indeterminate ? {} : { strokeDasharray: `${filled} ${CIRCUMFERENCE}`, strokeDashoffset: 0 }
5733
- }
5734
- }
5735
- )
5736
- ]
5899
+ }
5900
+ );
5901
+ });
5902
+ var FILL_COLOR = {
5903
+ primary: "var(--cmp-progress-color-active)",
5904
+ error: "var(--cmp-progress-color-error)",
5905
+ complete: "var(--cmp-progress-color-success)"
5906
+ };
5907
+ var clamp2 = (v) => Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 0;
5908
+ var SLinearProgress = /* @__PURE__ */ react.forwardRef(
5909
+ function SLinearProgress2({ value = 0, type = "primary", indeterminate = false, label, className, style }, ref) {
5910
+ const v = clamp2(value);
5911
+ const fill = FILL_COLOR[type];
5912
+ const valueColor = v === 0 ? "var(--sys-color-fg-tertiary, #888888)" : fill;
5913
+ const valueText = `${Math.round(v)}%`;
5914
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5915
+ "div",
5916
+ {
5917
+ ref,
5918
+ className: cn("inline-flex w-full flex-col gap-[12px]", className),
5919
+ style,
5920
+ children: [
5921
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-[20px] w-full overflow-hidden rounded-full bg-[var(--cmp-progress-color-track)]", children: [
5922
+ /* @__PURE__ */ jsxRuntime.jsx(
5923
+ "div",
5924
+ {
5925
+ className: cn(
5926
+ "absolute inset-y-0 left-0 h-full rounded-[inherit]",
5927
+ indeterminate ? "w-full origin-left animate-linear-indeterminate" : "transition-[width] duration-[400ms] ease-linear"
5928
+ ),
5929
+ style: { background: fill, width: indeterminate ? void 0 : `${v}%` }
5737
5930
  }
5738
5931
  ),
5739
- showPercent && innerValue && percentNode
5932
+ !indeterminate && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5933
+ /* @__PURE__ */ jsxRuntime.jsx(
5934
+ "span",
5935
+ {
5936
+ className: "pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 select-none text-center text-[12px] font-bold leading-[20px]",
5937
+ style: { color: valueColor },
5938
+ children: valueText
5939
+ }
5940
+ ),
5941
+ /* @__PURE__ */ jsxRuntime.jsx(
5942
+ "span",
5943
+ {
5944
+ className: "pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 select-none text-center text-[12px] font-bold leading-[20px] text-[color:var(--cmp-progress-bar-text)]",
5945
+ style: { clipPath: `inset(0 ${100 - v}% 0 0)` },
5946
+ children: valueText
5947
+ }
5948
+ )
5949
+ ] })
5740
5950
  ] }),
5741
- showPercent && !innerValue && percentNode,
5742
5951
  label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-center text-[12px] font-normal leading-[20px] text-[color:var(--cmp-progress-color-label)]", children: label })
5743
5952
  ]
5744
5953
  }
@@ -6230,10 +6439,15 @@ function SSwitch({
6230
6439
  }
6231
6440
  );
6232
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
+ };
6233
6446
  function SToggle({
6234
6447
  value = false,
6235
6448
  label = "",
6236
6449
  disabled = false,
6450
+ size = "sm",
6237
6451
  onValueChange,
6238
6452
  className,
6239
6453
  style
@@ -6249,7 +6463,8 @@ function SToggle({
6249
6463
  "aria-pressed": value,
6250
6464
  onClick: () => !disabled && onValueChange?.(!value),
6251
6465
  className: cn(
6252
- "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],
6253
6468
  disabled ? "cursor-not-allowed" : "cursor-pointer",
6254
6469
  // 미선택(off) hover 시 파란 채움 + 흰 글자
6255
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)]",
@@ -6301,10 +6516,10 @@ function SPopover({
6301
6516
  const descColor = type === "default" ? "var(--cmp-popover-default-description)" : "var(--cmp-popover-light-description)";
6302
6517
  const leftLinkLabelClass = type === "default" ? "text-[color:var(--cmp-popover-default-subAction)]" : "text-[color:var(--cmp-popover-light-subAction)]";
6303
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 }) });
6304
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open: actualOpen, onOpenChange: setOpen, children: [
6305
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Trigger, { asChild: true, children: triggerNode }),
6306
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
6307
- RPopover9__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,
6308
6523
  {
6309
6524
  side: placement,
6310
6525
  sideOffset: 8,
@@ -6370,7 +6585,7 @@ function SPopover({
6370
6585
  ]
6371
6586
  }
6372
6587
  ),
6373
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
6588
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
6374
6589
  ]
6375
6590
  }
6376
6591
  ) })
@@ -6401,7 +6616,7 @@ function SPortal({
6401
6616
  const portalContainer = usePortalContainer();
6402
6617
  const { minWidth: styleMinWidth, maxWidth: styleMaxWidth, ...restStyle } = style ?? {};
6403
6618
  return /* @__PURE__ */ jsxRuntime.jsxs(
6404
- RPopover9__namespace.Root,
6619
+ RPopover10__namespace.Root,
6405
6620
  {
6406
6621
  open,
6407
6622
  onOpenChange: (next) => {
@@ -6409,9 +6624,9 @@ function SPortal({
6409
6624
  if (!next) onClose?.();
6410
6625
  },
6411
6626
  children: [
6412
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Anchor, { virtualRef: anchorRef }),
6413
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
6414
- RPopover9__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,
6415
6630
  {
6416
6631
  side: placement,
6417
6632
  align,
@@ -6609,10 +6824,10 @@ function SGuide({
6609
6824
  );
6610
6825
  if (isNotion)
6611
6826
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className, style, children: trigger });
6612
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open, onOpenChange: setOpen, children: [
6613
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Trigger, { asChild: true, children: trigger }),
6614
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
6615
- RPopover9__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,
6616
6831
  {
6617
6832
  side: "bottom",
6618
6833
  align: "start",
@@ -6795,7 +7010,7 @@ function SModalContainer({
6795
7010
  "div",
6796
7011
  {
6797
7012
  className: cn(
6798
- "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",
6799
7014
  shaking && "animate-modal-shake",
6800
7015
  className
6801
7016
  ),
@@ -7052,7 +7267,7 @@ function SActionModal({
7052
7267
  showClose: true,
7053
7268
  width,
7054
7269
  height,
7055
- 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)]",
7056
7271
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-auto flex-col", children: [
7057
7272
  /* @__PURE__ */ jsxRuntime.jsx(SOverlayHeader, { title: modalTitle, description, reserveCloseSpace: true }),
7058
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 }),
@@ -7945,15 +8160,21 @@ var GNB_MENU = {
7945
8160
  depth2PaddingLeft: box("item-depth2-paddingLeft"),
7946
8161
  depth3PaddingLeft: box("item-depth3-paddingLeft"),
7947
8162
  depth1Icon: box("item-depth1-icon"),
8163
+ // box 는 depth1 전용 타이포 토큰이 없어 depth1 도 selected 타이포를 따른다
7948
8164
  depth1FontSize: 14,
7949
8165
  // token: gnb.box.item.typography.selected.fontSize
7950
8166
  depth1LineHeight: 24,
7951
8167
  // token: gnb.box.item.typography.selected.lineHeight
7952
- depth1FontWeight: 700,
8168
+ depth1FontWeight: 600,
8169
+ // token: gnb.box.item.typography.selected.fontWeight
7953
8170
  itemFontSize: 14,
7954
8171
  // token: gnb.box.item.typography.default.fontSize
7955
- itemLineHeight: 24
8172
+ itemLineHeight: 24,
7956
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
7957
8178
  },
7958
8179
  belt: {
7959
8180
  bodyPaddingY: gnb("body-padding"),
@@ -7975,10 +8196,15 @@ var GNB_MENU = {
7975
8196
  depth1LineHeight: 22,
7976
8197
  // token: gnb.belt.item.depth1.typography.lineHeight
7977
8198
  depth1FontWeight: 700,
8199
+ // token: gnb.belt.item.depth1.typography.fontWeight
7978
8200
  itemFontSize: 12,
7979
8201
  // token: gnb.belt.item.typography.default.fontSize
7980
- itemLineHeight: 20
8202
+ itemLineHeight: 20,
7981
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
7982
8208
  }
7983
8209
  };
7984
8210
  var GNB_COLORS = {
@@ -8276,7 +8502,7 @@ var SGnb = /* @__PURE__ */ react.forwardRef(function SGnb2({
8276
8502
  fontFamily: "inherit",
8277
8503
  fontSize: depth === 1 ? M.depth1FontSize : M.itemFontSize,
8278
8504
  lineHeight: `${depth === 1 ? M.depth1LineHeight : M.itemLineHeight}px`,
8279
- fontWeight: depth === 1 || isSelected ? M.depth1FontWeight : 400,
8505
+ fontWeight: depth === 1 ? M.depth1FontWeight : isSelected ? M.itemFontWeightSelected : M.itemFontWeight,
8280
8506
  opacity: item.disabled ? 0.4 : 1,
8281
8507
  cursor: item.disabled ? "not-allowed" : "pointer"
8282
8508
  };
@@ -8693,10 +8919,10 @@ var SGnb = /* @__PURE__ */ react.forwardRef(function SGnb2({
8693
8919
  }
8694
8920
  ),
8695
8921
  header === "fix" && menuResizeHandle,
8696
- /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open: labelTip !== null, onOpenChange: (open) => !open && setLabelTip(null), children: [
8697
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Anchor, { virtualRef: labelTipAnchorRef }),
8698
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
8699
- RPopover9__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,
8700
8926
  {
8701
8927
  side: "right",
8702
8928
  sideOffset: 4,
@@ -8767,13 +8993,156 @@ var SPageHeader = /* @__PURE__ */ withDisplayName(
8767
8993
  );
8768
8994
  var MIN_CONTENT_WIDTH = `calc(var(${SLAYOUT_MIN_WIDTH_VAR}, 0px) - 100cqw + 100%)`;
8769
8995
  var BACKGROUND_CLASS = {
8770
- frame: `bg-[var(--sys-color-bg-frame)] ${SCROLLBAR_TRACK_BORDER_CLASS} ${SCROLLBAR_TRACK_BG_CLASS}`,
8771
- neutral: `bg-[var(--sys-color-bg-neutralLight)] ${SCROLLBAR_TRACK_BORDER_CLASS} ${SCROLLBAR_TRACK_BG_WHITE_CLASS}`,
8772
- 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 };
9020
+ };
9021
+ var setForwardedRef = (ref, node) => {
9022
+ if (typeof ref === "function") {
9023
+ ref(node);
9024
+ return;
9025
+ }
9026
+ if (ref) ref.current = node;
8773
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
+ }
8774
9141
  var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8775
9142
  background = "frame",
8776
9143
  scrollEndSpacing = true,
9144
+ overlayScrollbar = false,
9145
+ contentHeight = "auto",
8777
9146
  header,
8778
9147
  className,
8779
9148
  style,
@@ -8783,29 +9152,71 @@ var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8783
9152
  }, ref) {
8784
9153
  const layout = react.useContext(SLayoutContext);
8785
9154
  const gridStyle = layout?.header === "full" ? { gridArea: "page" } : void 0;
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
+ );
8786
9163
  const [scrolled, setScrolled] = react.useState(false);
8787
9164
  const handleScroll = (e) => {
8788
9165
  onScroll?.(e);
8789
9166
  if (header != null && !header.fix) setScrolled(e.currentTarget.scrollTop > 0);
8790
9167
  };
9168
+ const fill = contentHeight === "fill";
8791
9169
  const main = /* @__PURE__ */ jsxRuntime.jsx(
8792
9170
  "main",
8793
9171
  {
8794
- ref,
9172
+ ref: setMainRef,
8795
9173
  className: cn(
8796
9174
  "box-border min-h-0 min-w-0 flex-1 overflow-auto p-[var(--cmp-pageBody-padding-default)]",
8797
- scrollEndSpacing && "pb-[var(--cmp-pageBody-padding-scrollEnd)]",
8798
- 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]}`,
8799
9178
  BACKGROUND_CLASS[background],
8800
9179
  className
8801
9180
  ),
8802
- style: header == null ? { ...gridStyle, ...style } : style,
9181
+ style: header == null && !overlayScrollbar ? { ...gridStyle, ...style } : style,
8803
9182
  onScroll: handleScroll,
8804
9183
  ...rest,
8805
- 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 })
8806
9185
  }
8807
9186
  );
8808
- if (header == null) return main;
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
+ }
8809
9220
  return (
8810
9221
  // fix 가 아닌 헤더는 자기 배경이 투명이라(SPageHeader.tsx) 이 배경이 그대로 비친다 —
8811
9222
  // 그래서 SPage 의 background 를 main 뿐 아니라 여기에도 칠한다(스크롤바 관련 클래스는
@@ -8817,7 +9228,10 @@ var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8817
9228
  style: gridStyle,
8818
9229
  children: [
8819
9230
  /* @__PURE__ */ jsxRuntime.jsx(SPageHeader, { ...header, scrolled }),
8820
- main
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
+ ] })
8821
9235
  ]
8822
9236
  }
8823
9237
  )
@@ -8826,12 +9240,10 @@ var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8826
9240
 
8827
9241
  // src/components/SPage/page.config.ts
8828
9242
  var PAGE_BACKGROUNDS = ["frame", "neutral", "screen"];
9243
+ var PAGE_CONTENT_HEIGHTS = ["auto", "fill"];
8829
9244
 
8830
9245
  // src/components/STabs/tabs.config.ts
8831
- var TAB_MAIN_SIZE = {
8832
- md: { height: 36, paddingX: 24 },
8833
- lg: { height: 44, paddingX: 32 }
8834
- };
9246
+ var TAB_MAIN_SIZE = { height: 36, paddingX: 24 };
8835
9247
  var TAB_TYPOGRAPHY = { fontSize: 12, lineHeight: 20 };
8836
9248
  var TAB_MAIN_COLORS = {
8837
9249
  selected: {
@@ -8853,13 +9265,11 @@ function STabs({
8853
9265
  value,
8854
9266
  tabs,
8855
9267
  onValueChange,
8856
- size = "md",
8857
9268
  isSub = false,
8858
9269
  vertical = false,
8859
9270
  className,
8860
9271
  style
8861
9272
  }) {
8862
- const s = TAB_MAIN_SIZE[size];
8863
9273
  const blue = TAB_MAIN_COLORS.selected.border;
8864
9274
  const isVertical = isSub && vertical;
8865
9275
  const tabRefs = react.useRef([]);
@@ -8900,8 +9310,8 @@ function STabs({
8900
9310
  const border = selected ? TAB_MAIN_COLORS.selected.border : TAB_MAIN_COLORS.default.border;
8901
9311
  const selectedBg = TAB_MAIN_COLORS.selected.bg;
8902
9312
  tabStyle = {
8903
- height: s.height,
8904
- padding: `0 ${s.paddingX}px`,
9313
+ height: TAB_MAIN_SIZE.height,
9314
+ padding: `0 ${TAB_MAIN_SIZE.paddingX}px`,
8905
9315
  fontSize: TAB_TYPOGRAPHY.fontSize,
8906
9316
  lineHeight: `${TAB_TYPOGRAPHY.lineHeight}px`,
8907
9317
  background: selected ? TAB_MAIN_COLORS.selected.bg : TAB_MAIN_COLORS.default.bg,
@@ -9643,6 +10053,7 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9643
10053
  readOnly = false,
9644
10054
  width,
9645
10055
  minWidth,
10056
+ maxWidth,
9646
10057
  multiline = false,
9647
10058
  borderless = false,
9648
10059
  children,
@@ -9678,7 +10089,10 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9678
10089
  const resolvedSuccess = status === "pass";
9679
10090
  const showError = resolvedError && errorMessage !== "";
9680
10091
  const interactive = !disabled && !readOnly;
9681
- 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;
9682
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)";
9683
10097
  const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
9684
10098
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
@@ -9734,8 +10148,12 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9734
10148
  /* @__PURE__ */ jsxRuntime.jsxs(
9735
10149
  "div",
9736
10150
  {
9737
- className: cn("flex min-w-0 flex-col", !hug && "flex-1"),
9738
- 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
+ },
9739
10157
  children: [
9740
10158
  /* @__PURE__ */ jsxRuntime.jsxs(
9741
10159
  "div",
@@ -10490,6 +10908,7 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10490
10908
  height,
10491
10909
  stickyColumn,
10492
10910
  radius = "default",
10911
+ bordered = true,
10493
10912
  noDataLabel = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
10494
10913
  noDataSlot,
10495
10914
  isLoading = false,
@@ -10748,7 +11167,7 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10748
11167
  };
10749
11168
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[var(--cmp-table-radius)] rounded-t-none" : "rounded-[var(--cmp-table-radius)]";
10750
11169
  const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-body-paddingY-default)";
10751
- 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)";
10752
11171
  const rowH = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-body-default-height)";
10753
11172
  const totalColSpan = visibleCols.length + (selectable ? 1 : 0);
10754
11173
  const stickyInfo = (i) => {
@@ -10802,7 +11221,8 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10802
11221
  // 만으로는 스태킹 컨텍스트가 생기지 않아, 모달 안에 들어가면 30·31이 모달 Content
10803
11222
  // 레벨까지 올라가 같은 컨텍스트의 floating portal 밴드(z-2)를 덮는다.
10804
11223
  // 자손은 이미 overflow-hidden으로 클리핑되므로 테이블 내부 순서는 달라지지 않는다.
10805
- "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)]",
10806
11226
  // height가 없으면 루트가 받은 높이를 끝까지 채운다. 호스트가 className으로
10807
11227
  // `flex-1 min-h-0` 등을 줘 루트 높이가 확정된 경우, 행이 적어도 테두리 박스가
10808
11228
  // 남은 영역을 그대로 차지한다. basis는 auto로 둬 높이가 확정되지 않은
@@ -10972,9 +11392,10 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10972
11392
  stickyShadowClass(i),
10973
11393
  typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
10974
11394
  );
11395
+ const tdPadX = cellPadXOf(col);
10975
11396
  const tdStyle = {
10976
- paddingLeft: cellPadX,
10977
- paddingRight: cellPadX,
11397
+ paddingLeft: tdPadX,
11398
+ paddingRight: tdPadX,
10978
11399
  paddingTop: cellPadY,
10979
11400
  paddingBottom: cellPadY,
10980
11401
  textAlign: col.align ?? "left",
@@ -11027,19 +11448,29 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11027
11448
  // SPagination은 전체 폭 기준 중앙, rows-per-page SSelect는 우측에 absolute 배치해 중앙 정렬을 유지한다.
11028
11449
  // 루트 높이가 확정된 경우(테이블이 남은 영역을 채우는 배치) 페이지네이션 바가
11029
11450
  // 눌리지 않도록 shrink-0으로 48px를 보장한다.
11030
- /* @__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: [
11031
- /* @__PURE__ */ jsxRuntime.jsx(SPagination, { currentPage: displayPage, lastPage, onPageChange: changePage }),
11032
- useRowsPerPageSelect && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-[10px] top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsxRuntime.jsx(
11033
- SSelect2,
11034
- {
11035
- options: rowsPerPageOption,
11036
- value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
11037
- emitValue: true,
11038
- onValueChange: (v) => changeRowsPerPage(Number(v)),
11039
- width: 128
11040
- }
11041
- ) })
11042
- ] })
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
+ )
11043
11474
  ]
11044
11475
  }
11045
11476
  );
@@ -11102,6 +11533,67 @@ var STableBar = /* @__PURE__ */ react.forwardRef(function STableBar2({ total, se
11102
11533
 
11103
11534
  // src/lib/autofill.ts
11104
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]";
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
+ }
11105
11597
  var FONT = {
11106
11598
  sm: { fontSize: 12, lineHeight: 20, paddingX: "var(--cmp-textinput-sm-paddingX)" },
11107
11599
  md: { fontSize: 14, lineHeight: 24, paddingX: "var(--cmp-textinput-md-paddingX)" }
@@ -11159,6 +11651,10 @@ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
11159
11651
  const resolvedError = error || ruleError !== "";
11160
11652
  const resolvedErrorMessage = ruleError !== "" ? ruleError : errorMessage;
11161
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
+ });
11162
11658
  return /* @__PURE__ */ jsxRuntime.jsx(
11163
11659
  SField,
11164
11660
  {
@@ -11199,7 +11695,7 @@ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
11199
11695
  /* @__PURE__ */ jsxRuntime.jsx(
11200
11696
  "input",
11201
11697
  {
11202
- ref,
11698
+ ref: mergeRefs(ref, valueTooltip.valueRef),
11203
11699
  type: inputType,
11204
11700
  value: value ?? "",
11205
11701
  disabled,
@@ -11224,9 +11720,18 @@ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
11224
11720
  color: disabled ? "var(--cmp-textinput-text-disabled)" : "var(--sys-color-field-text-default)",
11225
11721
  ...inputStyle
11226
11722
  },
11227
- ...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
+ }
11228
11732
  }
11229
11733
  ),
11734
+ valueTooltip.tooltip,
11230
11735
  suffix != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex flex-shrink-0 items-center", children: suffix }),
11231
11736
  clearable && hasValue && !disabled && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
11232
11737
  SGhostButton,
@@ -11460,8 +11965,10 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
11460
11965
  const [display, setDisplay] = react.useState(() => formatWithCommas(numValue));
11461
11966
  const [ruleError, setRuleError] = react.useState("");
11462
11967
  const innerRef = react.useRef(null);
11968
+ const valueTooltip = useTruncatedValueTooltip({ enabled: readOnly && !disabled, text: display });
11463
11969
  const setRefs2 = (el) => {
11464
11970
  innerRef.current = el;
11971
+ valueTooltip.valueRef.current = el;
11465
11972
  if (typeof ref === "function") ref(el);
11466
11973
  else if (ref) ref.current = el;
11467
11974
  };
@@ -11675,9 +12182,12 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
11675
12182
  textAlign: useButton ? "center" : "right",
11676
12183
  color: disabled ? "var(--cmp-numberInput-text-disabled)" : "var(--cmp-numberInput-text-default)",
11677
12184
  ...inputStyle
11678
- }
12185
+ },
12186
+ onMouseEnter: valueTooltip.onMouseEnter,
12187
+ onMouseLeave: valueTooltip.onMouseLeave
11679
12188
  }
11680
12189
  ),
12190
+ valueTooltip.tooltip,
11681
12191
  inputSuffix !== "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", style: { color: affixColor }, children: inputSuffix }),
11682
12192
  useButton && stepper("inc")
11683
12193
  ]
@@ -12056,12 +12566,275 @@ function SCalendar({
12056
12566
  }
12057
12567
  );
12058
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
+ }
12059
12831
  function SDatePicker({
12060
12832
  value,
12061
12833
  onValueChange,
12062
12834
  onViewChange,
12835
+ mode = "date",
12063
12836
  size = "sm",
12064
- placeholder = "YYYY-MM-DD",
12837
+ placeholder,
12065
12838
  selectable,
12066
12839
  disabled = false,
12067
12840
  width,
@@ -12087,6 +12860,7 @@ function SDatePicker({
12087
12860
  const [focused, setFocused] = react.useState(false);
12088
12861
  const [ruleError, setRuleError] = react.useState("");
12089
12862
  const hasValue = value != null && value !== "";
12863
+ const resolvedPlaceholder = placeholder ?? (mode === "year" ? "YYYY" : mode === "month" ? "YYYY-MM" : "YYYY-MM-DD");
12090
12864
  useFormField({
12091
12865
  name,
12092
12866
  validate: () => {
@@ -12098,12 +12872,39 @@ function SDatePicker({
12098
12872
  });
12099
12873
  const resolvedError = error || ruleError !== "";
12100
12874
  const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
12101
- 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
+ };
12102
12899
  const select = (date) => {
12103
12900
  onValueChange?.(date);
12104
12901
  if (rules && rules.length > 0) setRuleError(runRules(date, rules));
12105
12902
  setOpen(false);
12106
12903
  };
12904
+ const selectListboxValue = (nextValue) => {
12905
+ onValueChange?.(nextValue);
12906
+ if (rules && rules.length > 0) setRuleError(runRules(nextValue, rules));
12907
+ };
12107
12908
  return /* @__PURE__ */ jsxRuntime.jsx(
12108
12909
  SField,
12109
12910
  {
@@ -12122,11 +12923,13 @@ function SDatePicker({
12122
12923
  focused: focused || open,
12123
12924
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12124
12925
  width,
12926
+ minWidth: dims.fieldMinW,
12927
+ maxWidth: dims.fieldMaxW,
12125
12928
  disabled,
12126
12929
  className,
12127
12930
  style,
12128
- children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open, onOpenChange: (o) => !disabled && setOpen(o), children: [
12129
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__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(
12130
12933
  "button",
12131
12934
  {
12132
12935
  type: "button",
@@ -12156,23 +12959,23 @@ function SDatePicker({
12156
12959
  lineHeight: `${dims.lineHeight}px`,
12157
12960
  color: disabled ? "var(--cmp-datepicker-text-disabled)" : hasValue ? "var(--cmp-datepicker-text-default)" : "var(--cmp-field-hint-color)"
12158
12961
  },
12159
- children: hasValue ? value : placeholder
12962
+ children: hasValue ? value : resolvedPlaceholder
12160
12963
  }
12161
12964
  )
12162
12965
  ]
12163
12966
  }
12164
12967
  ) }),
12165
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
12166
- RPopover9__namespace.Content,
12968
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
12969
+ RPopover10__namespace.Content,
12167
12970
  {
12168
12971
  side: "bottom",
12169
- align: "start",
12972
+ align: mode === "date" ? "start" : "center",
12170
12973
  sideOffset: 4,
12171
12974
  className: cn(
12172
12975
  "z-50 max-w-[var(--radix-popover-content-available-width)]",
12173
12976
  FLOATING_SLIDE_ANIM
12174
12977
  ),
12175
- children: /* @__PURE__ */ jsxRuntime.jsx(
12978
+ children: mode === "date" ? /* @__PURE__ */ jsxRuntime.jsx(
12176
12979
  SCalendar,
12177
12980
  {
12178
12981
  value: value ?? void 0,
@@ -12181,6 +12984,24 @@ function SDatePicker({
12181
12984
  onValueChange: select,
12182
12985
  onViewChange
12183
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
+ }
12184
13005
  )
12185
13006
  }
12186
13007
  ) })
@@ -12562,6 +13383,9 @@ function SDateRangePicker({
12562
13383
  icon: 20,
12563
13384
  minW: 104,
12564
13385
  fieldMinW: 260,
13386
+ // 상한 등급 xl — 값이 `YYYY-MM-DD ~ YYYY-MM-DD` 라 단일 날짜의 두 배가 넘는다.
13387
+ // 최소너비(260)가 이미 lg(240)를 넘으므로 상한도 한 등급 위여야 한다
13388
+ fieldMaxW: 480,
12565
13389
  fontSize: 14,
12566
13390
  lineHeight: 24,
12567
13391
  radius: 6
@@ -12571,6 +13395,8 @@ function SDateRangePicker({
12571
13395
  icon: 16,
12572
13396
  minW: 80,
12573
13397
  fieldMinW: 216,
13398
+ // 상한 등급 lg — 최소너비 216 이 md(160)를 넘어 md 로는 담기지 않는다
13399
+ fieldMaxW: 240,
12574
13400
  fontSize: 12,
12575
13401
  lineHeight: 20,
12576
13402
  radius: 4
@@ -12601,11 +13427,12 @@ function SDateRangePicker({
12601
13427
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12602
13428
  width,
12603
13429
  minWidth: dims.fieldMinW,
13430
+ maxWidth: dims.fieldMaxW,
12604
13431
  disabled,
12605
13432
  className,
12606
13433
  style,
12607
13434
  children: /* @__PURE__ */ jsxRuntime.jsxs(
12608
- RPopover9__namespace.Root,
13435
+ RPopover10__namespace.Root,
12609
13436
  {
12610
13437
  open,
12611
13438
  onOpenChange: (o) => {
@@ -12614,7 +13441,7 @@ function SDateRangePicker({
12614
13441
  if (!o) setPendingStart(null);
12615
13442
  },
12616
13443
  children: [
12617
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
13444
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
12618
13445
  "button",
12619
13446
  {
12620
13447
  type: "button",
@@ -12655,8 +13482,8 @@ function SDateRangePicker({
12655
13482
  ]
12656
13483
  }
12657
13484
  ) }),
12658
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
12659
- RPopover9__namespace.Content,
13485
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
13486
+ RPopover10__namespace.Content,
12660
13487
  {
12661
13488
  side: "bottom",
12662
13489
  align: "start",
@@ -13065,6 +13892,7 @@ function SKeyValueTable({
13065
13892
  values = {},
13066
13893
  search = false,
13067
13894
  radius = "default",
13895
+ bordered = true,
13068
13896
  onChange,
13069
13897
  onSearch,
13070
13898
  className,
@@ -13129,7 +13957,8 @@ function SKeyValueTable({
13129
13957
  });
13130
13958
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[8px] rounded-t-none" : "rounded-[8px]";
13131
13959
  const frameClass = cn(
13132
- "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)]",
13133
13962
  radiusClass
13134
13963
  );
13135
13964
  const table = /* @__PURE__ */ jsxRuntime.jsxs(
@@ -13674,6 +14503,119 @@ function SRadioButton({
13674
14503
  }
13675
14504
  );
13676
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
+ });
13677
14619
  var CHIPFILTER_LISTBOX_WIDTH = 240;
13678
14620
  var CHIPFILTER_DATE_PRESET_LISTBOX_WIDTH = 246;
13679
14621
  var CHIPFILTER = {
@@ -13714,7 +14656,16 @@ var CHIPFILTER = {
13714
14656
  itemTextDefault: "var(--cmp-chipFilter-item-text-default)",
13715
14657
  itemTextSelected: "var(--cmp-chipFilter-item-text-selected)"
13716
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
+ ];
13717
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;
13718
14669
  var keywordEntries = (value) => {
13719
14670
  if (isKeywordValue(value)) return value.keywords;
13720
14671
  if (Array.isArray(value)) {
@@ -13738,12 +14689,33 @@ var todayIso = () => {
13738
14689
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
13739
14690
  };
13740
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
+ });
13741
14709
  var resolveFieldDefault = (field) => {
13742
14710
  if (field.defaultValue !== void 0) return field.defaultValue;
13743
14711
  if (isInlineDateField(field)) return todayIso();
13744
14712
  if (!field.required) return void 0;
13745
14713
  if (field.type === "single") return field.options?.[0]?.value;
13746
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
+ }
13747
14719
  return void 0;
13748
14720
  };
13749
14721
  var valuesEqual = (a, b) => {
@@ -13767,6 +14739,8 @@ var isAtDefaultValue = (field, value) => {
13767
14739
  return def !== void 0 && valuesEqual(value, def);
13768
14740
  };
13769
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);
13770
14744
  var isGroupedFields = (fields) => fields.length > 0 && "fields" in fields[0];
13771
14745
  var isGroupSatisfied = (group, values) => {
13772
14746
  if (!group.rule) return true;
@@ -13793,9 +14767,10 @@ var defaultGroupTooltipMessage = (rule, fields) => {
13793
14767
  };
13794
14768
  var matchPresetId = (presets, value) => {
13795
14769
  if (isEmptyValue(value)) return null;
14770
+ if (isPeriodValue(value)) return value.unit;
13796
14771
  for (const preset of presets) {
13797
- if (preset.custom || !preset.resolve) continue;
13798
- const resolved = preset.resolve();
14772
+ if (preset.custom) continue;
14773
+ const resolved = preset.resolve ? preset.resolve() : preset.value;
13799
14774
  if (Array.isArray(resolved) && Array.isArray(value)) {
13800
14775
  if (resolved[0] === value[0] && resolved[1] === value[1]) return preset.value;
13801
14776
  } else if (!Array.isArray(resolved) && !Array.isArray(value) && resolved === value) {
@@ -13811,18 +14786,36 @@ var formatValueList = (labels) => {
13811
14786
  };
13812
14787
  var displayValue = (field, value) => {
13813
14788
  if (isEmptyValue(value)) return "";
13814
- if (field.type === "multi")
13815
- return formatValueList(toArrayValue(value).map((v) => optionLabel(field, v)));
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
+ }
13816
14797
  if (field.type === "single") return optionLabel(field, value);
13817
14798
  if (field.type === "date")
13818
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
+ }
13819
14812
  if (field.type === "keyword") return formatValueList(keywordEntries(value));
13820
14813
  return String(value);
13821
14814
  };
13822
14815
  var chipDisplayValue = (field, value) => {
13823
14816
  const text = displayValue(field, value);
13824
14817
  if (field.type !== "date" || isEmptyValue(value)) return text;
13825
- const presets = field.presets ?? [];
14818
+ const presets = getFieldPresets(field);
13826
14819
  if (presets.length === 0) return text;
13827
14820
  const preset = presets.find((p) => p.value === matchPresetId(presets, value));
13828
14821
  return preset && !preset.custom ? `${preset.label}(${text})` : text;
@@ -13839,7 +14832,6 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
13839
14832
  onAddFilter,
13840
14833
  label = "\uAC80\uC0C9 \uD544\uD130",
13841
14834
  showLabel = false,
13842
- showAddButton = true,
13843
14835
  showReset = true,
13844
14836
  disabled = false,
13845
14837
  className,
@@ -13854,7 +14846,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
13854
14846
  const values = normalizeValues(value);
13855
14847
  const portalContainer = usePortalContainer();
13856
14848
  const [internalActiveKeys, setInternalActiveKeys] = react.useState(
13857
- () => fields.filter((field) => field.defaultActive !== false).map((field) => field.key)
14849
+ () => fields.filter((field) => isFixedField(field) || field.defaultActive !== false).map((field) => field.key)
13858
14850
  );
13859
14851
  const [openKey, setOpenKey] = react.useState(null);
13860
14852
  const [addOpen, setAddOpen] = react.useState(false);
@@ -13896,7 +14888,9 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
13896
14888
  setInternalActiveKeys((current) => {
13897
14889
  const known = new Set(fields.map((field) => field.key));
13898
14890
  const kept = current.filter((key) => known.has(key));
13899
- const additions = fields.filter((field) => field.defaultActive !== false && !kept.includes(field.key)).map((field) => field.key);
14891
+ const additions = fields.filter(
14892
+ (field) => (isFixedField(field) || field.defaultActive !== false) && !kept.includes(field.key)
14893
+ ).map((field) => field.key);
13900
14894
  return [...kept, ...additions];
13901
14895
  });
13902
14896
  }, [activeKeys, fields]);
@@ -13917,25 +14911,31 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
13917
14911
  });
13918
14912
  if (changed) onValueChange?.({ ...values, ...patch });
13919
14913
  }, [fields, values, onValueChange]);
13920
- const resolvedActiveKeys = activeKeys ?? internalActiveKeys;
14914
+ const fixedKeys = fields.filter(isFixedField).map((field) => field.key);
14915
+ const resolvedActiveKeys = Array.from(
14916
+ /* @__PURE__ */ new Set([...activeKeys ?? internalActiveKeys, ...fixedKeys])
14917
+ );
13921
14918
  const activeFields = fields.filter((field) => resolvedActiveKeys.includes(field.key));
13922
- const inactiveFields = fields.filter((field) => !resolvedActiveKeys.includes(field.key));
14919
+ const inactiveFields = fields.filter(
14920
+ (field) => isAddableField(field) && !resolvedActiveKeys.includes(field.key)
14921
+ );
13923
14922
  const groupIndexByKey = /* @__PURE__ */ new Map();
13924
14923
  groups.forEach((group, groupIndex) => {
13925
14924
  group.fields.forEach((field) => groupIndexByKey.set(field.key, groupIndex));
13926
14925
  });
13927
14926
  const hasValue = fields.some((field) => !isEmptyValue(values[field.key]));
13928
- const hasRightActions = showAddButton || showReset;
14927
+ const hasAddButton = fields.some(isAddableField);
14928
+ const hasRightActions = hasAddButton || showReset;
13929
14929
  const currentOpenField = fields.find((field) => field.key === openKey);
13930
14930
  const editorPopoverStyle = react.useMemo(() => {
13931
14931
  if (currentOpenField?.type === "keyword") {
13932
14932
  return { width: CHIPFILTER_LISTBOX_WIDTH };
13933
14933
  }
13934
- if (currentOpenField?.type === "date" && (currentOpenField.presets?.length ?? 0) > 0) {
14934
+ if ((currentOpenField?.type === "date" || currentOpenField?.type === "period") && getFieldPresets(currentOpenField).length > 0) {
13935
14935
  return { width: CHIPFILTER_DATE_PRESET_LISTBOX_WIDTH };
13936
14936
  }
13937
14937
  return { width: CHIPFILTER_LISTBOX_WIDTH };
13938
- }, [currentOpenField?.type, currentOpenField?.presets?.length]);
14938
+ }, [currentOpenField]);
13939
14939
  const setActive = (next) => {
13940
14940
  if (!activeKeys) setInternalActiveKeys(next);
13941
14941
  onActiveKeysChange?.(next);
@@ -13954,6 +14954,12 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
13954
14954
  const def = field ? resolveFieldDefault(field) : void 0;
13955
14955
  runSearch(emitValue(key, def !== void 0 ? def : null));
13956
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
+ };
13957
14963
  const reset = react.useCallback(() => {
13958
14964
  const next = fields.reduce((acc, field) => {
13959
14965
  const def = resolveFieldDefault(field);
@@ -14044,7 +15050,15 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14044
15050
  flushRun();
14045
15051
  runGroupIndex = groupIndex;
14046
15052
  }
14047
- const showClear = !isAtDefaultValue(field, values[field.key]);
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
+ };
14048
15062
  const node = isInlineDateField(field) ? /* @__PURE__ */ jsxRuntime.jsx(
14049
15063
  InlineDatePresetFilter,
14050
15064
  {
@@ -14054,15 +15068,27 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14054
15068
  warning,
14055
15069
  showClear,
14056
15070
  onValueChange: (next) => emitValueAndSearch(field.key, next),
14057
- onClear: () => clearValue(field.key)
15071
+ onClear: handleClear
14058
15072
  },
14059
15073
  field.key
14060
- ) : field.type === "custom" ? /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: field.render?.({
14061
- value: values[field.key],
14062
- disabled: disabled || field.disabled,
14063
- warning,
14064
- onValueChange: (next) => emitValueAndSearch(field.key, next)
14065
- }) }, field.key) : /* @__PURE__ */ jsxRuntime.jsx(
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(
14066
15092
  FilterChip,
14067
15093
  {
14068
15094
  field,
@@ -14072,9 +15098,10 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14072
15098
  disabled: disabled || field.disabled,
14073
15099
  warning,
14074
15100
  showClear,
15101
+ showClearWhenEmpty: addable,
14075
15102
  refCallback: (node2) => triggerRefs.current.set(field.key, node2),
14076
15103
  onOpen: () => setOpenKey(openKey === field.key ? null : field.key),
14077
- onClear: () => clearValue(field.key)
15104
+ onClear: handleClear
14078
15105
  },
14079
15106
  field.key
14080
15107
  );
@@ -14095,8 +15122,8 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14095
15122
  })(),
14096
15123
  hasRightActions && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
14097
15124
  /* @__PURE__ */ jsxRuntime.jsx(Divider, { inset: true }),
14098
- showAddButton && /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open: addOpen, onOpenChange: setAddOpen, children: [
14099
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
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(
14100
15127
  "button",
14101
15128
  {
14102
15129
  type: "button",
@@ -14121,8 +15148,8 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14121
15148
  ]
14122
15149
  }
14123
15150
  ) }),
14124
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
14125
- RPopover9__namespace.Content,
15151
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
15152
+ RPopover10__namespace.Content,
14126
15153
  {
14127
15154
  side: "bottom",
14128
15155
  align: "start",
@@ -14142,7 +15169,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14142
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",
14143
15170
  // 부모 flex gap(6px)은 칩 사이 간격 기준이라, 필터 추가 버튼과의 12px 간격을 맞추려면
14144
15171
  // 부족한 만큼(12px - gap)을 추가로 띄운다. 필터 추가가 없을 땐 divider와 이미 12px이라 불필요.
14145
- showAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
15172
+ hasAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
14146
15173
  ),
14147
15174
  style: {
14148
15175
  color: disabled || !hasValue ? "var(--cmp-textLink-content-disabled)" : "var(--cmp-textLink-content-default)"
@@ -14162,17 +15189,17 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14162
15189
  )
14163
15190
  ] })
14164
15191
  ] }),
14165
- /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open: openKey != null, onOpenChange: (next) => !next && setOpenKey(null), children: [
15192
+ /* @__PURE__ */ jsxRuntime.jsxs(RPopover10__namespace.Root, { open: openKey != null, onOpenChange: (next) => !next && setOpenKey(null), children: [
14166
15193
  /* @__PURE__ */ jsxRuntime.jsx(
14167
- RPopover9__namespace.Anchor,
15194
+ RPopover10__namespace.Anchor,
14168
15195
  {
14169
15196
  virtualRef: {
14170
15197
  current: openKey ? triggerRefs.current.get(openKey) : null
14171
15198
  }
14172
15199
  }
14173
15200
  ),
14174
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
14175
- RPopover9__namespace.Content,
15201
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
15202
+ RPopover10__namespace.Content,
14176
15203
  {
14177
15204
  side: "bottom",
14178
15205
  align: "start",
@@ -14226,6 +15253,7 @@ function FilterChip({
14226
15253
  disabled,
14227
15254
  warning,
14228
15255
  showClear,
15256
+ showClearWhenEmpty,
14229
15257
  refCallback,
14230
15258
  onOpen,
14231
15259
  onClear
@@ -14295,7 +15323,7 @@ function FilterChip({
14295
15323
  }
14296
15324
  )
14297
15325
  ] }),
14298
- applied && !disabled && showClear && /* @__PURE__ */ jsxRuntime.jsx(
15326
+ !disabled && showClear && (applied || showClearWhenEmpty) && /* @__PURE__ */ jsxRuntime.jsx(
14299
15327
  "span",
14300
15328
  {
14301
15329
  role: "button",
@@ -14327,7 +15355,7 @@ function EditorPanel({
14327
15355
  onValueChange,
14328
15356
  onClose
14329
15357
  }) {
14330
- const wrapsInPaddedTop = field.type === "date";
15358
+ const wrapsInPaddedTop = field.type === "date" || field.type === "period";
14331
15359
  const isMulti = field.type === "multi";
14332
15360
  const selectableValues = isMulti ? (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value) : [];
14333
15361
  const selectedValues = isMulti ? toArrayValue(value) : [];
@@ -14406,6 +15434,17 @@ function FilterEditor({
14406
15434
  disabled,
14407
15435
  onValueChange
14408
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
+ }
14409
15448
  if (field.type === "date") {
14410
15449
  return /* @__PURE__ */ jsxRuntime.jsx(
14411
15450
  DatePresetEditor,
@@ -14445,7 +15484,7 @@ function DatePresetEditor({
14445
15484
  disabled,
14446
15485
  onValueChange
14447
15486
  }) {
14448
- const presets = field.presets ?? [];
15487
+ const presets = getFieldPresets(field);
14449
15488
  const [selectedId, setSelectedId] = react.useState(() => matchPresetId(presets, value));
14450
15489
  const syncedInitialValueRef = react.useRef(!isEmptyValue(value));
14451
15490
  react.useEffect(() => {
@@ -14456,9 +15495,9 @@ function DatePresetEditor({
14456
15495
  const selectedPreset = presets.find((preset) => preset.value === selectedId);
14457
15496
  const selectPreset = (preset) => {
14458
15497
  setSelectedId(preset.value);
14459
- if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : null);
15498
+ if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : preset.value);
14460
15499
  };
14461
- if (presets.length === 0) {
15500
+ if (field.type === "date" && presets.length === 0) {
14462
15501
  return /* @__PURE__ */ jsxRuntime.jsx(
14463
15502
  SDatePicker,
14464
15503
  {
@@ -14494,6 +15533,130 @@ function DatePresetEditor({
14494
15533
  )
14495
15534
  ] });
14496
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
+ }
14497
15660
  function InlineDatePresetFilter({
14498
15661
  field,
14499
15662
  value,
@@ -14576,8 +15739,8 @@ function InlineDatePresetFilter({
14576
15739
  applied && (rangeValue ? (
14577
15740
  // 뱃지 모양은 그대로 두고, 눌렀을 때 캘린더가 열려 기간을 직접 다시 고를 수 있게 한다.
14578
15741
  // 직접 고르면 프리셋 선택은 해제된다.
14579
- /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open: pickerOpen, onOpenChange: setPickerOpen, children: [
14580
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
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(
14581
15744
  "button",
14582
15745
  {
14583
15746
  type: "button",
@@ -14586,8 +15749,8 @@ function InlineDatePresetFilter({
14586
15749
  children: badge
14587
15750
  }
14588
15751
  ) }),
14589
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
14590
- RPopover9__namespace.Content,
15752
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
15753
+ RPopover10__namespace.Content,
14591
15754
  {
14592
15755
  side: "bottom",
14593
15756
  align: "start",
@@ -15336,7 +16499,7 @@ var SChipInput = /* @__PURE__ */ react.forwardRef(function SChipInput2({
15336
16499
  }
15337
16500
  );
15338
16501
  });
15339
- var FONT2 = {
16502
+ var FONT3 = {
15340
16503
  sm: {
15341
16504
  fontSize: 12,
15342
16505
  paddingX: "var(--cmp-textinput-sm-paddingX)",
@@ -15384,7 +16547,7 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
15384
16547
  className,
15385
16548
  style
15386
16549
  }, ref) {
15387
- const f = FONT2[size];
16550
+ const f = FONT3[size];
15388
16551
  const [ruleError, setRuleError] = react.useState("");
15389
16552
  const hasValue = value !== void 0 && value !== null && String(value) !== "";
15390
16553
  useFormField({
@@ -15398,6 +16561,10 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
15398
16561
  });
15399
16562
  const resolvedError = error || ruleError !== "";
15400
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
+ });
15401
16568
  return /* @__PURE__ */ jsxRuntime.jsx(
15402
16569
  SField,
15403
16570
  {
@@ -15434,7 +16601,7 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
15434
16601
  /* @__PURE__ */ jsxRuntime.jsx(
15435
16602
  "input",
15436
16603
  {
15437
- ref,
16604
+ ref: mergeRefs(ref, valueTooltip.valueRef),
15438
16605
  name,
15439
16606
  type: "text",
15440
16607
  value: value ?? "",
@@ -15460,9 +16627,12 @@ var SBarcodeInput = /* @__PURE__ */ react.forwardRef(
15460
16627
  fontSize: f.fontSize,
15461
16628
  color: disabled ? "var(--cmp-textinput-text-disabled)" : "var(--cmp-textinput-text-default)",
15462
16629
  ...inputStyle
15463
- }
16630
+ },
16631
+ onMouseEnter: valueTooltip.onMouseEnter,
16632
+ onMouseLeave: valueTooltip.onMouseLeave
15464
16633
  }
15465
16634
  ),
16635
+ valueTooltip.tooltip,
15466
16636
  suffix,
15467
16637
  clearable && hasValue && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
15468
16638
  SGhostButton,
@@ -15498,7 +16668,9 @@ var TIMEPICKER_SIZE_CONFIG = {
15498
16668
  fontSize: 12,
15499
16669
  lineHeight: 20,
15500
16670
  minWidth: 80,
15501
- fieldMinWidth: 128
16671
+ fieldMinWidth: 128,
16672
+ // 상한 등급 md
16673
+ fieldMaxWidth: 160
15502
16674
  },
15503
16675
  md: {
15504
16676
  height: V3("md-height"),
@@ -15510,7 +16682,9 @@ var TIMEPICKER_SIZE_CONFIG = {
15510
16682
  fontSize: 14,
15511
16683
  lineHeight: 24,
15512
16684
  minWidth: 96,
15513
- fieldMinWidth: 160
16685
+ fieldMinWidth: 160,
16686
+ // 상한 등급 lg
16687
+ fieldMaxWidth: 240
15514
16688
  }
15515
16689
  };
15516
16690
  function clampTimePart(value, min, max) {
@@ -15834,11 +17008,12 @@ function STimePicker({
15834
17008
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
15835
17009
  width,
15836
17010
  minWidth: dims.fieldMinWidth,
17011
+ maxWidth: dims.fieldMaxWidth,
15837
17012
  disabled,
15838
17013
  className,
15839
17014
  style,
15840
- children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open, onOpenChange: commitOpen, children: [
15841
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__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(
15842
17017
  "div",
15843
17018
  {
15844
17019
  ref: triggerRef,
@@ -15898,8 +17073,8 @@ function STimePicker({
15898
17073
  ]
15899
17074
  }
15900
17075
  ) }),
15901
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
15902
- RPopover9__namespace.Content,
17076
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
17077
+ RPopover10__namespace.Content,
15903
17078
  {
15904
17079
  side: "bottom",
15905
17080
  align: "start",
@@ -15950,6 +17125,10 @@ var MIDDAY_FIELD_MIN_WIDTH = {
15950
17125
  sm: 178,
15951
17126
  md: 216
15952
17127
  };
17128
+ var MIDDAY_FIELD_MAX_WIDTH = {
17129
+ sm: 240,
17130
+ md: 240
17131
+ };
15953
17132
  function RangeTimeSelector({
15954
17133
  value,
15955
17134
  useMeridiem,
@@ -16296,11 +17475,12 @@ function STimeRangePicker({
16296
17475
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
16297
17476
  width,
16298
17477
  minWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MIN_WIDTH[size] : dims.fieldMinWidth,
17478
+ maxWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MAX_WIDTH[size] : dims.fieldMaxWidth,
16299
17479
  disabled,
16300
17480
  className,
16301
17481
  style,
16302
- children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open, onOpenChange: commitOpen, children: [
16303
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__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(
16304
17484
  "div",
16305
17485
  {
16306
17486
  ref: triggerRef,
@@ -16360,8 +17540,8 @@ function STimeRangePicker({
16360
17540
  ]
16361
17541
  }
16362
17542
  ) }),
16363
- /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
16364
- RPopover9__namespace.Content,
17543
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover10__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
17544
+ RPopover10__namespace.Content,
16365
17545
  {
16366
17546
  side: "bottom",
16367
17547
  align: "start",
@@ -16402,7 +17582,9 @@ exports.GNB_MENU_WIDTH = GNB_MENU_WIDTH;
16402
17582
  exports.GNB_RAIL = GNB_RAIL;
16403
17583
  exports.GNB_RAIL_WIDTH = GNB_RAIL_WIDTH;
16404
17584
  exports.ICONS = ICONS;
17585
+ exports.IMAGE_FITS = IMAGE_FITS;
16405
17586
  exports.PAGE_BACKGROUNDS = PAGE_BACKGROUNDS;
17587
+ exports.PAGE_CONTENT_HEIGHTS = PAGE_CONTENT_HEIGHTS;
16406
17588
  exports.RangeCalendar = RangeCalendar;
16407
17589
  exports.SActionModal = SActionModal;
16408
17590
  exports.SBadge = SBadge;
@@ -16419,6 +17601,8 @@ exports.SChipInput = SChipInput;
16419
17601
  exports.SCircleProgress = SCircleProgress;
16420
17602
  exports.SConfirmModal = SConfirmModal;
16421
17603
  exports.SDatePicker = SDatePicker;
17604
+ exports.SDatePickerMonthListbox = SDatePickerMonthListbox;
17605
+ exports.SDatePickerYearListbox = SDatePickerYearListbox;
16422
17606
  exports.SDateRangePicker = SDateRangePicker;
16423
17607
  exports.SDivider = SDivider;
16424
17608
  exports.SDraggableGroup = SDraggableGroup;
@@ -16436,6 +17620,7 @@ exports.SGhostButton = SGhostButton;
16436
17620
  exports.SGnb = SGnb;
16437
17621
  exports.SGuide = SGuide;
16438
17622
  exports.SIcon = SIcon;
17623
+ exports.SImage = SImage;
16439
17624
  exports.SInput = SInput;
16440
17625
  exports.SKeyValueTable = SKeyValueTable;
16441
17626
  exports.SLAYOUT_MIN_WIDTH = SLAYOUT_MIN_WIDTH;
@@ -16460,6 +17645,7 @@ exports.SRadioButton = SRadioButton;
16460
17645
  exports.SRadioGroup = SRadioGroup;
16461
17646
  exports.SSPLITTER_UNITS = SSPLITTER_UNITS;
16462
17647
  exports.SScrollArea = SScrollArea;
17648
+ exports.SSearchInput = SSearchInput;
16463
17649
  exports.SSectionHeaderCard = SSectionHeaderCard2;
16464
17650
  exports.SSelect = SSelect2;
16465
17651
  exports.SSplitter = SSplitter2;