sellmate-design-system-react 0.1.10 → 0.1.11

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 (129) hide show
  1. package/dist/components/SActionModal/SActionModal.cjs +3 -1
  2. package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
  3. package/dist/components/SActionModal/SActionModal.js +3 -1
  4. package/dist/components/SActionModal/SActionModal.js.map +1 -1
  5. package/dist/components/SBarcodeInput/SBarcodeInput.cjs +15 -2
  6. package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
  7. package/dist/components/SBarcodeInput/SBarcodeInput.d.ts +3 -1
  8. package/dist/components/SBarcodeInput/SBarcodeInput.js +15 -2
  9. package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
  10. package/dist/components/SCalendar/SCalendar.cjs +2 -0
  11. package/dist/components/SCalendar/SCalendar.cjs.map +1 -1
  12. package/dist/components/SCalendar/SCalendar.js +2 -0
  13. package/dist/components/SCalendar/SCalendar.js.map +1 -1
  14. package/dist/components/SChip/SChip.cjs +2 -0
  15. package/dist/components/SChip/SChip.cjs.map +1 -1
  16. package/dist/components/SChip/SChip.js +2 -0
  17. package/dist/components/SChip/SChip.js.map +1 -1
  18. package/dist/components/SChipInput/SChipInput.cjs +21 -3
  19. package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
  20. package/dist/components/SChipInput/SChipInput.d.ts +5 -0
  21. package/dist/components/SChipInput/SChipInput.js +21 -3
  22. package/dist/components/SChipInput/SChipInput.js.map +1 -1
  23. package/dist/components/SChipInput/index.d.ts +1 -1
  24. package/dist/components/SConfirmModal/SConfirmModal.cjs +13 -10
  25. package/dist/components/SConfirmModal/SConfirmModal.cjs.map +1 -1
  26. package/dist/components/SConfirmModal/SConfirmModal.d.ts +7 -4
  27. package/dist/components/SConfirmModal/SConfirmModal.js +13 -10
  28. package/dist/components/SConfirmModal/SConfirmModal.js.map +1 -1
  29. package/dist/components/SDatePicker/SDatePicker.cjs +15 -2
  30. package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
  31. package/dist/components/SDatePicker/SDatePicker.d.ts +3 -1
  32. package/dist/components/SDatePicker/SDatePicker.js +15 -2
  33. package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
  34. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +15 -2
  35. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  36. package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +3 -1
  37. package/dist/components/SDateRangePicker/SDateRangePicker.js +15 -2
  38. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  39. package/dist/components/SDropdownButton/SDropdownButton.cjs +2 -0
  40. package/dist/components/SDropdownButton/SDropdownButton.cjs.map +1 -1
  41. package/dist/components/SDropdownButton/SDropdownButton.js +2 -0
  42. package/dist/components/SDropdownButton/SDropdownButton.js.map +1 -1
  43. package/dist/components/SField/SField.cjs +13 -2
  44. package/dist/components/SField/SField.cjs.map +1 -1
  45. package/dist/components/SField/SField.d.ts +4 -1
  46. package/dist/components/SField/SField.js +13 -2
  47. package/dist/components/SField/SField.js.map +1 -1
  48. package/dist/components/SFilePicker/SFilePicker.cjs +16 -3
  49. package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
  50. package/dist/components/SFilePicker/SFilePicker.d.ts +3 -1
  51. package/dist/components/SFilePicker/SFilePicker.js +16 -3
  52. package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
  53. package/dist/components/SGhostButton/SGhostButton.cjs +2 -0
  54. package/dist/components/SGhostButton/SGhostButton.cjs.map +1 -1
  55. package/dist/components/SGhostButton/SGhostButton.js +2 -0
  56. package/dist/components/SGhostButton/SGhostButton.js.map +1 -1
  57. package/dist/components/SGuide/SGuide.cjs +2 -0
  58. package/dist/components/SGuide/SGuide.cjs.map +1 -1
  59. package/dist/components/SGuide/SGuide.js +2 -0
  60. package/dist/components/SGuide/SGuide.js.map +1 -1
  61. package/dist/components/SInput/SInput.cjs +15 -2
  62. package/dist/components/SInput/SInput.cjs.map +1 -1
  63. package/dist/components/SInput/SInput.d.ts +4 -1
  64. package/dist/components/SInput/SInput.js +15 -2
  65. package/dist/components/SInput/SInput.js.map +1 -1
  66. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +496 -206
  67. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  68. package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +27 -6
  69. package/dist/components/SKeyValueTable/SKeyValueTable.js +495 -205
  70. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  71. package/dist/components/SLoadingModal/SLoadingModal.cjs +3 -1
  72. package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
  73. package/dist/components/SLoadingModal/SLoadingModal.js +3 -1
  74. package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
  75. package/dist/components/SModalContainer/SModalContainer.cjs +3 -1
  76. package/dist/components/SModalContainer/SModalContainer.cjs.map +1 -1
  77. package/dist/components/SModalContainer/SModalContainer.js +3 -1
  78. package/dist/components/SModalContainer/SModalContainer.js.map +1 -1
  79. package/dist/components/SNumberInput/SNumberInput.cjs +15 -2
  80. package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
  81. package/dist/components/SNumberInput/SNumberInput.d.ts +2 -0
  82. package/dist/components/SNumberInput/SNumberInput.js +15 -2
  83. package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
  84. package/dist/components/SPopover/SPopover.cjs +2 -0
  85. package/dist/components/SPopover/SPopover.cjs.map +1 -1
  86. package/dist/components/SPopover/SPopover.js +2 -0
  87. package/dist/components/SPopover/SPopover.js.map +1 -1
  88. package/dist/components/SPortal/SPortal.cjs +2 -0
  89. package/dist/components/SPortal/SPortal.cjs.map +1 -1
  90. package/dist/components/SPortal/SPortal.js +2 -0
  91. package/dist/components/SPortal/SPortal.js.map +1 -1
  92. package/dist/components/SSelect/SSelect.cjs +530 -262
  93. package/dist/components/SSelect/SSelect.cjs.map +1 -1
  94. package/dist/components/SSelect/SSelect.d.ts +4 -2
  95. package/dist/components/SSelect/SSelect.js +531 -262
  96. package/dist/components/SSelect/SSelect.js.map +1 -1
  97. package/dist/components/STable/STable.cjs +529 -261
  98. package/dist/components/STable/STable.cjs.map +1 -1
  99. package/dist/components/STable/STable.js +530 -261
  100. package/dist/components/STable/STable.js.map +1 -1
  101. package/dist/components/STextLink/STextLink.cjs +2 -0
  102. package/dist/components/STextLink/STextLink.cjs.map +1 -1
  103. package/dist/components/STextLink/STextLink.js +2 -0
  104. package/dist/components/STextLink/STextLink.js.map +1 -1
  105. package/dist/components/STextarea/STextarea.cjs +15 -2
  106. package/dist/components/STextarea/STextarea.cjs.map +1 -1
  107. package/dist/components/STextarea/STextarea.d.ts +2 -0
  108. package/dist/components/STextarea/STextarea.js +15 -2
  109. package/dist/components/STextarea/STextarea.js.map +1 -1
  110. package/dist/components/SToast/SToast.cjs +2 -0
  111. package/dist/components/SToast/SToast.cjs.map +1 -1
  112. package/dist/components/SToast/SToast.js +2 -0
  113. package/dist/components/SToast/SToast.js.map +1 -1
  114. package/dist/components/SToast/SToastContainer.cjs +2 -0
  115. package/dist/components/SToast/SToastContainer.cjs.map +1 -1
  116. package/dist/components/SToast/SToastContainer.js +2 -0
  117. package/dist/components/SToast/SToastContainer.js.map +1 -1
  118. package/dist/components/STooltip/STooltip.cjs +2 -0
  119. package/dist/components/STooltip/STooltip.cjs.map +1 -1
  120. package/dist/components/STooltip/STooltip.js +2 -0
  121. package/dist/components/STooltip/STooltip.js.map +1 -1
  122. package/dist/index.cjs +412 -184
  123. package/dist/index.cjs.map +1 -1
  124. package/dist/index.d.ts +0 -1
  125. package/dist/index.js +413 -184
  126. package/dist/index.js.map +1 -1
  127. package/dist/lib/portal-container.d.ts +7 -0
  128. package/dist/styles.css +61 -37
  129. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3533,10 +3533,15 @@ var FLOATING_SLIDE_ANIM = [
3533
3533
  "data-[state=closed]:data-[side=right]:animate-floating-out-right"
3534
3534
  ].join(" ");
