sellmate-design-system-react 0.5.0 → 0.6.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 (30) hide show
  1. package/README.md +20 -0
  2. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +180 -37
  3. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  4. package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +6 -1
  5. package/dist/components/SDateRangePicker/SDateRangePicker.js +180 -37
  6. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  7. package/dist/components/SGnb/SGnb.cjs +124 -54
  8. package/dist/components/SGnb/SGnb.cjs.map +1 -1
  9. package/dist/components/SGnb/SGnb.d.ts +7 -1
  10. package/dist/components/SGnb/SGnb.js +125 -55
  11. package/dist/components/SGnb/SGnb.js.map +1 -1
  12. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +180 -37
  13. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  14. package/dist/components/SKeyValueTable/SKeyValueTable.js +180 -37
  15. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  16. package/dist/components/SLayout/SLayout.cjs +21 -6
  17. package/dist/components/SLayout/SLayout.cjs.map +1 -1
  18. package/dist/components/SLayout/SLayout.d.ts +17 -2
  19. package/dist/components/SLayout/SLayout.js +21 -6
  20. package/dist/components/SLayout/SLayout.js.map +1 -1
  21. package/dist/components/SPage/SPage.cjs +21 -6
  22. package/dist/components/SPage/SPage.cjs.map +1 -1
  23. package/dist/components/SPage/SPage.js +21 -6
  24. package/dist/components/SPage/SPage.js.map +1 -1
  25. package/dist/index.cjs +303 -90
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +303 -90
  28. package/dist/index.js.map +1 -1
  29. package/dist/styles.css +61 -0
  30. package/package.json +3 -1
