sellmate-design-system-react 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +84 -21
  2. package/dist/components/SActionModal/SActionModal.cjs +2 -0
  3. package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
  4. package/dist/components/SActionModal/SActionModal.js +2 -0
  5. package/dist/components/SActionModal/SActionModal.js.map +1 -1
  6. package/dist/components/SBarcodeInput/SBarcodeInput.cjs +2 -0
  7. package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
  8. package/dist/components/SBarcodeInput/SBarcodeInput.js +2 -0
  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 +2 -0
  19. package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
  20. package/dist/components/SChipInput/SChipInput.js +2 -0
  21. package/dist/components/SChipInput/SChipInput.js.map +1 -1
  22. package/dist/components/SConfirmModal/SConfirmModal.cjs +2 -0
  23. package/dist/components/SConfirmModal/SConfirmModal.cjs.map +1 -1
  24. package/dist/components/SConfirmModal/SConfirmModal.js +2 -0
  25. package/dist/components/SConfirmModal/SConfirmModal.js.map +1 -1
  26. package/dist/components/SDatePicker/SDatePicker.cjs +2 -0
  27. package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
  28. package/dist/components/SDatePicker/SDatePicker.js +2 -0
  29. package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
  30. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +2 -0
  31. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  32. package/dist/components/SDateRangePicker/SDateRangePicker.js +2 -0
  33. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  34. package/dist/components/SField/SField.cjs +2 -0
  35. package/dist/components/SField/SField.cjs.map +1 -1
  36. package/dist/components/SField/SField.js +2 -0
  37. package/dist/components/SField/SField.js.map +1 -1
  38. package/dist/components/SFilePicker/SFilePicker.cjs +2 -0
  39. package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
  40. package/dist/components/SFilePicker/SFilePicker.js +2 -0
  41. package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
  42. package/dist/components/SGhostButton/SGhostButton.cjs +2 -0
  43. package/dist/components/SGhostButton/SGhostButton.cjs.map +1 -1
  44. package/dist/components/SGhostButton/SGhostButton.d.ts +3 -1
  45. package/dist/components/SGhostButton/SGhostButton.js +2 -0
  46. package/dist/components/SGhostButton/SGhostButton.js.map +1 -1
  47. package/dist/components/SGnb/SGnb.cjs +394 -213
  48. package/dist/components/SGnb/SGnb.cjs.map +1 -1
  49. package/dist/components/SGnb/SGnb.d.ts +16 -12
  50. package/dist/components/SGnb/SGnb.js +375 -214
  51. package/dist/components/SGnb/SGnb.js.map +1 -1
  52. package/dist/components/SGnb/gnb.config.d.ts +35 -11
  53. package/dist/components/SGnb/index.d.ts +1 -1
  54. package/dist/components/SGuide/SGuide.cjs +2 -0
  55. package/dist/components/SGuide/SGuide.cjs.map +1 -1
  56. package/dist/components/SGuide/SGuide.js +2 -0
  57. package/dist/components/SGuide/SGuide.js.map +1 -1
  58. package/dist/components/SInput/SInput.cjs +2 -0
  59. package/dist/components/SInput/SInput.cjs.map +1 -1
  60. package/dist/components/SInput/SInput.js +2 -0
  61. package/dist/components/SInput/SInput.js.map +1 -1
  62. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +2 -0
  63. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  64. package/dist/components/SKeyValueTable/SKeyValueTable.js +2 -0
  65. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  66. package/dist/components/SLayout/SLayout.cjs +26 -8
  67. package/dist/components/SLayout/SLayout.cjs.map +1 -1
  68. package/dist/components/SLayout/SLayout.d.ts +15 -9
  69. package/dist/components/SLayout/SLayout.js +26 -8
  70. package/dist/components/SLayout/SLayout.js.map +1 -1
  71. package/dist/components/SLayout/index.d.ts +1 -1
  72. package/dist/components/SLoadingModal/SLoadingModal.cjs +2 -0
  73. package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
  74. package/dist/components/SLoadingModal/SLoadingModal.js +2 -0
  75. package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
  76. package/dist/components/SModalContainer/SModalContainer.cjs +2 -0
  77. package/dist/components/SModalContainer/SModalContainer.cjs.map +1 -1
  78. package/dist/components/SModalContainer/SModalContainer.js +2 -0
  79. package/dist/components/SModalContainer/SModalContainer.js.map +1 -1
  80. package/dist/components/SNumberInput/SNumberInput.cjs +2 -0
  81. package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
  82. package/dist/components/SNumberInput/SNumberInput.js +2 -0
  83. package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
  84. package/dist/components/SPage/SPage.cjs +27 -9
  85. package/dist/components/SPage/SPage.cjs.map +1 -1
  86. package/dist/components/SPage/SPage.d.ts +2 -2
  87. package/dist/components/SPage/SPage.js +27 -9
  88. package/dist/components/SPage/SPage.js.map +1 -1
  89. package/dist/components/SPopover/SPopover.cjs +2 -0
  90. package/dist/components/SPopover/SPopover.cjs.map +1 -1
  91. package/dist/components/SPopover/SPopover.js +2 -0
  92. package/dist/components/SPopover/SPopover.js.map +1 -1
  93. package/dist/components/SSelect/SSelect.cjs +2 -0
  94. package/dist/components/SSelect/SSelect.cjs.map +1 -1
  95. package/dist/components/SSelect/SSelect.js +2 -0
  96. package/dist/components/SSelect/SSelect.js.map +1 -1
  97. package/dist/components/STable/STable.cjs +3 -1
  98. package/dist/components/STable/STable.cjs.map +1 -1
  99. package/dist/components/STable/STable.js +3 -1
  100. package/dist/components/STable/STable.js.map +1 -1
  101. package/dist/components/STextarea/STextarea.cjs +2 -0
  102. package/dist/components/STextarea/STextarea.cjs.map +1 -1
  103. package/dist/components/STextarea/STextarea.js +2 -0
  104. package/dist/components/STextarea/STextarea.js.map +1 -1
  105. package/dist/components/STimePicker/STimePicker.cjs +3834 -0
  106. package/dist/components/STimePicker/STimePicker.cjs.map +1 -0
  107. package/dist/components/STimePicker/STimePicker.d.ts +50 -0
  108. package/dist/components/STimePicker/STimePicker.js +3811 -0
  109. package/dist/components/STimePicker/STimePicker.js.map +1 -0
  110. package/dist/components/STimePicker/index.d.ts +2 -0
  111. package/dist/components/STimePicker/timepicker.config.d.ts +23 -0
  112. package/dist/components/STimeRangePicker/STimeRangePicker.cjs +3908 -0
  113. package/dist/components/STimeRangePicker/STimeRangePicker.cjs.map +1 -0
  114. package/dist/components/STimeRangePicker/STimeRangePicker.d.ts +53 -0
  115. package/dist/components/STimeRangePicker/STimeRangePicker.js +3885 -0
  116. package/dist/components/STimeRangePicker/STimeRangePicker.js.map +1 -0
  117. package/dist/components/STimeRangePicker/index.d.ts +1 -0
  118. package/dist/components/STimeRangePicker/time-range-picker.config.d.ts +9 -0
  119. package/dist/components/SToast/SToast.cjs +2 -0
  120. package/dist/components/SToast/SToast.cjs.map +1 -1
  121. package/dist/components/SToast/SToast.js +2 -0
  122. package/dist/components/SToast/SToast.js.map +1 -1
  123. package/dist/components/SToast/SToastContainer.cjs +2 -0
  124. package/dist/components/SToast/SToastContainer.cjs.map +1 -1
  125. package/dist/components/SToast/SToastContainer.js +2 -0
  126. package/dist/components/SToast/SToastContainer.js.map +1 -1
  127. package/dist/components/STooltip/STooltip.cjs +2 -0
  128. package/dist/components/STooltip/STooltip.cjs.map +1 -1
  129. package/dist/components/STooltip/STooltip.js +2 -0
  130. package/dist/components/STooltip/STooltip.js.map +1 -1
  131. package/dist/index.cjs +1011 -136
  132. package/dist/index.cjs.map +1 -1
  133. package/dist/index.d.ts +3 -0
  134. package/dist/index.js +1006 -136
  135. package/dist/index.js.map +1 -1
  136. package/dist/styles.css +475 -74
  137. package/dist/theme.css +85 -70
  138. package/package.json +4 -1
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { clsx } from 'clsx';
2
3
  import { extendTailwindMerge } from 'tailwind-merge';