3535
3535
  var PortalContainerContext = react.createContext(null);
3536
+ var InsideModalContext = react.createContext(false);
3536
3537
  var PortalContainerProvider = PortalContainerContext.Provider;
3538
+ var InsideModalProvider = InsideModalContext.Provider;
3537
3539
  function usePortalContainer() {
3538
3540
  return react.useContext(PortalContainerContext) ?? void 0;
3539
3541
  }
3542
+ function useInsideModal() {
3543
+ return react.useContext(InsideModalContext);
3544
+ }
3540
3545
  var SIZE_CONFIG = {
3541
3546
  sm: {
3542
3547
  gap: 4,
@@ -4647,7 +4652,7 @@ function SModalContainer({
4647
4652
  className: "absolute right-[16px] top-[16px] z-10"
4648
4653
  }
4649
4654
  ),
4650
- /* @__PURE__ */ jsxRuntime.jsx(PortalContainerProvider, { value: portalContainer, children })
4655
+ /* @__PURE__ */ jsxRuntime.jsx(InsideModalProvider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(PortalContainerProvider, { value: portalContainer, children }) })
4651
4656
  ]
4652
4657
  }
4653
4658
  )
@@ -4693,7 +4698,8 @@ function SConfirmModal({
4693
4698
  tagSize = "sm",
4694
4699
  tagColor = "grey",
4695
4700
  slotLabel,
4696
- children,
4701
+ tagSlot,
4702
+ optionSlot,
4697
4703
  mainButtonLabel = "\uD655\uC778",
4698
4704
  mainButtonName,
4699
4705
  subButtonLabel,
@@ -4703,6 +4709,9 @@ function SConfirmModal({
4703
4709
  }) {
4704
4710
  const iconName = TYPE_ICON[type];
4705
4711
  const mainStyle = MAIN_BUTTON_STYLE[mainButtonName ?? DEFAULT_MAIN_BUTTON[type]];
4712
+ const tagNode = tagSlot ?? (tagLabel ? /* @__PURE__ */ jsxRuntime.jsx(STag, { shape: tagShape, size: tagSize, color: tagColor, label: tagLabel }) : null);
4713
+ const optionNode = optionSlot ?? (slotLabel ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: slotLabel }) : null);
4714
+ const hasBody = topMessage.length > 0 || bottomMessage.length > 0 || tagNode != null || optionNode != null;
4706
4715
  return /* @__PURE__ */ jsxRuntime.jsx(
4707
4716
  SModalContainer,
4708
4717
  {
@@ -4728,15 +4737,12 @@ function SConfirmModal({
4728
4737
  }
4729
4738
  )
4730
4739
  ] }),
4731
- (topMessage.length > 0 || bottomMessage.length > 0 || tagLabel || slotLabel || children) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-[20px] text-center text-[12px] font-normal leading-[20px] text-[color:var(--cmp-modal-confirm-message-color)]", children: [
4740
+ hasBody && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-[20px] text-center text-[12px] font-normal leading-[20px] text-[color:var(--cmp-modal-confirm-message-color)] w-full", children: [
4732
4741
  topMessage.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-[2px]", children: topMessage.map((m, i) => /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0", children: m }, i)) }),
4733
- (tagLabel || slotLabel || children) && // content-box: 흰 박스 + 1px 테두리 + 패딩 + 14/700 (원본 sd-confirm-modal__content-box)
4734
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-[8px] rounded-[8px] border border-solid border-[color:var(--color-grey-30)] bg-white px-[16px] py-[12px] text-[14px] font-bold leading-[22px]", children: [
4735
- tagLabel && /* @__PURE__ */ jsxRuntime.jsx(STag, { shape: tagShape, size: tagSize, color: tagColor, label: tagLabel }),
4736
- slotLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: slotLabel }),
4737
- children
4738
- ] }),
4739
- bottomMessage.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-[2px]", children: bottomMessage.map((m, i) => /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0", children: m }, i)) })
4742
+ tagNode != null && // tag 위치 content-box: 흰 박스 + 1px 테두리 + 패딩 + 14/700 (원본 sd-confirm-modal__content-box)
4743
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center gap-[8px] rounded-[8px] border border-solid border-[color:var(--color-grey-30)] bg-white px-[16px] py-[12px] text-[14px] font-bold leading-[22px] w-full", children: tagNode }),
4744
+ bottomMessage.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-[2px]", children: bottomMessage.map((m, i) => /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0", children: m }, i)) }),
4745
+ optionNode != null && optionNode
4740
4746
  ] })
4741
4747
  ] }),
