sellmate-design-system-react 0.4.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 (44) 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 +329 -100
  8. package/dist/components/SGnb/SGnb.cjs.map +1 -1
  9. package/dist/components/SGnb/SGnb.d.ts +24 -5
  10. package/dist/components/SGnb/SGnb.js +330 -101
  11. package/dist/components/SGnb/SGnb.js.map +1 -1
  12. package/dist/components/SGnb/gnb.config.d.ts +46 -12
  13. package/dist/components/SGnb/index.d.ts +1 -1
  14. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +180 -37
  15. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  16. package/dist/components/SKeyValueTable/SKeyValueTable.js +180 -37
  17. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  18. package/dist/components/SLayout/SLayout.cjs +37 -21
  19. package/dist/components/SLayout/SLayout.cjs.map +1 -1
  20. package/dist/components/SLayout/SLayout.d.ts +20 -1
  21. package/dist/components/SLayout/SLayout.js +37 -21
  22. package/dist/components/SLayout/SLayout.js.map +1 -1
  23. package/dist/components/SPage/SPage.cjs +38 -22
  24. package/dist/components/SPage/SPage.cjs.map +1 -1
  25. package/dist/components/SPage/SPage.js +38 -22
  26. package/dist/components/SPage/SPage.js.map +1 -1
  27. package/dist/components/STabs/STabs.cjs +15 -26
  28. package/dist/components/STabs/STabs.cjs.map +1 -1
  29. package/dist/components/STabs/STabs.d.ts +1 -1
  30. package/dist/components/STabs/STabs.js +15 -26
  31. package/dist/components/STabs/STabs.js.map +1 -1
  32. package/dist/components/STimePicker/STimePicker.cjs.map +1 -1
  33. package/dist/components/STimePicker/STimePicker.js.map +1 -1
  34. package/dist/components/STimeRangePicker/STimeRangePicker.cjs +3 -1
  35. package/dist/components/STimeRangePicker/STimeRangePicker.cjs.map +1 -1
  36. package/dist/components/STimeRangePicker/STimeRangePicker.js +3 -1
  37. package/dist/components/STimeRangePicker/STimeRangePicker.js.map +1 -1
  38. package/dist/index.cjs +531 -164
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.js +528 -165
  41. package/dist/index.js.map +1 -1
  42. package/dist/styles.css +73 -4
  43. package/dist/theme.css +1 -1
  44. package/package.json +3 -1