3
4
  import { forwardRef, useState, useEffect, useRef, useCallback, createContext, useImperativeHandle, useMemo, useContext, useId, useLayoutEffect, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
@@ -12,7 +13,6 @@ import * as RDialog from '@radix-ui/react-dialog';
12
13
  import * as RCheckbox from '@radix-ui/react-checkbox';
13
14
  import * as Ariakit from '@ariakit/react';
14
15
 
15
- // src/lib/cn.ts
16
16
  var twMerge = extendTailwindMerge({
17
17
  extend: {
18
18
  classGroups: {
@@ -3915,6 +3915,7 @@ function SGhostButton({
3915
3915
  size = "sm",
3916
3916
  intent = "default",
3917
3917
  ariaLabel = "",
3918
+ ariaPressed,
3918
3919
  tooltipText,
3919
3920
  disabled = false,
3920
3921
  onClick,
@@ -3951,6 +3952,7 @@ function SGhostButton({
3951
3952
  disabled,
3952
3953
  "aria-disabled": disabled ? "true" : void 0,
3953
3954
  "aria-label": ariaLabel.trim() || void 0,
3955
+ "aria-pressed": ariaPressed,
3954
3956
  tabIndex: disabled ? -1 : void 0,
3955
3957
  onClick: (e) => {
3956
3958
  if (disabled) {
@@ -4878,13 +4880,27 @@ var belt = (p) => `var(--cmp-gnbBelt-${p})`;
4878
4880
  var box = (p) => `var(--cmp-gnbBox-${p})`;
4879
4881
  var light = (p) => `var(--cmp-light-${p})`;
4880
4882
  var dark = (p) => `var(--cmp-dark-${p})`;
4881
- var GNB_LAYOUT = {
4882
- // 사용자 명명: type="box" → gnbBox 토큰(둥금), type="belt" → gnbBelt 토큰(각짐)
4883
- box: {
4883
+ var GNB_HEADER = {
4884
+ fix: {
4885
+ topHeight: belt("top-height"),
4886
+ topPaddingX: belt("top-paddingX"),
4887
+ topGap: belt("top-gap"),
4888
+ logoHeight: belt("logo-height")
4889
+ },
4890
+ full: {
4891
+ topHeight: box("top-height"),
4884
4892
  topPaddingX: box("paddingX"),
4885
4893
  topGap: box("gap"),
4886
4894
  // 상단바 gap (버튼 간 세부 gap 은 --cmp-gnbBox-button-gap)
4887
- bodyPadding: box("menu-body-padding"),
4895
+ logoHeight: box("logo-height")
4896
+ }
4897
+ };
4898
+ var GNB_MENU = {
4899
+ box: {
4900
+ // menu.body.padding 은 전방향 토큰 → 세로 패딩과 아이템 좌우 여백 양쪽에 쓴다
4901
+ bodyPaddingY: box("menu-body-padding"),
4902
+ itemMarginX: box("menu-body-padding"),
4903
+ itemPaddingY: box("item-paddingY"),
4888
4904
  itemPaddingRight: box("item-paddingRight"),
4889
4905
  itemGap: box("item-gap"),
4890
4906
  itemRadius: box("item-radius"),
@@ -4904,9 +4920,10 @@ var GNB_LAYOUT = {
4904
4920
  // token: gnbBox.item.typography.default.lineHeight
4905
4921
  },
4906
4922
  belt: {
4907
- topPaddingX: belt("top-paddingX"),
4908
- topGap: belt("top-gap"),
4909
- bodyPadding: belt("body-paddingY"),
4923
+ bodyPaddingY: belt("body-paddingY"),
4924
+ itemMarginX: "0px",
4925
+ // belt 토큰에 가로 여백 없음 → 풀폭 행 (들여쓰기는 depth*-paddingLeft 전담)
4926
+ itemPaddingY: belt("item-paddingY"),
4910
4927
  itemPaddingRight: belt("item-paddingRight"),
4911
4928
  itemGap: belt("item-gap"),
4912
4929
  itemRadius: "0px",
@@ -4936,7 +4953,9 @@ var GNB_COLORS = {
4936
4953
  itemBgHover: light("item-bg-hover"),
4937
4954
  itemBgSelected: light("item-bg-selected"),
4938
4955
  itemColorDefault: light("item-fg-default"),
4939
- itemColorActive: light("item-fg-selected")
4956
+ itemColorHover: light("item-fg-hover"),
4957
+ itemColorActive: light("item-fg-selected"),
4958
+ itemChevron: light("item-chevron")
4940
4959
  },
4941
4960
  dark: {
4942
4961
  panelBg: dark("bg"),
@@ -4945,18 +4964,19 @@ var GNB_COLORS = {
4945
4964
  itemBgHover: dark("item-bg-hover"),
4946
4965
  itemBgSelected: dark("item-bg-selected"),
4947
4966
  itemColorDefault: dark("item-fg-default"),
4948
- itemColorActive: dark("item-fg-selected")
4967
+ itemColorHover: dark("item-fg-hover"),
4968
+ itemColorActive: dark("item-fg-selected"),
4969
+ itemChevron: dark("item-chevron")
4949
4970
  }
4950
4971
  };
4951
4972
  var GNB_COMMON = {
4952
- topHeight: box("top-height"),
4953
- // 56 (box=belt 동일)
4954
- logoHeight: box("logo-height"),
4955
- // 32
4956
- itemPaddingY: box("item-paddingY"),
4957
- // 6
4958
- foldWidth: belt("fold-width")
4959
- // 48 (belt 토큰 공유)
4973
+ /** 접힘 레일 폭. header="full" 은 접으면 메뉴만 사라져 레일이 없으므로 fix 전용. */
4974
+ foldWidth: belt("fold-width"),
4975
+ /**
4976
+ * 상단바 아이콘 버튼 사이 간격. fix 레일(세로 스택)의 gap·상하 여백에 쓴다.
4977
+ * 레일 전용 여백 토큰이 따로 없어 같은 값(8px)인 이 토큰으로 재해석했다.
4978
+ */
4979
+ buttonGap: box("button-gap")
4960
4980
  };
4961
4981
  var GNB_MENU_WIDTH = 240;
4962
4982
  function findGnbAncestors(items, value) {
@@ -4980,6 +5000,7 @@ function useSLayout() {
4980
5000
  }
4981
5001
  var SLayout = forwardRef(function SLayout2({
4982
5002
  type = "box",
5003
+ header = "fix",
4983
5004
  folded,
4984
5005
  defaultFolded = false,
4985
5006
  onFoldedChange,
@@ -4999,22 +5020,23 @@ var SLayout = forwardRef(function SLayout2({
4999
5020
  [isControlled, onFoldedChange]
5000
5021
  );
5001
5022
  const ctx = useMemo(
5002
- () => ({ type, folded: currentFolded, setFolded }),
5003
- [type, currentFolded, setFolded]
5023
+ () => ({ type, header, folded: currentFolded, setFolded }),
5024
+ [type, header, currentFolded, setFolded]
5004
5025
  );
5005
- const isBox = type === "box";
5006
- const frameStyle = isBox ? {
5026
+ const isFullHeader = header === "full";
5027
+ const frameStyle = isFullHeader ? {
5007
5028
  gridTemplateColumns: `${currentFolded ? 0 : GNB_MENU_WIDTH}px 1fr`,
5008
- gridTemplateRows: `${GNB_COMMON.topHeight} 1fr`
5029
+ gridTemplateRows: `${GNB_HEADER[header].topHeight} 1fr`
5009
5030
  } : void 0;
5010
5031
  return /* @__PURE__ */ jsx(SLayoutContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx(
5011
5032
  "div",
5012
5033
  {
5013
5034
  ref,
5014
5035
  "data-type": type,
5036
+ "data-header": header,
5015
5037
  className: cn(
5016
5038
  "box-border h-full overflow-hidden",
5017
- isBox ? "grid" : "flex flex-row",
5039
+ isFullHeader ? "grid" : "flex flex-row",
5018
5040
  className
5019
5041
  ),
5020
5042
  style: { ...frameStyle, ...style },
@@ -5023,9 +5045,10 @@ var SLayout = forwardRef(function SLayout2({
5023
5045
  }
5024
5046
  ) });
5025
5047
  });
5026
- var DEPTH_PADDING_LEFT = (L, depth) => depth === 1 ? L.depth1PaddingLeft : depth === 2 ? L.depth2PaddingLeft : L.depth3PaddingLeft;
5048
+ var DEPTH_PADDING_LEFT = (M, depth) => depth === 1 ? M.depth1PaddingLeft : depth === 2 ? M.depth2PaddingLeft : M.depth3PaddingLeft;
5027
5049
  var SGnb = forwardRef(function SGnb2({
5028
5050
  type: typeProp,
5051
+ header: headerProp,
5029
5052
  color = "light",
5030
5053
  items,
5031
5054
  value = "",
@@ -5034,14 +5057,17 @@ var SGnb = forwardRef(function SGnb2({
5034
5057
  onFoldChange,
5035
5058
  onLauncherClick,
5036
5059
  logo,
5037
- search,
5060
+ ariaLabel = "global navigation",
5038
5061
  className,
5039
- style
5062
+ style,
5063
+ ...rest
5040
5064
  }, ref) {
5041
5065
  const layout = useContext(SLayoutContext);
5042
5066
  const type = typeProp ?? layout?.type ?? "box";
5067
+ const header = headerProp ?? layout?.header ?? "fix";
5043
5068
  const folded = foldedProp ?? layout?.folded ?? false;
5044
- const L = GNB_LAYOUT[type];
5069
+ const H = GNB_HEADER[header];
5070
+ const M = GNB_MENU[type];
5045
5071
  const C = GNB_COLORS[color];
5046
5072
  const [expandedKeys, setExpandedKeys] = useState(
5047
5073
  () => new Set(findGnbAncestors(items, value))
@@ -5051,6 +5077,7 @@ var SGnb = forwardRef(function SGnb2({
5051
5077
  const ancestors = findGnbAncestors(items, value);
5052
5078
  if (ancestors.length === 0) return;
5053
5079
  setExpandedKeys((prev) => {
5080
+ if (ancestors.every((a) => prev.has(a))) return prev;
5054
5081
  const next = new Set(prev);
5055
5082
  ancestors.forEach((a) => next.add(a));
5056
5083
  return next;
@@ -5075,148 +5102,142 @@ var SGnb = forwardRef(function SGnb2({
5075
5102
  const handleFoldToggle = () => {
5076
5103
  const next = !folded;
5077
5104
  onFoldChange?.(next);
5078
- layout?.setFolded(next);
5105
+ if (foldedProp === void 0) layout?.setFolded(next);
5079
5106
  };
5080
- const iconBtnClass = "inline-flex shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-1.5";
5081
5107
  const renderItem = (item, depth) => {
5082
5108
  const hasChildren = !!(item.children && item.children.length > 0);
5083
5109
  const isSelected = !hasChildren && item.value === value;
5084
5110
  const isExpanded = expandedKeys.has(item.value);
5085
- const isHovered = hoveredKey === item.value;
5086
- const bg = isSelected ? C.itemBgSelected : isHovered && !item.disabled ? C.itemBgHover : C.itemBgDefault;
5111
+ const isHovered = hoveredKey === item.value && !item.disabled;
5087
5112
  const itemStyle = {
5088
- gap: L.itemGap,
5089
- paddingTop: GNB_COMMON.itemPaddingY,
5090
- paddingBottom: GNB_COMMON.itemPaddingY,
5091
- paddingRight: L.itemPaddingRight,
5092
- paddingLeft: DEPTH_PADDING_LEFT(L, depth),
5093
- marginLeft: L.bodyPadding,
5094
- marginRight: L.bodyPadding,
5095
- borderRadius: L.itemRadius,
5096
- backgroundColor: bg,
5097
- color: isSelected ? C.itemColorActive : C.itemColorDefault,
5098
- fontSize: depth === 1 ? L.depth1FontSize : L.itemFontSize,
5099
- lineHeight: `${depth === 1 ? L.depth1LineHeight : L.itemLineHeight}px`,
5100
- fontWeight: depth === 1 || isSelected ? L.depth1FontWeight : 400,
5113
+ gap: M.itemGap,
5114
+ paddingTop: M.itemPaddingY,
5115
+ paddingBottom: M.itemPaddingY,
5116
+ paddingRight: M.itemPaddingRight,
5117
+ paddingLeft: DEPTH_PADDING_LEFT(M, depth),
5118
+ marginLeft: M.itemMarginX,
5119
+ marginRight: M.itemMarginX,
5120
+ borderRadius: M.itemRadius,
5121
+ backgroundColor: isSelected ? C.itemBgSelected : isHovered ? C.itemBgHover : C.itemBgDefault,
5122
+ color: isSelected ? C.itemColorActive : isHovered ? C.itemColorHover : C.itemColorDefault,
5123
+ // <button> UA 폰트(Arial 13.3px)를 끊고 앱 폰트를 잇는다
5124
+ fontFamily: "inherit",
5125
+ fontSize: depth === 1 ? M.depth1FontSize : M.itemFontSize,
5126
+ lineHeight: `${depth === 1 ? M.depth1LineHeight : M.itemLineHeight}px`,
5127
+ fontWeight: depth === 1 || isSelected ? M.depth1FontWeight : 400,
5101
5128
  opacity: item.disabled ? 0.4 : 1,
5102
5129
  cursor: item.disabled ? "not-allowed" : "pointer"
5103
5130
  };
5104
- return /* @__PURE__ */ jsxs("div", { children: [
5105
- /* @__PURE__ */ jsxs(
5106
- "div",
5107
- {
5108
- role: hasChildren ? "button" : "menuitem",
5109
- tabIndex: item.disabled ? -1 : 0,
5110
- "aria-disabled": item.disabled || void 0,
5111
- "aria-expanded": hasChildren ? isExpanded : void 0,
5112
- "aria-current": isSelected ? "page" : void 0,
5113
- className: "box-border flex select-none items-center transition-colors duration-150",
5114
- style: itemStyle,
5115
- onClick: () => handleItemClick(item),
5116
- onMouseEnter: () => setHoveredKey(item.value),
5117
- onMouseLeave: () => setHoveredKey((prev) => prev === item.value ? null : prev),
5118
- onKeyDown: (e) => {
5119
- if (e.key === "Enter" || e.key === " ") {
5120
- e.preventDefault();
5121
- handleItemClick(item);
5122
- }
5123
- },
5124
- children: [
5125
- depth === 1 && item.icon && /* @__PURE__ */ jsx(SIcon, { name: item.icon, size: L.depth1Icon, className: "shrink-0" }),
5126
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap", children: item.label }),
5127
- item.tag !== void 0 && item.tag !== "" && /* @__PURE__ */ jsx(
5128
- STag,
5129
- {
5130
- shape: "square",
5131
- size: "xs",
5132
- color: isSelected ? "blue" : "grey",
5133
- label: item.tag,
5134
- className: "shrink-0"
5135
- }
5136
- ),
5137
- hasChildren && /* @__PURE__ */ jsx(
5138
- SIcon,
5139
- {
5140
- name: "chevronDown",
5141
- size: L.itemArrow,
5142
- rotate: isExpanded ? 180 : 0,
5143
- className: "shrink-0 transition-transform duration-150"
5144
- }
5145
- )
5146
- ]
5147
- }
5148
- ),
5149
- hasChildren && isExpanded && item.children.map((child) => renderItem(child, depth + 1))
5150
- ] }, item.value);
5131
+ return (
5132
+ // li flex-col 로 둬야 버튼이 좌우 마진만큼 안쪽으로 들어가고,
5133
+ // 하위 ul 은 마진 없이 전폭을 써서 depth*-paddingLeft 가 들여쓰기를 단독으로 책임진다.
5134
+ /* @__PURE__ */ jsxs("li", { className: "flex flex-col", children: [
5135
+ /* @__PURE__ */ jsxs(
5136
+ "button",
5137
+ {
5138
+ type: "button",
5139
+ disabled: item.disabled,
5140
+ "aria-expanded": hasChildren ? isExpanded : void 0,
5141
+ "aria-current": isSelected ? "page" : void 0,
5142
+ className: "box-border flex select-none items-center border-none bg-transparent text-left transition-colors duration-150",
5143
+ style: itemStyle,
5144
+ onClick: () => handleItemClick(item),
5145
+ onMouseEnter: () => setHoveredKey(item.value),
5146
+ onMouseLeave: () => setHoveredKey((prev) => prev === item.value ? null : prev),
5147
+ children: [
5148
+ depth === 1 && item.icon && /* @__PURE__ */ jsx(SIcon, { name: item.icon, size: M.depth1Icon, className: "shrink-0" }),
5149
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap", children: item.label }),
5150
+ item.tag !== void 0 && item.tag !== "" && /* @__PURE__ */ jsx(
5151
+ STag,
5152
+ {
5153
+ shape: "square",
5154
+ size: "xs",
5155
+ color: item.tagColor ?? "blue",
5156
+ label: item.tag,
5157
+ className: "shrink-0"
5158
+ }
5159
+ ),
5160
+ hasChildren && /* @__PURE__ */ jsx(
5161
+ SIcon,
5162
+ {
5163
+ name: "chevronDown",
5164
+ size: M.itemArrow,
5165
+ color: C.itemChevron,
5166
+ rotate: isExpanded ? 180 : 0,
5167
+ className: "shrink-0 transition-transform duration-150"
5168
+ }
5169
+ )
5170
+ ]
5171
+ }
5172
+ ),
5173
+ hasChildren && isExpanded && /* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col p-0", children: item.children.map((child) => renderItem(child, depth + 1)) })
5174
+ ] }, item.value)
5175
+ );
5176
+ };
5177
+ const railFolded = folded && header === "fix";
5178
+ const rootWidth = header === "full" ? "100%" : folded ? GNB_COMMON.foldWidth : GNB_MENU_WIDTH;
5179
+ const rootStyle = {
5180
+ width: rootWidth,
5181
+ ...header === "full" ? { gridArea: "1 / 1 / -1 / -1", pointerEvents: "none" } : { backgroundColor: C.panelBg },
5182
+ ...style
5151
5183
  };
5152
- const railFolded = folded && type === "belt";
5153
- const rootWidth = type === "box" ? "100%" : folded ? GNB_COMMON.foldWidth : GNB_MENU_WIDTH;
5154
- const inBoxGrid = !!layout && type === "box";
5155
- const rootStyle = useMemo(
5156
- () => ({
5157
- width: rootWidth,
5158
- ...inBoxGrid ? { gridArea: "1 / 1 / -1 / -1" } : null,
5159
- ...style
5160
- }),
5161
- [rootWidth, inBoxGrid, style]
5162
- );
5163
- const iconColor = C.itemColorDefault;
5164
5184
  return /* @__PURE__ */ jsxs(
5165
5185
  "div",
5166
5186
  {
5167
5187
  ref,
5168
- className: cn(
5169
- "box-border flex flex-col",
5170
- type === "box" && !inBoxGrid ? "h-auto" : "h-full",
5171
- className
5172
- ),
5188
+ className: cn("box-border flex h-full shrink-0 flex-col", className),
5173
5189
  style: rootStyle,
5174
5190
  "data-type": type,
5191
+ "data-header": header,
5175
5192
  "data-color": color,
5193
+ ...rest,
5176
5194
  children: [
5177
5195
  /* @__PURE__ */ jsxs(
5178
5196
  "div",
5179
5197
  {
5180
5198
  className: cn("box-border flex items-center", railFolded && "flex-col"),
5181
5199
  style: {
5182
- gap: railFolded ? 8 : L.topGap,
5183
- height: railFolded ? "auto" : GNB_COMMON.topHeight,
5184
- padding: railFolded ? "8px 0" : `0 ${L.topPaddingX}`,
5200
+ gap: railFolded ? GNB_COMMON.buttonGap : H.topGap,
5201
+ height: railFolded ? "auto" : H.topHeight,
5202
+ padding: railFolded ? `${GNB_COMMON.buttonGap} 0` : `0 ${H.topPaddingX}`,
5185
5203
  borderBottom: `1px solid ${C.topBorder}`,
5186
5204
  backgroundColor: C.panelBg,
5187
- color: iconColor
5205
+ pointerEvents: "auto",
5206
+ // 아이콘 버튼은 자체 ghostButton 토큰을 쓰므로, 이 색은 logo 슬롯 상속용
5207
+ color: C.itemColorDefault
5188
5208
  },
5189
5209
  children: [
5190
- /* @__PURE__ */ jsx(
5191
- "button",
5210
+ onLauncherClick && /* @__PURE__ */ jsx(
5211
+ SGhostButton,
5192
5212
  {
5193
- type: "button",
5194
- "aria-label": "app launcher",
5195
- className: iconBtnClass,
5213
+ icon: "launcher",
5214
+ size: "xs",
5215
+ intent: color === "dark" ? "inverse" : "default",
5216
+ ariaLabel: "app launcher",
5196
5217
  onClick: onLauncherClick,
5197
- children: /* @__PURE__ */ jsx(SIcon, { name: "launcher", size: 20 })
5218
+ className: "shrink-0"
5198
5219
  }
5199
5220
  ),
5200
- !railFolded && logo && /* @__PURE__ */ jsx("div", { className: "flex items-center", style: { height: GNB_COMMON.logoHeight }, children: logo }),
5201
- !railFolded && /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: search }),
5221
+ !railFolded && logo && /* @__PURE__ */ jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo }),
5202
5222
  /* @__PURE__ */ jsx(
5203
- "button",
5223
+ SGhostButton,
5204
5224
  {
5205
- type: "button",
5206
- "aria-label": "toggle navigation",
5207
- "aria-pressed": folded,
5208
- className: cn(iconBtnClass, !railFolded && "ml-auto"),
5225
+ icon: "sidebar",
5226
+ size: "xs",
5227
+ intent: color === "dark" ? "inverse" : "default",
5228
+ ariaLabel: "toggle navigation",
5229
+ ariaPressed: folded,
5209
5230
  onClick: handleFoldToggle,
5210
- children: /* @__PURE__ */ jsx(SIcon, { name: "sidebar", size: 20 })
5231
+ className: cn("shrink-0", !railFolded && "ml-auto")
5211
5232
  }
5212
5233
  )
5213
5234
  ]
5214
5235
  }
5215
5236
  ),
5216
5237
  !folded && /* @__PURE__ */ jsx(
5217
- "div",
5238
+ "nav",
5218
5239
  {
5219
- role: "menu",
5240
+ "aria-label": ariaLabel,
5220
5241
  className: "flex flex-col overflow-y-auto",
5221
5242
  style: {
5222
5243
  width: GNB_MENU_WIDTH,
@@ -5225,11 +5246,12 @@ var SGnb = forwardRef(function SGnb2({
5225
5246
  flexShrink: 1,
5226
5247
  flexBasis: "auto",
5227
5248
  minHeight: 0,
5228
- paddingTop: L.bodyPadding,
5229
- paddingBottom: L.bodyPadding,
5230
- backgroundColor: C.panelBg
5249
+ paddingTop: M.bodyPaddingY,
5250
+ paddingBottom: M.bodyPaddingY,
5251
+ backgroundColor: C.panelBg,
5252
+ pointerEvents: "auto"
5231
5253
  },
5232
- children: items.map((item) => renderItem(item, 1))
5254
+ children: /* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col p-0", children: items.map((item) => renderItem(item, 1)) })
5233
5255
  }
5234
5256
  )
5235
5257
  ]
@@ -5238,7 +5260,7 @@ var SGnb = forwardRef(function SGnb2({
5238
5260
  });
5239
5261
  var SPage = forwardRef(function SPage2({ className, style, children, ...rest }, ref) {
5240
5262
  const layout = useContext(SLayoutContext);
5241
- const gridStyle = layout?.type === "box" ? { gridColumn: 2, gridRow: 2 } : void 0;
5263
+ const gridStyle = layout?.header === "full" ? { gridColumn: 2, gridRow: 2 } : void 0;
5242
5264
  return /* @__PURE__ */ jsx(
5243
5265
  "main",
5244
5266
  {
@@ -6610,7 +6632,7 @@ var STable = forwardRef(function STable2({
6610
6632
  ref: scrollRef,
6611
6633
  onScroll: handleScroll,
6612
6634
  className: cn(
6613
- "relative overflow-auto border border-solid border-[color:var(--cmp-table-border-color)]",
6635
+ "bg-white relative overflow-auto border border-solid border-[color:var(--cmp-table-border-color)]",
6614
6636
  radiusClass,
6615
6637
  // 원본 sd-table__scroll-container--loading/--no-data: 로딩·데이터 없음 시 스크롤·인터랙션 차단
6616
6638
  (isLoading || rowCount === 0) && "overflow-hidden pointer-events-none"
@@ -9784,6 +9806,854 @@ var SBarcodeInput = forwardRef(
9784
9806
  }
9785
9807
  );
9786
9808
 
9787
- export { BADGE_COLORS, BUTTON_COLORS, BUTTON_SIZES, COLOR_KEYS, GNB_COLORS, GNB_COMMON, GNB_LAYOUT, GNB_MENU_WIDTH, ICONS, SActionModal, SBadge, SBarcodeInput, SButton, SCalendar, SCallout, SCard, SCheckbox, SChip, SChipInput, SCircleProgress, SConfirmModal, SDatePicker, SDateRangePicker, SDivider, SDropdownButton, SField, SFilePicker, SForm, SGhostButton, SGnb, SGuide, SIcon, SInput, SKeyValueTable, SLayout, SLayoutContext, SLinearProgress, SLoadingContainer, SLoadingModal, SLoadingViewport, SNumberInput, SPage, SPagination, SPopover, SPopup, SPortal, SRadio, SRadioButton, SRadioGroup, SSelect, SSwitch, STable, STabs, STag, STextLink, STextarea, SToast, SToastContainer, SToggle, STooltip, TAG_COLORS, TAG_SHAPES, TAG_SIZES, buttonPreset, cn, findGnbAncestors, isColorKey, loading, resolveColor, useSLayout };
9809
+ // src/components/STimePicker/timepicker.config.ts
9810
+ var V2 = (path) => `var(--cmp-timepicker-${path})`;
9811
+ var TIMEPICKER_SIZES = ["sm", "md"];
9812
+ var TIMEPICKER_SIZE_CONFIG = {
9813
+ sm: {
9814
+ height: V2("sm-height"),
9815
+ paddingX: V2("sm-paddingX"),
9816
+ gap: V2("sm-gap"),
9817
+ icon: V2("sm-icon"),
9818
+ radius: V2("sm-radius"),
9819
+ // TODO: component.timepicker 토큰에 typography가 추가되면 CSS 변수로 교체한다.
9820
+ fontSize: 12,
9821
+ lineHeight: 20,
9822
+ minWidth: 80,
9823
+ fieldMinWidth: 128
9824
+ },
9825
+ md: {
9826
+ height: V2("md-height"),
9827
+ paddingX: V2("md-paddingX"),
9828
+ gap: V2("md-gap"),
9829
+ icon: V2("md-icon"),
9830
+ radius: V2("md-radius"),
9831
+ // TODO: component.timepicker 토큰에 typography가 추가되면 CSS 변수로 교체한다.
9832
+ fontSize: 14,
9833
+ lineHeight: 24,
9834
+ minWidth: 96,
9835
+ fieldMinWidth: 160
9836
+ }
9837
+ };
9838
+ function clampTimePart(value, min, max) {
9839
+ if (!Number.isFinite(value)) return min;
9840
+ return Math.min(Math.max(value, min), max);
9841
+ }
9842
+ function padTimePart(value) {
9843
+ return String(value).padStart(2, "0");
9844
+ }
9845
+ function parseTimeValue(value) {
9846
+ const match = /^(\d{1,2}):(\d{1,2})$/.exec(value ?? "");
9847
+ if (!match) return { hour: 0, minute: 0 };
9848
+ return {
9849
+ hour: clampTimePart(Number(match[1]), 0, 23),
9850
+ minute: clampTimePart(Number(match[2]), 0, 59)
9851
+ };
9852
+ }
9853
+ function formatTimeValue(hour, minute) {
9854
+ return `${padTimePart(clampTimePart(hour, 0, 23))}:${padTimePart(clampTimePart(minute, 0, 59))}`;
9855
+ }
9856
+ function formatTimeDisplay(value, type) {
9857
+ if (value == null || value === "") return "";
9858
+ const { hour, minute } = parseTimeValue(value);
9859
+ if (type === "default") return formatTimeValue(hour, minute);
9860
+ const meridiem = hour >= 12 ? "\uC624\uD6C4" : "\uC624\uC804";
9861
+ const displayHour = hour % 12 === 0 ? 12 : hour % 12;
9862
+ return `${meridiem} ${padTimePart(displayHour)}:${padTimePart(minute)}`;
9863
+ }
9864
+ function TimeSelector({
9865
+ value,
9866
+ useMeridiem,
9867
+ minuteStep,
9868
+ onChange
9869
+ }) {
9870
+ const parsed = useMemo(() => parseTimeValue(value), [value]);
9871
+ const [hour, setHour] = useState(parsed.hour);
9872
+ const [minute, setMinute] = useState(parsed.minute);
9873
+ const [editingPart, setEditingPart] = useState(null);
9874
+ const [inputDraft, setInputDraft] = useState({});
9875
+ useEffect(() => {
9876
+ setHour(parsed.hour);
9877
+ setMinute(parsed.minute);
9878
+ }, [parsed.hour, parsed.minute]);
9879
+ const meridiem = hour >= 12 ? "PM" : "AM";
9880
+ const displayHour = useMeridiem ? hour % 12 === 0 ? 12 : hour % 12 : hour;
9881
+ const commit = (nextHour, nextMinute) => {
9882
+ const h = clampTimePart(nextHour, 0, 23);
9883
+ const m = clampTimePart(nextMinute, 0, 59);
9884
+ setHour(h);
9885
+ setMinute(m);
9886
+ onChange(formatTimeValue(h, m));
9887
+ };
9888
+ const setMeridiem = (next) => {
9889
+ if (next === meridiem) return;
9890
+ commit(next === "AM" ? hour - 12 : hour + 12, minute);
9891
+ };
9892
+ const changeMinute = (delta) => {
9893
+ const next = minute + delta;
9894
+ if (next > 59) {
9895
+ commit((hour + 1) % 24, next - 60);
9896
+ return;
9897
+ }
9898
+ if (next < 0) {
9899
+ commit((hour + 23) % 24, next + 60);
9900
+ return;
9901
+ }
9902
+ commit(hour, next);
9903
+ };
9904
+ const changeHour = (delta) => commit((hour + delta + 24) % 24, minute);
9905
+ const sanitizeInput = (next) => next.replace(/\D/g, "").slice(0, 2);
9906
+ const beginEdit = (part, currentValue) => {
9907
+ setEditingPart(part);
9908
+ setInputDraft((prev) => ({ ...prev, [part]: String(currentValue).padStart(2, "0") }));
9909
+ };
9910
+ const commitHourDraft = (draft) => {
9911
+ const clamped = clampTimePart(Number(draft), useMeridiem ? 1 : 0, useMeridiem ? 12 : 23);
9912
+ const nextHour = useMeridiem ? meridiem === "PM" ? clamped === 12 ? 12 : clamped + 12 : clamped === 12 ? 0 : clamped : clamped;
9913
+ commit(nextHour, minute);
9914
+ return clamped;
9915
+ };
9916
+ const commitMinuteDraft = (draft) => {
9917
+ const clamped = clampTimePart(Number(draft), 0, 59);
9918
+ commit(hour, clamped);
9919
+ return clamped;
9920
+ };
9921
+ const endEdit = (part) => {
9922
+ const draft = inputDraft[part];
9923
+ if (part === "hour" && draft != null && draft !== "") {
9924
+ commitHourDraft(draft);
9925
+ }
9926
+ if (part === "minute" && draft != null && draft !== "") {
9927
+ commitMinuteDraft(draft);
9928
+ }
9929
+ setEditingPart(null);
9930
+ setInputDraft({});
9931
+ };
9932
+ const inputStyle = {
9933
+ width: "var(--cmp-timepicker-selector-input-width)",
9934
+ height: "var(--cmp-textinput-sm-height)",
9935
+ borderRadius: "var(--cmp-textinput-sm-radius)",
9936
+ border: "var(--cmp-textinput-borderWidth) solid var(--cmp-textinput-border-default)",
9937
+ color: "var(--cmp-textinput-text-default)",
9938
+ fontSize: 12,
9939
+ lineHeight: "20px"
9940
+ };
9941
+ const renderColumn = (type) => {
9942
+ const isHour = type === "hour";
9943
+ const val = isHour ? displayHour : minute;
9944
+ const displayValue = editingPart === type ? inputDraft[type] ?? "" : String(val).padStart(2, "0");
9945
+ return /* @__PURE__ */ jsxs(
9946
+ "div",
9947
+ {
9948
+ className: "flex flex-col items-center justify-center",
9949
+ style: { gap: "var(--cmp-timepicker-selector-time-column-gap)" },
9950
+ children: [
9951
+ /* @__PURE__ */ jsx(
9952
+ SGhostButton,
9953
+ {
9954
+ icon: "chevronUp",
9955
+ size: "xs",
9956
+ ariaLabel: `${isHour ? "\uC2DC" : "\uBD84"} \uC99D\uAC00`,
9957
+ onClick: () => isHour ? changeHour(1) : changeMinute(minuteStep)
9958
+ }
9959
+ ),
9960
+ /* @__PURE__ */ jsx(
9961
+ "input",
9962
+ {
9963
+ "aria-label": isHour ? "\uC2DC" : "\uBD84",
9964
+ inputMode: "numeric",
9965
+ value: displayValue,
9966
+ placeholder: "-",
9967
+ onFocus: () => beginEdit(type, val),
9968
+ onBlur: () => endEdit(type),
9969
+ onChange: (event) => {
9970
+ const draft = sanitizeInput(event.target.value);
9971
+ setInputDraft((prev) => ({ ...prev, [type]: draft }));
9972
+ if (draft === "") return;
9973
+ const raw = Number(draft);
9974
+ if (isHour) {
9975
+ if (draft.length < 2) return;
9976
+ const clamped2 = commitHourDraft(draft);
9977
+ const displayDraft2 = raw === clamped2 ? draft : String(clamped2);
9978
+ setInputDraft((prev) => ({ ...prev, [type]: displayDraft2 }));
9979
+ return;
9980
+ }
9981
+ if (draft.length < 2) return;
9982
+ const clamped = commitMinuteDraft(draft);
9983
+ const displayDraft = raw === clamped ? draft : String(clamped);
9984
+ setInputDraft((prev) => ({ ...prev, [type]: displayDraft }));
9985
+ },
9986
+ className: "box-border bg-white text-center outline-none",
9987
+ style: inputStyle
9988
+ }
9989
+ ),
9990
+ /* @__PURE__ */ jsx(
9991
+ SGhostButton,
9992
+ {
9993
+ icon: "chevronDown",
9994
+ size: "xs",
9995
+ ariaLabel: `${isHour ? "\uC2DC" : "\uBD84"} \uAC10\uC18C`,
9996
+ onClick: () => isHour ? changeHour(-1) : changeMinute(-minuteStep)
9997
+ }
9998
+ )
9999
+ ]
10000
+ }
10001
+ );
10002
+ };
10003
+ return /* @__PURE__ */ jsxs(
10004
+ "div",
10005
+ {
10006
+ className: "flex flex-col items-stretch justify-center overflow-hidden",
10007
+ style: {
10008
+ background: "var(--cmp-timepicker-selector-bg)",
10009
+ borderRadius: "var(--cmp-timepicker-selector-radius)",
10010
+ boxShadow: "4px 8px 16px -4px rgba(34,34,34,0.1), 0 0 24px -6px rgba(34,34,34,0.12)"
10011
+ },
10012
+ children: [
10013
+ useMeridiem && /* @__PURE__ */ jsx(
10014
+ "div",
10015
+ {
10016
+ className: "flex items-center justify-center",
10017
+ style: {
10018
+ padding: "var(--cmp-timepicker-selector-section-paddingY) var(--cmp-timepicker-selector-section-paddingX)"
10019
+ },
10020
+ children: /* @__PURE__ */ jsx("div", { className: "isolate flex", children: ["AM", "PM"].map((item, index) => {
10021
+ const selected = item === meridiem;
10022
+ return /* @__PURE__ */ jsx(
10023
+ "button",
10024
+ {
10025
+ type: "button",
10026
+ onClick: () => setMeridiem(item),
10027
+ className: cn(
10028
+ "box-border h-[var(--cmp-textinput-sm-height)] w-[61px] cursor-pointer border border-solid bg-white text-center text-[12px] font-medium leading-[20px]",
10029
+ index === 0 ? "rounded-l-[var(--cmp-select-sm-radius)]" : "rounded-r-[var(--cmp-select-sm-radius)]",
10030
+ index === 0 ? "-mr-px" : "",
10031
+ selected ? "z-[1] border-[var(--cmp-timepicker-border-focus)] text-[var(--cmp-timepicker-border-focus)]" : "border-[var(--cmp-timepicker-border-default)] text-[var(--cmp-field-hint-color)]"
10032
+ ),
10033
+ children: item === "AM" ? "\uC624\uC804" : "\uC624\uD6C4"
10034
+ },
10035
+ item
10036
+ );
10037
+ }) })
10038
+ }
10039
+ ),
10040
+ useMeridiem && /* @__PURE__ */ jsx("div", { className: "h-px w-full bg-[var(--sys-color-divider-default)]" }),
10041
+ /* @__PURE__ */ jsxs(
10042
+ "div",
10043
+ {
10044
+ className: "flex items-center justify-center",
10045
+ style: {
10046
+ gap: "var(--cmp-timepicker-selector-time-gap)",
10047
+ padding: "var(--cmp-timepicker-selector-section-paddingY) var(--cmp-timepicker-selector-section-paddingX)"
10048
+ },
10049
+ children: [
10050
+ renderColumn("hour"),
10051
+ /* @__PURE__ */ jsx(
10052
+ "span",
10053
+ {
10054
+ className: "text-center",
10055
+ style: {
10056
+ width: "var(--cmp-timepicker-selector-colon-size)",
10057
+ color: "var(--cmp-field-hint-color)",
10058
+ fontSize: "var(--cmp-timepicker-selector-colon-size)",
10059
+ lineHeight: "var(--cmp-timepicker-selector-colon-size)"
10060
+ },
10061
+ children: ":"
10062
+ }
10063
+ ),
10064
+ renderColumn("minute")
10065
+ ]
10066
+ }
10067
+ )
10068
+ ]
10069
+ }
10070
+ );
10071
+ }
10072
+ function STimePicker({
10073
+ value,
10074
+ onValueChange,
10075
+ onOpenChange,
10076
+ type = "default",
10077
+ size = "sm",
10078
+ placeholder = "00:00",
10079
+ disabled = false,
10080
+ clearable = false,
10081
+ useMeridiem,
10082
+ minuteStep = 1,
10083
+ width,
10084
+ name,
10085
+ rules,
10086
+ status,
10087
+ label,
10088
+ labelWidth,
10089
+ icon,
10090
+ iconColor,
10091
+ labelTooltip,
10092
+ labelTooltipProps,
10093
+ addonLabel,
10094
+ addonAlign,
10095
+ hint,
10096
+ error,
10097
+ errorMessage,
10098
+ className,
10099
+ style
10100
+ }) {
10101
+ const portalContainer = usePortalContainer();
10102
+ const [open, setOpen] = useState(false);
10103
+ const [focused, setFocused] = useState(false);
10104
+ const [ruleError, setRuleError] = useState("");
10105
+ const dims = TIMEPICKER_SIZE_CONFIG[size] ?? TIMEPICKER_SIZE_CONFIG.sm;
10106
+ const normalized = value ? formatTimeValue(parseTimeValue(value).hour, parseTimeValue(value).minute) : null;
10107
+ const effectiveUseMeridiem = useMeridiem ?? type === "midday";
10108
+ const displayText = formatTimeDisplay(normalized, effectiveUseMeridiem ? "midday" : "default");
10109
+ const hasValue = normalized != null && normalized !== "";
10110
+ useFormField({
10111
+ name,
10112
+ validate: () => {
10113
+ const msg = runRules(value, rules);
10114
+ setRuleError(msg);
10115
+ return msg === "";
10116
+ },
10117
+ resetValidation: () => setRuleError("")
10118
+ });
10119
+ const resolvedError = error || ruleError !== "";
10120
+ const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
10121
+ const commitOpen = (next) => {
10122
+ if (disabled) return;
10123
+ setOpen(next);
10124
+ onOpenChange?.(next);
10125
+ if (!next && rules && rules.length > 0) setRuleError(runRules(value, rules));
10126
+ };
10127
+ const commitValue = (next) => {
10128
+ onValueChange?.(next);
10129
+ if (rules && rules.length > 0) setRuleError(runRules(next, rules));
10130
+ };
10131
+ return /* @__PURE__ */ jsx(
10132
+ SField,
10133
+ {
10134
+ label,
10135
+ labelWidth,
10136
+ icon,
10137
+ iconColor,
10138
+ labelTooltip,
10139
+ labelTooltipProps,
10140
+ addonLabel,
10141
+ addonAlign,
10142
+ size,
10143
+ hint,
10144
+ error: resolvedError,
10145
+ status: resolvedStatus,
10146
+ focused: focused || open,
10147
+ errorMessage: ruleError !== "" ? ruleError : errorMessage,
10148
+ width,
10149
+ disabled,
10150
+ className,
10151
+ style: { minWidth: dims.fieldMinWidth, ...style },
10152
+ children: /* @__PURE__ */ jsxs(RPopover2.Root, { open, onOpenChange: commitOpen, children: [
10153
+ /* @__PURE__ */ jsx(RPopover2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
10154
+ "button",
10155
+ {
10156
+ type: "button",
10157
+ name,
10158
+ disabled,
10159
+ onFocus: () => setFocused(true),
10160
+ onBlur: () => setFocused(false),
10161
+ className: "flex h-full w-full items-center overflow-hidden bg-transparent outline-none disabled:cursor-not-allowed",
10162
+ style: { padding: `0 ${dims.paddingX}`, gap: dims.gap, borderRadius: dims.radius },
10163
+ children: [
10164
+ /* @__PURE__ */ jsx(
10165
+ SIcon,
10166
+ {
10167
+ name: "clockOutline",
10168
+ size: dims.icon,
10169
+ color: disabled ? "var(--cmp-timepicker-icon-disabled)" : "var(--cmp-timepicker-icon-default)",
10170
+ className: "flex-shrink-0"
10171
+ }
10172
+ ),
10173
+ /* @__PURE__ */ jsx(
10174
+ "span",
10175
+ {
10176
+ className: "flex-1 truncate text-center",
10177
+ style: {
10178
+ minWidth: dims.minWidth,
10179
+ fontSize: dims.fontSize,
10180
+ lineHeight: `${dims.lineHeight}px`,
10181
+ color: disabled ? "var(--cmp-timepicker-text-disabled)" : hasValue ? "var(--cmp-timepicker-text-default)" : "var(--cmp-field-hint-color)"
10182
+ },
10183
+ children: hasValue ? displayText : placeholder
10184
+ }
10185
+ ),
10186
+ clearable && hasValue && !disabled && /* @__PURE__ */ jsx(
10187
+ SGhostButton,
10188
+ {
10189
+ icon: "close",
10190
+ size: "xs",
10191
+ ariaLabel: "\uC2DC\uAC04 \uC9C0\uC6B0\uAE30",
10192
+ onClick: (event) => {
10193
+ event.stopPropagation();
10194
+ commitValue(null);
10195
+ }
10196
+ }
10197
+ )
10198
+ ]
10199
+ }
10200
+ ) }),
10201
+ /* @__PURE__ */ jsx(RPopover2.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx(
10202
+ RPopover2.Content,
10203
+ {
10204
+ side: "bottom",
10205
+ align: "start",
10206
+ sideOffset: 4,
10207
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
10208
+ children: /* @__PURE__ */ jsx(
10209
+ TimeSelector,
10210
+ {
10211
+ value: normalized,
10212
+ useMeridiem: effectiveUseMeridiem,
10213
+ minuteStep,
10214
+ onChange: commitValue
10215
+ }
10216
+ )
10217
+ }
10218
+ ) })
10219
+ ] })
10220
+ }
10221
+ );
10222
+ }
10223
+
10224
+ // src/components/STimeRangePicker/time-range-picker.config.ts
10225
+ function getTimeMinutes(value) {
10226
+ const { hour, minute } = parseTimeValue(value);
10227
+ return hour * 60 + minute;
10228
+ }
10229
+ function normalizeTimeRangeValue(value, rangeOrder = "strict") {
10230
+ if (value == null) return null;
10231
+ const normalized = value.map((item) => {
10232
+ const parsed = parseTimeValue(item);
10233
+ return formatTimeValue(parsed.hour, parsed.minute);
10234
+ });
10235
+ if (rangeOrder === "strict" && getTimeMinutes(normalized[0]) > getTimeMinutes(normalized[1])) {
10236
+ return [normalized[0], normalized[0]];
10237
+ }
10238
+ return normalized;
10239
+ }
10240
+ function formatTimeRangeDisplay(value, type) {
10241
+ if (value == null || value[0] === "" || value[1] === "") return "";
10242
+ return `${formatTimeDisplay(value[0], type)} ~ ${formatTimeDisplay(value[1], type)}`;
10243
+ }
10244
+ function RangeTimeSelector({
10245
+ value,
10246
+ useMeridiem,
10247
+ minuteStep,
10248
+ rangeOrder,
10249
+ onChange
10250
+ }) {
10251
+ const range = useMemo(() => value ?? ["00:00", "23:59"], [value]);
10252
+ const [editingKey, setEditingKey] = useState(null);
10253
+ const [inputDraft, setInputDraft] = useState({});
10254
+ const autoAdjustedRef = useRef(null);
10255
+ const getParts = (side) => parseTimeValue(side === "start" ? range[0] : range[1]);
10256
+ const getMeridiem = (side) => getParts(side).hour >= 12 ? "PM" : "AM";
10257
+ const getDisplayHour = (side) => {
10258
+ const { hour } = getParts(side);
10259
+ if (!useMeridiem) return hour;
10260
+ const displayHour = hour % 12;
10261
+ return displayHour === 0 ? 12 : displayHour;
10262
+ };
10263
+ const commit = (side, hour, minute, direction = "unknown") => {
10264
+ const next = formatTimeValue(hour, minute);
10265
+ onChange(normalizeNextRange(side === "start" ? [next, range[1]] : [range[0], next], side, direction));
10266
+ };
10267
+ const getTimeMinutes2 = (time) => {
10268
+ const { hour, minute } = parseTimeValue(time);
10269
+ return hour * 60 + minute;
10270
+ };
10271
+ const normalizeNextRange = (nextRange, changedSide, direction) => {
10272
+ if (rangeOrder === "allow-cross-day") return nextRange;
10273
+ const [start, end] = nextRange;
10274
+ const [currentStart, currentEnd] = range;
10275
+ if (getTimeMinutes2(start) <= getTimeMinutes2(end)) {
10276
+ const shouldKeepSynced = autoAdjustedRef.current?.side === changedSide && currentStart === currentEnd && start !== end && (direction === "unknown" || autoAdjustedRef.current.direction === direction);
10277
+ if (shouldKeepSynced) {
10278
+ return changedSide === "start" ? [start, start] : [end, end];
10279
+ }
10280
+ autoAdjustedRef.current = null;
10281
+ return nextRange;
10282
+ }
10283
+ autoAdjustedRef.current = {
10284
+ side: changedSide,
10285
+ direction: changedSide === "start" ? "increase" : "decrease"
10286
+ };
10287
+ return changedSide === "start" ? [start, start] : [end, end];
10288
+ };
10289
+ const changeHour = (side, delta) => {
10290
+ const { hour, minute } = getParts(side);
10291
+ commit(side, (hour + delta + 24) % 24, minute, delta > 0 ? "increase" : "decrease");
10292
+ };
10293
+ const changeMinute = (side, delta) => {
10294
+ const { hour, minute } = getParts(side);
10295
+ const next = minute + delta;
10296
+ if (next > 59) {
10297
+ commit(side, (hour + 1) % 24, next - 60, delta > 0 ? "increase" : "decrease");
10298
+ return;
10299
+ }
10300
+ if (next < 0) {
10301
+ commit(side, (hour + 23) % 24, next + 60, delta > 0 ? "increase" : "decrease");
10302
+ return;
10303
+ }
10304
+ commit(side, hour, next, delta > 0 ? "increase" : "decrease");
10305
+ };
10306
+ const setMeridiem = (side, next) => {
10307
+ const current = getMeridiem(side);
10308
+ if (current === next) return;
10309
+ const { hour, minute } = getParts(side);
10310
+ commit(side, next === "AM" ? hour - 12 : hour + 12, minute);
10311
+ };
10312
+ const getEditingKey = (side, part) => `${side}-${part}`;
10313
+ const sanitizeInput = (next) => next.replace(/\D/g, "").slice(0, 2);
10314
+ const beginEdit = (side, part, currentValue) => {
10315
+ const key = getEditingKey(side, part);
10316
+ setEditingKey(key);
10317
+ setInputDraft((prev) => ({ ...prev, [key]: String(currentValue).padStart(2, "0") }));
10318
+ };
10319
+ const commitHourDraft = (side, draft) => {
10320
+ const clamped = clampTimePart(Number(draft), useMeridiem ? 1 : 0, useMeridiem ? 12 : 23);
10321
+ const parts = getParts(side);
10322
+ const meridiem = getMeridiem(side);
10323
+ const nextHour = useMeridiem ? meridiem === "PM" ? clamped === 12 ? 12 : clamped + 12 : clamped === 12 ? 0 : clamped : clamped;
10324
+ commit(side, nextHour, parts.minute);
10325
+ return clamped;
10326
+ };
10327
+ const commitMinuteDraft = (side, draft) => {
10328
+ const { hour } = getParts(side);
10329
+ const clamped = clampTimePart(Number(draft), 0, 59);
10330
+ commit(side, hour, clamped);
10331
+ return clamped;
10332
+ };
10333
+ const endEdit = (side, part) => {
10334
+ const key = getEditingKey(side, part);
10335
+ const draft = inputDraft[key];
10336
+ if (part === "hour" && draft != null && draft !== "") {
10337
+ commitHourDraft(side, draft);
10338
+ }
10339
+ if (part === "minute" && draft != null && draft !== "") {
10340
+ commitMinuteDraft(side, draft);
10341
+ }
10342
+ setEditingKey(null);
10343
+ setInputDraft({});
10344
+ };
10345
+ const inputStyle = {
10346
+ width: "var(--cmp-timepicker-selector-input-width)",
10347
+ height: "var(--cmp-textinput-sm-height)",
10348
+ borderRadius: "var(--cmp-textinput-sm-radius)",
10349
+ border: "var(--cmp-textinput-borderWidth) solid var(--cmp-textinput-border-default)",
10350
+ color: "var(--cmp-textinput-text-default)",
10351
+ fontSize: 12,
10352
+ lineHeight: "20px"
10353
+ };
10354
+ const renderColumn = (side, kind) => {
10355
+ const isHour = kind === "hour";
10356
+ const { minute } = getParts(side);
10357
+ const val = isHour ? getDisplayHour(side) : minute;
10358
+ const currentEditingKey = getEditingKey(side, kind);
10359
+ const displayValue = editingKey === currentEditingKey ? inputDraft[currentEditingKey] ?? "" : String(val).padStart(2, "0");
10360
+ return /* @__PURE__ */ jsxs(
10361
+ "div",
10362
+ {
10363
+ className: "flex flex-col items-center justify-center",
10364
+ style: { gap: "var(--cmp-timepicker-selector-time-column-gap)" },
10365
+ children: [
10366
+ /* @__PURE__ */ jsx(
10367
+ SGhostButton,
10368
+ {
10369
+ icon: "chevronUp",
10370
+ size: "xs",
10371
+ ariaLabel: `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"} \uC99D\uAC00`,
10372
+ onClick: () => isHour ? changeHour(side, 1) : changeMinute(side, minuteStep)
10373
+ }
10374
+ ),
10375
+ /* @__PURE__ */ jsx(
10376
+ "input",
10377
+ {
10378
+ "aria-label": `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"}`,
10379
+ inputMode: "numeric",
10380
+ value: displayValue,
10381
+ placeholder: "-",
10382
+ onFocus: () => beginEdit(side, kind, val),
10383
+ onBlur: () => endEdit(side, kind),
10384
+ onChange: (event) => {
10385
+ const draft = sanitizeInput(event.target.value);
10386
+ setInputDraft((prev) => ({ ...prev, [currentEditingKey]: draft }));
10387
+ if (draft === "") return;
10388
+ const raw = Number(draft);
10389
+ if (isHour) {
10390
+ if (draft.length < 2) return;
10391
+ const clamped2 = commitHourDraft(side, draft);
10392
+ const displayDraft2 = raw === clamped2 ? draft : String(clamped2);
10393
+ setInputDraft((prev) => ({ ...prev, [currentEditingKey]: displayDraft2 }));
10394
+ return;
10395
+ }
10396
+ if (draft.length < 2) return;
10397
+ const clamped = commitMinuteDraft(side, draft);
10398
+ const displayDraft = raw === clamped ? draft : String(clamped);
10399
+ setInputDraft((prev) => ({ ...prev, [currentEditingKey]: displayDraft }));
10400
+ },
10401
+ className: "box-border bg-white text-center outline-none",
10402
+ style: inputStyle
10403
+ }
10404
+ ),
10405
+ /* @__PURE__ */ jsx(
10406
+ SGhostButton,
10407
+ {
10408
+ icon: "chevronDown",
10409
+ size: "xs",
10410
+ ariaLabel: `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"} \uAC10\uC18C`,
10411
+ onClick: () => isHour ? changeHour(side, -1) : changeMinute(side, -minuteStep)
10412
+ }
10413
+ )
10414
+ ]
10415
+ }
10416
+ );
10417
+ };
10418
+ const renderPanel = (side, title) => /* @__PURE__ */ jsxs(
10419
+ "div",
10420
+ {
10421
+ className: "flex flex-col items-center justify-center",
10422
+ style: { gap: "var(--cmp-timepicker-selector-range-section-gap)" },
10423
+ children: [
10424
+ /* @__PURE__ */ jsxs(
10425
+ "div",
10426
+ {
10427
+ className: "flex flex-col items-center",
10428
+ style: { gap: "var(--cmp-timepicker-selector-range-heading-gap)" },
10429
+ children: [
10430
+ /* @__PURE__ */ jsx("div", { className: "whitespace-nowrap text-center text-[12px] font-bold leading-[20px] text-[var(--cmp-timepicker-selector-range-heading-color)]", children: title }),
10431
+ useMeridiem && /* @__PURE__ */ jsx("div", { className: "isolate flex", children: ["AM", "PM"].map((item, index) => {
10432
+ const selected = item === getMeridiem(side);
10433
+ return /* @__PURE__ */ jsx(
10434
+ "button",
10435
+ {
10436
+ type: "button",
10437
+ onClick: () => setMeridiem(side, item),
10438
+ className: cn(
10439
+ "box-border h-[var(--cmp-textinput-sm-height)] w-[61px] cursor-pointer border border-solid bg-white text-center text-[12px] font-medium leading-[20px]",
10440
+ index === 0 ? "rounded-l-[var(--cmp-select-sm-radius)] -mr-px" : "rounded-r-[var(--cmp-select-sm-radius)]",
10441
+ selected ? "z-[1] border-[var(--cmp-timepicker-border-focus)] text-[var(--cmp-timepicker-border-focus)]" : "border-[var(--cmp-timepicker-border-default)] text-[var(--cmp-field-hint-color)]"
10442
+ ),
10443
+ children: item === "AM" ? "\uC624\uC804" : "\uC624\uD6C4"
10444
+ },
10445
+ item
10446
+ );
10447
+ }) })
10448
+ ]
10449
+ }
10450
+ ),
10451
+ /* @__PURE__ */ jsxs(
10452
+ "div",
10453
+ {
10454
+ className: "flex items-center justify-center",
10455
+ style: {
10456
+ gap: "var(--cmp-timepicker-selector-time-gap)",
10457
+ padding: "0 var(--cmp-timepicker-selector-section-paddingX)"
10458
+ },
10459
+ children: [
10460
+ renderColumn(side, "hour"),
10461
+ /* @__PURE__ */ jsx(
10462
+ "span",
10463
+ {
10464
+ className: "text-center",
10465
+ style: {
10466
+ width: "var(--cmp-timepicker-selector-colon-size)",
10467
+ color: "var(--cmp-field-hint-color)",
10468
+ fontSize: "var(--cmp-timepicker-selector-colon-size)",
10469
+ lineHeight: "var(--cmp-timepicker-selector-colon-size)"
10470
+ },
10471
+ children: ":"
10472
+ }
10473
+ ),
10474
+ renderColumn(side, "minute")
10475
+ ]
10476
+ }
10477
+ )
10478
+ ]
10479
+ }
10480
+ );
10481
+ return /* @__PURE__ */ jsxs(
10482
+ "div",
10483
+ {
10484
+ className: "flex items-start justify-center overflow-hidden",
10485
+ style: {
10486
+ gap: "var(--cmp-timepicker-selector-range-panelGap)",
10487
+ padding: "var(--cmp-timepicker-selector-range-paddingXY)",
10488
+ background: "var(--cmp-timepicker-selector-bg)",
10489
+ borderRadius: "var(--cmp-timepicker-selector-radius)",
10490
+ boxShadow: "var(--shadow-spread-sm), var(--shadow-spread-md)"
10491
+ },
10492
+ children: [
10493
+ renderPanel("start", "\uC2DC\uC791 \uC2DC\uAC04"),
10494
+ /* @__PURE__ */ jsx("div", { className: "self-stretch w-px bg-[var(--sys-color-divider-default)]" }),
10495
+ renderPanel("end", "\uC885\uB8CC \uC2DC\uAC04")
10496
+ ]
10497
+ }
10498
+ );
10499
+ }
10500
+ function STimeRangePicker({
10501
+ value,
10502
+ onValueChange,
10503
+ onOpenChange,
10504
+ type = "default",
10505
+ size = "sm",
10506
+ placeholder = "00:00 ~ 23:59",
10507
+ disabled = false,
10508
+ clearable = false,
10509
+ useMeridiem,
10510
+ minuteStep = 1,
10511
+ rangeOrder = "strict",
10512
+ width,
10513
+ name,
10514
+ rules,
10515
+ status,
10516
+ label,
10517
+ labelWidth,
10518
+ icon,
10519
+ iconColor,
10520
+ labelTooltip,
10521
+ labelTooltipProps,
10522
+ addonLabel,
10523
+ addonAlign,
10524
+ hint,
10525
+ error,
10526
+ errorMessage,
10527
+ className,
10528
+ style
10529
+ }) {
10530
+ const portalContainer = usePortalContainer();
10531
+ const [open, setOpen] = useState(false);
10532
+ const [focused, setFocused] = useState(false);
10533
+ const [ruleError, setRuleError] = useState("");
10534
+ const dims = TIMEPICKER_SIZE_CONFIG[size] ?? TIMEPICKER_SIZE_CONFIG.sm;
10535
+ const normalized = normalizeTimeRangeValue(value ?? null, rangeOrder);
10536
+ const effectiveUseMeridiem = useMeridiem ?? type === "midday";
10537
+ const displayText = formatTimeRangeDisplay(
10538
+ normalized,
10539
+ effectiveUseMeridiem ? "midday" : "default"
10540
+ );
10541
+ const hasValue = normalized != null && normalized[0] !== "" && normalized[1] !== "";
10542
+ useFormField({
10543
+ name,
10544
+ validate: () => {
10545
+ const msg = runRules(value, rules);
10546
+ setRuleError(msg);
10547
+ return msg === "";
10548
+ },
10549
+ resetValidation: () => setRuleError("")
10550
+ });
10551
+ const resolvedError = error || ruleError !== "";
10552
+ const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
10553
+ const commitOpen = (next) => {
10554
+ if (disabled) return;
10555
+ setOpen(next);
10556
+ onOpenChange?.(next);
10557
+ if (!next && rules && rules.length > 0) setRuleError(runRules(value, rules));
10558
+ };
10559
+ const commitValue = (next) => {
10560
+ onValueChange?.(next);
10561
+ if (rules && rules.length > 0) setRuleError(runRules(next, rules));
10562
+ };
10563
+ return /* @__PURE__ */ jsx(
10564
+ SField,
10565
+ {
10566
+ label,
10567
+ labelWidth,
10568
+ icon,
10569
+ iconColor,
10570
+ labelTooltip,
10571
+ labelTooltipProps,
10572
+ addonLabel,
10573
+ addonAlign,
10574
+ size,
10575
+ hint,
10576
+ error: resolvedError,
10577
+ status: resolvedStatus,
10578
+ focused: focused || open,
10579
+ errorMessage: ruleError !== "" ? ruleError : errorMessage,
10580
+ width,
10581
+ disabled,
10582
+ className,
10583
+ style: { minWidth: dims.fieldMinWidth, ...style },
10584
+ children: /* @__PURE__ */ jsxs(RPopover2.Root, { open, onOpenChange: commitOpen, children: [
10585
+ /* @__PURE__ */ jsx(RPopover2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
10586
+ "button",
10587
+ {
10588
+ type: "button",
10589
+ name,
10590
+ disabled,
10591
+ onFocus: () => setFocused(true),
10592
+ onBlur: () => setFocused(false),
10593
+ className: "flex h-full w-full items-center overflow-hidden bg-transparent outline-none disabled:cursor-not-allowed",
10594
+ style: { padding: `0 ${dims.paddingX}`, gap: dims.gap, borderRadius: dims.radius },
10595
+ children: [
10596
+ /* @__PURE__ */ jsx(
10597
+ SIcon,
10598
+ {
10599
+ name: "clockOutline",
10600
+ size: dims.icon,
10601
+ color: disabled ? "var(--cmp-timepicker-icon-disabled)" : "var(--cmp-timepicker-icon-default)",
10602
+ className: "flex-shrink-0"
10603
+ }
10604
+ ),
10605
+ /* @__PURE__ */ jsx(
10606
+ "span",
10607
+ {
10608
+ className: "flex-1 truncate text-center",
10609
+ style: {
10610
+ minWidth: dims.minWidth,
10611
+ fontSize: dims.fontSize,
10612
+ lineHeight: `${dims.lineHeight}px`,
10613
+ color: disabled ? "var(--cmp-timepicker-text-disabled)" : hasValue ? "var(--cmp-timepicker-text-default)" : "var(--cmp-field-hint-color)"
10614
+ },
10615
+ children: hasValue ? displayText : placeholder
10616
+ }
10617
+ ),
10618
+ clearable && hasValue && !disabled && /* @__PURE__ */ jsx(
10619
+ SGhostButton,
10620
+ {
10621
+ icon: "close",
10622
+ size: "xs",
10623
+ ariaLabel: "\uC2DC\uAC04 \uBC94\uC704 \uC9C0\uC6B0\uAE30",
10624
+ onClick: (event) => {
10625
+ event.stopPropagation();
10626
+ commitValue(null);
10627
+ }
10628
+ }
10629
+ )
10630
+ ]
10631
+ }
10632
+ ) }),
10633
+ /* @__PURE__ */ jsx(RPopover2.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx(
10634
+ RPopover2.Content,
10635
+ {
10636
+ side: "bottom",
10637
+ align: "start",
10638
+ sideOffset: 4,
10639
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
10640
+ children: /* @__PURE__ */ jsx(
10641
+ RangeTimeSelector,
10642
+ {
10643
+ value: normalized,
10644
+ useMeridiem: effectiveUseMeridiem,
10645
+ minuteStep,
10646
+ rangeOrder,
10647
+ onChange: commitValue
10648
+ }
10649
+ )
10650
+ }
10651
+ ) })
10652
+ ] })
10653
+ }
10654
+ );
10655
+ }
10656
+
10657
+ export { BADGE_COLORS, BUTTON_COLORS, BUTTON_SIZES, COLOR_KEYS, GNB_COLORS, GNB_COMMON, GNB_HEADER, GNB_MENU, GNB_MENU_WIDTH, ICONS, SActionModal, SBadge, SBarcodeInput, SButton, SCalendar, SCallout, SCard, SCheckbox, SChip, SChipInput, SCircleProgress, SConfirmModal, SDatePicker, SDateRangePicker, SDivider, SDropdownButton, SField, SFilePicker, SForm, SGhostButton, SGnb, SGuide, SIcon, SInput, SKeyValueTable, SLayout, SLayoutContext, SLinearProgress, SLoadingContainer, SLoadingModal, SLoadingViewport, SNumberInput, SPage, SPagination, SPopover, SPopup, SPortal, SRadio, SRadioButton, SRadioGroup, SSelect, SSwitch, STable, STabs, STag, STextLink, STextarea, STimePicker, STimeRangePicker, SToast, SToastContainer, SToggle, STooltip, TAG_COLORS, TAG_SHAPES, TAG_SIZES, TIMEPICKER_SIZES, TIMEPICKER_SIZE_CONFIG, buttonPreset, cn, findGnbAncestors, isColorKey, loading, resolveColor, useSLayout };
9788
10658
  //# sourceMappingURL=index.js.map
9789
10659
  //# sourceMappingURL=index.js.map