4742
4748
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-[8px]", children: [
@@ -5237,6 +5243,7 @@ var SField = react.forwardRef(function SField2({
5237
5243
  name,
5238
5244
  label = "",
5239
5245
  icon,
5246
+ iconColor,
5240
5247
  labelTooltip = "",
5241
5248
  labelTooltipProps,
5242
5249
  labelWidth,
@@ -5314,7 +5321,15 @@ var SField = react.forwardRef(function SField2({
5314
5321
  lineHeight: `${s.lineHeight}px`
5315
5322
  },
5316
5323
  children: [
5317
- icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, style: { marginRight: s.labelGap } }),
5324
+ icon && /* @__PURE__ */ jsxRuntime.jsx(
5325
+ SIcon,
5326
+ {
5327
+ name: icon,
5328
+ size: s.icon,
5329
+ color: iconColor,
5330
+ style: { marginRight: s.labelGap }
5331
+ }
5332
+ ),
5318
5333
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap", children: label }),
5319
5334
  (labelTooltip !== "" || labelTooltipProps) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", style: { marginLeft: s.labelGap }, children: /* @__PURE__ */ jsxRuntime.jsx(
5320
5335
  STooltip,
@@ -5364,7 +5379,7 @@ var SField = react.forwardRef(function SField2({
5364
5379
  borderBottomLeftRadius: s.radius - 1
5365
5380
  },
5366
5381
  children: [
5367
- icon && !label && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon }),
5382
+ icon && !label && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, color: iconColor }),
5368
5383
  effectiveAddonLabel
5369
5384
  ]
5370
5385
  }
@@ -5436,6 +5451,7 @@ var SSelect = react.forwardRef(function SSelect2({
5436
5451
  label,
5437
5452
  labelWidth,
5438
5453
  icon,
5454
+ iconColor,
5439
5455
  labelTooltip,
5440
5456
  addonLabel,
5441
5457
  addonAlign,
@@ -5447,18 +5463,15 @@ var SSelect = react.forwardRef(function SSelect2({
5447
5463
  }, ref) {
5448
5464
  const multi = type === "multi" || type === "multi_depth";
5449
5465
  const portalContainer = usePortalContainer();
5466
+ const insideModal = useInsideModal();
5450
5467
  const [search, setSearch] = react.useState("");
5451
5468
  const [ruleError, setRuleError] = react.useState("");
5452
5469
  const [open, setOpen] = react.useState(false);
5470
+ const [pendingOpen, setPendingOpen] = react.useState(false);
5471
+ const [focusedIndex, setFocusedIndex] = react.useState(-1);
5453
5472
  const triggerRef = react.useRef(null);
5454
- react.useImperativeHandle(
5455
- ref,
5456
- () => ({
5457
- focus: () => triggerRef.current?.focus(),
5458
- open: () => setOpen(true)
5459
- }),
5460
- []
5461
- );
5473
+ const searchInputRef = react.useRef(null);
5474
+ const listRef = react.useRef(null);
5462
5475
  const flat = react.useMemo(() => flattenOptions(options), [options]);
5463
5476
  const selectable = react.useMemo(() => selectableOptions(flat), [flat]);
5464
5477
  const hasLeaf = react.useMemo(() => flat.some((o) => !o.isGroup), [flat]);
@@ -5476,6 +5489,46 @@ var SSelect = react.forwardRef(function SSelect2({
5476
5489
  const arr = Array.isArray(value) ? value : [value];
5477
5490
  return arr.map((v) => v != null && typeof v === "object" ? v.value : v).filter((v) => v != null && v !== "").map(String);
5478
5491
  }, [value]);
5492
+ const selectedRef = react.useRef(selected);
5493
+ selectedRef.current = selected;
5494
+ const emit2 = (next) => {
5495
+ if (!onValueChange) return;
5496
+ const mapped = next.map(
5497
+ (v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
5498
+ );
5499
+ onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
5500
+ };
5501
+ const commitOpen = (next) => {
5502
+ setOpen(next);
5503
+ onOpenChange?.(next);
5504
+ if (!next) {
5505
+ setPendingOpen(false);
5506
+ setSearch("");
5507
+ if (rules && rules.length > 0) {
5508
+ setRuleError(runRules(multi ? selectedRef.current : selectedRef.current[0] ?? "", rules));
5509
+ }
5510
+ }
5511
+ };
5512
+ const requestOpen = (next) => {
5513
+ if (next && insideModal && portalContainer == null) {
5514
+ setPendingOpen(true);
5515
+ return;
5516
+ }
5517
+ commitOpen(next);
5518
+ };
5519
+ react.useEffect(() => {
5520
+ if (!insideModal || portalContainer == null || !pendingOpen) return;
5521
+ setPendingOpen(false);
5522
+ commitOpen(true);
5523
+ }, [insideModal, pendingOpen, portalContainer]);
5524
+ react.useImperativeHandle(
5525
+ ref,
5526
+ () => ({
5527
+ focus: () => triggerRef.current?.focus(),
5528
+ open: () => requestOpen(true)
5529
+ }),
5530
+ [insideModal, portalContainer]
5531
+ );
5479
5532
  useFormField({
5480
5533
  name,
5481
5534
  validate: () => {
@@ -5485,13 +5538,6 @@ var SSelect = react.forwardRef(function SSelect2({
5485
5538
  },
5486
5539
  resetValidation: () => setRuleError("")
5487
5540
  });
5488
- const emit2 = (next) => {
5489
- if (!onValueChange) return;
5490
- const mapped = next.map(
5491
- (v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
5492
- );
5493
- onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
5494
- };
5495
5541
  const filtered = react.useMemo(() => {
5496
5542
  if (!showSearch || search.trim() === "") return flat;
5497
5543
  const q = search.trim().toLowerCase();
@@ -5502,6 +5548,16 @@ var SSelect = react.forwardRef(function SSelect2({
5502
5548
  const allValues = selectable.map((o) => o.value);
5503
5549
  const allSelected = allValues.length > 0 && allValues.every((v) => selected.includes(v));
5504
5550
  const hasItems = filtered.some((o) => !o.isGroup);
5551
+ const navigableItems = react.useMemo(() => {
5552
+ const items = [];
5553
+ if (multi && useSelectAll && allValues.length > 0) {
5554
+ items.push({ value: "__select_all__", isSelectAll: true });
5555
+ }
5556
+ filtered.forEach((o) => {
5557
+ if (!o.isGroup && !o.disabled) items.push({ value: o.value });
5558
+ });
5559
+ return items;
5560
+ }, [allValues.length, filtered, multi, useSelectAll]);
5505
5561
  const triggerText = (() => {
5506
5562
  if (selected.length === 0) return placeholder;
5507
5563
  if (multi) {
@@ -5511,26 +5567,119 @@ var SSelect = react.forwardRef(function SSelect2({
5511
5567
  return labelByValue.get(selected[0]) ?? selected[0];
5512
5568
  })();
5513
5569
  const isPlaceholder = selected.length === 0;
5514
- const selectStore = /* @__PURE__ */ jsxRuntime.jsxs(
5515
- Ariakit__namespace.SelectProvider,
5570
+ const handleSelect = (nextValue) => {
5571
+ if (multi) {
5572
+ const next = selected.includes(nextValue) ? selected.filter((v) => v !== nextValue) : [...selected, nextValue];
5573
+ emit2(next);
5574
+ return;
5575
+ }
5576
+ emit2([nextValue]);
5577
+ commitOpen(false);
5578
+ };
5579
+ const handleSelectAll = () => {
5580
+ emit2(allSelected ? [] : allValues);
5581
+ };
5582
+ react.useEffect(() => {
5583
+ if (!open) {
5584
+ setFocusedIndex(-1);
5585
+ return;
5586
+ }
5587
+ setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
5588
+ }, [open, navigableItems.length]);
5589
+ react.useEffect(() => {
5590
+ if (!open) return;
5591
+ setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
5592
+ }, [search, open, navigableItems.length]);
5593
+ react.useEffect(() => {
5594
+ if (!open || focusedIndex < 0) return;
5595
+ const list = listRef.current;
5596
+ const focusedEl = list?.querySelector('[data-select-focused="true"]');
5597
+ if (!list || !focusedEl) return;
5598
+ const listRect = list.getBoundingClientRect();
5599
+ const itemRect = focusedEl.getBoundingClientRect();
5600
+ if (itemRect.top < listRect.top) {
5601
+ list.scrollTop += itemRect.top - listRect.top;
5602
+ } else if (itemRect.bottom > listRect.bottom) {
5603
+ list.scrollTop += itemRect.bottom - listRect.bottom;
5604
+ }
5605
+ }, [focusedIndex, open]);
5606
+ const moveFocusedIndex = (direction) => {
5607
+ if (!open || navigableItems.length === 0) return;
5608
+ setFocusedIndex((current) => {
5609
+ if (direction === "next") {
5610
+ return current < 0 ? 0 : (current + 1) % navigableItems.length;
5611
+ }
5612
+ return current <= 0 ? navigableItems.length - 1 : current - 1;
5613
+ });
5614
+ };
5615
+ const selectFocusedItem = () => {
5616
+ if (focusedIndex < 0 || focusedIndex >= navigableItems.length) return;
5617
+ const focused = navigableItems[focusedIndex];
5618
+ if (focused.isSelectAll) {
5619
+ handleSelectAll();
5620
+ return;
5621
+ }
5622
+ handleSelect(focused.value);
5623
+ };
5624
+ const handleKeyDown = (event) => {
5625
+ if (!open) {
5626
+ if ((event.key === "ArrowDown" || event.key === "ArrowUp") && !effectiveDisabled) {
5627
+ event.preventDefault();
5628
+ requestOpen(true);
5629
+ }
5630
+ return;
5631
+ }
5632
+ if (event.key === "ArrowDown") {
5633
+ event.preventDefault();
5634
+ moveFocusedIndex("next");
5635
+ return;
5636
+ }
5637
+ if (event.key === "ArrowUp") {
5638
+ event.preventDefault();
5639
+ moveFocusedIndex("prev");
5640
+ return;
5641
+ }
5642
+ if (event.key === "Enter") {
5643
+ event.preventDefault();
5644
+ selectFocusedItem();
5645
+ return;
5646
+ }
5647
+ if (event.key === "Escape") {
5648
+ event.preventDefault();
5649
+ commitOpen(false);
5650
+ }
5651
+ };
5652
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5653
+ SField,
5516
5654
  {
5517
- animated: portalContainer == null ? 150 : false,
5518
- value: multi ? selected : selected[0] ?? "",
5519
- setValue: (v) => emit2(Array.isArray(v) ? v : v === "" ? [] : [v]),
5520
- open,
5521
- setOpen: (o) => {
5522
- setOpen(o);
5523
- onOpenChange?.(o);
5524
- if (!o && rules && rules.length > 0) {
5525
- setRuleError(runRules(multi ? selected : selected[0] ?? "", rules));
5526
- }
5527
- },
5655
+ label,
5656
+ labelWidth,
5657
+ icon,
5658
+ iconColor,
5659
+ labelTooltip,
5660
+ labelTooltipProps,
5661
+ addonLabel,
5662
+ addonAlign,
5663
+ hint,
5664
+ error: error || ruleError !== "",
5665
+ status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
5666
+ errorMessage: ruleError !== "" ? ruleError : errorMessage,
5667
+ disabled: effectiveDisabled,
5668
+ width,
5669
+ className,
5670
+ style,
5528
5671
  children: [
5672
+ name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selected.join(",") }),
5529
5673
  /* @__PURE__ */ jsxRuntime.jsxs(
5530
- Ariakit__namespace.Select,
5674
+ "button",
5531
5675
  {
5532
5676
  ref: triggerRef,
5677
+ type: "button",
5533
5678
  disabled: effectiveDisabled,
5679
+ "aria-haspopup": "listbox",
5680
+ "aria-expanded": open,
5681
+ onClick: () => requestOpen(!open),
5682
+ onKeyDown: handleKeyDown,
5534
5683
  className: "flex h-full w-full cursor-pointer items-center justify-between gap-[8px] bg-transparent px-[12px] text-left text-[12px] leading-[20px] outline-none disabled:cursor-not-allowed",
5535
5684
  style: {
5536
5685
  color: effectiveDisabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
@@ -5555,178 +5704,229 @@ var SSelect = react.forwardRef(function SSelect2({
5555
5704
  ]
5556
5705
  }
5557
5706
  ),
5558
- /* @__PURE__ */ jsxRuntime.jsxs(
5559
- Ariakit__namespace.SelectPopover,
5707
+ /* @__PURE__ */ jsxRuntime.jsx(
5708
+ SPortal,
5560
5709
  {
5561
- portal: true,
5562
- portalElement: portalContainer,
5563
- gutter: 4,
5564
- sameWidth: !dropdownWidth,
5565
- className: "z-50 overflow-auto rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)] data-[enter]:animate-floating-in-bottom data-[leave]:animate-floating-out-bottom",
5710
+ open,
5711
+ onClose: () => commitOpen(false),
5712
+ onOpenChange: (next) => {
5713
+ if (next) requestOpen(true);
5714
+ },
5715
+ anchorRef: triggerRef,
5716
+ placement: "bottom",
5717
+ align: "start",
5718
+ offset: 4,
5719
+ fit: !dropdownWidth,
5720
+ onOpenAutoFocus: (event) => {
5721
+ if (!showSearch) {
5722
+ event.preventDefault();
5723
+ return;
5724
+ }
5725
+ event.preventDefault();
5726
+ requestAnimationFrame(() => searchInputRef.current?.focus());
5727
+ },
5728
+ onCloseAutoFocus: (event) => {
5729
+ event.preventDefault();
5730
+ triggerRef.current?.focus();
5731
+ },
5732
+ className: "rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] py-[4px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
5566
5733
  style: {
5567
- maxHeight: dropdownHeight,
5568
5734
  ...dropdownWidth ? { width: dropdownWidth } : {},
5569
5735
  maxWidth: maxDropdownWidth
5570
5736
  },
5571
- children: [
5572
- showSearch && // 원본 sd-select-list-item-search: sticky + 검색 아이콘 + 테두리 input + focus-within + clear
5573
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-[1] bg-[var(--cmp-listBox-bg)] px-[8px] py-[4px]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-[6px] rounded-[4px] border border-solid border-[color:var(--color-grey-45)] bg-white px-[8px] py-[4px] focus-within:border-[color:var(--sys-color-field-border-focus)]", children: [
5574
- /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "search", size: 16, color: "var(--color-grey-70)", className: "shrink-0" }),
5575
- /* @__PURE__ */ jsxRuntime.jsx(
5576
- Ariakit__namespace.Combobox,
5577
- {
5578
- autoSelect: true,
5579
- placeholder: "\uAC80\uC0C9",
5580
- className: "min-w-0 flex-1 border-none bg-transparent text-[12px] leading-[20px] text-[color:var(--cmp-select-text-default)] outline-none placeholder:text-[color:var(--color-grey-55)]"
5581
- }
5582
- ),
5583
- search !== "" && /* @__PURE__ */ jsxRuntime.jsx(
5584
- SGhostButton,
5585
- {
5586
- icon: "close",
5587
- size: "xxs",
5588
- intent: "default",
5589
- ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
5590
- onClick: () => setSearch(""),
5591
- className: "shrink-0"
5592
- }
5593
- )
5594
- ] }) }),
5595
- multi && useSelectAll && /* @__PURE__ */ jsxRuntime.jsx(
5596
- "button",
5597
- {
5598
- type: "button",
5599
- className: "flex w-full items-center px-[12px] py-[4px] text-left text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)] hover:bg-[var(--cmp-listItem-bg-hover)] hover:text-[color:var(--cmp-listItem-content-hover)]",
5600
- onClick: () => emit2(allSelected ? [] : allValues),
5601
- children: allSelectedLabel
5602
- }
5603
- ),
5604
- hasItems ? /* @__PURE__ */ jsxRuntime.jsx(ItemList, { items: filtered, multi, selected, useSearch: showSearch }) : (
5605
- // 원본 sd-select-listbox__empty (표시할 항목이 없을 때)
5606
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-[12px] py-[12px] text-left text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-disabled)]", children: "\uD45C\uC2DC\uD560 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." })
5607
- )
5608
- ]
5737
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
5738
+ "div",
5739
+ {
5740
+ ref: listRef,
5741
+ className: "overflow-auto [scrollbar-width:auto] [&::-webkit-scrollbar]:w-[8px] [&::-webkit-scrollbar]:bg-[var(--color-grey-25)] [&::-webkit-scrollbar:horizontal]:h-[8px] [&::-webkit-scrollbar-thumb]:rounded-[4px] [&::-webkit-scrollbar-thumb]:bg-[var(--color-grey-45)] [&::-webkit-scrollbar-track]:bg-[var(--color-grey-25)]",
5742
+ style: { maxHeight: dropdownHeight },
5743
+ onMouseLeave: () => setFocusedIndex(-1),
5744
+ children: [
5745
+ showSearch && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-[1] bg-[var(--cmp-listBox-bg)] px-[8px] py-[4px]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-[6px] rounded-[4px] border border-solid border-[color:var(--color-grey-45)] bg-white px-[8px] py-[4px] focus-within:border-[color:var(--sys-color-field-border-focus)]", children: [
5746
+ /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "search", size: 16, color: "var(--color-grey-70)", className: "shrink-0" }),
5747
+ /* @__PURE__ */ jsxRuntime.jsx(
5748
+ "input",
5749
+ {
5750
+ ref: searchInputRef,
5751
+ value: search,
5752
+ onChange: (e) => setSearch(e.target.value),
5753
+ onKeyDown: handleKeyDown,
5754
+ placeholder: "\uAC80\uC0C9",
5755
+ className: "min-w-0 flex-1 border-none bg-transparent text-[12px] leading-[20px] text-[color:var(--cmp-select-text-default)] outline-none placeholder:text-[color:var(--color-grey-55)]"
5756
+ }
5757
+ ),
5758
+ search !== "" && /* @__PURE__ */ jsxRuntime.jsx(
5759
+ SGhostButton,
5760
+ {
5761
+ icon: "close",
5762
+ size: "xxs",
5763
+ intent: "default",
5764
+ ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
5765
+ onClick: () => setSearch(""),
5766
+ className: "shrink-0"
5767
+ }
5768
+ )
5769
+ ] }) }),
5770
+ hasItems ? /* @__PURE__ */ jsxRuntime.jsx(
5771
+ ItemList,
5772
+ {
5773
+ items: filtered,
5774
+ multi,
5775
+ selected,
5776
+ focusedValue: navigableItems[focusedIndex]?.isSelectAll ? "__select_all__" : navigableItems[focusedIndex]?.value,
5777
+ showSelectAll: multi && useSelectAll && allValues.length > 0,
5778
+ selectAllLabel: allSelectedLabel,
5779
+ selectAllSelected: allSelected,
5780
+ onSelectAll: handleSelectAll,
5781
+ onSelect: handleSelect,
5782
+ onHoverValue: (value2) => {
5783
+ const index = navigableItems.findIndex(
5784
+ (item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
5785
+ );
5786
+ if (index >= 0) setFocusedIndex(index);
5787
+ }
5788
+ }
5789
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-[12px] py-[12px] text-left text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-disabled)]", children: "\uD45C\uC2DC\uD560 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." })
5790
+ ]
5791
+ }
5792
+ )
5609
5793
  }
5610
5794
  )
5611
5795
  ]
5612
5796
  }
5613
5797
  );
5614
- const control = showSearch ? /* @__PURE__ */ jsxRuntime.jsx(Ariakit__namespace.ComboboxProvider, { value: search, setValue: setSearch, resetValueOnHide: true, children: selectStore }) : selectStore;
5615
- return /* @__PURE__ */ jsxRuntime.jsxs(
5616
- SField,
5617
- {
5618
- label,
5619
- labelWidth,
5620
- icon,
5621
- labelTooltip,
5622
- labelTooltipProps,
5623
- addonLabel,
5624
- addonAlign,
5625
- hint,
5626
- error: error || ruleError !== "",
5627
- status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
5628
- errorMessage: ruleError !== "" ? ruleError : errorMessage,
5629
- disabled: effectiveDisabled,
5630
- width,
5631
- className,
5632
- style,
5633
- children: [
5634
- name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selected.join(",") }),
5635
- control
5636
- ]
5637
- }
5638
- );
5639
5798
  });
5640
5799
  SSelect.displayName = "SSelect";
5641
5800
  function ItemList({
5642
5801
  items,
5643
5802
  multi,
5644
5803
  selected,
5645
- useSearch
5804
+ focusedValue,
5805
+ showSelectAll,
5806
+ selectAllLabel,
5807
+ selectAllSelected,
5808
+ onSelectAll,
5809
+ onSelect,
5810
+ onHoverValue
5646
5811
  }) {
5647
5812
  const itemBase = cn(
5648
- // group: 체크박스 inverse(group-data-[active-item])를 위해 필요
5649
- "group flex cursor-pointer select-none items-center gap-[8px] text-[12px] leading-[20px] outline-none",
5813
+ "group flex w-full cursor-pointer select-none items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none",
5650
5814
  "text-[color:var(--cmp-listItem-content-default)]",
5651
- // Ariakit은 disabled 항목에 aria-disabled를 붙이므로 aria-disabled 변형으로 스타일 (data-disabled 아님)
5652
- "aria-disabled:cursor-not-allowed aria-disabled:text-[color:var(--cmp-listItem-content-disabled)]"
5653
- );
5654
- const activeClass = "data-[active-item]:bg-[var(--cmp-listItem-bg-hover)] data-[active-item]:text-[color:var(--cmp-listItem-content-hover)]";
5655
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: items.map((o) => {
5656
- const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
5657
- if (o.isGroup) {
5658
- const isDepth1 = o.depth === 1;
5659
- return /* @__PURE__ */ jsxRuntime.jsx(
5660
- "div",
5661
- {
5662
- className: cn(
5663
- "select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
5664
- isDepth1 ? "border-t border-solid border-[color:var(--cmp-listItem-depth1-border-color)] bg-[var(--cmp-listItem-depth1-bg-default)] font-bold" : "bg-[var(--cmp-listItem-depth2-middle-bg)] font-medium",
5665
- // 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
5666
- // 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
5667
- o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
5668
- o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
5669
- ),
5670
- style: { paddingLeft: padLeft },
5671
- children: o.label
5672
- },
5673
- o.value
5674
- );
5675
- }
5676
- const isSelected = selected.includes(o.value);
5677
- return /* @__PURE__ */ jsxRuntime.jsxs(
5678
- Ariakit__namespace.SelectItem,
5815
+ "disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listItem-content-disabled)]"
5816
+ );
5817
+ const activeClass = "hover:bg-[var(--cmp-listItem-bg-hover)] hover:text-[color:var(--cmp-listItem-content-hover)] focus:bg-[var(--cmp-listItem-bg-hover)] focus:text-[color:var(--cmp-listItem-content-hover)]";
5818
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "listbox", "aria-multiselectable": multi || void 0, children: [
5819
+ showSelectAll && /* @__PURE__ */ jsxRuntime.jsxs(
5820
+ "button",
5679
5821
  {
5680
- value: o.value,
5681
- disabled: o.disabled,
5682
- accessibleWhenDisabled: true,
5822
+ type: "button",
5823
+ "data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
5824
+ "data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
5683
5825
  className: cn(
5684
5826
  itemBase,
5685
- isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
5686
- // Ariakit은 disabled 항목도 hover 시 active(data-active-item) 처리하므로,
5687
- // 비활성 항목에는 hover 하이라이트를 적용하지 않아 회색 비활성 스타일을 유지한다.
5688
- // (원본 Stencil의 `--focused...:not(--disabled)` 규칙과 동일한 의도)
5689
- !o.disabled && activeClass
5827
+ focusedValue === "__select_all__" && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]"
5690
5828
  ),
5691
- style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
5692
- render: useSearch ? /* @__PURE__ */ jsxRuntime.jsx(Ariakit__namespace.ComboboxItem, {}) : void 0,
5829
+ style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: 12 },
5830
+ onMouseEnter: () => onHoverValue("__select_all__"),
5831
+ onClick: onSelectAll,
5693
5832
  children: [
5694
- multi && // hover(active) 시 checkbox inverse — 파란 hover 배경 위에서 checked/unchecked 모두 흰 테두리 + 투명 배경
5695
5833
  /* @__PURE__ */ jsxRuntime.jsx(
5696
5834
  "span",
5697
5835
  {
5698
5836
  className: cn(
5699
5837
  "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
5700
- o.disabled ? (
5701
- // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
5702
- "cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
5703
- ) : cn(
5704
- "group-data-[active-item]:border-[color:var(--cmp-checkbox-checked-border-inverse)]",
5705
- isSelected ? "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)] group-data-[active-item]:bg-transparent" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-transparent"
5706
- )
5838
+ selectAllSelected ? focusedValue === "__select_all__" ? "border-[color:var(--cmp-checkbox-checked-border-inverse)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-[var(--cmp-checkbox-unchecked-bg-default)]"
5707
5839
  ),
5708
- children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(
5840
+ children: selectAllSelected && /* @__PURE__ */ jsxRuntime.jsx(
5709
5841
  SIcon,
5710
5842
  {
5711
5843
  name: "checkboxCheck",
5712
5844
  size: 12,
5713
- color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
5845
+ color: "var(--cmp-checkbox-checked-icon-default)"
5714
5846
  }
5715
5847
  )
5716
5848
  }
5717
5849
  ),
5718
- isHtmlLabel(o.label) ? /* @__PURE__ */ jsxRuntime.jsx(
5719
- "span",
5720
- {
5721
- className: "flex-1 truncate",
5722
- dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
5723
- }
5724
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: o.label })
5850
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
5725
5851
  ]
5726
- },
5727
- o.value
5728
- );
5729
- }) });
5852
+ }
5853
+ ),
5854
+ items.map((o) => {
5855
+ const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
5856
+ if (o.isGroup) {
5857
+ const isDepth1 = o.depth === 1;
5858
+ return /* @__PURE__ */ jsxRuntime.jsx(
5859
+ "div",
5860
+ {
5861
+ className: cn(
5862
+ "select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
5863
+ isDepth1 ? "border-t border-solid border-[color:var(--cmp-listItem-depth1-border-color)] bg-[var(--cmp-listItem-depth1-bg-default)] font-bold" : "bg-[var(--cmp-listItem-depth2-middle-bg)] font-medium",
5864
+ // 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
5865
+ // 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
5866
+ o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
5867
+ o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
5868
+ ),
5869
+ style: { paddingLeft: padLeft },
5870
+ children: o.label
5871
+ },
5872
+ o.value
5873
+ );
5874
+ }
5875
+ const isSelected = selected.includes(o.value);
5876
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5877
+ "button",
5878
+ {
5879
+ type: "button",
5880
+ disabled: o.disabled,
5881
+ role: "option",
5882
+ "aria-selected": isSelected,
5883
+ "data-select-focused": focusedValue === o.value ? "true" : void 0,
5884
+ "data-select-active": focusedValue === o.value ? "true" : void 0,
5885
+ className: cn(
5886
+ itemBase,
5887
+ isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
5888
+ focusedValue === o.value && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]",
5889
+ activeClass
5890
+ ),
5891
+ style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
5892
+ onMouseEnter: () => onHoverValue(o.value),
5893
+ onClick: () => onSelect(o.value),
5894
+ children: [
5895
+ multi && /* @__PURE__ */ jsxRuntime.jsx(
5896
+ "span",
5897
+ {
5898
+ className: cn(
5899
+ "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
5900
+ o.disabled ? (
5901
+ // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
5902
+ "cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
5903
+ ) : cn(
5904
+ isSelected ? focusedValue === o.value ? "border-[color:var(--cmp-checkbox-checked-border-inverse)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-[var(--cmp-checkbox-unchecked-bg-default)]"
5905
+ )
5906
+ ),
5907
+ children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(
5908
+ SIcon,
5909
+ {
5910
+ name: "checkboxCheck",
5911
+ size: 12,
5912
+ color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
5913
+ }
5914
+ )
5915
+ }
5916
+ ),
5917
+ isHtmlLabel(o.label) ? /* @__PURE__ */ jsxRuntime.jsx(
5918
+ "span",
5919
+ {
5920
+ className: "flex-1 truncate",
5921
+ dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
5922
+ }
5923
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: o.label })
5924
+ ]
5925
+ },
5926
+ o.value
5927
+ );
5928
+ })
5929
+ ] });
5730
5930
  }