@@ -20,6 +20,11 @@ export interface SDateRangePickerProps {
20
20
  selectable?: [string, string];
21
21
  /** 최대 선택 일수 */
22
22
  maxRange?: number;
23
+ /**
24
+ * 시간(시:분) 선택 푸터 사용 여부.
25
+ * true이면 value는 "YYYY-MM-DD HH:mm" 형식이 되고, 캘린더 하단에 시작·종료 시간 입력이 표시된다.
26
+ */
27
+ useTimePicker?: boolean;
23
28
  disabled?: boolean;
24
29
  width?: number | string;
25
30
  name?: string;
@@ -40,4 +45,4 @@ export interface SDateRangePickerProps {
40
45
  style?: CSSProperties;
41
46
  }
42
47
  /** SDateRangePicker — sd-date-range-picker 포팅. SField + SPopover(범위 캘린더). */
43
- export declare function SDateRangePicker({ value, onValueChange, onViewChange, size, placeholder, selectable, maxRange, disabled, width, name, rules, status, label, labelWidth, icon, iconColor, labelTooltip, labelTooltipProps, addonLabel, addonAlign, hint, error, errorMessage, className, style, }: SDateRangePickerProps): import("react").JSX.Element;
48
+ export declare function SDateRangePicker({ value, onValueChange, onViewChange, size, placeholder, selectable, maxRange, useTimePicker, disabled, width, name, rules, status, label, labelWidth, icon, iconColor, labelTooltip, labelTooltipProps, addonLabel, addonAlign, hint, error, errorMessage, className, style, }: SDateRangePickerProps): import("react").JSX.Element;
@@ -3426,25 +3426,45 @@ function monthCells(year, month) {
3426
3426
  return cells;
3427
3427
  }
3428
3428
  var WEEK_LABELS = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
3429
+ function splitDateTime(v) {
3430
+ if (!v) return { date: null, hour: "00", minute: "00" };
3431
+ const [date = "", time = ""] = v.split(" ");
3432
+ const [hour = "00", minute = "00"] = time.split(":");
3433
+ return { date: date || null, hour, minute };
3434
+ }
3429
3435
  function RangeCalendar({
3430
3436
  value,
3431
3437
  selectable,
3432
3438
  maxRange,
3439
+ useTimePicker = false,
3433
3440
  onSelect,
3434
3441
  onPendingStartChange,
3435
3442
  onViewChange
3436
3443
  }) {
3437
3444
  const today = todayStr();
3438
- const base = value?.[0] || today;
3445
+ const initial = splitDateTime(value?.[0]);
3446
+ const base = initial.date || today;
3439
3447
  const [y, setY] = useState(Number(base.split("-")[0]));
3440
3448
  const [m, setM] = useState(Number(base.split("-")[1]));
3441
- const [start, setStart] = useState(value?.[0] ?? null);
3442
- const [end, setEnd] = useState(value?.[1] ?? null);
3449
+ const [start, setStart] = useState(initial.date);
3450
+ const [end, setEnd] = useState(splitDateTime(value?.[1]).date);
3443
3451
  const [hover, setHover] = useState(null);
3452
+ const [sH, setSH] = useState(initial.hour);
3453
+ const [sM, setSM] = useState(initial.minute);
3454
+ const [eH, setEH] = useState(splitDateTime(value?.[1]).hour);
3455
+ const [eM, setEM] = useState(splitDateTime(value?.[1]).minute);
3456
+ const lastEmitted = useRef("");
3444
3457
  useEffect(() => {
3445
- setStart(value?.[0] ?? null);
3446
- setEnd(value?.[1] ?? null);
3447
- }, [value]);
3458
+ if (useTimePicker && JSON.stringify(value) === lastEmitted.current) return;
3459
+ const s = splitDateTime(value?.[0]);
3460
+ const e = splitDateTime(value?.[1]);
3461
+ setStart(s.date);
3462
+ setEnd(e.date);
3463
+ setSH(s.hour);
3464
+ setSM(s.minute);
3465
+ setEH(e.hour);
3466
+ setEM(e.minute);
3467
+ }, [value, useTimePicker]);
3448
3468
  const setBase = (ny, nm) => {
3449
3469
  setY(ny);
3450
3470
  setM(nm);
@@ -3488,18 +3508,53 @@ function RangeCalendar({
3488
3508
  const hasRange = !!(rStart && rEnd && rStart !== rEnd);
3489
3509
  const inRange = (d) => !!(rStart && rEnd && d > rStart && d < rEnd);
3490
3510
  const RANGE_BG = "var(--cmp-datepicker-calendar-range-bg)";
3511
+ const pad = (v) => String(Number(v) || 0).padStart(2, "0");
3512
+ const emit = (dateStart, dateEnd, time) => {
3513
+ const t = time ?? { sH, sM, eH, eM };
3514
+ const range = useTimePicker ? [`${dateStart} ${pad(t.sH)}:${pad(t.sM)}`, `${dateEnd} ${pad(t.eH)}:${pad(t.eM)}`] : [dateStart, dateEnd];
3515
+ lastEmitted.current = JSON.stringify(range);
3516
+ onSelect(range);
3517
+ };
3491
3518
  const click = (d) => {
3492
3519
  if (isDisabled(d)) return;
3493
3520
  if (!start || complete || d < start) {
3494
3521
  setStart(d);
3495
3522
  setEnd(null);
3496
3523
  setHover(null);
3524
+ if (useTimePicker) {
3525
+ setSH("00");
3526
+ setSM("00");
3527
+ setEH("23");
3528
+ setEM("59");
3529
+ }
3497
3530
  onPendingStartChange?.(d);
3498
3531
  return;
3499
3532
  }
3500
3533
  setEnd(d);
3501
3534
  onPendingStartChange?.(null);
3502
- onSelect([start, d]);
3535
+ emit(start, d, useTimePicker ? { sH, sM, eH: "23", eM: "59" } : void 0);
3536
+ };
3537
+ const changeTime = (which, part, raw) => {
3538
+ const digits = raw.replace(/\D/g, "").slice(0, 2);
3539
+ const max = part === "h" ? 23 : 59;
3540
+ const next = digits !== "" && Number(digits) > max ? String(max) : digits;
3541
+ const nextTime = { sH, sM, eH, eM };
3542
+ if (which === "start") nextTime[part === "h" ? "sH" : "sM"] = next;
3543
+ else nextTime[part === "h" ? "eH" : "eM"] = next;
3544
+ setSH(nextTime.sH);
3545
+ setSM(nextTime.sM);
3546
+ setEH(nextTime.eH);
3547
+ setEM(nextTime.eM);
3548
+ if (start && end) emit(start, end, nextTime);
3549
+ };
3550
+ const blurTime = (which, part) => {
3551
+ if (which === "start") {
3552
+ if (part === "h") setSH(pad(sH));
3553
+ else setSM(pad(sM));
3554
+ } else {
3555
+ if (part === "h") setEH(pad(eH));
3556
+ else setEM(pad(eM));
3557
+ }
3503
3558
  };
3504
3559
  const GRID_COLS = "grid grid-cols-[repeat(7,var(--cmp-datepicker-calendar-day-width))]";
3505
3560
  const renderPanel = (gy, gm, isLeft) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-[var(--cmp-datepicker-calendar-gap)]", children: [
@@ -3574,35 +3629,120 @@ function RangeCalendar({
3574
3629
  );
3575
3630
  }) })
