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

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
@@ -5120,6 +5120,10 @@ var BODY_PADDING_CLASS = {
5120
5120
  wide: "p-(--sys-space-panel-padding-wide)",
5121
5121
  none: ""
5122
5122
  };
5123
+ var BODY_BACKGROUND_CLASS = {
5124
+ frame: "",
5125
+ neutral: "bg-(--sys-color-bg-neutralLight)"
5126
+ };
5123
5127
  var topBorderColorClass = {
5124
5128
  default: "bg-(--cmp-sectionHeaderCard-topBorder-color-default)",
5125
5129
  accent: "bg-(--cmp-sectionHeaderCard-topBorder-color-accent)"
@@ -5129,8 +5133,8 @@ var titleSizeClass = {
5129
5133
  sm: "typo-heading-sm"
5130
5134
  };
5131
5135
  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 });
5136
+ function SSectionHeaderCardBody({ padding = "default", background = "frame", children }, ref) {
5137
+ return /* @__PURE__ */ jsx("div", { ref, className: cn(BODY_PADDING_CLASS[padding], BODY_BACKGROUND_CLASS[background]), children });
5134
5138
  }
5135
5139
  );
5136
5140
  var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ forwardRef(
@@ -5143,8 +5147,7 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ forwardRef(
5143
5147
  subtitle,
5144
5148
  slot,
5145
5149
  thickness = false,
5146
- className,
5147
- ...rest
5150
+ className
5148
5151
  }, ref) {
5149
5152
  const hasHelp = helpText != null && helpText.length > 0;
5150
5153
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -5165,7 +5168,6 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ forwardRef(
5165
5168
  "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
5169
  className
5167
5170
  ),
5168
- ...rest,
5169
5171
  children: [
5170
5172
  marker && /* @__PURE__ */ jsx(SBadge, { color: "darkblue", className: "shrink-0" }),
5171
5173
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 shrink-0 items-center gap-[8px]", children: [
@@ -5209,8 +5211,24 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ forwardRef(
5209
5211
  }
5210
5212
  );
5211
5213
  var SSectionHeaderCardRootImpl = /* @__PURE__ */ forwardRef(
5212
- function SSectionHeaderCard({ className, children, ...rest }, ref) {
5213
- return /* @__PURE__ */ jsx(
5214
+ function SSectionHeaderCard({
5215
+ title,
5216
+ titleSize,
5217
+ marker,
5218
+ required,
5219
+ helpText,
5220
+ subtitle,
5221
+ slot,
5222
+ thickness,
5223
+ headerClassName,
5224
+ padding,
5225
+ background,
5226
+ className,
5227
+ children,
5228
+ ...rest
5229
+ }, ref) {
5230
+ const hasHeader = title != null;
5231
+ return /* @__PURE__ */ jsxs(
5214
5232
  "div",
5215
5233
  {
5216
5234
  ref,
@@ -5219,24 +5237,28 @@ var SSectionHeaderCardRootImpl = /* @__PURE__ */ forwardRef(
5219
5237
  className
5220
5238
  ),
5221
5239
  ...rest,
5222
- children
5240
+ children: [
5241
+ hasHeader && /* @__PURE__ */ jsx(
5242
+ SSectionHeaderCardHeaderImpl,
5243
+ {
5244
+ title,
5245
+ titleSize,
5246
+ marker,
5247
+ required,
5248
+ helpText,
5249
+ subtitle,
5250
+ slot,
5251
+ thickness,
5252
+ className: headerClassName
5253
+ }
5254
+ ),
5255
+ /* @__PURE__ */ jsx(SSectionHeaderCardBodyImpl, { padding, background, children })
5256
+ ]
5223
5257
  }
5224
5258
  );
5225
5259
  }
5226
5260
  );
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
- });
5261
+ var SSectionHeaderCard2 = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5240
5262
  var SDivider = /* @__PURE__ */ forwardRef(function SDivider2({ vertical = false, className, style, ...rest }, ref) {
5241
5263
  return /* @__PURE__ */ jsx(
5242
5264
  "hr",
@@ -6867,7 +6889,7 @@ function SModalContainer({
6867
6889
  "div",
6868
6890
  {
6869
6891
  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",
6892
+ "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
6893
  shaking && "animate-modal-shake",
6872
6894
  className
6873
6895
  ),
@@ -7124,7 +7146,7 @@ function SActionModal({
7124
7146
  showClose: true,
7125
7147
  width,
7126
7148
  height,
7127
- className: "w-fit min-w-[min(480px,calc(100dvw-48px))] min-h-[min(320px,calc(100dvh-48px))]",
7149
+ className: "w-fit min-w-[min(480px,calc(100dvw-48px))] min-h-[min(320px,85dvh)]",
7128
7150
  children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-auto flex-col", children: [
7129
7151
  /* @__PURE__ */ jsx(SOverlayHeader, { title: modalTitle, description, reserveCloseSpace: true }),
7130
7152
  /* @__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 +8868,7 @@ var BACKGROUND_CLASS = {
8846
8868
  var SPage = /* @__PURE__ */ forwardRef(function SPage2({
8847
8869
  background = "frame",
8848
8870
  scrollEndSpacing = true,
8871
+ contentHeight = "auto",
8849
8872
  header,
8850
8873
  className,
8851
8874
  style,
@@ -8860,13 +8883,14 @@ var SPage = /* @__PURE__ */ forwardRef(function SPage2({
8860
8883
  onScroll?.(e);
8861
8884
  if (header != null && !header.fix) setScrolled(e.currentTarget.scrollTop > 0);
8862
8885
  };
8886
+ const fill = contentHeight === "fill";
8863
8887
  const main = /* @__PURE__ */ jsx(
8864
8888
  "main",
8865
8889
  {
8866
8890
  ref,
8867
8891
  className: cn(
8868
8892
  "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)]",
8893
+ !fill && scrollEndSpacing && "pb-[var(--cmp-pageBody-padding-scrollEnd)]",
8870
8894
  SCROLLBAR_CLASS,
8871
8895
  BACKGROUND_CLASS[background],
8872
8896
  className
@@ -8874,7 +8898,7 @@ var SPage = /* @__PURE__ */ forwardRef(function SPage2({
8874
8898
  style: header == null ? { ...gridStyle, ...style } : style,
8875
8899
  onScroll: handleScroll,
8876
8900
  ...rest,
8877
- children: /* @__PURE__ */ jsx("div", { style: { minWidth: MIN_CONTENT_WIDTH }, children })
8901
+ children: /* @__PURE__ */ jsx("div", { className: cn(fill && "h-full"), style: { minWidth: MIN_CONTENT_WIDTH }, children })
8878
8902
  }
8879
8903
  );
8880
8904
  if (header == null) return main;
@@ -8898,6 +8922,7 @@ var SPage = /* @__PURE__ */ forwardRef(function SPage2({
8898
8922
 
8899
8923
  // src/components/SPage/page.config.ts
8900
8924
  var PAGE_BACKGROUNDS = ["frame", "neutral", "screen"];
8925
+ var PAGE_CONTENT_HEIGHTS = ["auto", "fill"];
8901
8926
 
8902
8927
  // src/components/STabs/tabs.config.ts
8903
8928
  var TAB_MAIN_SIZE = {
@@ -9715,6 +9740,7 @@ var SField = /* @__PURE__ */ forwardRef(function SField2({
9715
9740
  readOnly = false,
9716
9741
  width,
9717
9742
  minWidth,
9743
+ maxWidth,
9718
9744
  multiline = false,
9719
9745
  borderless = false,
9720
9746
  children,
@@ -9750,7 +9776,10 @@ var SField = /* @__PURE__ */ forwardRef(function SField2({
9750
9776
  const resolvedSuccess = status === "pass";
9751
9777
  const showError = resolvedError && errorMessage !== "";
9752
9778
  const interactive = !disabled && !readOnly;
9753
- const hug = cssLen(width) !== void 0;
9779
+ const isPercentWidth = typeof width === "string" && width.trim().endsWith("%");
9780
+ const hug = cssLen(width) !== void 0 && !isPercentWidth;
9781
+ const addonWidth = effectiveAddonLabel !== "" ? cssLen(labelWidth) : void 0;
9782
+ const resolvedMaxWidth = cssLen(maxWidth) === void 0 || effectiveAddonLabel === "" ? cssLen(maxWidth) : addonWidth ? `calc(${cssLen(maxWidth)} + ${addonWidth})` : void 0;
9754
9783
  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
9784
  const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
9756
9785
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
@@ -9806,8 +9835,12 @@ var SField = /* @__PURE__ */ forwardRef(function SField2({
9806
9835
  /* @__PURE__ */ jsxs(
9807
9836
  "div",
9808
9837
  {
9809
- className: cn("flex min-w-0 flex-col", !hug && "flex-1"),
9810
- style: { width: cssLen(width), minWidth: cssLen(minWidth) },
9838
+ className: cn("flex min-w-0 flex-col", !hug && !isPercentWidth && "flex-1"),
9839
+ style: {
9840
+ width: cssLen(width),
9841
+ minWidth: cssLen(minWidth),
9842
+ maxWidth: resolvedMaxWidth
9843
+ },
9811
9844
  children: [
9812
9845
  /* @__PURE__ */ jsxs(
9813
9846
  "div",
@@ -10562,6 +10595,7 @@ var STable = /* @__PURE__ */ forwardRef(function STable2({
10562
10595
  height,
10563
10596
  stickyColumn,
10564
10597
  radius = "default",
10598
+ bordered = true,
10565
10599
  noDataLabel = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
10566
10600
  noDataSlot,
10567
10601
  isLoading = false,
@@ -10820,7 +10854,7 @@ var STable = /* @__PURE__ */ forwardRef(function STable2({
10820
10854
  };
10821
10855
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[var(--cmp-table-radius)] rounded-t-none" : "rounded-[var(--cmp-table-radius)]";
10822
10856
  const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-body-paddingY-default)";
10823
- const cellPadX = "var(--cmp-table-body-paddingX-wide)";
10857
+ const cellPadXOf = (col) => col.contentType === "control" ? "var(--cmp-table-body-paddingX-default)" : "var(--cmp-table-body-paddingX-wide)";
10824
10858
  const rowH = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-body-default-height)";
10825
10859
  const totalColSpan = visibleCols.length + (selectable ? 1 : 0);
10826
10860
  const stickyInfo = (i) => {
@@ -10874,7 +10908,8 @@ var STable = /* @__PURE__ */ forwardRef(function STable2({
10874
10908
  // 만으로는 스태킹 컨텍스트가 생기지 않아, 모달 안에 들어가면 30·31이 모달 Content
10875
10909
  // 레벨까지 올라가 같은 컨텍스트의 floating portal 밴드(z-2)를 덮는다.
10876
10910
  // 자손은 이미 overflow-hidden으로 클리핑되므로 테이블 내부 순서는 달라지지 않는다.
10877
- "bg-white relative isolate flex flex-col overflow-hidden border border-solid border-[color:var(--cmp-table-border-color)]",
10911
+ "bg-white relative isolate flex flex-col overflow-hidden",
10912
+ bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
10878
10913
  // height가 없으면 루트가 받은 높이를 끝까지 채운다. 호스트가 className으로
10879
10914
  // `flex-1 min-h-0` 등을 줘 루트 높이가 확정된 경우, 행이 적어도 테두리 박스가
10880
10915
  // 남은 영역을 그대로 차지한다. basis는 auto로 둬 높이가 확정되지 않은
@@ -11044,9 +11079,10 @@ var STable = /* @__PURE__ */ forwardRef(function STable2({
11044
11079
  stickyShadowClass(i),
11045
11080
  typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
11046
11081
  );
11082
+ const tdPadX = cellPadXOf(col);
11047
11083
  const tdStyle = {
11048
- paddingLeft: cellPadX,
11049
- paddingRight: cellPadX,
11084
+ paddingLeft: tdPadX,
11085
+ paddingRight: tdPadX,
11050
11086
  paddingTop: cellPadY,
11051
11087
  paddingBottom: cellPadY,
11052
11088
  textAlign: col.align ?? "left",
@@ -11099,19 +11135,29 @@ var STable = /* @__PURE__ */ forwardRef(function STable2({
11099
11135
  // SPagination은 전체 폭 기준 중앙, rows-per-page SSelect는 우측에 absolute 배치해 중앙 정렬을 유지한다.
11100
11136
  // 루트 높이가 확정된 경우(테이블이 남은 영역을 채우는 배치) 페이지네이션 바가
11101
11137
  // 눌리지 않도록 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
- ] })
11138
+ /* @__PURE__ */ jsxs(
11139
+ "div",
11140
+ {
11141
+ className: cn(
11142
+ "relative flex h-[48px] shrink-0 items-center justify-center rounded-[var(--cmp-table-radius)] bg-[var(--color-grey-05)]",
11143
+ // 테두리를 끄면 겹칠 대상이 없어진다 — -mt-px 를 남기면 1px 어긋남만 생긴다.
11144
+ bordered && "-mt-px border border-solid border-[color:var(--cmp-table-border-color)]"
11145
+ ),
11146
+ children: [
11147
+ /* @__PURE__ */ jsx(SPagination, { currentPage: displayPage, lastPage, onPageChange: changePage }),
11148
+ useRowsPerPageSelect && /* @__PURE__ */ jsx("div", { className: "absolute right-[10px] top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsx(
11149
+ SSelect2,
11150
+ {
11151
+ options: rowsPerPageOption,
11152
+ value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
11153
+ emitValue: true,
11154
+ onValueChange: (v) => changeRowsPerPage(Number(v)),
11155
+ width: 128
11156
+ }
11157
+ ) })
11158
+ ]
11159
+ }
11160
+ )
11115
11161
  ]
11116
11162
  }
11117
11163
  );
@@ -12249,6 +12295,44 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
12249
12295
  }
12250
12296
  ) });
12251
12297
  }
12298
+ function SDatePickerYearListbox({
12299
+ value,
12300
+ anchorYear,
12301
+ selectable,
12302
+ onValueChange,
12303
+ className,
12304
+ style
12305
+ }) {
12306
+ const today = getTodayParts();
12307
+ const resolvedAnchorYear = anchorYear ?? today.year;
12308
+ const [year, setYear] = useState(() => parseYear(value) ?? resolvedAnchorYear);
12309
+ useEffect(() => {
12310
+ setYear(parseYear(value) ?? resolvedAnchorYear);
12311
+ }, [value, resolvedAnchorYear]);
12312
+ const commitYear = (nextYear) => {
12313
+ setYear(nextYear);
12314
+ onValueChange?.(String(nextYear));
12315
+ };
12316
+ return /* @__PURE__ */ jsx(
12317
+ "div",
12318
+ {
12319
+ className: cn(
12320
+ "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",
12321
+ className
12322
+ ),
12323
+ style,
12324
+ children: /* @__PURE__ */ jsx(
12325
+ YearWheel,
12326
+ {
12327
+ value: year,
12328
+ anchorYear: resolvedAnchorYear,
12329
+ selectable,
12330
+ onCommit: commitYear
12331
+ }
12332
+ )
12333
+ }
12334
+ );
12335
+ }
12252
12336
  function MonthWheel({ year, value, selectable, onCommit }) {
12253
12337
  const listRef = useRef(null);
12254
12338
  const initialValueRef = useRef(value);
@@ -12295,66 +12379,59 @@ function MonthWheel({ year, value, selectable, onCommit }) {
12295
12379
  }
12296
12380
  ) });
12297
12381
  }
12298
- function DatePickerListbox({
12299
- mode,
12382
+ function SDatePickerMonthListbox({
12300
12383
  value,
12384
+ anchorYear,
12301
12385
  selectable,
12302
12386
  onValueChange,
12303
- onClose
12387
+ onMonthSelect,
12388
+ className,
12389
+ style
12304
12390
  }) {
12305
12391
  const today = getTodayParts();
12306
- const [year, setYear] = useState(() => parseYear(value) ?? today.year);
12392
+ const resolvedAnchorYear = anchorYear ?? today.year;
12393
+ const [year, setYear] = useState(() => parseYear(value) ?? resolvedAnchorYear);
12307
12394
  const [month, setMonth] = useState(() => parseMonth(value) ?? today.month);
12395
+ useEffect(() => {
12396
+ setYear(parseYear(value) ?? resolvedAnchorYear);
12397
+ setMonth(parseMonth(value) ?? today.month);
12398
+ }, [value, resolvedAnchorYear, today.month]);
12308
12399
  const commitYear = (nextYear) => {
12309
12400
  setYear(nextYear);
12310
- if (mode === "year") {
12311
- onValueChange?.(String(nextYear));
12312
- onClose();
12313
- return;
12314
- }
12315
12401
  onValueChange?.(formatMonthValue(nextYear, month));
12316
12402
  };
12317
12403
  const commitMonth = (nextMonth) => {
12318
12404
  setMonth(nextMonth);
12319
- onValueChange?.(formatMonthValue(year, nextMonth));
12320
- onClose();
12405
+ const nextValue = formatMonthValue(year, nextMonth);
12406
+ onValueChange?.(nextValue);
12407
+ onMonthSelect?.(nextValue);
12321
12408
  };
12322
12409
  return /* @__PURE__ */ jsxs(
12323
12410
  "div",
12324
12411
  {
12325
12412
  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)]"
12413
+ "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",
12414
+ className
12328
12415
  ),
12416
+ style,
12329
12417
  children: [
12418
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsx(
12419
+ YearWheel,
12420
+ {
12421
+ value: year,
12422
+ anchorYear: resolvedAnchorYear,
12423
+ selectable,
12424
+ onCommit: commitYear
12425
+ }
12426
+ ) }),
12330
12427
  /* @__PURE__ */ jsx(
12331
12428
  "div",
12332
12429
  {
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
- )
12430
+ "aria-hidden": true,
12431
+ className: "w-[var(--border-width-1)] self-stretch bg-[var(--sys-color-divider-default)]"
12346
12432
  }
12347
12433
  ),
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
- ] })
12434
+ /* @__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
12435
  ]
12359
12436
  }
12360
12437
  );
@@ -12409,6 +12486,9 @@ function SDatePicker({
12409
12486
  icon: 20,
12410
12487
  minW: 104,
12411
12488
  fieldMinW: 168,
12489
+ // 상한 등급 lg — 값 길이가 YYYY-MM-DD 로 정해져 있어, width="100%" 를 받아도
12490
+ // 행 전체로 늘어나지 않게 막는다 (SKeyValueTable 이 모든 컨트롤에 100% 를 넘긴다)
12491
+ fieldMaxW: 240,
12412
12492
  fontSize: 14,
12413
12493
  lineHeight: 24,
12414
12494
  radius: 6
@@ -12418,6 +12498,8 @@ function SDatePicker({
12418
12498
  icon: 16,
12419
12499
  minW: 80,
12420
12500
  fieldMinW: 128,
12501
+ // 상한 등급 md
12502
+ fieldMaxW: 160,
12421
12503
  fontSize: 12,
12422
12504
  lineHeight: 20,
12423
12505
  radius: 4
@@ -12450,6 +12532,7 @@ function SDatePicker({
12450
12532
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12451
12533
  width,
12452
12534
  minWidth: dims.fieldMinW,
12535
+ maxWidth: dims.fieldMaxW,
12453
12536
  disabled,
12454
12537
  className,
12455
12538
  style,
@@ -12494,7 +12577,7 @@ function SDatePicker({
12494
12577
  RPopover9.Content,
12495
12578
  {
12496
12579
  side: "bottom",
12497
- align: "start",
12580
+ align: mode === "date" ? "start" : "center",
12498
12581
  sideOffset: 4,
12499
12582
  className: cn(
12500
12583
  "z-50 max-w-[var(--radix-popover-content-available-width)]",
@@ -12509,14 +12592,23 @@ function SDatePicker({
12509
12592
  onValueChange: select,
12510
12593
  onViewChange
12511
12594
  }
12595
+ ) : mode === "year" ? /* @__PURE__ */ jsx(
12596
+ SDatePickerYearListbox,
12597
+ {
12598
+ value,
12599
+ selectable,
12600
+ onValueChange: (nextValue) => {
12601
+ selectListboxValue(nextValue);
12602
+ setOpen(false);
12603
+ }
12604
+ }
12512
12605
  ) : /* @__PURE__ */ jsx(
12513
- DatePickerListbox,
12606
+ SDatePickerMonthListbox,
12514
12607
  {
12515
- mode,
12516
12608
  value,
12517
12609
  selectable,
12518
12610
  onValueChange: selectListboxValue,
12519
- onClose: () => setOpen(false)
12611
+ onMonthSelect: () => setOpen(false)
12520
12612
  }
12521
12613
  )
12522
12614
  }
@@ -12899,6 +12991,9 @@ function SDateRangePicker({
12899
12991
  icon: 20,
12900
12992
  minW: 104,
12901
12993
  fieldMinW: 260,
12994
+ // 상한 등급 xl — 값이 `YYYY-MM-DD ~ YYYY-MM-DD` 라 단일 날짜의 두 배가 넘는다.
12995
+ // 최소너비(260)가 이미 lg(240)를 넘으므로 상한도 한 등급 위여야 한다
12996
+ fieldMaxW: 480,
12902
12997
  fontSize: 14,
12903
12998
  lineHeight: 24,
12904
12999
  radius: 6
@@ -12908,6 +13003,8 @@ function SDateRangePicker({
12908
13003
  icon: 16,
12909
13004
  minW: 80,
12910
13005
  fieldMinW: 216,
13006
+ // 상한 등급 lg — 최소너비 216 이 md(160)를 넘어 md 로는 담기지 않는다
13007
+ fieldMaxW: 240,
12911
13008
  fontSize: 12,
12912
13009
  lineHeight: 20,
12913
13010
  radius: 4
@@ -12938,6 +13035,7 @@ function SDateRangePicker({
12938
13035
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12939
13036
  width,
12940
13037
  minWidth: dims.fieldMinW,
13038
+ maxWidth: dims.fieldMaxW,
12941
13039
  disabled,
12942
13040
  className,
12943
13041
  style,
@@ -13402,6 +13500,7 @@ function SKeyValueTable({
13402
13500
  values = {},
13403
13501
  search = false,
13404
13502
  radius = "default",
13503
+ bordered = true,
13405
13504
  onChange,
13406
13505
  onSearch,
13407
13506
  className,
@@ -13466,7 +13565,8 @@ function SKeyValueTable({
13466
13565
  });
13467
13566
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[8px] rounded-t-none" : "rounded-[8px]";
13468
13567
  const frameClass = cn(
13469
- "overflow-hidden border border-solid border-[color:var(--cmp-table-border-color)]",
13568
+ "overflow-hidden",
13569
+ bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
13470
13570
  radiusClass
13471
13571
  );
13472
13572
  const table = /* @__PURE__ */ jsxs(
@@ -14051,7 +14151,16 @@ var CHIPFILTER = {
14051
14151
  itemTextDefault: "var(--cmp-chipFilter-item-text-default)",
14052
14152
  itemTextSelected: "var(--cmp-chipFilter-item-text-selected)"
14053
14153
  };
14154
+ var DEFAULT_PERIOD_PRESETS = [
14155
+ { value: "day", label: "\uC77C\uBCC4" },
14156
+ { value: "month", label: "\uC6D4\uBCC4" },
14157
+ { value: "quarter", label: "\uBD84\uAE30\uBCC4" },
14158
+ { value: "half", label: "\uBC18\uAE30\uBCC4" },
14159
+ { value: "year", label: "\uC5F0\uB3C4\uBCC4" },
14160
+ { value: "custom", label: "\uC0AC\uC6A9\uC790 \uC9C0\uC815", custom: true }
14161
+ ];
14054
14162
  var isKeywordValue = (value) => typeof value === "object" && value != null && !Array.isArray(value) && "keywords" in value;
14163
+ var isPeriodValue = (value) => typeof value === "object" && value != null && !Array.isArray(value) && "unit" in value;
14055
14164
  var keywordEntries = (value) => {
14056
14165
  if (isKeywordValue(value)) return value.keywords;
14057
14166
  if (Array.isArray(value)) {
@@ -14075,12 +14184,33 @@ var todayIso = () => {
14075
14184
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
14076
14185
  };
14077
14186
  var isInlineDateField = (field) => field.type === "date" && field.radioButton === true && (field.presets?.length ?? 0) > 0;
14187
+ var getFieldPresets = (field) => {
14188
+ if (field.type === "period") return field.presets ?? DEFAULT_PERIOD_PRESETS;
14189
+ return field.presets ?? [];
14190
+ };
14191
+ var getDefaultPeriodInputValue = (unit) => {
14192
+ const today = todayIso();
14193
+ if (unit === "day") return today;
14194
+ if (unit === "month") return today.slice(0, 7);
14195
+ if (unit === "quarter") return 1;
14196
+ if (unit === "half") return "first";
14197
+ if (unit === "year") return today.slice(0, 4);
14198
+ return [today, today];
14199
+ };
14200
+ var createPeriodValue = (unit) => ({
14201
+ unit,
14202
+ value: getDefaultPeriodInputValue(unit)
14203
+ });
14078
14204
  var resolveFieldDefault = (field) => {
14079
14205
  if (field.defaultValue !== void 0) return field.defaultValue;
14080
14206
  if (isInlineDateField(field)) return todayIso();
14081
14207
  if (!field.required) return void 0;
14082
14208
  if (field.type === "single") return field.options?.[0]?.value;
14083
14209
  if (field.type === "date") return todayIso();
14210
+ if (field.type === "period") {
14211
+ const unit = getFieldPresets(field).find((preset) => !preset.custom)?.value;
14212
+ return unit ? createPeriodValue(unit) : void 0;
14213
+ }
14084
14214
  return void 0;
14085
14215
  };
14086
14216
  var valuesEqual = (a, b) => {
@@ -14104,6 +14234,8 @@ var isAtDefaultValue = (field, value) => {
14104
14234
  return def !== void 0 && valuesEqual(value, def);
14105
14235
  };
14106
14236
  var isInlineField = (field) => isInlineDateField(field) || field.type === "custom";
14237
+ var isFixedField = (field) => field.fix === true || field.required === true;
14238
+ var isAddableField = (field) => !isFixedField(field);
14107
14239
  var isGroupedFields = (fields) => fields.length > 0 && "fields" in fields[0];
14108
14240
  var isGroupSatisfied = (group, values) => {
14109
14241
  if (!group.rule) return true;
@@ -14130,9 +14262,10 @@ var defaultGroupTooltipMessage = (rule, fields) => {
14130
14262
  };
14131
14263
  var matchPresetId = (presets, value) => {
14132
14264
  if (isEmptyValue(value)) return null;
14265
+ if (isPeriodValue(value)) return value.unit;
14133
14266
  for (const preset of presets) {
14134
- if (preset.custom || !preset.resolve) continue;
14135
- const resolved = preset.resolve();
14267
+ if (preset.custom) continue;
14268
+ const resolved = preset.resolve ? preset.resolve() : preset.value;
14136
14269
  if (Array.isArray(resolved) && Array.isArray(value)) {
14137
14270
  if (resolved[0] === value[0] && resolved[1] === value[1]) return preset.value;
14138
14271
  } else if (!Array.isArray(resolved) && !Array.isArray(value) && resolved === value) {
@@ -14148,18 +14281,36 @@ var formatValueList = (labels) => {
14148
14281
  };
14149
14282
  var displayValue = (field, value) => {
14150
14283
  if (isEmptyValue(value)) return "";
14151
- if (field.type === "multi")
14152
- return formatValueList(toArrayValue(value).map((v) => optionLabel(field, v)));
14284
+ if (field.type === "multi") {
14285
+ const values = toArrayValue(value);
14286
+ const selectableValues = (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value);
14287
+ if (selectableValues.length > 0 && selectableValues.every((selectableValue) => values.includes(selectableValue))) {
14288
+ return "\uC804\uCCB4";
14289
+ }
14290
+ return formatValueList(values.map((v) => optionLabel(field, v)));
14291
+ }
14153
14292
  if (field.type === "single") return optionLabel(field, value);
14154
14293
  if (field.type === "date")
14155
14294
  return Array.isArray(value) ? `${value[0]} ~ ${value[1]}` : String(value);
14295
+ if (field.type === "period") {
14296
+ if (!isPeriodValue(value)) {
14297
+ const preset2 = getFieldPresets(field).find((p) => p.value === value);
14298
+ return preset2?.label ?? String(value);
14299
+ }
14300
+ const preset = getFieldPresets(field).find((p) => p.value === value.unit);
14301
+ const label = preset?.label ?? value.unit;
14302
+ if (value.unit === "quarter") return `${label}(${value.value ?? 1}\uBD84\uAE30)`;
14303
+ if (value.unit === "half") return `${label}(${value.value === "second" ? "\uD558\uBC18\uAE30" : "\uC0C1\uBC18\uAE30"})`;
14304
+ if (Array.isArray(value.value)) return `${value.value[0]} ~ ${value.value[1]}`;
14305
+ return value.value != null && value.value !== "" ? String(value.value) : label;
14306
+ }
14156
14307
  if (field.type === "keyword") return formatValueList(keywordEntries(value));
14157
14308
  return String(value);
14158
14309
  };
14159
14310
  var chipDisplayValue = (field, value) => {
14160
14311
  const text = displayValue(field, value);
14161
14312
  if (field.type !== "date" || isEmptyValue(value)) return text;
14162
- const presets = field.presets ?? [];
14313
+ const presets = getFieldPresets(field);
14163
14314
  if (presets.length === 0) return text;
14164
14315
  const preset = presets.find((p) => p.value === matchPresetId(presets, value));
14165
14316
  return preset && !preset.custom ? `${preset.label}(${text})` : text;
@@ -14176,7 +14327,6 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14176
14327
  onAddFilter,
14177
14328
  label = "\uAC80\uC0C9 \uD544\uD130",
14178
14329
  showLabel = false,
14179
- showAddButton = true,
14180
14330
  showReset = true,
14181
14331
  disabled = false,
14182
14332
  className,
@@ -14191,7 +14341,7 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14191
14341
  const values = normalizeValues(value);
14192
14342
  const portalContainer = usePortalContainer();
14193
14343
  const [internalActiveKeys, setInternalActiveKeys] = useState(
14194
- () => fields.filter((field) => field.defaultActive !== false).map((field) => field.key)
14344
+ () => fields.filter((field) => isFixedField(field) || field.defaultActive !== false).map((field) => field.key)
14195
14345
  );
14196
14346
  const [openKey, setOpenKey] = useState(null);
14197
14347
  const [addOpen, setAddOpen] = useState(false);
@@ -14233,7 +14383,9 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14233
14383
  setInternalActiveKeys((current) => {
14234
14384
  const known = new Set(fields.map((field) => field.key));
14235
14385
  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);
14386
+ const additions = fields.filter(
14387
+ (field) => (isFixedField(field) || field.defaultActive !== false) && !kept.includes(field.key)
14388
+ ).map((field) => field.key);
14237
14389
  return [...kept, ...additions];
14238
14390
  });
14239
14391
  }, [activeKeys, fields]);
@@ -14254,25 +14406,31 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14254
14406
  });
14255
14407
  if (changed) onValueChange?.({ ...values, ...patch });
14256
14408
  }, [fields, values, onValueChange]);
14257
- const resolvedActiveKeys = activeKeys ?? internalActiveKeys;
14409
+ const fixedKeys = fields.filter(isFixedField).map((field) => field.key);
14410
+ const resolvedActiveKeys = Array.from(
14411
+ /* @__PURE__ */ new Set([...activeKeys ?? internalActiveKeys, ...fixedKeys])
14412
+ );
14258
14413
  const activeFields = fields.filter((field) => resolvedActiveKeys.includes(field.key));
14259
- const inactiveFields = fields.filter((field) => !resolvedActiveKeys.includes(field.key));
14414
+ const inactiveFields = fields.filter(
14415
+ (field) => isAddableField(field) && !resolvedActiveKeys.includes(field.key)
14416
+ );
14260
14417
  const groupIndexByKey = /* @__PURE__ */ new Map();
14261
14418
  groups.forEach((group, groupIndex) => {
14262
14419
  group.fields.forEach((field) => groupIndexByKey.set(field.key, groupIndex));
14263
14420
  });
14264
14421
  const hasValue = fields.some((field) => !isEmptyValue(values[field.key]));
14265
- const hasRightActions = showAddButton || showReset;
14422
+ const hasAddButton = fields.some(isAddableField);
14423
+ const hasRightActions = hasAddButton || showReset;
14266
14424
  const currentOpenField = fields.find((field) => field.key === openKey);
14267
14425
  const editorPopoverStyle = useMemo(() => {
14268
14426
  if (currentOpenField?.type === "keyword") {
14269
14427
  return { width: CHIPFILTER_LISTBOX_WIDTH };
14270
14428
  }
14271
- if (currentOpenField?.type === "date" && (currentOpenField.presets?.length ?? 0) > 0) {
14429
+ if ((currentOpenField?.type === "date" || currentOpenField?.type === "period") && getFieldPresets(currentOpenField).length > 0) {
14272
14430
  return { width: CHIPFILTER_DATE_PRESET_LISTBOX_WIDTH };
14273
14431
  }
14274
14432
  return { width: CHIPFILTER_LISTBOX_WIDTH };
14275
- }, [currentOpenField?.type, currentOpenField?.presets?.length]);
14433
+ }, [currentOpenField]);
14276
14434
  const setActive = (next) => {
14277
14435
  if (!activeKeys) setInternalActiveKeys(next);
14278
14436
  onActiveKeysChange?.(next);
@@ -14291,6 +14449,12 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14291
14449
  const def = field ? resolveFieldDefault(field) : void 0;
14292
14450
  runSearch(emitValue(key, def !== void 0 ? def : null));
14293
14451
  };
14452
+ const removeField = (key) => {
14453
+ const nextValues = emitValue(key, null);
14454
+ setActive(resolvedActiveKeys.filter((activeKey) => activeKey !== key));
14455
+ if (openKey === key) setOpenKey(null);
14456
+ runSearch(nextValues);
14457
+ };
14294
14458
  const reset = useCallback(() => {
14295
14459
  const next = fields.reduce((acc, field) => {
14296
14460
  const def = resolveFieldDefault(field);
@@ -14381,7 +14545,15 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14381
14545
  flushRun();
14382
14546
  runGroupIndex = groupIndex;
14383
14547
  }
14384
- const showClear = !isAtDefaultValue(field, values[field.key]);
14548
+ const addable = isAddableField(field);
14549
+ const showClear = addable || !isAtDefaultValue(field, values[field.key]);
14550
+ const handleClear = () => {
14551
+ if (addable) {
14552
+ removeField(field.key);
14553
+ return;
14554
+ }
14555
+ clearValue(field.key);
14556
+ };
14385
14557
  const node = isInlineDateField(field) ? /* @__PURE__ */ jsx(
14386
14558
  InlineDatePresetFilter,
14387
14559
  {
@@ -14391,15 +14563,27 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14391
14563
  warning,
14392
14564
  showClear,
14393
14565
  onValueChange: (next) => emitValueAndSearch(field.key, next),
14394
- onClear: () => clearValue(field.key)
14566
+ onClear: handleClear
14395
14567
  },
14396
14568
  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(
14569
+ ) : field.type === "custom" ? /* @__PURE__ */ jsxs("div", { className: "inline-flex shrink-0 items-center gap-[4px]", children: [
14570
+ field.render?.({
14571
+ value: values[field.key],
14572
+ disabled: disabled || field.disabled,
14573
+ warning,
14574
+ onValueChange: (next) => emitValueAndSearch(field.key, next)
14575
+ }),
14576
+ addable && !disabled && !field.disabled && showClear && /* @__PURE__ */ jsx(
14577
+ "button",
14578
+ {
14579
+ type: "button",
14580
+ "aria-label": `${field.label} \uD544\uD130 \uC81C\uAC70`,
14581
+ className: "inline-flex size-[16px] shrink-0 items-center justify-center rounded-[var(--cmp-ghostButton-radius-sm)] border-none bg-transparent p-0",
14582
+ onClick: handleClear,
14583
+ children: /* @__PURE__ */ jsx(SIcon, { name: "close", size: 12, color: "var(--cmp-ghostButton-default-content)" })
14584
+ }
14585
+ )
14586
+ ] }, field.key) : /* @__PURE__ */ jsx(
14403
14587
  FilterChip,
14404
14588
  {
14405
14589
  field,
@@ -14409,9 +14593,10 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14409
14593
  disabled: disabled || field.disabled,
14410
14594
  warning,
14411
14595
  showClear,
14596
+ showClearWhenEmpty: addable,
14412
14597
  refCallback: (node2) => triggerRefs.current.set(field.key, node2),
14413
14598
  onOpen: () => setOpenKey(openKey === field.key ? null : field.key),
14414
- onClear: () => clearValue(field.key)
14599
+ onClear: handleClear
14415
14600
  },
14416
14601
  field.key
14417
14602
  );
@@ -14432,7 +14617,7 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14432
14617
  })(),
14433
14618
  hasRightActions && /* @__PURE__ */ jsxs(Fragment, { children: [
14434
14619
  /* @__PURE__ */ jsx(Divider, { inset: true }),
14435
- showAddButton && /* @__PURE__ */ jsxs(RPopover9.Root, { open: addOpen, onOpenChange: setAddOpen, children: [
14620
+ hasAddButton && /* @__PURE__ */ jsxs(RPopover9.Root, { open: addOpen, onOpenChange: setAddOpen, children: [
14436
14621
  /* @__PURE__ */ jsx(RPopover9.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
14437
14622
  "button",
14438
14623
  {
@@ -14479,7 +14664,7 @@ var SChipFilter = /* @__PURE__ */ forwardRef(function SChipFilter2({
14479
14664
  "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
14665
  // 부모 flex gap(6px)은 칩 사이 간격 기준이라, 필터 추가 버튼과의 12px 간격을 맞추려면
14481
14666
  // 부족한 만큼(12px - gap)을 추가로 띄운다. 필터 추가가 없을 땐 divider와 이미 12px이라 불필요.
14482
- showAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
14667
+ hasAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
14483
14668
  ),
14484
14669
  style: {
14485
14670
  color: disabled || !hasValue ? "var(--cmp-textLink-content-disabled)" : "var(--cmp-textLink-content-default)"
@@ -14563,6 +14748,7 @@ function FilterChip({
14563
14748
  disabled,
14564
14749
  warning,
14565
14750
  showClear,
14751
+ showClearWhenEmpty,
14566
14752
  refCallback,
14567
14753
  onOpen,
14568
14754
  onClear
@@ -14632,7 +14818,7 @@ function FilterChip({
14632
14818
  }
14633
14819
  )
14634
14820
  ] }),
14635
- applied && !disabled && showClear && /* @__PURE__ */ jsx(
14821
+ !disabled && showClear && (applied || showClearWhenEmpty) && /* @__PURE__ */ jsx(
14636
14822
  "span",
14637
14823
  {
14638
14824
  role: "button",
@@ -14664,7 +14850,7 @@ function EditorPanel({
14664
14850
  onValueChange,
14665
14851
  onClose
14666
14852
  }) {
14667
- const wrapsInPaddedTop = field.type === "date";
14853
+ const wrapsInPaddedTop = field.type === "date" || field.type === "period";
14668
14854
  const isMulti = field.type === "multi";
14669
14855
  const selectableValues = isMulti ? (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value) : [];
14670
14856
  const selectedValues = isMulti ? toArrayValue(value) : [];
@@ -14743,6 +14929,17 @@ function FilterEditor({
14743
14929
  disabled,
14744
14930
  onValueChange
14745
14931
  }) {
14932
+ if (field.type === "period") {
14933
+ return /* @__PURE__ */ jsx(
14934
+ PeriodPresetEditor,
14935
+ {
14936
+ field,
14937
+ value,
14938
+ disabled,
14939
+ onValueChange
14940
+ }
14941
+ );
14942
+ }
14746
14943
  if (field.type === "date") {
14747
14944
  return /* @__PURE__ */ jsx(
14748
14945
  DatePresetEditor,
@@ -14782,7 +14979,7 @@ function DatePresetEditor({
14782
14979
  disabled,
14783
14980
  onValueChange
14784
14981
  }) {
14785
- const presets = field.presets ?? [];
14982
+ const presets = getFieldPresets(field);
14786
14983
  const [selectedId, setSelectedId] = useState(() => matchPresetId(presets, value));
14787
14984
  const syncedInitialValueRef = useRef(!isEmptyValue(value));
14788
14985
  useEffect(() => {
@@ -14793,9 +14990,9 @@ function DatePresetEditor({
14793
14990
  const selectedPreset = presets.find((preset) => preset.value === selectedId);
14794
14991
  const selectPreset = (preset) => {
14795
14992
  setSelectedId(preset.value);
14796
- if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : null);
14993
+ if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : preset.value);
14797
14994
  };
14798
- if (presets.length === 0) {
14995
+ if (field.type === "date" && presets.length === 0) {
14799
14996
  return /* @__PURE__ */ jsx(
14800
14997
  SDatePicker,
14801
14998
  {
@@ -14831,6 +15028,130 @@ function DatePresetEditor({
14831
15028
  )
14832
15029
  ] });
14833
15030
  }
15031
+ function PeriodPresetEditor({
15032
+ field,
15033
+ value,
15034
+ disabled,
15035
+ onValueChange
15036
+ }) {
15037
+ const presets = getFieldPresets(field);
15038
+ const firstUnit = presets.find((preset) => !preset.custom)?.value ?? "day";
15039
+ const [selectedId, setSelectedId] = useState(() => matchPresetId(presets, value) ?? firstUnit);
15040
+ const periodValue = isPeriodValue(value) ? value : createPeriodValue(selectedId ?? firstUnit);
15041
+ const selectedUnit = selectedId ?? periodValue.unit;
15042
+ useEffect(() => {
15043
+ if (isEmptyValue(value)) return;
15044
+ setSelectedId(matchPresetId(presets, value) ?? firstUnit);
15045
+ }, [firstUnit, presets, value]);
15046
+ const selectPreset = (preset) => {
15047
+ const nextUnit = preset.value;
15048
+ setSelectedId(nextUnit);
15049
+ onValueChange(createPeriodValue(nextUnit));
15050
+ };
15051
+ const updateInputValue = (nextValue) => {
15052
+ onValueChange({ unit: selectedUnit, value: nextValue });
15053
+ };
15054
+ const renderInput = () => {
15055
+ if (selectedUnit === "day") {
15056
+ return /* @__PURE__ */ jsx(
15057
+ SDatePicker,
15058
+ {
15059
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15060
+ onValueChange: updateInputValue,
15061
+ selectable: field.selectable,
15062
+ disabled,
15063
+ width: "100%"
15064
+ }
15065
+ );
15066
+ }
15067
+ if (selectedUnit === "month") {
15068
+ return /* @__PURE__ */ jsx(
15069
+ SDatePicker,
15070
+ {
15071
+ mode: "month",
15072
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15073
+ onValueChange: updateInputValue,
15074
+ selectable: field.selectable,
15075
+ disabled,
15076
+ width: "100%"
15077
+ }
15078
+ );
15079
+ }
15080
+ if (selectedUnit === "quarter") {
15081
+ return /* @__PURE__ */ jsx(
15082
+ SSelect2,
15083
+ {
15084
+ value: typeof periodValue.value === "number" ? periodValue.value : 1,
15085
+ onValueChange: updateInputValue,
15086
+ emitValue: true,
15087
+ disabled,
15088
+ width: "100%",
15089
+ options: [
15090
+ { value: 1, label: "1\uBD84\uAE30" },
15091
+ { value: 2, label: "2\uBD84\uAE30" },
15092
+ { value: 3, label: "3\uBD84\uAE30" },
15093
+ { value: 4, label: "4\uBD84\uAE30" }
15094
+ ]
15095
+ }
15096
+ );
15097
+ }
15098
+ if (selectedUnit === "half") {
15099
+ return /* @__PURE__ */ jsx(
15100
+ SSelect2,
15101
+ {
15102
+ value: typeof periodValue.value === "string" ? periodValue.value : "first",
15103
+ onValueChange: updateInputValue,
15104
+ emitValue: true,
15105
+ disabled,
15106
+ width: "100%",
15107
+ options: [
15108
+ { value: "first", label: "\uC0C1\uBC18\uAE30" },
15109
+ { value: "second", label: "\uD558\uBC18\uAE30" }
15110
+ ]
15111
+ }
15112
+ );
15113
+ }
15114
+ if (selectedUnit === "year") {
15115
+ return /* @__PURE__ */ jsx(
15116
+ SDatePicker,
15117
+ {
15118
+ mode: "year",
15119
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15120
+ onValueChange: updateInputValue,
15121
+ selectable: field.selectable,
15122
+ disabled,
15123
+ width: "100%"
15124
+ }
15125
+ );
15126
+ }
15127
+ return /* @__PURE__ */ jsx(
15128
+ SDateRangePicker,
15129
+ {
15130
+ value: Array.isArray(periodValue.value) && periodValue.value.length === 2 ? periodValue.value : null,
15131
+ onValueChange: updateInputValue,
15132
+ selectable: field.selectable,
15133
+ maxRange: field.maxRange,
15134
+ disabled,
15135
+ width: "100%"
15136
+ }
15137
+ );
15138
+ };
15139
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-[var(--cmp-chipFilter-listbox-top-gap)]", children: [
15140
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-[8px]", children: presets.map((preset) => /* @__PURE__ */ jsx(
15141
+ SRadio,
15142
+ {
15143
+ val: preset.value,
15144
+ value: selectedId,
15145
+ label: preset.label,
15146
+ disabled,
15147
+ onValueChange: () => selectPreset(preset)
15148
+ },
15149
+ preset.value
15150
+ )) }),
15151
+ /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "h-px w-full", style: { background: CHIPFILTER.listboxBorder } }),
15152
+ /* @__PURE__ */ jsx("div", { className: "w-full [&>*]:w-full", children: renderInput() })
15153
+ ] });
15154
+ }
14834
15155
  function InlineDatePresetFilter({
14835
15156
  field,
14836
15157
  value,
@@ -15835,7 +16156,9 @@ var TIMEPICKER_SIZE_CONFIG = {
15835
16156
  fontSize: 12,
15836
16157
  lineHeight: 20,
15837
16158
  minWidth: 80,
15838
- fieldMinWidth: 128
16159
+ fieldMinWidth: 128,
16160
+ // 상한 등급 md
16161
+ fieldMaxWidth: 160
15839
16162
  },
15840
16163
  md: {
15841
16164
  height: V3("md-height"),
@@ -15847,7 +16170,9 @@ var TIMEPICKER_SIZE_CONFIG = {
15847
16170
  fontSize: 14,
15848
16171
  lineHeight: 24,
15849
16172
  minWidth: 96,
15850
- fieldMinWidth: 160
16173
+ fieldMinWidth: 160,
16174
+ // 상한 등급 lg
16175
+ fieldMaxWidth: 240
15851
16176
  }
15852
16177
  };
15853
16178
  function clampTimePart(value, min, max) {
@@ -16171,6 +16496,7 @@ function STimePicker({
16171
16496
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
16172
16497
  width,
16173
16498
  minWidth: dims.fieldMinWidth,
16499
+ maxWidth: dims.fieldMaxWidth,
16174
16500
  disabled,
16175
16501
  className,
16176
16502
  style,
@@ -16287,6 +16613,10 @@ var MIDDAY_FIELD_MIN_WIDTH = {
16287
16613
  sm: 178,
16288
16614
  md: 216
16289
16615
  };
16616
+ var MIDDAY_FIELD_MAX_WIDTH = {
16617
+ sm: 240,
16618
+ md: 240
16619
+ };
16290
16620
  function RangeTimeSelector({
16291
16621
  value,
16292
16622
  useMeridiem,
@@ -16633,6 +16963,7 @@ function STimeRangePicker({
16633
16963
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
16634
16964
  width,
16635
16965
  minWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MIN_WIDTH[size] : dims.fieldMinWidth,
16966
+ maxWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MAX_WIDTH[size] : dims.fieldMaxWidth,
16636
16967
  disabled,
16637
16968
  className,
16638
16969
  style,
@@ -16725,6 +17056,6 @@ function STimeRangePicker({
16725
17056
  );
16726
17057
  }
16727
17058
 
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 };
17059
+ 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
17060
  //# sourceMappingURL=index.js.map
16730
17061
  //# sourceMappingURL=index.js.map