5731
5931
  var DEFAULT_ROW_HEIGHT = 44;
5732
5932
  var DENSE_ROW_HEIGHT = 32;
@@ -6002,7 +6202,7 @@ var STable = react.forwardRef(function STable2({
6002
6202
  };
6003
6203
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[var(--cmp-table-radius)] rounded-t-none" : "rounded-[var(--cmp-table-radius)]";
6004
6204
  const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-body-paddingY-default)";
6005
- const cellPadX = dense ? "var(--cmp-table-body-paddingX-default)" : "var(--cmp-table-body-paddingX-wide)";
6205
+ const cellPadX = "var(--cmp-table-body-paddingX-wide)";
6006
6206
  const rowH = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-body-default-height)";
6007
6207
  const totalColSpan = visibleCols.length + (selectable ? 1 : 0);
6008
6208
  const stickyInfo = (i) => {
@@ -6249,6 +6449,7 @@ var SInput = react.forwardRef(function SInput2({
6249
6449
  type = "text",
6250
6450
  size = "sm",
6251
6451
  icon,
6452
+ iconColor,
6252
6453
  prefix,
6253
6454
  suffix,
6254
6455
  clearable = false,
@@ -6302,6 +6503,7 @@ var SInput = react.forwardRef(function SInput2({
6302
6503
  labelTooltip,
6303
6504
  labelTooltipProps,
6304
6505
  icon,
6506
+ iconColor,
6305
6507
  size,
6306
6508
  hint,
6307
6509
  error: resolvedError,
@@ -6403,6 +6605,7 @@ var STextarea = react.forwardRef(function STextarea2({
6403
6605
  label,
6404
6606
  labelWidth,
6405
6607
  icon,
6608
+ iconColor,
6406
6609
  labelTooltip,
6407
6610
  labelTooltipProps,
6408
6611
  addonLabel,
@@ -6439,6 +6642,7 @@ var STextarea = react.forwardRef(function STextarea2({
6439
6642
  label,
6440
6643
  labelWidth,
6441
6644
  icon,
6645
+ iconColor,
6442
6646
  labelTooltip,
6443
6647
  labelTooltipProps,
6444
6648
  addonLabel,
@@ -6571,6 +6775,7 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
6571
6775
  label,
6572
6776
  labelWidth,
6573
6777
  icon,
6778
+ iconColor,
6574
6779
  labelTooltip,
6575
6780
  labelTooltipProps,
6576
6781
  addonLabel,
@@ -6729,6 +6934,7 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
6729
6934
  label,
6730
6935
  labelWidth,
6731
6936
  icon,
6937
+ iconColor,
6732
6938
  labelTooltip,
6733
6939
  labelTooltipProps,
6734
6940
  addonLabel,
@@ -7204,6 +7410,7 @@ function SDatePicker({
7204
7410
  label,
7205
7411
  labelWidth,
7206
7412
  icon,
7413
+ iconColor,
7207
7414
  labelTooltip,
7208
7415
  labelTooltipProps,
7209
7416
  addonLabel,
@@ -7242,6 +7449,7 @@ function SDatePicker({
7242
7449
  label,
7243
7450
  labelWidth,
7244
7451
  icon,
7452
+ iconColor,
7245
7453
  labelTooltip,
7246
7454
  labelTooltipProps,
7247
7455
  addonLabel,
@@ -7510,6 +7718,7 @@ function SDateRangePicker({
7510
7718
  label,
7511
7719
  labelWidth,
7512
7720
  icon,
7721
+ iconColor,
7513
7722
  labelTooltip,
7514
7723
  labelTooltipProps,
7515
7724
  addonLabel,
@@ -7552,6 +7761,7 @@ function SDateRangePicker({
7552
7761
  label,
7553
7762
  labelWidth,
7554
7763
  icon,
7764
+ iconColor,
7555
7765
  labelTooltip,
7556
7766
  labelTooltipProps,
7557
7767
  addonLabel,
@@ -7813,6 +8023,7 @@ function SFilePicker({
7813
8023
  label,
7814
8024
  labelWidth,
7815
8025
  icon,
8026
+ iconColor,
7816
8027
  labelTooltip,
7817
8028
  labelTooltipProps,
7818
8029
  addonLabel,
@@ -7982,7 +8193,7 @@ function SFilePicker({
7982
8193
  unmountOnHide: true,
7983
8194
  getAnchorRect: () => contentBoxRef.current?.getBoundingClientRect() ?? null,
7984
8195
  style: { minWidth: contentBoxRef.current?.offsetWidth },
7985
- className: "z-50 box-border flex flex-col items-stretch gap-[8px] rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] p-[8px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
8196
+ className: "z-50 box-border flex flex-col items-start gap-[8px] rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] p-[8px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
7986
8197
  children: filesArr.map((f, i) => /* @__PURE__ */ jsxRuntime.jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i))
7987
8198
  }
7988
8199
  )
@@ -7996,6 +8207,7 @@ function SFilePicker({
7996
8207
  label,
7997
8208
  labelWidth,
7998
8209
  icon,
8210
+ iconColor,
7999
8211
  labelTooltip,
8000
8212
  labelTooltipProps,
8001
8213
  addonLabel,
@@ -8018,21 +8230,26 @@ function SFilePicker({
8018
8230
  var cssLen2 = (v) => v == null ? void 0 : typeof v === "number" ? `${v}px` : v;
8019
8231
  function SKeyValueTable({
8020
8232
  fields = [],
8233
+ values = {},
8021
8234
  search = false,
8022
8235
  radius = "default",
8023
- dense = false,
8024
8236
  onChange,
8025
8237
  onSearch,
8026
8238
  className,
8027
8239
  style
8028
8240
  }) {
8029
8241
  if (fields.length === 0) return null;
8030
- const emit2 = (name, value) => onChange?.({ name, value });
8031
- const cellHeight = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-keyValue-height)";
8032
- const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-keyValue-body-paddingY)";
8242
+ const emit2 = (name, value) => onChange?.({
8243
+ name,
8244
+ value,
8245
+ values: { ...values, [name]: value }
8246
+ });
8247
+ const cellHeight = "var(--cmp-table-keyValue-height)";
8248
+ const cellPadY = "var(--cmp-table-keyValue-body-paddingY)";
8033
8249
  const renderField = (f) => {
8034
8250
  if (f.render) return f.render;
8035
- const opt = f.options ?? {};
8251
+ const opt = { ...f.options ?? {} };
8252
+ if (Object.prototype.hasOwnProperty.call(values, f.name)) opt.value = values[f.name];
8036
8253
  const up = (v) => emit2(f.name, v);
8037
8254
  switch (f.type) {
8038
8255
  case "input":
@@ -8144,7 +8361,10 @@ function SKeyValueTable({
8144
8361
  rowSpan: f.tdRowSpan && f.tdRowSpan > 1 ? f.tdRowSpan : void 0,
8145
8362
  colSpan: f.tdColSpan && f.tdColSpan > 1 ? f.tdColSpan : void 0,
8146
8363
  className: cn(
8147
- "box-border bg-[var(--sys-color-bg-frame)] px-[var(--cmp-table-keyValue-body-paddingX-default)] align-middle",
8364
+ "box-border bg-[var(--sys-color-bg-frame)] align-middle",
8365
+ // text 타입(및 미지정 기본값)은 읽기 전용 콘텐츠라 넓은 paddingX(wide),
8366
+ // 폼 필드 셀은 default paddingX 유지
8367
+ f.type == null || f.type === "text" ? "px-[var(--cmp-table-keyValue-body-paddingX-wide)]" : "px-[var(--cmp-table-keyValue-body-paddingX-default)]",
8148
8368
  cellBorder,
8149
8369
  f.tdClass
8150
8370
  ),
@@ -8637,6 +8857,7 @@ var SChipInput = react.forwardRef(function SChipInput2({
8637
8857
  error,
8638
8858
  useReset = false,
8639
8859
  maxCount,
8860
+ metaPlacement = "end",
8640
8861
  duplicateLabel,
8641
8862
  suggestions = [],
8642
8863
  loadingSuggestions = false,
@@ -8650,6 +8871,7 @@ var SChipInput = react.forwardRef(function SChipInput2({
8650
8871
  width,
8651
8872
  status,
8652
8873
  icon,
8874
+ iconColor,
8653
8875
  labelTooltip,
8654
8876
  labelTooltipProps,
8655
8877
  className,
@@ -8784,6 +9006,7 @@ var SChipInput = react.forwardRef(function SChipInput2({
8784
9006
  labelWidth,
8785
9007
  labelHeight: `calc(var(--cmp-chipInput-${size}-minHeight) + 2px)`,
8786
9008
  icon,
9009
+ iconColor,
8787
9010
  labelTooltip,
8788
9011
  labelTooltipProps,
8789
9012
  size,
@@ -8931,7 +9154,11 @@ var SChipInput = react.forwardRef(function SChipInput2({
8931
9154
  showMetaInfo && /* @__PURE__ */ jsxRuntime.jsxs(
8932
9155
  "div",
8933
9156
  {
8934
- className: "s-chip-input__meta ml-auto flex items-center gap-8",
9157
+ className: cn(
9158
+ "s-chip-input__meta flex items-center gap-8",
9159
+ metaPlacement === "end" && "ml-auto"
9160
+ ),
9161
+ "data-placement": metaPlacement,
8935
9162
  onMouseDown: (e) => e.preventDefault(),
8936
9163
  children: [
8937
9164
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -9071,6 +9298,7 @@ var SBarcodeInput = react.forwardRef(
9071
9298
  label,
9072
9299
  labelWidth,
9073
9300
  icon,
9301
+ iconColor,
9074
9302
  labelTooltip,
9075
9303
  labelTooltipProps,
9076
9304
  addonLabel,
@@ -9102,6 +9330,7 @@ var SBarcodeInput = react.forwardRef(
9102
9330
  label,
9103
9331
  labelWidth,
9104
9332
  icon,
9333
+ iconColor,
9105
9334
  labelTooltip,
9106
9335
  labelTooltipProps,
9107
9336
  addonLabel,
@@ -9213,7 +9442,6 @@ exports.SLinearProgress = SLinearProgress;
9213
9442
  exports.SLoadingContainer = SLoadingContainer;
9214
9443
  exports.SLoadingModal = SLoadingModal;
9215
9444
  exports.SLoadingViewport = SLoadingViewport;
9216
- exports.SModalContainer = SModalContainer;
9217
9445
  exports.SNumberInput = SNumberInput;
9218
9446
  exports.SPagination = SPagination;
9219
9447
  exports.SPopover = SPopover;