sellmate-design-system-react 9.0.0-beta.3 → 9.0.0-beta.5

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.
package/dist/index.js CHANGED
@@ -4701,7 +4701,7 @@ var STreeItemImpl = /* @__PURE__ */ forwardRef(function STreeItem({
4701
4701
  "aria-disabled": node.disabled || void 0,
4702
4702
  onClick: handleRowClick,
4703
4703
  className: cn(
4704
- "group/tree-item flex min-h-[32px] w-full items-center gap-(--cmp-tree-item-rowGap) bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left",
4704
+ "group/tree-item box-border flex h-[32px] w-full items-center gap-(--cmp-tree-item-rowGap) overflow-hidden bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left",
4705
4705
  border && "border-b border-(--cmp-tree-item-border-default)",
4706
4706
  !node.disabled && "hover:bg-(--cmp-tree-item-bg-hover)",
4707
4707
  node.disabled && "cursor-not-allowed opacity-60",
@@ -4720,7 +4720,7 @@ var STreeItemImpl = /* @__PURE__ */ forwardRef(function STreeItem({
4720
4720
  ancestorLast
4721
4721
  }
4722
4722
  ),
4723
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap) py-(--cmp-tree-item-paddingY)", children: [
4723
+ /* @__PURE__ */ jsxs("div", { className: "box-border flex h-full min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap) py-(--cmp-tree-item-paddingY)", children: [
4724
4724
  hasChildren ? /* @__PURE__ */ jsx(
4725
4725
  SGhostButton,
4726
4726
  {
@@ -4756,25 +4756,17 @@ var STreeItemImpl = /* @__PURE__ */ forwardRef(function STreeItem({
4756
4756
  /* @__PURE__ */ jsx(
4757
4757
  "span",
4758
4758
  {
4759
+ onClick: (event) => {
4760
+ if (!selectable) return;
4761
+ event.stopPropagation();
4762
+ if (!node.disabled && selectable) onTreeSelect?.(node, event);
4763
+ },
4759
4764
  className: cn(
4760
- "min-w-0 flex-1 text-[14px] leading-[24px]",
4761
- selected || indeterminate ? "font-bold text-(--cmp-tree-item-text-selected)" : "font-medium text-(--cmp-tree-item-text-default)"
4765
+ "block min-w-0 flex-1 truncate text-[14px] leading-[24px]",
4766
+ selected || indeterminate ? "font-bold text-(--cmp-tree-item-text-selected)" : "font-medium text-(--cmp-tree-item-text-default)",
4767
+ node.disabled ? "cursor-not-allowed" : selectable ? "cursor-pointer" : void 0
4762
4768
  ),
4763
- children: /* @__PURE__ */ jsx(
4764
- "span",
4765
- {
4766
- onClick: (event) => {
4767
- if (!selectable) return;
4768
- event.stopPropagation();
4769
- if (!node.disabled && selectable) onTreeSelect?.(node, event);
4770
- },
4771
- className: cn(
4772
- "inline-block max-w-full truncate",
4773
- node.disabled ? "cursor-not-allowed" : selectable ? "cursor-pointer" : void 0
4774
- ),
4775
- children: node.label
4776
- }
4777
- )
4769
+ children: node.label
4778
4770
  }
4779
4771
  ),
4780
4772
  trailingNode
@@ -5120,6 +5112,10 @@ var BODY_PADDING_CLASS = {
5120
5112
  wide: "p-(--sys-space-panel-padding-wide)",
5121
5113
  none: ""
5122
5114
  };
5115
+ var BODY_BACKGROUND_CLASS = {
5116
+ frame: "",
5117
+ neutral: "bg-(--sys-color-bg-neutralLight)"
5118
+ };
5123
5119
  var topBorderColorClass = {
5124
5120
  default: "bg-(--cmp-sectionHeaderCard-topBorder-color-default)",
5125
5121
  accent: "bg-(--cmp-sectionHeaderCard-topBorder-color-accent)"
@@ -5129,8 +5125,8 @@ var titleSizeClass = {
5129
5125
  sm: "typo-heading-sm"
5130
5126
  };
5131
5127
  var SSectionHeaderCardBodyImpl = /* @__PURE__ */ forwardRef(
5132
- function SSectionHeaderCardBody({ padding = "default", className, children, ...rest }, ref) {
5133
- return /* @__PURE__ */ jsx("div", { ref, className: cn(BODY_PADDING_CLASS[padding], className), ...rest, children });
5128
+ function SSectionHeaderCardBody({ padding = "default", background = "frame", children }, ref) {
5129
+ return /* @__PURE__ */ jsx("div", { ref, className: cn(BODY_PADDING_CLASS[padding], BODY_BACKGROUND_CLASS[background]), children });
5134
5130
  }
5135
5131
  );
5136
5132
  var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ forwardRef(
@@ -5143,8 +5139,7 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ forwardRef(
5143
5139
  subtitle,
5144
5140
  slot,
5145
5141
  thickness = false,
5146
- className,
5147
- ...rest
5142
+ className
5148
5143
  }, ref) {
5149
5144
  const hasHelp = helpText != null && helpText.length > 0;
5150
5145
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -5165,7 +5160,6 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ forwardRef(
5165
5160
  "box-border flex min-h-(--cmp-sectionHeaderCard-header-minHeight) w-full shrink-0 items-center gap-(--cmp-sectionHeaderCard-header-gap) overflow-hidden border-b border-(--cmp-sectionHeaderCard-header-border) bg-(--cmp-sectionHeaderCard-header-bg) px-(--cmp-sectionHeaderCard-header-paddingX) py-(--cmp-sectionHeaderCard-header-paddingY)",
5166
5161
  className
5167
5162
  ),
5168
- ...rest,
5169
5163
  children: [
5170
5164
  marker && /* @__PURE__ */ jsx(SBadge, { color: "darkblue", className: "shrink-0" }),
5171
5165
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 shrink-0 items-center gap-[8px]", children: [
@@ -5209,8 +5203,24 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ forwardRef(
5209
5203
  }
5210
5204
  );
5211
5205
  var SSectionHeaderCardRootImpl = /* @__PURE__ */ forwardRef(
5212
- function SSectionHeaderCard({ className, children, ...rest }, ref) {
5213
- return /* @__PURE__ */ jsx(
5206
+ function SSectionHeaderCard({
5207
+ title,
5208
+ titleSize,
5209
+ marker,
5210
+ required,
5211
+ helpText,
5212
+ subtitle,
5213
+ slot,
5214
+ thickness,
5215
+ headerClassName,
5216
+ padding,
5217
+ background,
5218
+ className,
5219
+ children,
5220
+ ...rest
5221
+ }, ref) {
5222
+ const hasHeader = title != null;
5223
+ return /* @__PURE__ */ jsxs(
5214
5224
  "div",
5215
5225
  {
5216
5226
  ref,
@@ -5219,24 +5229,28 @@ var SSectionHeaderCardRootImpl = /* @__PURE__ */ forwardRef(
5219
5229
  className
5220
5230
  ),
5221
5231
  ...rest,
5222
- children
5232
+ children: [
5233
+ hasHeader && /* @__PURE__ */ jsx(
5234
+ SSectionHeaderCardHeaderImpl,
5235
+ {
5236
+ title,
5237
+ titleSize,
5238
+ marker,
5239
+ required,
5240
+ helpText,
5241
+ subtitle,
5242
+ slot,
5243
+ thickness,
5244
+ className: headerClassName
5245
+ }
5246
+ ),
5247
+ /* @__PURE__ */ jsx(SSectionHeaderCardBodyImpl, { padding, background, children })
5248
+ ]
5223
5249
  }
5224
5250
  );
5225
5251
  }
5226
5252
  );
5227
- var SSectionHeaderCardRoot = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5228
- var SSectionHeaderCardHeader2 = /* @__PURE__ */ withDisplayName(
5229
- SSectionHeaderCardHeaderImpl,
5230
- "SSectionHeaderCard.Header"
5231
- );
5232
- var SSectionHeaderCardBody2 = /* @__PURE__ */ withDisplayName(
5233
- SSectionHeaderCardBodyImpl,
5234
- "SSectionHeaderCard.Body"
5235
- );
5236
- var SSectionHeaderCard2 = /* @__PURE__ */ Object.assign(SSectionHeaderCardRoot, {
5237
- Header: SSectionHeaderCardHeader2,
5238
- Body: SSectionHeaderCardBody2
5239
- });
5253
+ var SSectionHeaderCard2 = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5240
5254
  var SDivider = /* @__PURE__ */ forwardRef(function SDivider2({ vertical = false, className, style, ...rest }, ref) {
5241
5255
  return /* @__PURE__ */ jsx(
5242
5256
  "hr",
@@ -6867,7 +6881,7 @@ function SModalContainer({
6867
6881
  "div",
6868
6882
  {
6869
6883
  className: cn(
6870
- "relative flex max-h-[calc(100dvh-48px)] max-w-[calc(100dvw-48px)] flex-col overflow-hidden rounded-[var(--cmp-modal-radius)] bg-[var(--cmp-modal-bg)] shadow-normal-md",
6884
+ "relative flex max-h-[85dvh] max-w-[calc(100dvw-48px)] flex-col overflow-hidden rounded-[var(--cmp-modal-radius)] bg-[var(--cmp-modal-bg)] shadow-normal-md",
6871
6885
  shaking && "animate-modal-shake",
6872
6886
  className
6873
6887
  ),
@@ -7124,7 +7138,7 @@ function SActionModal({
7124
7138
  showClose: true,
7125
7139
  width,
7126
7140
  height,
7127
- className: "w-fit min-w-[min(480px,calc(100dvw-48px))] min-h-[min(320px,calc(100dvh-48px))]",
7141
+ className: "w-fit min-w-[min(480px,calc(100dvw-48px))] min-h-[min(320px,85dvh)]",
7128
7142
  children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-auto flex-col", children: [
7129
7143
  /* @__PURE__ */ jsx(SOverlayHeader, { title: modalTitle, description, reserveCloseSpace: true }),
7130
7144
  /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto px-[var(--cmp-modal-action-body-paddingX)] pb-[var(--cmp-modal-action-body-paddingBottom)]", children }),
@@ -8846,6 +8860,7 @@ var BACKGROUND_CLASS = {
8846
8860
  var SPage = /* @__PURE__ */ forwardRef(function SPage2({
8847
8861
  background = "frame",
8848
8862
  scrollEndSpacing = true,
8863
+ contentHeight = "auto",
8849
8864
  header,
8850
8865
  className,
8851
8866
  style,
@@ -8860,13 +8875,14 @@ var SPage = /* @__PURE__ */ forwardRef(function SPage2({
8860
8875
  onScroll?.(e);
8861
8876
  if (header != null && !header.fix) setScrolled(e.currentTarget.scrollTop > 0);
8862
8877
  };
8878
+ const fill = contentHeight === "fill";
8863
8879
  const main = /* @__PURE__ */ jsx(
8864
8880
  "main",
8865
8881
  {
8866
8882
  ref,
8867
8883
  className: cn(
8868
8884
  "box-border min-h-0 min-w-0 flex-1 overflow-auto p-[var(--cmp-pageBody-padding-default)]",
8869
- scrollEndSpacing && "pb-[var(--cmp-pageBody-padding-scrollEnd)]",
8885
+ !fill && scrollEndSpacing && "pb-[var(--cmp-pageBody-padding-scrollEnd)]",
8870
8886
  SCROLLBAR_CLASS,
8871
8887
  BACKGROUND_CLASS[background],
8872
8888
  className
@@ -8874,7 +8890,7 @@ var SPage = /* @__PURE__ */ forwardRef(function SPage2({
8874
8890
  style: header == null ? { ...gridStyle, ...style } : style,
8875
8891
  onScroll: handleScroll,
8876
8892
  ...rest,
8877
- children: /* @__PURE__ */ jsx("div", { style: { minWidth: MIN_CONTENT_WIDTH }, children })
8893
+ children: /* @__PURE__ */ jsx("div", { className: cn(fill && "h-full"), style: { minWidth: MIN_CONTENT_WIDTH }, children })
8878
8894
  }
8879
8895
  );
8880
8896
  if (header == null) return main;
@@ -8898,6 +8914,7 @@ var SPage = /* @__PURE__ */ forwardRef(function SPage2({
8898
8914
 
8899
8915
  // src/components/SPage/page.config.ts
8900
8916
  var PAGE_BACKGROUNDS = ["frame", "neutral", "screen"];
8917
+ var PAGE_CONTENT_HEIGHTS = ["auto", "fill"];
8901
8918
 
8902
8919
  // src/components/STabs/tabs.config.ts
8903
8920
  var TAB_MAIN_SIZE = {
@@ -9715,6 +9732,7 @@ var SField = /* @__PURE__ */ forwardRef(function SField2({
9715
9732
  readOnly = false,
9716
9733
  width,
9717
9734
  minWidth,
9735
+ maxWidth,
9718
9736
  multiline = false,
9719
9737
  borderless = false,
9720
9738
  children,
@@ -9750,7 +9768,10 @@ var SField = /* @__PURE__ */ forwardRef(function SField2({
9750
9768
  const resolvedSuccess = status === "pass";
9751
9769
  const showError = resolvedError && errorMessage !== "";
9752
9770
  const interactive = !disabled && !readOnly;
9753
- const hug = cssLen(width) !== void 0;
9771
+ const isPercentWidth = typeof width === "string" && width.trim().endsWith("%");
9772
+ const hug = cssLen(width) !== void 0 && !isPercentWidth;
9773
+ const addonWidth = effectiveAddonLabel !== "" ? cssLen(labelWidth) : void 0;
9774
+ const resolvedMaxWidth = cssLen(maxWidth) === void 0 || effectiveAddonLabel === "" ? cssLen(maxWidth) : addonWidth ? `calc(${cssLen(maxWidth)} + ${addonWidth})` : void 0;
9754
9775
  const borderColor = borderless ? "transparent" : disabled ? "var(--color-grey-45)" : interactive && focused ? "var(--sys-color-field-border-focus)" : resolvedSuccess ? "var(--sys-color-field-border-success)" : resolvedError ? "var(--sys-color-field-border-danger)" : "var(--sys-color-field-border-default)";
9755
9776
  const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
9756
9777
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
@@ -9806,8 +9827,12 @@ var SField = /* @__PURE__ */ forwardRef(function SField2({
9806
9827
  /* @__PURE__ */ jsxs(
9807
9828
  "div",
9808
9829
  {
9809
- className: cn("flex min-w-0 flex-col", !hug && "flex-1"),
9810
- style: { width: cssLen(width), minWidth: cssLen(minWidth) },
9830
+ className: cn("flex min-w-0 flex-col", !hug && !isPercentWidth && "flex-1"),
9831
+ style: {
9832
+ width: cssLen(width),
9833
+ minWidth: cssLen(minWidth),
9834
+ maxWidth: resolvedMaxWidth
9835
+ },
9811
9836
  children: [
9812
9837
  /* @__PURE__ */ jsxs(
9813
9838
  "div",
@@ -10562,6 +10587,7 @@ var STable = /* @__PURE__ */ forwardRef(function STable2({
10562
10587
  height,
10563
10588
  stickyColumn,
10564
10589
  radius = "default",
10590
+ bordered = true,
10565
10591
  noDataLabel = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
10566
10592
  noDataSlot,
10567
10593
  isLoading = false,
@@ -10820,7 +10846,7 @@ var STable = /* @__PURE__ */ forwardRef(function STable2({
10820
10846
  };
10821
10847
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[var(--cmp-table-radius)] rounded-t-none" : "rounded-[var(--cmp-table-radius)]";
10822
10848
  const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-body-paddingY-default)";
10823
- const cellPadX = "var(--cmp-table-body-paddingX-wide)";
10849
+ const cellPadXOf = (col) => col.contentType === "control" ? "var(--cmp-table-body-paddingX-default)" : "var(--cmp-table-body-paddingX-wide)";
10824
10850
  const rowH = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-body-default-height)";
10825
10851
  const totalColSpan = visibleCols.length + (selectable ? 1 : 0);
10826
10852
  const stickyInfo = (i) => {
@@ -10874,7 +10900,8 @@ var STable = /* @__PURE__ */ forwardRef(function STable2({
10874
10900
  // 만으로는 스태킹 컨텍스트가 생기지 않아, 모달 안에 들어가면 30·31이 모달 Content
10875
10901
  // 레벨까지 올라가 같은 컨텍스트의 floating portal 밴드(z-2)를 덮는다.
10876
10902
  // 자손은 이미 overflow-hidden으로 클리핑되므로 테이블 내부 순서는 달라지지 않는다.
10877
- "bg-white relative isolate flex flex-col overflow-hidden border border-solid border-[color:var(--cmp-table-border-color)]",
10903
+ "bg-white relative isolate flex flex-col overflow-hidden",
10904
+ bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
10878
10905
  // height가 없으면 루트가 받은 높이를 끝까지 채운다. 호스트가 className으로
10879
10906
  // `flex-1 min-h-0` 등을 줘 루트 높이가 확정된 경우, 행이 적어도 테두리 박스가
10880
10907
  // 남은 영역을 그대로 차지한다. basis는 auto로 둬 높이가 확정되지 않은
@@ -11044,9 +11071,10 @@ var STable = /* @__PURE__ */ forwardRef(function STable2({
11044
11071
  stickyShadowClass(i),
11045
11072
  typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
11046
11073
  );
11074
+ const tdPadX = cellPadXOf(col);
11047
11075
  const tdStyle = {
11048
- paddingLeft: cellPadX,
11049
- paddingRight: cellPadX,
11076
+ paddingLeft: tdPadX,
11077
+ paddingRight: tdPadX,
11050
11078
  paddingTop: cellPadY,
11051
11079
  paddingBottom: cellPadY,
11052
11080
  textAlign: col.align ?? "left",
@@ -11099,19 +11127,29 @@ var STable = /* @__PURE__ */ forwardRef(function STable2({
11099
11127
  // SPagination은 전체 폭 기준 중앙, rows-per-page SSelect는 우측에 absolute 배치해 중앙 정렬을 유지한다.
11100
11128
  // 루트 높이가 확정된 경우(테이블이 남은 영역을 채우는 배치) 페이지네이션 바가
11101
11129
  // 눌리지 않도록 shrink-0으로 48px를 보장한다.
11102
- /* @__PURE__ */ jsxs("div", { className: "relative -mt-px flex h-[48px] shrink-0 items-center justify-center rounded-[var(--cmp-table-radius)] border border-solid border-[color:var(--cmp-table-border-color)] bg-[var(--color-grey-05)]", children: [
11103
- /* @__PURE__ */ jsx(SPagination, { currentPage: displayPage, lastPage, onPageChange: changePage }),
11104
- useRowsPerPageSelect && /* @__PURE__ */ jsx("div", { className: "absolute right-[10px] top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsx(
11105
- SSelect2,
11106
- {
11107
- options: rowsPerPageOption,
11108
- value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
11109
- emitValue: true,
11110
- onValueChange: (v) => changeRowsPerPage(Number(v)),
11111
- width: 128
11112
- }
11113
- ) })
11114
- ] })
11130
+ /* @__PURE__ */ jsxs(
11131
+ "div",
11132
+ {
11133
+ className: cn(
11134
+ "relative flex h-[48px] shrink-0 items-center justify-center rounded-[var(--cmp-table-radius)] bg-[var(--color-grey-05)]",
11135
+ // 테두리를 끄면 겹칠 대상이 없어진다 — -mt-px 를 남기면 1px 어긋남만 생긴다.
11136
+ bordered && "-mt-px border border-solid border-[color:var(--cmp-table-border-color)]"
11137
+ ),
11138
+ children: [
11139
+ /* @__PURE__ */ jsx(SPagination, { currentPage: displayPage, lastPage, onPageChange: changePage }),
11140
+ useRowsPerPageSelect && /* @__PURE__ */ jsx("div", { className: "absolute right-[10px] top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsx(
11141
+ SSelect2,
11142
+ {
11143
+ options: rowsPerPageOption,
11144
+ value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
11145
+ emitValue: true,
11146
+ onValueChange: (v) => changeRowsPerPage(Number(v)),
11147
+ width: 128
11148
+ }
11149
+ ) })
11150
+ ]
11151
+ }
11152
+ )
11115
11153
  ]
11116
11154
  }
11117
11155
  );
@@ -12249,6 +12287,44 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
12249
12287
  }
12250
12288
  ) });
12251
12289
  }
12290
+ function SDatePickerYearListbox({
12291
+ value,
12292
+ anchorYear,
12293
+ selectable,
12294
+ onValueChange,
12295
+ className,
12296
+ style
12297
+ }) {
12298
+ const today = getTodayParts();
12299
+ const resolvedAnchorYear = anchorYear ?? today.year;
12300
+ const [year, setYear] = useState(() => parseYear(value) ?? resolvedAnchorYear);
12301
+ useEffect(() => {
12302
+ setYear(parseYear(value) ?? resolvedAnchorYear);
12303
+ }, [value, resolvedAnchorYear]);
12304
+ const commitYear = (nextYear) => {
12305
+ setYear(nextYear);
12306
+ onValueChange?.(String(nextYear));
12307
+ };
12308
+ return /* @__PURE__ */ jsx(
12309
+ "div",
12310
+ {
12311
+ className: cn(
12312
+ "inline-flex h-[240px] w-[var(--cmp-datepicker-mode-year-width)] overflow-hidden rounded-[var(--cmp-datepicker-mode-radius)] bg-[var(--cmp-datepicker-mode-bg)] shadow-normal-sm",
12313
+ className
12314
+ ),
12315
+ style,
12316
+ children: /* @__PURE__ */ jsx(
12317
+ YearWheel,
12318
+ {
12319
+ value: year,
12320
+ anchorYear: resolvedAnchorYear,
12321
+ selectable,
12322
+ onCommit: commitYear
12323
+ }
12324
+ )
12325
+ }
12326
+ );
12327
+ }
12252
12328
  function MonthWheel({ year, value, selectable, onCommit }) {
12253
12329
  const listRef = useRef(null);
12254
12330
  const initialValueRef = useRef(value);
@@ -12295,66 +12371,59 @@ function MonthWheel({ year, value, selectable, onCommit }) {
12295
12371
  }
12296
12372
  ) });
12297
12373
  }
12298
- function DatePickerListbox({
12299
- mode,
12374
+ function SDatePickerMonthListbox({
12300
12375
  value,
12376
+ anchorYear,
12301
12377
  selectable,
12302
12378
  onValueChange,
12303
- onClose
12379
+ onMonthSelect,
12380
+ className,
12381
+ style
12304
12382
  }) {
12305
12383
  const today = getTodayParts();
12306
- const [year, setYear] = useState(() => parseYear(value) ?? today.year);
12384
+ const resolvedAnchorYear = anchorYear ?? today.year;
12385
+ const [year, setYear] = useState(() => parseYear(value) ?? resolvedAnchorYear);
12307
12386
  const [month, setMonth] = useState(() => parseMonth(value) ?? today.month);
12387
+ useEffect(() => {
12388
+ setYear(parseYear(value) ?? resolvedAnchorYear);
12389
+ setMonth(parseMonth(value) ?? today.month);
12390
+ }, [value, resolvedAnchorYear, today.month]);
12308
12391
  const commitYear = (nextYear) => {
12309
12392
  setYear(nextYear);
12310
- if (mode === "year") {
12311
- onValueChange?.(String(nextYear));
12312
- onClose();
12313
- return;
12314
- }
12315
12393
  onValueChange?.(formatMonthValue(nextYear, month));
12316
12394
  };
12317
12395
  const commitMonth = (nextMonth) => {
12318
12396
  setMonth(nextMonth);
12319
- onValueChange?.(formatMonthValue(year, nextMonth));
12320
- onClose();
12397
+ const nextValue = formatMonthValue(year, nextMonth);
12398
+ onValueChange?.(nextValue);
12399
+ onMonthSelect?.(nextValue);
12321
12400
  };
12322
12401
  return /* @__PURE__ */ jsxs(
12323
12402
  "div",
12324
12403
  {
12325
12404
  className: cn(
12326
- "inline-flex h-[240px] overflow-hidden rounded-[var(--cmp-datepicker-mode-radius)] bg-[var(--cmp-datepicker-mode-bg)] shadow-normal-sm",
12327
- mode === "month" ? "gap-[var(--cmp-datepicker-mode-yearMonth-gap)]" : "w-[var(--cmp-datepicker-mode-year-width)]"
12405
+ "inline-flex h-[240px] gap-[var(--cmp-datepicker-mode-yearMonth-gap)] overflow-hidden rounded-[var(--cmp-datepicker-mode-radius)] bg-[var(--cmp-datepicker-mode-bg)] shadow-normal-sm",
12406
+ className
12328
12407
  ),
12408
+ style,
12329
12409
  children: [
12410
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsx(
12411
+ YearWheel,
12412
+ {
12413
+ value: year,
12414
+ anchorYear: resolvedAnchorYear,
12415
+ selectable,
12416
+ onCommit: commitYear
12417
+ }
12418
+ ) }),
12330
12419
  /* @__PURE__ */ jsx(
12331
12420
  "div",
12332
12421
  {
12333
- className: cn(
12334
- "min-w-0",
12335
- mode === "month" ? "w-[var(--cmp-datepicker-mode-yearMonth-column-width)]" : "w-full"
12336
- ),
12337
- children: /* @__PURE__ */ jsx(
12338
- YearWheel,
12339
- {
12340
- value: year,
12341
- anchorYear: today.year,
12342
- selectable,
12343
- onCommit: commitYear
12344
- }
12345
- )
12422
+ "aria-hidden": true,
12423
+ className: "w-[var(--border-width-1)] self-stretch bg-[var(--sys-color-divider-default)]"
12346
12424
  }
12347
12425
  ),
12348
- mode === "month" && /* @__PURE__ */ jsxs(Fragment, { children: [
12349
- /* @__PURE__ */ jsx(
12350
- "div",
12351
- {
12352
- "aria-hidden": true,
12353
- className: "w-[var(--border-width-1)] self-stretch bg-[var(--sys-color-divider-default)]"
12354
- }
12355
- ),
12356
- /* @__PURE__ */ jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsx(MonthWheel, { year, value: month, selectable, onCommit: commitMonth }) })
12357
- ] })
12426
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsx(MonthWheel, { year, value: month, selectable, onCommit: commitMonth }) })
12358
12427
  ]
12359
12428
  }
12360
12429
  );
@@ -12409,6 +12478,9 @@ function SDatePicker({
12409
12478
  icon: 20,
12410
12479
  minW: 104,
12411
12480
  fieldMinW: 168,
12481
+ // 상한 등급 lg — 값 길이가 YYYY-MM-DD 로 정해져 있어, width="100%" 를 받아도
12482
+ // 행 전체로 늘어나지 않게 막는다 (SKeyValueTable 이 모든 컨트롤에 100% 를 넘긴다)
12483
+ fieldMaxW: 240,
12412
12484
  fontSize: 14,
12413
12485
  lineHeight: 24,
12414
12486
  radius: 6
@@ -12418,6 +12490,8 @@ function SDatePicker({
12418
12490
  icon: 16,
12419
12491
  minW: 80,
12420
12492
  fieldMinW: 128,
12493
+ // 상한 등급 md
12494
+ fieldMaxW: 160,
12421
12495
  fontSize: 12,
12422
12496
  lineHeight: 20,
12423
12497
  radius: 4
@@ -12450,6 +12524,7 @@ function SDatePicker({
12450
12524
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12451
12525
  width,
12452
12526
  minWidth: dims.fieldMinW,
12527
+ maxWidth: dims.fieldMaxW,
12453
12528
  disabled,
12454
12529
  className,
12455
12530
  style,
@@ -12494,7 +12569,7 @@ function SDatePicker({
12494
12569
  RPopover9.Content,
12495
12570
  {
12496
12571
  side: "bottom",
12497
- align: "start",
12572
+ align: mode === "date" ? "start" : "center",
12498
12573
  sideOffset: 4,
12499
12574
  className: cn(
12500
12575
  "z-50 max-w-[var(--radix-popover-content-available-width)]",
@@ -12509,14 +12584,23 @@ function SDatePicker({
12509
12584
  onValueChange: select,
12510
12585
  onViewChange
12511
12586
  }
12587
+ ) : mode === "year" ? /* @__PURE__ */ jsx(
12588
+ SDatePickerYearListbox,
12589
+ {
12590
+ value,
12591
+ selectable,
12592
+ onValueChange: (nextValue) => {
12593
+ selectListboxValue(nextValue);
12594
+ setOpen(false);
12595
+ }
12596
+ }
12512
12597
  ) : /* @__PURE__ */ jsx(
12513
- DatePickerListbox,
12598
+ SDatePickerMonthListbox,
12514
12599
  {
12515
- mode,
12516
12600
  value,
12517
12601
  selectable,
12518
12602
  onValueChange: selectListboxValue,
12519
- onClose: () => setOpen(false)
12603
+ onMonthSelect: () => setOpen(false)
12520
12604
  }
12521
12605
  )
12522
12606
  }
@@ -12899,6 +12983,9 @@ function SDateRangePicker({
12899
12983
  icon: 20,
12900
12984
  minW: 104,
12901
12985
  fieldMinW: 260,
12986
+ // 상한 등급 xl — 값이 `YYYY-MM-DD ~ YYYY-MM-DD` 라 단일 날짜의 두 배가 넘는다.
12987
+ // 최소너비(260)가 이미 lg(240)를 넘으므로 상한도 한 등급 위여야 한다
12988
+ fieldMaxW: 480,
12902
12989
  fontSize: 14,
12903
12990
  lineHeight: 24,
12904
12991
  radius: 6
@@ -12908,6 +12995,8 @@ function SDateRangePicker({
12908
12995
  icon: 16,
12909
12996
  minW: 80,
12910
12997
  fieldMinW: 216,
12998
+ // 상한 등급 lg — 최소너비 216 이 md(160)를 넘어 md 로는 담기지 않는다
12999
+ fieldMaxW: 240,
12911
13000
  fontSize: 12,
12912
13001
  lineHeight: 20,
12913
13002
  radius: 4
@@ -12938,6 +13027,7 @@ function SDateRangePicker({
12938
13027
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12939
13028
  width,
12940
13029
  minWidth: dims.fieldMinW,
13030
+ maxWidth: dims.fieldMaxW,
12941
13031
  disabled,
12942
13032
  className,
12943
13033
  style,
@@ -13402,6 +13492,7 @@ function SKeyValueTable({
13402
13492
  values = {},
13403
13493
  search = false,
13404
13494
  radius = "default",
13495
+ bordered = true,
13405
13496
  onChange,
13406
13497
  onSearch,
13407
13498
  className,
@@ -13466,7 +13557,8 @@ function SKeyValueTable({
13466
13557
  });
13467
13558
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[8px] rounded-t-none" : "rounded-[8px]";
13468
13559
  const frameClass = cn(
13469
- "overflow-hidden border border-solid border-[color:var(--cmp-table-border-color)]",
13560
+ "overflow-hidden",
13561
+ bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
13470
13562
  radiusClass
13471
13563
  );
13472
13564
  const table = /* @__PURE__ */ jsxs(
@@ -14051,7 +14143,16 @@ var CHIPFILTER = {
14051
14143
  itemTextDefault: "var(--cmp-chipFilter-item-text-default)",
14052
14144
  itemTextSelected: "var(--cmp-chipFilter-item-text-selected)"
14053
14145
  };
14146
+ var DEFAULT_PERIOD_PRESETS = [
14147
+ { value: "day", label: "\uC77C\uBCC4" },
14148
+ { value: "month", label: "\uC6D4\uBCC4" },
14149
+ { value: "quarter", label: "\uBD84\uAE30\uBCC4" },
14150
+ { value: "half", label: "\uBC18\uAE30\uBCC4" },
14151
+ { value: "year", label: "\uC5F0\uB3C4\uBCC4" },
14152
+ { value: "custom", label: "\uC0AC\uC6A9\uC790 \uC9C0\uC815", custom: true }
14153
+ ];
14054
14154
  var isKeywordValue = (value) => typeof value === "object" && value != null && !Array.isArray(value) && "keywords" in value;
14155
+ var isPeriodValue = (value) => typeof value === "object" && value != null && !Array.isArray(value) && "unit" in value;
14055
14156
  var keywordEntries = (value) => {
14056
14157
  if (isKeywordValue(value)) return value.keywords;
14057
14158
  if (Array.isArray(value)) {
@@ -14075,12 +14176,33 @@ var todayIso = () => {
14075
14176
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
14076
14177
  };
14077
14178
  var isInlineDateField = (field) => field.type === "date" && field.radioButton === true && (field.presets?.length ?? 0) > 0;
14179
+ var getFieldPresets = (field) => {
14180
+ if (field.type === "period") return field.presets ?? DEFAULT_PERIOD_PRESETS;
14181
+ return field.presets ?? [];
14182
+ };
14183
+ var getDefaultPeriodInputValue = (unit) => {
14184
+ const today = todayIso();
14185
+ if (unit === "day") return today;
14186
+ if (unit === "month") return today.slice(0, 7);
14187
+ if (unit === "quarter") return 1;
14188
+ if (unit === "half") return "first";
14189
+ if (unit === "year") return today.slice(0, 4);
14190
+ return [today, today];
14191
+ };
14192
+ var createPeriodValue = (unit) => ({
14193
+ unit,
14194
+ value: getDefaultPeriodInputValue(unit)
14195
+ });
14078
14196
  var resolveFieldDefault = (field) => {
14079
14197
  if (field.defaultValue !== void 0) return field.defaultValue;
14080
14198
  if (isInlineDateField(field)) return todayIso();
14081
14199
  if (!field.required) return void 0;
14082
14200
  if (field.type === "single") return field.options?.[0]?.value;
14083
14201
  if (field.type === "date") return todayIso();
14202
+ if (field.type === "period") {
14203
+ const unit = getFieldPresets(field).find((preset) => !preset.custom)?.value;
14204
+ return unit ? createPeriodValue(unit) : void 0;
14205
+ }
14084
14206
  return void 0;
14085
14207
  };
14086
14208
  var valuesEqual = (a, b) => {
@@ -14104,6 +14226,8 @@ var isAtDefaultValue = (field, value) => {
14104
14226
  return def !== void 0 && valuesEqual(value, def);
14105
14227
  };
14106
14228
  var isInlineField = (field) => isInlineDateField(field) || field.type === "custom";
14229
+ var isFixedField = (field) => field.fix === true || field.required === true;
14230
+ var isAddableField = (field) => !isFixedField(field);
14107
14231
  var isGroupedFields = (fields) => fields.length > 0 && "fields" in fields[0];
14108
14232
  var isGroupSatisfied = (group, values) => {
14109
14233
  if (!group.rule) return true;
@@ -14130,9 +14254,10 @@ var defaultGroupTooltipMessage = (rule, fields) => {
14130
14254
  };
14131
14255
  var matchPresetId = (presets, value) => {
14132
14256
  if (isEmptyValue(value)) return null;
14257
+ if (isPeriodValue(value)) return value.unit;
14133
14258
  for (const preset of presets) {
14134
- if (preset.custom || !preset.resolve) continue;
14135
- const resolved = preset.resolve();
14259
+ if (preset.custom) continue;
14260
+ const resolved = preset.resolve ? preset.resolve() : preset.value;
14136
14261
  if (Array.isArray(resolved) && Array.isArray(value)) {
14137
14262
  if (resolved[0] === value[0] && resolved[1] === value[1]) return preset.value;
14138
14263
  } else if (!Array.isArray(resolved) && !Array.isArray(value) && resolved === value) {
@@ -14148,18 +14273,36 @@ var formatValueList = (labels) => {
14148
14273
  };
14149
14274
  var displayValue = (field, value) => {
14150
14275
  if (isEmptyValue(value)) return "";
14151
- if (field.type === "multi")
14152
- return formatValueList(toArrayValue(value).map((v) => optionLabel(field, v)));
14276
+ if (field.type === "multi") {
14277
+ const values = toArrayValue(value);
14278
+ const selectableValues = (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value);
14279
+ if (selectableValues.length > 0 && selectableValues.every((selectableValue) => values.includes(selectableValue))) {
14280
+ return "\uC804\uCCB4";
14281
+ }
14282
+ return formatValueList(values.map((v) => optionLabel(field, v)));
14283
+ }
14153
14284
  if (field.type === "single") return optionLabel(field, value);
14154
14285
  if (field.type === "date")
14155
14286
  return Array.isArray(value) ? `${value[0]} ~ ${value[1]}` : String(value);
14287
+ if (field.type === "period") {
14288
+ if (!isPeriodValue(value)) {
14289
+ const preset2 = getFieldPresets(field).find((p) => p.value === value);
14290
+ return preset2?.label ?? String(value);
14291
+ }
14292
+ const preset = getFieldPresets(field).find((p) => p.value === value.unit);
14293
+ const label = preset?.label ?? value.unit;
14294
+ if (value.unit === "quarter") return `${label}(${value.value ?? 1}\uBD84\uAE30)`;
14295
+ if (value.unit === "half") return `${label}(${value.value === "second" ? "\uD558\uBC18\uAE30" : "\uC0C1\uBC18\uAE30"})`;
14296
+ if (Array.isArray(value.value)) return `${value.value[0]} ~ ${value.value[1]}`;
14297
+ return value.value != null && value.value !== "" ? String(value.value) : label;
14298
+ }
14156
14299
  if (field.type === "keyword") return formatValueList(keywordEntries(value));
14157
14300
  return String(value);
14158
14301
  };
14159
14302
  var chipDisplayValue = (field, value) => {
14160
14303
  const text = displayValue(field, value);
14161
14304
  if (field.type !== "date" || isEmptyValue(value)) return text;
14162
- const presets = field.presets ?? [];
14305
+ const presets = getFieldPresets(field);
14163
14306
  if (presets.length === 0) return text;
14164
14307
  const preset = presets.find((p) => p.value === matchPresetId(presets, value));
14165
14308
  return preset && !preset.custom ? `${preset.label}(${text})` : text;
@@ -14176,7 +14319,6 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14176
14319
  onAddFilter,
14177
14320
  label = "\uAC80\uC0C9 \uD544\uD130",
14178
14321
  showLabel = false,
14179
- showAddButton = true,
14180
14322
  showReset = true,
14181
14323
  disabled = false,
14182
14324
  className,
@@ -14191,7 +14333,7 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14191
14333
  const values = normalizeValues(value);
14192
14334
  const portalContainer = usePortalContainer();
14193
14335
  const [internalActiveKeys, setInternalActiveKeys] = useState(
14194
- () => fields.filter((field) => field.defaultActive !== false).map((field) => field.key)
14336
+ () => fields.filter((field) => isFixedField(field) || field.defaultActive !== false).map((field) => field.key)
14195
14337
  );
14196
14338
  const [openKey, setOpenKey] = useState(null);
14197
14339
  const [addOpen, setAddOpen] = useState(false);
@@ -14233,7 +14375,9 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14233
14375
  setInternalActiveKeys((current) => {
14234
14376
  const known = new Set(fields.map((field) => field.key));
14235
14377
  const kept = current.filter((key) => known.has(key));
14236
- const additions = fields.filter((field) => field.defaultActive !== false && !kept.includes(field.key)).map((field) => field.key);
14378
+ const additions = fields.filter(
14379
+ (field) => (isFixedField(field) || field.defaultActive !== false) && !kept.includes(field.key)
14380
+ ).map((field) => field.key);
14237
14381
  return [...kept, ...additions];
14238
14382
  });
14239
14383
  }, [activeKeys, fields]);
@@ -14254,25 +14398,31 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14254
14398
  });
14255
14399
  if (changed) onValueChange?.({ ...values, ...patch });
14256
14400
  }, [fields, values, onValueChange]);
14257
- const resolvedActiveKeys = activeKeys ?? internalActiveKeys;
14401
+ const fixedKeys = fields.filter(isFixedField).map((field) => field.key);
14402
+ const resolvedActiveKeys = Array.from(
14403
+ /* @__PURE__ */ new Set([...activeKeys ?? internalActiveKeys, ...fixedKeys])
14404
+ );
14258
14405
  const activeFields = fields.filter((field) => resolvedActiveKeys.includes(field.key));
14259
- const inactiveFields = fields.filter((field) => !resolvedActiveKeys.includes(field.key));
14406
+ const inactiveFields = fields.filter(
14407
+ (field) => isAddableField(field) && !resolvedActiveKeys.includes(field.key)
14408
+ );
14260
14409
  const groupIndexByKey = /* @__PURE__ */ new Map();
14261
14410
  groups.forEach((group, groupIndex) => {
14262
14411
  group.fields.forEach((field) => groupIndexByKey.set(field.key, groupIndex));
14263
14412
  });
14264
14413
  const hasValue = fields.some((field) => !isEmptyValue(values[field.key]));
14265
- const hasRightActions = showAddButton || showReset;
14414
+ const hasAddButton = fields.some(isAddableField);
14415
+ const hasRightActions = hasAddButton || showReset;
14266
14416
  const currentOpenField = fields.find((field) => field.key === openKey);
14267
14417
  const editorPopoverStyle = useMemo(() => {
14268
14418
  if (currentOpenField?.type === "keyword") {
14269
14419
  return { width: CHIPFILTER_LISTBOX_WIDTH };
14270
14420
  }
14271
- if (currentOpenField?.type === "date" && (currentOpenField.presets?.length ?? 0) > 0) {
14421
+ if ((currentOpenField?.type === "date" || currentOpenField?.type === "period") && getFieldPresets(currentOpenField).length > 0) {
14272
14422
  return { width: CHIPFILTER_DATE_PRESET_LISTBOX_WIDTH };
14273
14423
  }
14274
14424
  return { width: CHIPFILTER_LISTBOX_WIDTH };
14275
- }, [currentOpenField?.type, currentOpenField?.presets?.length]);
14425
+ }, [currentOpenField]);
14276
14426
  const setActive = (next) => {
14277
14427
  if (!activeKeys) setInternalActiveKeys(next);
14278
14428
  onActiveKeysChange?.(next);
@@ -14291,6 +14441,12 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14291
14441
  const def = field ? resolveFieldDefault(field) : void 0;
14292
14442
  runSearch(emitValue(key, def !== void 0 ? def : null));
14293
14443
  };
14444
+ const removeField = (key) => {
14445
+ const nextValues = emitValue(key, null);
14446
+ setActive(resolvedActiveKeys.filter((activeKey) => activeKey !== key));
14447
+ if (openKey === key) setOpenKey(null);
14448
+ runSearch(nextValues);
14449
+ };
14294
14450
  const reset = useCallback(() => {
14295
14451
  const next = fields.reduce((acc, field) => {
14296
14452
  const def = resolveFieldDefault(field);
@@ -14381,7 +14537,15 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14381
14537
  flushRun();
14382
14538
  runGroupIndex = groupIndex;
14383
14539
  }
14384
- const showClear = !isAtDefaultValue(field, values[field.key]);
14540
+ const addable = isAddableField(field);
14541
+ const showClear = addable || !isAtDefaultValue(field, values[field.key]);
14542
+ const handleClear = () => {
14543
+ if (addable) {
14544
+ removeField(field.key);
14545
+ return;
14546
+ }
14547
+ clearValue(field.key);
14548
+ };
14385
14549
  const node = isInlineDateField(field) ? /* @__PURE__ */ jsx(
14386
14550
  InlineDatePresetFilter,
14387
14551
  {
@@ -14391,15 +14555,27 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14391
14555
  warning,
14392
14556
  showClear,
14393
14557
  onValueChange: (next) => emitValueAndSearch(field.key, next),
14394
- onClear: () => clearValue(field.key)
14558
+ onClear: handleClear
14395
14559
  },
14396
14560
  field.key
14397
- ) : field.type === "custom" ? /* @__PURE__ */ jsx(Fragment$1, { children: field.render?.({
14398
- value: values[field.key],
14399
- disabled: disabled || field.disabled,
14400
- warning,
14401
- onValueChange: (next) => emitValueAndSearch(field.key, next)
14402
- }) }, field.key) : /* @__PURE__ */ jsx(
14561
+ ) : field.type === "custom" ? /* @__PURE__ */ jsxs("div", { className: "inline-flex shrink-0 items-center gap-[4px]", children: [
14562
+ field.render?.({
14563
+ value: values[field.key],
14564
+ disabled: disabled || field.disabled,
14565
+ warning,
14566
+ onValueChange: (next) => emitValueAndSearch(field.key, next)
14567
+ }),
14568
+ addable && !disabled && !field.disabled && showClear && /* @__PURE__ */ jsx(
14569
+ "button",
14570
+ {
14571
+ type: "button",
14572
+ "aria-label": `${field.label} \uD544\uD130 \uC81C\uAC70`,
14573
+ className: "inline-flex size-[16px] shrink-0 items-center justify-center rounded-[var(--cmp-ghostButton-radius-sm)] border-none bg-transparent p-0",
14574
+ onClick: handleClear,
14575
+ children: /* @__PURE__ */ jsx(SIcon, { name: "close", size: 12, color: "var(--cmp-ghostButton-default-content)" })
14576
+ }
14577
+ )
14578
+ ] }, field.key) : /* @__PURE__ */ jsx(
14403
14579
  FilterChip,
14404
14580
  {
14405
14581
  field,
@@ -14409,9 +14585,10 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14409
14585
  disabled: disabled || field.disabled,
14410
14586
  warning,
14411
14587
  showClear,
14588
+ showClearWhenEmpty: addable,
14412
14589
  refCallback: (node2) => triggerRefs.current.set(field.key, node2),
14413
14590
  onOpen: () => setOpenKey(openKey === field.key ? null : field.key),
14414
- onClear: () => clearValue(field.key)
14591
+ onClear: handleClear
14415
14592
  },
14416
14593
  field.key
14417
14594
  );
@@ -14432,7 +14609,7 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14432
14609
  })(),
14433
14610
  hasRightActions && /* @__PURE__ */ jsxs(Fragment, { children: [
14434
14611
  /* @__PURE__ */ jsx(Divider, { inset: true }),
14435
- showAddButton && /* @__PURE__ */ jsxs(RPopover9.Root, { open: addOpen, onOpenChange: setAddOpen, children: [
14612
+ hasAddButton && /* @__PURE__ */ jsxs(RPopover9.Root, { open: addOpen, onOpenChange: setAddOpen, children: [
14436
14613
  /* @__PURE__ */ jsx(RPopover9.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
14437
14614
  "button",
14438
14615
  {
@@ -14479,7 +14656,7 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14479
14656
  "inline-flex shrink-0 cursor-pointer items-center gap-[4px] border-none bg-transparent p-0 text-[12px] font-medium leading-[20px] disabled:cursor-not-allowed",
14480
14657
  // 부모 flex gap(6px)은 칩 사이 간격 기준이라, 필터 추가 버튼과의 12px 간격을 맞추려면
14481
14658
  // 부족한 만큼(12px - gap)을 추가로 띄운다. 필터 추가가 없을 땐 divider와 이미 12px이라 불필요.
14482
- showAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
14659
+ hasAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
14483
14660
  ),
14484
14661
  style: {
14485
14662
  color: disabled || !hasValue ? "var(--cmp-textLink-content-disabled)" : "var(--cmp-textLink-content-default)"
@@ -14563,6 +14740,7 @@ function FilterChip({
14563
14740
  disabled,
14564
14741
  warning,
14565
14742
  showClear,
14743
+ showClearWhenEmpty,
14566
14744
  refCallback,
14567
14745
  onOpen,
14568
14746
  onClear
@@ -14632,7 +14810,7 @@ function FilterChip({
14632
14810
  }
14633
14811
  )
14634
14812
  ] }),
14635
- applied && !disabled && showClear && /* @__PURE__ */ jsx(
14813
+ !disabled && showClear && (applied || showClearWhenEmpty) && /* @__PURE__ */ jsx(
14636
14814
  "span",
14637
14815
  {
14638
14816
  role: "button",
@@ -14664,7 +14842,7 @@ function EditorPanel({
14664
14842
  onValueChange,
14665
14843
  onClose
14666
14844
  }) {
14667
- const wrapsInPaddedTop = field.type === "date";
14845
+ const wrapsInPaddedTop = field.type === "date" || field.type === "period";
14668
14846
  const isMulti = field.type === "multi";
14669
14847
  const selectableValues = isMulti ? (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value) : [];
14670
14848
  const selectedValues = isMulti ? toArrayValue(value) : [];
@@ -14743,6 +14921,17 @@ function FilterEditor({
14743
14921
  disabled,
14744
14922
  onValueChange
14745
14923
  }) {
14924
+ if (field.type === "period") {
14925
+ return /* @__PURE__ */ jsx(
14926
+ PeriodPresetEditor,
14927
+ {
14928
+ field,
14929
+ value,
14930
+ disabled,
14931
+ onValueChange
14932
+ }
14933
+ );
14934
+ }
14746
14935
  if (field.type === "date") {
14747
14936
  return /* @__PURE__ */ jsx(
14748
14937
  DatePresetEditor,
@@ -14782,7 +14971,7 @@ function DatePresetEditor({
14782
14971
  disabled,
14783
14972
  onValueChange
14784
14973
  }) {
14785
- const presets = field.presets ?? [];
14974
+ const presets = getFieldPresets(field);
14786
14975
  const [selectedId, setSelectedId] = useState(() => matchPresetId(presets, value));
14787
14976
  const syncedInitialValueRef = useRef(!isEmptyValue(value));
14788
14977
  useEffect(() => {
@@ -14793,9 +14982,9 @@ function DatePresetEditor({
14793
14982
  const selectedPreset = presets.find((preset) => preset.value === selectedId);
14794
14983
  const selectPreset = (preset) => {
14795
14984
  setSelectedId(preset.value);
14796
- if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : null);
14985
+ if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : preset.value);
14797
14986
  };
14798
- if (presets.length === 0) {
14987
+ if (field.type === "date" && presets.length === 0) {
14799
14988
  return /* @__PURE__ */ jsx(
14800
14989
  SDatePicker,
14801
14990
  {
@@ -14831,6 +15020,130 @@ function DatePresetEditor({
14831
15020
  )
14832
15021
  ] });
14833
15022
  }
15023
+ function PeriodPresetEditor({
15024
+ field,
15025
+ value,
15026
+ disabled,
15027
+ onValueChange
15028
+ }) {
15029
+ const presets = getFieldPresets(field);
15030
+ const firstUnit = presets.find((preset) => !preset.custom)?.value ?? "day";
15031
+ const [selectedId, setSelectedId] = useState(() => matchPresetId(presets, value) ?? firstUnit);
15032
+ const periodValue = isPeriodValue(value) ? value : createPeriodValue(selectedId ?? firstUnit);
15033
+ const selectedUnit = selectedId ?? periodValue.unit;
15034
+ useEffect(() => {
15035
+ if (isEmptyValue(value)) return;
15036
+ setSelectedId(matchPresetId(presets, value) ?? firstUnit);
15037
+ }, [firstUnit, presets, value]);
15038
+ const selectPreset = (preset) => {
15039
+ const nextUnit = preset.value;
15040
+ setSelectedId(nextUnit);
15041
+ onValueChange(createPeriodValue(nextUnit));
15042
+ };
15043
+ const updateInputValue = (nextValue) => {
15044
+ onValueChange({ unit: selectedUnit, value: nextValue });
15045
+ };
15046
+ const renderInput = () => {
15047
+ if (selectedUnit === "day") {
15048
+ return /* @__PURE__ */ jsx(
15049
+ SDatePicker,
15050
+ {
15051
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15052
+ onValueChange: updateInputValue,
15053
+ selectable: field.selectable,
15054
+ disabled,
15055
+ width: "100%"
15056
+ }
15057
+ );
15058
+ }
15059
+ if (selectedUnit === "month") {
15060
+ return /* @__PURE__ */ jsx(
15061
+ SDatePicker,
15062
+ {
15063
+ mode: "month",
15064
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15065
+ onValueChange: updateInputValue,
15066
+ selectable: field.selectable,
15067
+ disabled,
15068
+ width: "100%"
15069
+ }
15070
+ );
15071
+ }
15072
+ if (selectedUnit === "quarter") {
15073
+ return /* @__PURE__ */ jsx(
15074
+ SSelect2,
15075
+ {
15076
+ value: typeof periodValue.value === "number" ? periodValue.value : 1,
15077
+ onValueChange: updateInputValue,
15078
+ emitValue: true,
15079
+ disabled,
15080
+ width: "100%",
15081
+ options: [
15082
+ { value: 1, label: "1\uBD84\uAE30" },
15083
+ { value: 2, label: "2\uBD84\uAE30" },
15084
+ { value: 3, label: "3\uBD84\uAE30" },
15085
+ { value: 4, label: "4\uBD84\uAE30" }
15086
+ ]
15087
+ }
15088
+ );
15089
+ }
15090
+ if (selectedUnit === "half") {
15091
+ return /* @__PURE__ */ jsx(
15092
+ SSelect2,
15093
+ {
15094
+ value: typeof periodValue.value === "string" ? periodValue.value : "first",
15095
+ onValueChange: updateInputValue,
15096
+ emitValue: true,
15097
+ disabled,
15098
+ width: "100%",
15099
+ options: [
15100
+ { value: "first", label: "\uC0C1\uBC18\uAE30" },
15101
+ { value: "second", label: "\uD558\uBC18\uAE30" }
15102
+ ]
15103
+ }
15104
+ );
15105
+ }
15106
+ if (selectedUnit === "year") {
15107
+ return /* @__PURE__ */ jsx(
15108
+ SDatePicker,
15109
+ {
15110
+ mode: "year",
15111
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15112
+ onValueChange: updateInputValue,
15113
+ selectable: field.selectable,
15114
+ disabled,
15115
+ width: "100%"
15116
+ }
15117
+ );
15118
+ }
15119
+ return /* @__PURE__ */ jsx(
15120
+ SDateRangePicker,
15121
+ {
15122
+ value: Array.isArray(periodValue.value) && periodValue.value.length === 2 ? periodValue.value : null,
15123
+ onValueChange: updateInputValue,
15124
+ selectable: field.selectable,
15125
+ maxRange: field.maxRange,
15126
+ disabled,
15127
+ width: "100%"
15128
+ }
15129
+ );
15130
+ };
15131
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-[var(--cmp-chipFilter-listbox-top-gap)]", children: [
15132
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-[8px]", children: presets.map((preset) => /* @__PURE__ */ jsx(
15133
+ SRadio,
15134
+ {
15135
+ val: preset.value,
15136
+ value: selectedId,
15137
+ label: preset.label,
15138
+ disabled,
15139
+ onValueChange: () => selectPreset(preset)
15140
+ },
15141
+ preset.value
15142
+ )) }),
15143
+ /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "h-px w-full", style: { background: CHIPFILTER.listboxBorder } }),
15144
+ /* @__PURE__ */ jsx("div", { className: "w-full [&>*]:w-full", children: renderInput() })
15145
+ ] });
15146
+ }
14834
15147
  function InlineDatePresetFilter({
14835
15148
  field,
14836
15149
  value,
@@ -15835,7 +16148,9 @@ var TIMEPICKER_SIZE_CONFIG = {
15835
16148
  fontSize: 12,
15836
16149
  lineHeight: 20,
15837
16150
  minWidth: 80,
15838
- fieldMinWidth: 128
16151
+ fieldMinWidth: 128,
16152
+ // 상한 등급 md
16153
+ fieldMaxWidth: 160
15839
16154
  },
15840
16155
  md: {
15841
16156
  height: V3("md-height"),
@@ -15847,7 +16162,9 @@ var TIMEPICKER_SIZE_CONFIG = {
15847
16162
  fontSize: 14,
15848
16163
  lineHeight: 24,
15849
16164
  minWidth: 96,
15850
- fieldMinWidth: 160
16165
+ fieldMinWidth: 160,
16166
+ // 상한 등급 lg
16167
+ fieldMaxWidth: 240
15851
16168
  }
15852
16169
  };
15853
16170
  function clampTimePart(value, min, max) {
@@ -16171,6 +16488,7 @@ function STimePicker({
16171
16488
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
16172
16489
  width,
16173
16490
  minWidth: dims.fieldMinWidth,
16491
+ maxWidth: dims.fieldMaxWidth,
16174
16492
  disabled,
16175
16493
  className,
16176
16494
  style,
@@ -16287,6 +16605,10 @@ var MIDDAY_FIELD_MIN_WIDTH = {
16287
16605
  sm: 178,
16288
16606
  md: 216
16289
16607
  };
16608
+ var MIDDAY_FIELD_MAX_WIDTH = {
16609
+ sm: 240,
16610
+ md: 240
16611
+ };
16290
16612
  function RangeTimeSelector({
16291
16613
  value,
16292
16614
  useMeridiem,
@@ -16633,6 +16955,7 @@ function STimeRangePicker({
16633
16955
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
16634
16956
  width,
16635
16957
  minWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MIN_WIDTH[size] : dims.fieldMinWidth,
16958
+ maxWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MAX_WIDTH[size] : dims.fieldMaxWidth,
16636
16959
  disabled,
16637
16960
  className,
16638
16961
  style,
@@ -16725,6 +17048,6 @@ function STimeRangePicker({
16725
17048
  );
16726
17049
  }
16727
17050
 
16728
- export { BADGE_COLORS, BUTTON_COLORS, BUTTON_SIZES, COLOR_KEYS, GNB_COLORS, GNB_COMMON, GNB_FOLD_MS, GNB_FULL_LOGO_WIDTH, GNB_HEADER, GNB_MENU, GNB_MENU_WIDTH, GNB_RAIL, GNB_RAIL_WIDTH, ICONS, IMAGE_FITS, PAGE_BACKGROUNDS, RangeCalendar, SActionModal, SBadge, SBarcodeInput, SButton, SCROLL_AREA_AXES, SCalendar, SCallout, SCard, SCheckbox, SChip2 as SChip, SChipFilter, SChipInput, SCircleProgress, SConfirmModal, SDatePicker, SDateRangePicker, SDivider, SDraggableGroup, SDraggableItem2 as SDraggableItem, SDraggableList, SDraggableProvider, SDrawer, SDropdownButton2 as SDropdownButton, SExpansionItem, SExpansionList2 as SExpansionList, SField, SFilePicker, SForm, SGhostButton, SGnb, SGuide, SIcon, SImage, SInput, SKeyValueTable, SLAYOUT_MIN_WIDTH, SLayout, SLayoutContext, SLinearProgress, SList2 as SList, SListItem2 as SListItem, SLoadingContainer, SLoadingModal, SLoadingViewport, SModal, SModalOutlet, SNumberInput, SPage, SPagination, SPopover, SPopup, SPortal, SRadio, SRadioButton, SRadioGroup, SSPLITTER_UNITS, SScrollArea, SSectionHeaderCard2 as SSectionHeaderCard, SSelect2 as SSelect, SSplitter2 as SSplitter, SStepper, SSwitch, STEPPER_SIZES, STable, STableBar, STabs, STag, STextLink, STextarea, STimePicker, STimeRangePicker, SToast, SToastContainer, SToggle, STooltip2 as STooltip, STree2 as STree, STreeItem2 as STreeItem, TAG_COLORS, TAG_SHAPES, TAG_SIZES, TIMEPICKER_SIZES, TIMEPICKER_SIZE_CONFIG, buttonPreset, cn, findGnbAncestors, findGnbRailValue, isColorKey, loading, resolveColor, useSLayout };
17051
+ export { BADGE_COLORS, BUTTON_COLORS, BUTTON_SIZES, COLOR_KEYS, GNB_COLORS, GNB_COMMON, GNB_FOLD_MS, GNB_FULL_LOGO_WIDTH, GNB_HEADER, GNB_MENU, GNB_MENU_WIDTH, GNB_RAIL, GNB_RAIL_WIDTH, ICONS, IMAGE_FITS, PAGE_BACKGROUNDS, PAGE_CONTENT_HEIGHTS, RangeCalendar, SActionModal, SBadge, SBarcodeInput, SButton, SCROLL_AREA_AXES, SCalendar, SCallout, SCard, SCheckbox, SChip2 as SChip, SChipFilter, SChipInput, SCircleProgress, SConfirmModal, SDatePicker, SDatePickerMonthListbox, SDatePickerYearListbox, SDateRangePicker, SDivider, SDraggableGroup, SDraggableItem2 as SDraggableItem, SDraggableList, SDraggableProvider, SDrawer, SDropdownButton2 as SDropdownButton, SExpansionItem, SExpansionList2 as SExpansionList, SField, SFilePicker, SForm, SGhostButton, SGnb, SGuide, SIcon, SImage, SInput, SKeyValueTable, SLAYOUT_MIN_WIDTH, SLayout, SLayoutContext, SLinearProgress, SList2 as SList, SListItem2 as SListItem, SLoadingContainer, SLoadingModal, SLoadingViewport, SModal, SModalOutlet, SNumberInput, SPage, SPagination, SPopover, SPopup, SPortal, SRadio, SRadioButton, SRadioGroup, SSPLITTER_UNITS, SScrollArea, SSectionHeaderCard2 as SSectionHeaderCard, SSelect2 as SSelect, SSplitter2 as SSplitter, SStepper, SSwitch, STEPPER_SIZES, STable, STableBar, STabs, STag, STextLink, STextarea, STimePicker, STimeRangePicker, SToast, SToastContainer, SToggle, STooltip2 as STooltip, STree2 as STree, STreeItem2 as STreeItem, TAG_COLORS, TAG_SHAPES, TAG_SIZES, TIMEPICKER_SIZES, TIMEPICKER_SIZE_CONFIG, buttonPreset, cn, findGnbAncestors, findGnbRailValue, isColorKey, loading, resolveColor, useSLayout };
16729
17052
  //# sourceMappingURL=index.js.map
16730
17053
  //# sourceMappingURL=index.js.map