package/dist/index.js CHANGED
@@ -5041,15 +5041,30 @@ var SLayout = forwardRef(function SLayout2({
5041
5041
  },
5042
5042
  [isControlled, onFoldedChange]
5043
5043
  );
5044
+ const [gnbState, setGnbState] = useState({ forceFull: false, menuVisible: true });
5045
+ const reportGnbState = useCallback(
5046
+ (next) => setGnbState(
5047
+ (prev) => prev.forceFull === next.forceFull && prev.menuVisible === next.menuVisible ? prev : next
5048
+ ),
5049
+ []
5050
+ );
5051
+ const effectiveHeader = header === "full" || gnbState.forceFull ? "full" : "fix";
5044
5052
  const ctx = useMemo(
5045
- () => ({ type, header, useRail, folded: currentFolded, setFolded }),
5046
- [type, header, useRail, currentFolded, setFolded]
5053
+ () => ({
5054
+ type,
5055
+ header: effectiveHeader,
5056
+ useRail,
5057
+ folded: currentFolded,
5058
+ setFolded,
5059
+ reportGnbState
5060
+ }),
5061
+ [type, effectiveHeader, useRail, currentFolded, setFolded, reportGnbState]
5047
5062
  );
5048
- const isFullHeader = header === "full";
5049
- const gnbColumnWidth = useRail ? GNB_RAIL_WIDTH + GNB_MENU_WIDTH : GNB_MENU_WIDTH;
5063
+ const isFullHeader = effectiveHeader === "full";
5064
+ const gnbColumnWidth = (useRail ? GNB_RAIL_WIDTH : 0) + (gnbState.menuVisible ? GNB_MENU_WIDTH : 0);
5050
5065
  const frameStyle = isFullHeader ? {
5051
5066
  gridTemplateColumns: `${currentFolded ? 0 : gnbColumnWidth}px 1fr`,
5052
- gridTemplateRows: `${GNB_HEADER[header].topHeight} 1fr`,
5067
+ gridTemplateRows: `${GNB_HEADER[effectiveHeader].topHeight} 1fr`,
5053
5068
  // 메뉴가 미끄러지는 동안 페이지 열도 같은 길이로 따라와야 한 덩어리로 읽힌다.
5054
5069
  // (fix 는 SGnb 가 제 폭을 직접 전환하므로 프레임이 손댈 게 없다)
5055
5070
  transition: `grid-template-columns ${GNB_FOLD_MS}ms ease-out`
@@ -5059,7 +5074,7 @@ var SLayout = forwardRef(function SLayout2({
5059
5074
  {
5060
5075
  ref,
5061
5076
  "data-type": type,
5062
- "data-header": header,
5077
+ "data-header": effectiveHeader,
5063
5078
  className: cn(
5064
5079
  "box-border h-full overflow-hidden",
5065
5080
  isFullHeader ? "grid" : "flex flex-row",
@@ -5092,9 +5107,10 @@ var SGnb = forwardRef(function SGnb2({
5092
5107
  }, ref) {
5093
5108
  const layout = useContext(SLayoutContext);
5094
5109
  const type = typeProp ?? layout?.type ?? "box";
5095
- const header = headerProp ?? layout?.header ?? "fix";
5096
5110
  const folded = foldedProp ?? layout?.folded ?? false;
5097
5111
  const useRail = useRailProp ?? layout?.useRail ?? false;
5112
+ const railHasLeaf = useRail && items.some((item) => !item.children || item.children.length === 0);
5113
+ const header = railHasLeaf ? "full" : headerProp ?? layout?.header ?? "fix";
5098
5114
  const H = GNB_HEADER[header];
5099
5115
  const M = GNB_MENU[type];
5100
5116
  const C = GNB_COLORS[color];
@@ -5282,7 +5298,11 @@ var SGnb = forwardRef(function SGnb2({
5282
5298
  };
5283
5299
  const railItems = useRail ? items : [];
5284
5300
  const menuItems = useRail ? items.find((item) => item.value === activeRail)?.children ?? [] : items;
5285
- const bodyWidth = useRail ? GNB_RAIL_WIDTH + GNB_MENU_WIDTH : GNB_MENU_WIDTH;
5301
+ const showMenu = !useRail || menuItems.length > 0;
5302
+ const bodyWidth = (useRail ? GNB_RAIL_WIDTH : 0) + (showMenu ? GNB_MENU_WIDTH : 0);
5303
+ useEffect(() => {
5304
+ layout?.reportGnbState({ forceFull: railHasLeaf, menuVisible: showMenu });
5305
+ }, [layout, railHasLeaf, showMenu]);
5286
5306
  const railFolded = folded && header === "fix";
5287
5307
  const rootWidth = header === "full" ? "100%" : folded ? GNB_COMMON.foldWidth : bodyWidth;
5288
5308
  const rootStyle = {
@@ -5292,6 +5312,13 @@ var SGnb = forwardRef(function SGnb2({
5292
5312
  ...style
5293
5313
  };
5294
5314
  const hideAfterSlide = (hidden) => `visibility 0s linear ${hidden ? `${GNB_FOLD_MS}ms` : "0s"}`;
5315
+ const foldInline = !!logo;
5316
+ const sideFoldStyle = {
5317
+ opacity: railFolded ? 0 : 1,
5318
+ position: railFolded ? "absolute" : "static",
5319
+ visibility: railFolded ? "hidden" : "visible",
5320
+ transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}`
5321
+ };
5295
5322
  return /* @__PURE__ */ jsxs(
5296
5323
  "div",
5297
5324
  {
@@ -5303,12 +5330,14 @@ var SGnb = forwardRef(function SGnb2({
5303
5330
  "data-color": color,
5304
5331
  ...rest,
5305
5332
  children: [
5306
- /* @__PURE__ */ jsxs(
5333
+ /* @__PURE__ */ jsx(
5307
5334
  "div",
5308
5335
  {
5309
5336
  className: "relative box-border flex shrink-0 items-center overflow-hidden",
5310
5337
  style: {
5311
5338
  height: H.topHeight,
5339
+ // 인라인 간격은 컨테이너 gap 대신 각 요소 margin 으로 준다 — 런처↔폴드(8px)와
5340
+ // 폴드↔로고(top-gap)를 다르게 두기 위함. 접히면 런처·로고가 흐름에서 빠져 margin 도 무의미해진다.
5312
5341
  padding: `0 ${railFolded ? GNB_RAIL.padding : H.topPaddingX}`,
5313
5342
  borderBottom: `1px solid ${C.topBorder}`,
5314
5343
  backgroundColor: C.panelBg,
@@ -5317,49 +5346,90 @@ var SGnb = forwardRef(function SGnb2({
5317
5346
  // 아이콘 버튼은 자체 ghostButton 토큰을 쓰므로, 이 색은 logo 슬롯 상속용
5318
5347
  color: C.itemColorDefault
5319
5348
  },
5320
- children: [
5321
- (onLauncherClick || logo) && /* @__PURE__ */ jsxs(
5322
- "div",
5323
- {
5324
- className: "absolute inset-y-0 flex items-center",
5325
- style: {
5326
- left: H.topPaddingX,
5327
- gap: H.topGap,
5328
- opacity: railFolded ? 0 : 1,
5329
- transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}, transform ${GNB_FOLD_MS}ms ease-out`,
5330
- // 메뉴와 같은 방향으로 빠지되, 좁은 레일 안에서 꼬리가 비치지 않게 페이드가 거리를 대신한다
5331
- transform: railFolded ? `translateX(-${H.topPaddingX})` : "none",
5332
- visibility: railFolded ? "hidden" : "visible"
5333
- },
5334
- children: [
5335
- onLauncherClick && /* @__PURE__ */ jsx(
5336
- SGhostButton,
5337
- {
5338
- icon: "launcher",
5339
- size: "xs",
5340
- intent: color === "dark" ? "inverse" : "default",
5341
- ariaLabel: "app launcher",
5342
- onClick: onLauncherClick,
5343
- className: "shrink-0"
5344
- }
5345
- ),
5346
- logo && /* @__PURE__ */ jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo })
5347
- ]
5348
- }
5349
- ),
5350
- /* @__PURE__ */ jsx(
5351
- SGhostButton,
5352
- {
5353
- icon: "sidebar",
5354
- size: "xs",
5355
- intent: color === "dark" ? "inverse" : "default",
5356
- ariaLabel: "toggle navigation",
5357
- ariaPressed: folded,
5358
- onClick: handleFoldToggle,
5359
- className: "ml-auto shrink-0"
5360
- }
5361
- )
5362
- ]
5349
+ children: foldInline ? (
5350
+ // 런처(있을 )·폴드·로고를 흐름에 나란히 둔다 폴드 버튼이 (런처와) 로고 사이에 선다.
5351
+ // 접히면 런처·로고가 페이드하며 흐름에서 빠지고(sideFoldStyle), 폴드 버튼만 남아 레일 가운데에 선다.
5352
+ /* @__PURE__ */ jsxs(Fragment, { children: [
5353
+ onLauncherClick && /* @__PURE__ */ jsx(
5354
+ SGhostButton,
5355
+ {
5356
+ icon: "launcher",
5357
+ size: "xs",
5358
+ intent: color === "dark" ? "inverse" : "default",
5359
+ ariaLabel: "app launcher",
5360
+ onClick: onLauncherClick,
5361
+ className: "shrink-0",
5362
+ style: { ...sideFoldStyle, marginRight: "8px" }
5363
+ }
5364
+ ),
5365
+ /* @__PURE__ */ jsx(
5366
+ SGhostButton,
5367
+ {
5368
+ icon: "sidebar",
5369
+ size: "xs",
5370
+ intent: color === "dark" ? "inverse" : "default",
5371
+ ariaLabel: "toggle navigation",
5372
+ ariaPressed: folded,
5373
+ onClick: handleFoldToggle,
5374
+ className: "shrink-0"
5375
+ }
5376
+ ),
5377
+ /* @__PURE__ */ jsx(
5378
+ "div",
5379
+ {
5380
+ className: "flex shrink-0 items-center",
5381
+ style: { height: H.logoHeight, marginLeft: H.topGap, ...sideFoldStyle },
5382
+ children: logo
5383
+ }
5384
+ )
5385
+ ] })
5386
+ ) : (
5387
+ // 런처·로고는 좌측 그룹(흐름에서 빼 폭 축소 중 폴드 버튼을 밀어내지 않는다),
5388
+ // 폴드 버튼은 우측 끝(ml-auto)이라 폭·패딩이 줄면 저절로 레일 가운데에 선다.
5389
+ /* @__PURE__ */ jsxs(Fragment, { children: [
5390
+ (onLauncherClick || logo) && /* @__PURE__ */ jsxs(
5391
+ "div",
5392
+ {
5393
+ className: "absolute inset-y-0 flex items-center",
5394
+ style: {
5395
+ left: H.topPaddingX,
5396
+ gap: H.topGap,
5397
+ opacity: railFolded ? 0 : 1,
5398
+ transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}, transform ${GNB_FOLD_MS}ms ease-out`,
5399
+ // 메뉴와 같은 방향으로 빠지되, 좁은 레일 안에서 꼬리가 비치지 않게 페이드가 거리를 대신한다
5400
+ transform: railFolded ? `translateX(-${H.topPaddingX})` : "none",
5401
+ visibility: railFolded ? "hidden" : "visible"
5402
+ },
5403
+ children: [
5404
+ onLauncherClick && /* @__PURE__ */ jsx(
5405
+ SGhostButton,
5406
+ {
5407
+ icon: "launcher",
5408
+ size: "xs",
5409
+ intent: color === "dark" ? "inverse" : "default",
5410
+ ariaLabel: "app launcher",
5411
+ onClick: onLauncherClick,
5412
+ className: "shrink-0"
5413
+ }
5414
+ ),
5415
+ logo && /* @__PURE__ */ jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo })
5416
+ ]
5417
+ }
5418
+ ),
5419
+ /* @__PURE__ */ jsx(
5420
+ SGhostButton,
5421
+ {
5422
+ icon: "sidebar",
5423
+ size: "xs",
5424
+ intent: color === "dark" ? "inverse" : "default",
5425
+ ariaLabel: "toggle navigation",
5426
+ ariaPressed: folded,
5427
+ onClick: handleFoldToggle,
5428
+ className: "ml-auto shrink-0"
5429
+ }
5430
+ )
5431
+ ] })
5432
+ )
5363
5433
  }
5364
5434
  ),
5365
5435
  /* @__PURE__ */ jsxs(
@@ -5395,7 +5465,7 @@ var SGnb = forwardRef(function SGnb2({
5395
5465
  children: /* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col p-0", style: { gap: GNB_RAIL.gap }, children: railItems.map(renderRailItem) })
5396
5466
  }
5397
5467
  ),
5398
- /* @__PURE__ */ jsx(
5468
+ showMenu && /* @__PURE__ */ jsx(
5399
5469
  "nav",
5400
5470
  {
5401
5471
  "aria-label": ariaLabel,
@@ -8060,25 +8130,45 @@ function SDatePicker({
8060
8130
  }
8061
8131
  );
8062
8132
  }
8133
+ function splitDateTime(v) {
8134
+ if (!v) return { date: null, hour: "00", minute: "00" };
8135
+ const [date = "", time = ""] = v.split(" ");
8136
+ const [hour = "00", minute = "00"] = time.split(":");
8137
+ return { date: date || null, hour, minute };
8138
+ }
8063
8139
  function RangeCalendar({
8064
8140
  value,
8065
8141
  selectable,
8066
8142
  maxRange,
8143
+ useTimePicker = false,
8067
8144
  onSelect,
8068
8145
  onPendingStartChange,
8069
8146
  onViewChange
8070
8147
  }) {
8071
8148
  const today = todayStr();
8072
- const base = value?.[0] || today;
8149
+ const initial = splitDateTime(value?.[0]);
8150
+ const base = initial.date || today;
8073
8151
  const [y, setY] = useState(Number(base.split("-")[0]));
8074
8152
  const [m, setM] = useState(Number(base.split("-")[1]));
8075
- const [start, setStart] = useState(value?.[0] ?? null);
8076
- const [end, setEnd] = useState(value?.[1] ?? null);
8153
+ const [start, setStart] = useState(initial.date);
8154
+ const [end, setEnd] = useState(splitDateTime(value?.[1]).date);
8077
8155
  const [hover, setHover] = useState(null);
8156
+ const [sH, setSH] = useState(initial.hour);
8157
+ const [sM, setSM] = useState(initial.minute);
8158
+ const [eH, setEH] = useState(splitDateTime(value?.[1]).hour);
8159
+ const [eM, setEM] = useState(splitDateTime(value?.[1]).minute);
8160
+ const lastEmitted = useRef("");
8078
8161
  useEffect(() => {
8079
- setStart(value?.[0] ?? null);
8080
- setEnd(value?.[1] ?? null);
8081
- }, [value]);
8162
+ if (useTimePicker && JSON.stringify(value) === lastEmitted.current) return;
8163
+ const s = splitDateTime(value?.[0]);
8164
+ const e = splitDateTime(value?.[1]);
8165
+ setStart(s.date);
8166
+ setEnd(e.date);
8167
+ setSH(s.hour);
8168
+ setSM(s.minute);
8169
+ setEH(e.hour);
8170
+ setEM(e.minute);
8171
+ }, [value, useTimePicker]);
8082
8172
  const setBase = (ny, nm) => {
8083
8173
  setY(ny);
8084
8174
  setM(nm);
@@ -8122,18 +8212,53 @@ function RangeCalendar({
8122
8212
  const hasRange = !!(rStart && rEnd && rStart !== rEnd);
8123
8213
  const inRange = (d) => !!(rStart && rEnd && d > rStart && d < rEnd);
8124
8214
  const RANGE_BG = "var(--cmp-datepicker-calendar-range-bg)";
8215
+ const pad = (v) => String(Number(v) || 0).padStart(2, "0");
8216
+ const emit2 = (dateStart, dateEnd, time) => {
8217
+ const t = time ?? { sH, sM, eH, eM };
8218
+ const range = useTimePicker ? [`${dateStart} ${pad(t.sH)}:${pad(t.sM)}`, `${dateEnd} ${pad(t.eH)}:${pad(t.eM)}`] : [dateStart, dateEnd];
8219
+ lastEmitted.current = JSON.stringify(range);
8220
+ onSelect(range);
8221
+ };
8125
8222
  const click = (d) => {
8126
8223
  if (isDisabled(d)) return;
8127
8224
  if (!start || complete || d < start) {
8128
8225
  setStart(d);
8129
8226
  setEnd(null);
8130
8227
  setHover(null);
8228
+ if (useTimePicker) {
8229
+ setSH("00");
8230
+ setSM("00");
8231
+ setEH("23");
8232
+ setEM("59");
8233
+ }
8131
8234
  onPendingStartChange?.(d);
8132
8235
  return;
8133
8236
  }
8134
8237
  setEnd(d);
8135
8238
  onPendingStartChange?.(null);
8136
- onSelect([start, d]);
8239
+ emit2(start, d, useTimePicker ? { sH, sM, eH: "23", eM: "59" } : void 0);
8240
+ };
8241
+ const changeTime = (which, part, raw) => {
8242
+ const digits = raw.replace(/\D/g, "").slice(0, 2);
8243
+ const max = part === "h" ? 23 : 59;
8244
+ const next = digits !== "" && Number(digits) > max ? String(max) : digits;
8245
+ const nextTime = { sH, sM, eH, eM };
8246
+ if (which === "start") nextTime[part === "h" ? "sH" : "sM"] = next;
8247
+ else nextTime[part === "h" ? "eH" : "eM"] = next;
8248
+ setSH(nextTime.sH);
8249
+ setSM(nextTime.sM);
8250
+ setEH(nextTime.eH);
8251
+ setEM(nextTime.eM);
8252
+ if (start && end) emit2(start, end, nextTime);
8253
+ };
8254
+ const blurTime = (which, part) => {
8255
+ if (which === "start") {
8256
+ if (part === "h") setSH(pad(sH));
8257
+ else setSM(pad(sM));
8258
+ } else {
8259
+ if (part === "h") setEH(pad(eH));
8260
+ else setEM(pad(eM));
8261
+ }
8137
8262
  };
8138
8263
  const GRID_COLS = "grid grid-cols-[repeat(7,var(--cmp-datepicker-calendar-day-width))]";
8139
8264
  const renderPanel = (gy, gm, isLeft) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-[var(--cmp-datepicker-calendar-gap)]", children: [
@@ -8208,35 +8333,120 @@ function RangeCalendar({
8208
8333
  );
8209
8334
  }) })
8210
8335
  ] });
8211
- return /* @__PURE__ */ jsxs("div", { className: "inline-flex h-[364px] flex-col gap-[var(--cmp-datepicker-calendar-gap)] rounded-[var(--cmp-datepicker-calendar-radius)] bg-[var(--cmp-datepicker-calendar-bg)] p-[var(--cmp-datepicker-calendar-paddingXY)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.2)] select-none", children: [
8212
- /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
8213
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-[var(--cmp-datepicker-calendar-gap)] text-[14px] font-medium leading-[24px]", children: [
8214
- /* @__PURE__ */ jsx(SGhostButton, { icon: "chevronLeft", size: "xxs", ariaLabel: "prevYear", onClick: goPrevYear }),
8215
- /* @__PURE__ */ jsx("span", { className: "inline-flex min-w-[40px] items-center justify-center text-center", children: y }),
8216
- /* @__PURE__ */ jsx(SGhostButton, { icon: "chevronRight", size: "xxs", ariaLabel: "nextYear", onClick: goNextYear })
8217
- ] }),
8218
- /* @__PURE__ */ jsx(
8219
- "button",
8220
- {
8221
- type: "button",
8222
- onClick: goToday,
8223
- className: "absolute right-0 h-[24px] min-w-[37px] cursor-pointer bg-transparent text-center text-[12px] font-medium leading-[24px] text-[color:var(--cmp-datepicker-calendar-day-default-text)] hover:underline",
8224
- children: "\uC624\uB298"
8225
- }
8226
- )
8227
- ] }),
8228
- /* @__PURE__ */ jsxs("div", { className: "flex gap-[var(--cmp-datepicker-calendar-range-panelGap)]", children: [
8229
- renderPanel(y, m, true),
8230
- /* @__PURE__ */ jsx(
8231
- "span",
8232
- {
8233
- className: "w-px shrink-0 self-stretch bg-[var(--cmp-datepicker-calendar-range-divider)]",
8234
- "aria-hidden": true
8235
- }
8336
+ const renderTimeSection = (which, date, suffix) => {
8337
+ const active = !!date;
8338
+ const [yy = "", mm = "", dd = ""] = date ? date.split("-") : [];
8339
+ const hour = which === "start" ? sH : eH;
8340
+ const minute = which === "start" ? sM : eM;
8341
+ const num = (n, strip) => active && n !== "" ? strip ? String(Number(n)) : n : "-";
8342
+ const INPUT_CLS = "h-[var(--cmp-datepicker-sm-height)] w-[40px] rounded-[var(--cmp-datepicker-sm-radius)] border border-solid border-[color:var(--cmp-datepicker-border-default)] bg-[var(--cmp-datepicker-bg-default)] text-center text-[12px] font-medium leading-[20px] text-[color:var(--cmp-datepicker-text-default)] outline-none placeholder:text-[color:var(--cmp-datepicker-text-disabled)] focus:border-[color:var(--cmp-datepicker-border-focus)] disabled:cursor-not-allowed";
8343
+ const LABEL = "text-[color:var(--cmp-datepicker-calendar-time-date-label)]";
8344
+ const UNIT = "text-[color:var(--cmp-datepicker-calendar-time-date-unit)]";
8345
+ return /* @__PURE__ */ jsxs(
8346
+ "div",
8347
+ {
8348
+ className: cn(
8349
+ "flex flex-1 items-center justify-center gap-[var(--cmp-datepicker-calendar-time-section-gap)] rounded-[var(--cmp-datepicker-calendar-time-radius)] px-[var(--cmp-datepicker-calendar-time-paddingX)] py-[var(--cmp-datepicker-calendar-time-paddingY)] text-[12px] font-medium leading-[20px]",
8350
+ active ? "bg-[var(--cmp-datepicker-calendar-time-bg-selected)]" : "bg-[var(--cmp-datepicker-calendar-time-bg-default)]"
8351
+ ),
8352
+ children: [
8353
+ /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-[var(--cmp-datepicker-calendar-time-date-gap)]", children: [
8354
+ /* @__PURE__ */ jsx("span", { className: LABEL, children: num(yy, false) }),
8355
+ /* @__PURE__ */ jsx("span", { className: UNIT, children: "\uB144" }),
8356
+ /* @__PURE__ */ jsx("span", { className: LABEL, children: num(mm, true) }),
8357
+ /* @__PURE__ */ jsx("span", { className: UNIT, children: "\uC6D4" }),
8358
+ /* @__PURE__ */ jsx("span", { className: LABEL, children: num(dd, true) }),
8359
+ /* @__PURE__ */ jsx("span", { className: UNIT, children: "\uC77C" })
8360
+ ] }),
8361
+ /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-[var(--cmp-datepicker-calendar-time-unit-gap)]", children: [
8362
+ /* @__PURE__ */ jsx(
8363
+ "input",
8364
+ {
8365
+ type: "text",
8366
+ inputMode: "numeric",
8367
+ maxLength: 2,
8368
+ "aria-label": `${suffix} \uC2DC`,
8369
+ disabled: !active,
8370
+ value: active ? hour : "",
8371
+ placeholder: "--",
8372
+ onChange: (e) => changeTime(which, "h", e.target.value),
8373
+ onBlur: () => blurTime(which, "h"),
8374
+ className: INPUT_CLS
8375
+ }
8376
+ ),
8377
+ /* @__PURE__ */ jsx("span", { className: LABEL, children: ":" }),
8378
+ /* @__PURE__ */ jsx(
8379
+ "input",
8380
+ {
8381
+ type: "text",
8382
+ inputMode: "numeric",
8383
+ maxLength: 2,
8384
+ "aria-label": `${suffix} \uBD84`,
8385
+ disabled: !active,
8386
+ value: active ? minute : "",
8387
+ placeholder: "--",
8388
+ onChange: (e) => changeTime(which, "m", e.target.value),
8389
+ onBlur: () => blurTime(which, "m"),
8390
+ className: INPUT_CLS
8391
+ }
8392
+ )
8393
+ ] }),
8394
+ /* @__PURE__ */ jsx("span", { className: UNIT, children: suffix })
8395
+ ]
8396
+ }
8397
+ );
8398
+ };
8399
+ return /* @__PURE__ */ jsxs(
8400
+ "div",
8401
+ {
8402
+ className: cn(
8403
+ "inline-flex flex-col gap-[var(--cmp-datepicker-calendar-gap)] rounded-[var(--cmp-datepicker-calendar-radius)] bg-[var(--cmp-datepicker-calendar-bg)] p-[var(--cmp-datepicker-calendar-paddingXY)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.2)] select-none",
8404
+ useTimePicker ? "h-auto" : "h-[364px]"
8236
8405
  ),
8237
- renderPanel(nextMonthYM.year, nextMonthYM.month, false)
8238
- ] })
8239
- ] });
8406
+ children: [
8407
+ /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
8408
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-[var(--cmp-datepicker-calendar-gap)] text-[14px] font-medium leading-[24px]", children: [
8409
+ /* @__PURE__ */ jsx(SGhostButton, { icon: "chevronLeft", size: "xxs", ariaLabel: "prevYear", onClick: goPrevYear }),
8410
+ /* @__PURE__ */ jsx("span", { className: "inline-flex min-w-[40px] items-center justify-center text-center", children: y }),
8411
+ /* @__PURE__ */ jsx(SGhostButton, { icon: "chevronRight", size: "xxs", ariaLabel: "nextYear", onClick: goNextYear })
8412
+ ] }),
8413
+ /* @__PURE__ */ jsx(
8414
+ "button",
8415
+ {
8416
+ type: "button",
8417
+ onClick: goToday,
8418
+ className: "absolute right-0 h-[24px] min-w-[37px] cursor-pointer bg-transparent text-center text-[12px] font-medium leading-[24px] text-[color:var(--cmp-datepicker-calendar-day-default-text)] hover:underline",
8419
+ children: "\uC624\uB298"
8420
+ }
8421
+ )
8422
+ ] }),
8423
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-[var(--cmp-datepicker-calendar-range-panelGap)]", children: [
8424
+ renderPanel(y, m, true),
8425
+ /* @__PURE__ */ jsx(
8426
+ "span",
8427
+ {
8428
+ className: "w-px shrink-0 self-stretch bg-[var(--cmp-datepicker-calendar-range-divider)]",
8429
+ "aria-hidden": true
8430
+ }
8431
+ ),
8432
+ renderPanel(nextMonthYM.year, nextMonthYM.month, false)
8433
+ ] }),
8434
+ useTimePicker && /* @__PURE__ */ jsxs(Fragment, { children: [
8435
+ /* @__PURE__ */ jsx(
8436
+ "span",
8437
+ {
8438
+ className: "h-px w-full bg-[var(--cmp-datepicker-calendar-range-divider)]",
8439
+ "aria-hidden": true
8440
+ }
8441
+ ),
8442
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-[var(--cmp-datepicker-calendar-time-gap)]", children: [
8443
+ renderTimeSection("start", start, "\uBD80\uD130"),
8444
+ renderTimeSection("end", end, "\uAE4C\uC9C0")
8445
+ ] })
8446
+ ] })
8447
+ ]
8448
+ }
8449
+ );
8240
8450
  }
8241
8451
  function SDateRangePicker({
8242
8452
  value,
@@ -8246,6 +8456,7 @@ function SDateRangePicker({
8246
8456
  placeholder = "YYYY-MM-DD ~ YYYY-MM-DD",
8247
8457
  selectable,
8248
8458
  maxRange,
8459
+ useTimePicker = false,
8249
8460
  disabled = false,
8250
8461
  width,
8251
8462
  name,
@@ -8289,8 +8500,9 @@ function SDateRangePicker({
8289
8500
  setPendingStart(null);
8290
8501
  onValueChange?.(r);
8291
8502
  if (rules && rules.length > 0) setRuleError(runRules(r, rules));
8292
- setOpen(false);
8503
+ if (!useTimePicker) setOpen(false);
8293
8504
  };
8505
+ const resolvedPlaceholder = useTimePicker && placeholder === "YYYY-MM-DD ~ YYYY-MM-DD" ? "YYYY-MM-DD HH:mm ~ YYYY-MM-DD HH:mm" : placeholder;
8294
8506
  return /* @__PURE__ */ jsx(
8295
8507
  SField,
8296
8508
  {
@@ -8357,7 +8569,7 @@ function SDateRangePicker({
8357
8569
  /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-right", children: startText }),
8358
8570
  /* @__PURE__ */ jsx("span", { className: "px-[4px]", children: "~" }),
8359
8571
  /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: endText })
8360
- ] }) : placeholder
8572
+ ] }) : resolvedPlaceholder
8361
8573
  }
8362
8574
  )
8363
8575
  ]
@@ -8376,6 +8588,7 @@ function SDateRangePicker({
8376
8588
  value: value ?? null,
8377
8589
  selectable,
8378
8590
  maxRange,
8591
+ useTimePicker,
8379
8592
  onSelect: select,
8380
8593
  onPendingStartChange: setPendingStart,
8381
8594
  onViewChange