3576
3631
  ] });
3577
- 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: [
3578
- /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
3579
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-[var(--cmp-datepicker-calendar-gap)] text-[14px] font-medium leading-[24px]", children: [
3580
- /* @__PURE__ */ jsx(SGhostButton, { icon: "chevronLeft", size: "xxs", ariaLabel: "prevYear", onClick: goPrevYear }),
3581
- /* @__PURE__ */ jsx("span", { className: "inline-flex min-w-[40px] items-center justify-center text-center", children: y }),
3582
- /* @__PURE__ */ jsx(SGhostButton, { icon: "chevronRight", size: "xxs", ariaLabel: "nextYear", onClick: goNextYear })
3583
- ] }),
3584
- /* @__PURE__ */ jsx(
3585
- "button",
3586
- {
3587
- type: "button",
3588
- onClick: goToday,
3589
- 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",
3590
- children: "\uC624\uB298"
3591
- }
3592
- )
3593
- ] }),
3594
- /* @__PURE__ */ jsxs("div", { className: "flex gap-[var(--cmp-datepicker-calendar-range-panelGap)]", children: [
3595
- renderPanel(y, m, true),
3596
- /* @__PURE__ */ jsx(
3597
- "span",
3598
- {
3599
- className: "w-px shrink-0 self-stretch bg-[var(--cmp-datepicker-calendar-range-divider)]",
3600
- "aria-hidden": true
3601
- }
3632
+ const renderTimeSection = (which, date, suffix) => {
3633
+ const active = !!date;
3634
+ const [yy = "", mm = "", dd = ""] = date ? date.split("-") : [];
3635
+ const hour = which === "start" ? sH : eH;
3636
+ const minute = which === "start" ? sM : eM;
3637
+ const num = (n, strip) => active && n !== "" ? strip ? String(Number(n)) : n : "-";
3638
+ 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";
3639
+ const LABEL = "text-[color:var(--cmp-datepicker-calendar-time-date-label)]";
3640
+ const UNIT = "text-[color:var(--cmp-datepicker-calendar-time-date-unit)]";
3641
+ return /* @__PURE__ */ jsxs(
3642
+ "div",
3643
+ {
3644
+ className: cn(
3645
+ "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]",
3646
+ active ? "bg-[var(--cmp-datepicker-calendar-time-bg-selected)]" : "bg-[var(--cmp-datepicker-calendar-time-bg-default)]"
3647
+ ),
3648
+ children: [
3649
+ /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-[var(--cmp-datepicker-calendar-time-date-gap)]", children: [
3650
+ /* @__PURE__ */ jsx("span", { className: LABEL, children: num(yy, false) }),
3651
+ /* @__PURE__ */ jsx("span", { className: UNIT, children: "\uB144" }),
3652
+ /* @__PURE__ */ jsx("span", { className: LABEL, children: num(mm, true) }),
3653
+ /* @__PURE__ */ jsx("span", { className: UNIT, children: "\uC6D4" }),
3654
+ /* @__PURE__ */ jsx("span", { className: LABEL, children: num(dd, true) }),
3655
+ /* @__PURE__ */ jsx("span", { className: UNIT, children: "\uC77C" })
3656
+ ] }),
3657
+ /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-[var(--cmp-datepicker-calendar-time-unit-gap)]", children: [
3658
+ /* @__PURE__ */ jsx(
3659
+ "input",
3660
+ {
3661
+ type: "text",
3662
+ inputMode: "numeric",
3663
+ maxLength: 2,
3664
+ "aria-label": `${suffix} \uC2DC`,
3665
+ disabled: !active,
3666
+ value: active ? hour : "",
3667
+ placeholder: "--",
3668
+ onChange: (e) => changeTime(which, "h", e.target.value),
3669
+ onBlur: () => blurTime(which, "h"),
3670
+ className: INPUT_CLS
3671
+ }
3672
+ ),
3673
+ /* @__PURE__ */ jsx("span", { className: LABEL, children: ":" }),
3674
+ /* @__PURE__ */ jsx(
3675
+ "input",
3676
+ {
3677
+ type: "text",
3678
+ inputMode: "numeric",
3679
+ maxLength: 2,
3680
+ "aria-label": `${suffix} \uBD84`,
3681
+ disabled: !active,
3682
+ value: active ? minute : "",
3683
+ placeholder: "--",
3684
+ onChange: (e) => changeTime(which, "m", e.target.value),
3685
+ onBlur: () => blurTime(which, "m"),
3686
+ className: INPUT_CLS
3687
+ }
3688
+ )
3689
+ ] }),
3690
+ /* @__PURE__ */ jsx("span", { className: UNIT, children: suffix })
3691
+ ]
3692
+ }
3693
+ );
3694
+ };
3695
+ return /* @__PURE__ */ jsxs(
3696
+ "div",
3697
+ {
3698
+ className: cn(
3699
+ "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",
3700
+ useTimePicker ? "h-auto" : "h-[364px]"
3602
3701
  ),
3603
- renderPanel(nextMonthYM.year, nextMonthYM.month, false)
3604
- ] })
3605
- ] });
3702
+ children: [
3703
+ /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
3704
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-[var(--cmp-datepicker-calendar-gap)] text-[14px] font-medium leading-[24px]", children: [
3705
+ /* @__PURE__ */ jsx(SGhostButton, { icon: "chevronLeft", size: "xxs", ariaLabel: "prevYear", onClick: goPrevYear }),
3706
+ /* @__PURE__ */ jsx("span", { className: "inline-flex min-w-[40px] items-center justify-center text-center", children: y }),
3707
+ /* @__PURE__ */ jsx(SGhostButton, { icon: "chevronRight", size: "xxs", ariaLabel: "nextYear", onClick: goNextYear })
3708
+ ] }),
3709
+ /* @__PURE__ */ jsx(
3710
+ "button",
3711
+ {
3712
+ type: "button",
3713
+ onClick: goToday,
3714
+ 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",
3715
+ children: "\uC624\uB298"
3716
+ }
3717
+ )
3718
+ ] }),
3719
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-[var(--cmp-datepicker-calendar-range-panelGap)]", children: [
3720
+ renderPanel(y, m, true),
3721
+ /* @__PURE__ */ jsx(
3722
+ "span",
3723
+ {
3724
+ className: "w-px shrink-0 self-stretch bg-[var(--cmp-datepicker-calendar-range-divider)]",
3725
+ "aria-hidden": true
3726
+ }
3727
+ ),
3728
+ renderPanel(nextMonthYM.year, nextMonthYM.month, false)
3729
+ ] }),
3730
+ useTimePicker && /* @__PURE__ */ jsxs(Fragment, { children: [
3731
+ /* @__PURE__ */ jsx(
3732
+ "span",
3733
+ {
3734
+ className: "h-px w-full bg-[var(--cmp-datepicker-calendar-range-divider)]",
3735
+ "aria-hidden": true
3736
+ }
3737
+ ),
3738
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-[var(--cmp-datepicker-calendar-time-gap)]", children: [
3739
+ renderTimeSection("start", start, "\uBD80\uD130"),
3740
+ renderTimeSection("end", end, "\uAE4C\uC9C0")
3741
+ ] })
3742
+ ] })
3743
+ ]
3744
+ }
3745
+ );
3606
3746
  }
3607
3747
  function SDateRangePicker({
3608
3748
  value,
@@ -3612,6 +3752,7 @@ function SDateRangePicker({
3612
3752
  placeholder = "YYYY-MM-DD ~ YYYY-MM-DD",
3613
3753
  selectable,
3614
3754
  maxRange,
3755
+ useTimePicker = false,
3615
3756
  disabled = false,
3616
3757
  width,
3617
3758
  name,
@@ -3655,8 +3796,9 @@ function SDateRangePicker({
3655
3796
  setPendingStart(null);
3656
3797
  onValueChange?.(r);
3657
3798
  if (rules && rules.length > 0) setRuleError(runRules(r, rules));
3658
- setOpen(false);
3799
+ if (!useTimePicker) setOpen(false);
3659
3800
  };
3801
+ const resolvedPlaceholder = useTimePicker && placeholder === "YYYY-MM-DD ~ YYYY-MM-DD" ? "YYYY-MM-DD HH:mm ~ YYYY-MM-DD HH:mm" : placeholder;
3660
3802
  return /* @__PURE__ */ jsx(
3661
3803
  SField,
3662
3804
  {
@@ -3723,7 +3865,7 @@ function SDateRangePicker({
3723
3865
  /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-right", children: startText }),
3724
3866
  /* @__PURE__ */ jsx("span", { className: "px-[4px]", children: "~" }),
3725
3867
  /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: endText })
3726
- ] }) : placeholder
3868
+ ] }) : resolvedPlaceholder
3727
3869
  }
3728
3870
  )
3729
3871
  ]
@@ -3742,6 +3884,7 @@ function SDateRangePicker({
3742
3884
  value: value ?? null,
3743
3885
  selectable,
3744
3886
  maxRange,
3887
+ useTimePicker,
3745
3888
  onSelect: select,
3746
3889
  onPendingStartChange: setPendingStart,
3747
3890
  onViewChange