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.cjs CHANGED
@@ -5148,6 +5148,10 @@ var BODY_PADDING_CLASS = {
5148
5148
  wide: "p-(--sys-space-panel-padding-wide)",
5149
5149
  none: ""
5150
5150
  };
5151
+ var BODY_BACKGROUND_CLASS = {
5152
+ frame: "",
5153
+ neutral: "bg-(--sys-color-bg-neutralLight)"
5154
+ };
5151
5155
  var topBorderColorClass = {
5152
5156
  default: "bg-(--cmp-sectionHeaderCard-topBorder-color-default)",
5153
5157
  accent: "bg-(--cmp-sectionHeaderCard-topBorder-color-accent)"
@@ -5157,8 +5161,8 @@ var titleSizeClass = {
5157
5161
  sm: "typo-heading-sm"
5158
5162
  };
5159
5163
  var SSectionHeaderCardBodyImpl = /* @__PURE__ */ react.forwardRef(
5160
- function SSectionHeaderCardBody({ padding = "default", className, children, ...rest }, ref) {
5161
- return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn(BODY_PADDING_CLASS[padding], className), ...rest, children });
5164
+ function SSectionHeaderCardBody({ padding = "default", background = "frame", children }, ref) {
5165
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn(BODY_PADDING_CLASS[padding], BODY_BACKGROUND_CLASS[background]), children });
5162
5166
  }
5163
5167
  );
5164
5168
  var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
@@ -5171,8 +5175,7 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
5171
5175
  subtitle,
5172
5176
  slot,
5173
5177
  thickness = false,
5174
- className,
5175
- ...rest
5178
+ className
5176
5179
  }, ref) {
5177
5180
  const hasHelp = helpText != null && helpText.length > 0;
5178
5181
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -5193,7 +5196,6 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
5193
5196
  "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)",
5194
5197
  className
5195
5198
  ),
5196
- ...rest,
5197
5199
  children: [
5198
5200
  marker && /* @__PURE__ */ jsxRuntime.jsx(SBadge, { color: "darkblue", className: "shrink-0" }),
5199
5201
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 shrink-0 items-center gap-[8px]", children: [
@@ -5237,8 +5239,24 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
5237
5239
  }
5238
5240
  );
5239
5241
  var SSectionHeaderCardRootImpl = /* @__PURE__ */ react.forwardRef(
5240
- function SSectionHeaderCard({ className, children, ...rest }, ref) {
5241
- return /* @__PURE__ */ jsxRuntime.jsx(
5242
+ function SSectionHeaderCard({
5243
+ title,
5244
+ titleSize,
5245
+ marker,
5246
+ required,
5247
+ helpText,
5248
+ subtitle,
5249
+ slot,
5250
+ thickness,
5251
+ headerClassName,
5252
+ padding,
5253
+ background,
5254
+ className,
5255
+ children,
5256
+ ...rest
5257
+ }, ref) {
5258
+ const hasHeader = title != null;
5259
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5242
5260
  "div",
5243
5261
  {
5244
5262
  ref,
@@ -5247,24 +5265,28 @@ var SSectionHeaderCardRootImpl = /* @__PURE__ */ react.forwardRef(
5247
5265
  className
5248
5266
  ),
5249
5267
  ...rest,
5250
- children
5268
+ children: [
5269
+ hasHeader && /* @__PURE__ */ jsxRuntime.jsx(
5270
+ SSectionHeaderCardHeaderImpl,
5271
+ {
5272
+ title,
5273
+ titleSize,
5274
+ marker,
5275
+ required,
5276
+ helpText,
5277
+ subtitle,
5278
+ slot,
5279
+ thickness,
5280
+ className: headerClassName
5281
+ }
5282
+ ),
5283
+ /* @__PURE__ */ jsxRuntime.jsx(SSectionHeaderCardBodyImpl, { padding, background, children })
5284
+ ]
5251
5285
  }
5252
5286
  );
5253
5287
  }
5254
5288
  );
5255
- var SSectionHeaderCardRoot = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5256
- var SSectionHeaderCardHeader2 = /* @__PURE__ */ withDisplayName(
5257
- SSectionHeaderCardHeaderImpl,
5258
- "SSectionHeaderCard.Header"
5259
- );
5260
- var SSectionHeaderCardBody2 = /* @__PURE__ */ withDisplayName(
5261
- SSectionHeaderCardBodyImpl,
5262
- "SSectionHeaderCard.Body"
5263
- );
5264
- var SSectionHeaderCard2 = /* @__PURE__ */ Object.assign(SSectionHeaderCardRoot, {
5265
- Header: SSectionHeaderCardHeader2,
5266
- Body: SSectionHeaderCardBody2
5267
- });
5289
+ var SSectionHeaderCard2 = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5268
5290
  var SDivider = /* @__PURE__ */ react.forwardRef(function SDivider2({ vertical = false, className, style, ...rest }, ref) {
5269
5291
  return /* @__PURE__ */ jsxRuntime.jsx(
5270
5292
  "hr",
@@ -6895,7 +6917,7 @@ function SModalContainer({
6895
6917
  "div",
6896
6918
  {
6897
6919
  className: cn(
6898
- "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",
6920
+ "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",
6899
6921
  shaking && "animate-modal-shake",
6900
6922
  className
6901
6923
  ),
@@ -7152,7 +7174,7 @@ function SActionModal({
7152
7174
  showClose: true,
7153
7175
  width,
7154
7176
  height,
7155
- className: "w-fit min-w-[min(480px,calc(100dvw-48px))] min-h-[min(320px,calc(100dvh-48px))]",
7177
+ className: "w-fit min-w-[min(480px,calc(100dvw-48px))] min-h-[min(320px,85dvh)]",
7156
7178
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-auto flex-col", children: [
7157
7179
  /* @__PURE__ */ jsxRuntime.jsx(SOverlayHeader, { title: modalTitle, description, reserveCloseSpace: true }),
7158
7180
  /* @__PURE__ */ jsxRuntime.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 }),
@@ -8874,6 +8896,7 @@ var BACKGROUND_CLASS = {
8874
8896
  var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8875
8897
  background = "frame",
8876
8898
  scrollEndSpacing = true,
8899
+ contentHeight = "auto",
8877
8900
  header,
8878
8901
  className,
8879
8902
  style,
@@ -8888,13 +8911,14 @@ var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8888
8911
  onScroll?.(e);
8889
8912
  if (header != null && !header.fix) setScrolled(e.currentTarget.scrollTop > 0);
8890
8913
  };
8914
+ const fill = contentHeight === "fill";
8891
8915
  const main = /* @__PURE__ */ jsxRuntime.jsx(
8892
8916
  "main",
8893
8917
  {
8894
8918
  ref,
8895
8919
  className: cn(
8896
8920
  "box-border min-h-0 min-w-0 flex-1 overflow-auto p-[var(--cmp-pageBody-padding-default)]",
8897
- scrollEndSpacing && "pb-[var(--cmp-pageBody-padding-scrollEnd)]",
8921
+ !fill && scrollEndSpacing && "pb-[var(--cmp-pageBody-padding-scrollEnd)]",
8898
8922
  SCROLLBAR_CLASS,
8899
8923
  BACKGROUND_CLASS[background],
8900
8924
  className
@@ -8902,7 +8926,7 @@ var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8902
8926
  style: header == null ? { ...gridStyle, ...style } : style,
8903
8927
  onScroll: handleScroll,
8904
8928
  ...rest,
8905
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { minWidth: MIN_CONTENT_WIDTH }, children })
8929
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(fill && "h-full"), style: { minWidth: MIN_CONTENT_WIDTH }, children })
8906
8930
  }
8907
8931
  );
8908
8932
  if (header == null) return main;
@@ -8926,6 +8950,7 @@ var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8926
8950
 
8927
8951
  // src/components/SPage/page.config.ts
8928
8952
  var PAGE_BACKGROUNDS = ["frame", "neutral", "screen"];
8953
+ var PAGE_CONTENT_HEIGHTS = ["auto", "fill"];
8929
8954
 
8930
8955
  // src/components/STabs/tabs.config.ts
8931
8956
  var TAB_MAIN_SIZE = {
@@ -9743,6 +9768,7 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9743
9768
  readOnly = false,
9744
9769
  width,
9745
9770
  minWidth,
9771
+ maxWidth,
9746
9772
  multiline = false,
9747
9773
  borderless = false,
9748
9774
  children,
@@ -9778,7 +9804,10 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9778
9804
  const resolvedSuccess = status === "pass";
9779
9805
  const showError = resolvedError && errorMessage !== "";
9780
9806
  const interactive = !disabled && !readOnly;
9781
- const hug = cssLen(width) !== void 0;
9807
+ const isPercentWidth = typeof width === "string" && width.trim().endsWith("%");
9808
+ const hug = cssLen(width) !== void 0 && !isPercentWidth;
9809
+ const addonWidth = effectiveAddonLabel !== "" ? cssLen(labelWidth) : void 0;
9810
+ const resolvedMaxWidth = cssLen(maxWidth) === void 0 || effectiveAddonLabel === "" ? cssLen(maxWidth) : addonWidth ? `calc(${cssLen(maxWidth)} + ${addonWidth})` : void 0;
9782
9811
  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)";
9783
9812
  const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
9784
9813
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
@@ -9834,8 +9863,12 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9834
9863
  /* @__PURE__ */ jsxRuntime.jsxs(
9835
9864
  "div",
9836
9865
  {
9837
- className: cn("flex min-w-0 flex-col", !hug && "flex-1"),
9838
- style: { width: cssLen(width), minWidth: cssLen(minWidth) },
9866
+ className: cn("flex min-w-0 flex-col", !hug && !isPercentWidth && "flex-1"),
9867
+ style: {
9868
+ width: cssLen(width),
9869
+ minWidth: cssLen(minWidth),
9870
+ maxWidth: resolvedMaxWidth
9871
+ },
9839
9872
  children: [
9840
9873
  /* @__PURE__ */ jsxRuntime.jsxs(
9841
9874
  "div",
@@ -10590,6 +10623,7 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10590
10623
  height,
10591
10624
  stickyColumn,
10592
10625
  radius = "default",
10626
+ bordered = true,
10593
10627
  noDataLabel = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
10594
10628
  noDataSlot,
10595
10629
  isLoading = false,
@@ -10848,7 +10882,7 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10848
10882
  };
10849
10883
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[var(--cmp-table-radius)] rounded-t-none" : "rounded-[var(--cmp-table-radius)]";
10850
10884
  const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-body-paddingY-default)";
10851
- const cellPadX = "var(--cmp-table-body-paddingX-wide)";
10885
+ const cellPadXOf = (col) => col.contentType === "control" ? "var(--cmp-table-body-paddingX-default)" : "var(--cmp-table-body-paddingX-wide)";
10852
10886
  const rowH = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-body-default-height)";
10853
10887
  const totalColSpan = visibleCols.length + (selectable ? 1 : 0);
10854
10888
  const stickyInfo = (i) => {
@@ -10902,7 +10936,8 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10902
10936
  // 만으로는 스태킹 컨텍스트가 생기지 않아, 모달 안에 들어가면 30·31이 모달 Content
10903
10937
  // 레벨까지 올라가 같은 컨텍스트의 floating portal 밴드(z-2)를 덮는다.
10904
10938
  // 자손은 이미 overflow-hidden으로 클리핑되므로 테이블 내부 순서는 달라지지 않는다.
10905
- "bg-white relative isolate flex flex-col overflow-hidden border border-solid border-[color:var(--cmp-table-border-color)]",
10939
+ "bg-white relative isolate flex flex-col overflow-hidden",
10940
+ bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
10906
10941
  // height가 없으면 루트가 받은 높이를 끝까지 채운다. 호스트가 className으로
10907
10942
  // `flex-1 min-h-0` 등을 줘 루트 높이가 확정된 경우, 행이 적어도 테두리 박스가
10908
10943
  // 남은 영역을 그대로 차지한다. basis는 auto로 둬 높이가 확정되지 않은
@@ -11072,9 +11107,10 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11072
11107
  stickyShadowClass(i),
11073
11108
  typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
11074
11109
  );
11110
+ const tdPadX = cellPadXOf(col);
11075
11111
  const tdStyle = {
11076
- paddingLeft: cellPadX,
11077
- paddingRight: cellPadX,
11112
+ paddingLeft: tdPadX,
11113
+ paddingRight: tdPadX,
11078
11114
  paddingTop: cellPadY,
11079
11115
  paddingBottom: cellPadY,
11080
11116
  textAlign: col.align ?? "left",
@@ -11127,19 +11163,29 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11127
11163
  // SPagination은 전체 폭 기준 중앙, rows-per-page SSelect는 우측에 absolute 배치해 중앙 정렬을 유지한다.
11128
11164
  // 루트 높이가 확정된 경우(테이블이 남은 영역을 채우는 배치) 페이지네이션 바가
11129
11165
  // 눌리지 않도록 shrink-0으로 48px를 보장한다.
11130
- /* @__PURE__ */ jsxRuntime.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: [
11131
- /* @__PURE__ */ jsxRuntime.jsx(SPagination, { currentPage: displayPage, lastPage, onPageChange: changePage }),
11132
- useRowsPerPageSelect && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-[10px] top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsxRuntime.jsx(
11133
- SSelect2,
11134
- {
11135
- options: rowsPerPageOption,
11136
- value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
11137
- emitValue: true,
11138
- onValueChange: (v) => changeRowsPerPage(Number(v)),
11139
- width: 128
11140
- }
11141
- ) })
11142
- ] })
11166
+ /* @__PURE__ */ jsxRuntime.jsxs(
11167
+ "div",
11168
+ {
11169
+ className: cn(
11170
+ "relative flex h-[48px] shrink-0 items-center justify-center rounded-[var(--cmp-table-radius)] bg-[var(--color-grey-05)]",
11171
+ // 테두리를 끄면 겹칠 대상이 없어진다 — -mt-px 를 남기면 1px 어긋남만 생긴다.
11172
+ bordered && "-mt-px border border-solid border-[color:var(--cmp-table-border-color)]"
11173
+ ),
11174
+ children: [
11175
+ /* @__PURE__ */ jsxRuntime.jsx(SPagination, { currentPage: displayPage, lastPage, onPageChange: changePage }),
11176
+ useRowsPerPageSelect && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-[10px] top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsxRuntime.jsx(
11177
+ SSelect2,
11178
+ {
11179
+ options: rowsPerPageOption,
11180
+ value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
11181
+ emitValue: true,
11182
+ onValueChange: (v) => changeRowsPerPage(Number(v)),
11183
+ width: 128
11184
+ }
11185
+ ) })
11186
+ ]
11187
+ }
11188
+ )
11143
11189
  ]
11144
11190
  }
11145
11191
  );
@@ -12277,6 +12323,44 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
12277
12323
  }
12278
12324
  ) });
12279
12325
  }
12326
+ function SDatePickerYearListbox({
12327
+ value,
12328
+ anchorYear,
12329
+ selectable,
12330
+ onValueChange,
12331
+ className,
12332
+ style
12333
+ }) {
12334
+ const today = getTodayParts();
12335
+ const resolvedAnchorYear = anchorYear ?? today.year;
12336
+ const [year, setYear] = react.useState(() => parseYear(value) ?? resolvedAnchorYear);
12337
+ react.useEffect(() => {
12338
+ setYear(parseYear(value) ?? resolvedAnchorYear);
12339
+ }, [value, resolvedAnchorYear]);
12340
+ const commitYear = (nextYear) => {
12341
+ setYear(nextYear);
12342
+ onValueChange?.(String(nextYear));
12343
+ };
12344
+ return /* @__PURE__ */ jsxRuntime.jsx(
12345
+ "div",
12346
+ {
12347
+ className: cn(
12348
+ "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",
12349
+ className
12350
+ ),
12351
+ style,
12352
+ children: /* @__PURE__ */ jsxRuntime.jsx(
12353
+ YearWheel,
12354
+ {
12355
+ value: year,
12356
+ anchorYear: resolvedAnchorYear,
12357
+ selectable,
12358
+ onCommit: commitYear
12359
+ }
12360
+ )
12361
+ }
12362
+ );
12363
+ }
12280
12364
  function MonthWheel({ year, value, selectable, onCommit }) {
12281
12365
  const listRef = react.useRef(null);
12282
12366
  const initialValueRef = react.useRef(value);
@@ -12323,66 +12407,59 @@ function MonthWheel({ year, value, selectable, onCommit }) {
12323
12407
  }
12324
12408
  ) });
12325
12409
  }
12326
- function DatePickerListbox({
12327
- mode,
12410
+ function SDatePickerMonthListbox({
12328
12411
  value,
12412
+ anchorYear,
12329
12413
  selectable,
12330
12414
  onValueChange,
12331
- onClose
12415
+ onMonthSelect,
12416
+ className,
12417
+ style
12332
12418
  }) {
12333
12419
  const today = getTodayParts();
12334
- const [year, setYear] = react.useState(() => parseYear(value) ?? today.year);
12420
+ const resolvedAnchorYear = anchorYear ?? today.year;
12421
+ const [year, setYear] = react.useState(() => parseYear(value) ?? resolvedAnchorYear);
12335
12422
  const [month, setMonth] = react.useState(() => parseMonth(value) ?? today.month);
12423
+ react.useEffect(() => {
12424
+ setYear(parseYear(value) ?? resolvedAnchorYear);
12425
+ setMonth(parseMonth(value) ?? today.month);
12426
+ }, [value, resolvedAnchorYear, today.month]);
12336
12427
  const commitYear = (nextYear) => {
12337
12428
  setYear(nextYear);
12338
- if (mode === "year") {
12339
- onValueChange?.(String(nextYear));
12340
- onClose();
12341
- return;
12342
- }
12343
12429
  onValueChange?.(formatMonthValue(nextYear, month));
12344
12430
  };
12345
12431
  const commitMonth = (nextMonth) => {
12346
12432
  setMonth(nextMonth);
12347
- onValueChange?.(formatMonthValue(year, nextMonth));
12348
- onClose();
12433
+ const nextValue = formatMonthValue(year, nextMonth);
12434
+ onValueChange?.(nextValue);
12435
+ onMonthSelect?.(nextValue);
12349
12436
  };
12350
12437
  return /* @__PURE__ */ jsxRuntime.jsxs(
12351
12438
  "div",
12352
12439
  {
12353
12440
  className: cn(
12354
- "inline-flex h-[240px] overflow-hidden rounded-[var(--cmp-datepicker-mode-radius)] bg-[var(--cmp-datepicker-mode-bg)] shadow-normal-sm",
12355
- mode === "month" ? "gap-[var(--cmp-datepicker-mode-yearMonth-gap)]" : "w-[var(--cmp-datepicker-mode-year-width)]"
12441
+ "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",
12442
+ className
12356
12443
  ),
12444
+ style,
12357
12445
  children: [
12446
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsxRuntime.jsx(
12447
+ YearWheel,
12448
+ {
12449
+ value: year,
12450
+ anchorYear: resolvedAnchorYear,
12451
+ selectable,
12452
+ onCommit: commitYear
12453
+ }
12454
+ ) }),
12358
12455
  /* @__PURE__ */ jsxRuntime.jsx(
12359
12456
  "div",
12360
12457
  {
12361
- className: cn(
12362
- "min-w-0",
12363
- mode === "month" ? "w-[var(--cmp-datepicker-mode-yearMonth-column-width)]" : "w-full"
12364
- ),
12365
- children: /* @__PURE__ */ jsxRuntime.jsx(
12366
- YearWheel,
12367
- {
12368
- value: year,
12369
- anchorYear: today.year,
12370
- selectable,
12371
- onCommit: commitYear
12372
- }
12373
- )
12458
+ "aria-hidden": true,
12459
+ className: "w-[var(--border-width-1)] self-stretch bg-[var(--sys-color-divider-default)]"
12374
12460
  }
12375
12461
  ),
12376
- mode === "month" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
12377
- /* @__PURE__ */ jsxRuntime.jsx(
12378
- "div",
12379
- {
12380
- "aria-hidden": true,
12381
- className: "w-[var(--border-width-1)] self-stretch bg-[var(--sys-color-divider-default)]"
12382
- }
12383
- ),
12384
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsxRuntime.jsx(MonthWheel, { year, value: month, selectable, onCommit: commitMonth }) })
12385
- ] })
12462
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsxRuntime.jsx(MonthWheel, { year, value: month, selectable, onCommit: commitMonth }) })
12386
12463
  ]
12387
12464
  }
12388
12465
  );
@@ -12437,6 +12514,9 @@ function SDatePicker({
12437
12514
  icon: 20,
12438
12515
  minW: 104,
12439
12516
  fieldMinW: 168,
12517
+ // 상한 등급 lg — 값 길이가 YYYY-MM-DD 로 정해져 있어, width="100%" 를 받아도
12518
+ // 행 전체로 늘어나지 않게 막는다 (SKeyValueTable 이 모든 컨트롤에 100% 를 넘긴다)
12519
+ fieldMaxW: 240,
12440
12520
  fontSize: 14,
12441
12521
  lineHeight: 24,
12442
12522
  radius: 6
@@ -12446,6 +12526,8 @@ function SDatePicker({
12446
12526
  icon: 16,
12447
12527
  minW: 80,
12448
12528
  fieldMinW: 128,
12529
+ // 상한 등급 md
12530
+ fieldMaxW: 160,
12449
12531
  fontSize: 12,
12450
12532
  lineHeight: 20,
12451
12533
  radius: 4
@@ -12478,6 +12560,7 @@ function SDatePicker({
12478
12560
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12479
12561
  width,
12480
12562
  minWidth: dims.fieldMinW,
12563
+ maxWidth: dims.fieldMaxW,
12481
12564
  disabled,
12482
12565
  className,
12483
12566
  style,
@@ -12522,7 +12605,7 @@ function SDatePicker({
12522
12605
  RPopover9__namespace.Content,
12523
12606
  {
12524
12607
  side: "bottom",
12525
- align: "start",
12608
+ align: mode === "date" ? "start" : "center",
12526
12609
  sideOffset: 4,
12527
12610
  className: cn(
12528
12611
  "z-50 max-w-[var(--radix-popover-content-available-width)]",
@@ -12537,14 +12620,23 @@ function SDatePicker({
12537
12620
  onValueChange: select,
12538
12621
  onViewChange
12539
12622
  }
12623
+ ) : mode === "year" ? /* @__PURE__ */ jsxRuntime.jsx(
12624
+ SDatePickerYearListbox,
12625
+ {
12626
+ value,
12627
+ selectable,
12628
+ onValueChange: (nextValue) => {
12629
+ selectListboxValue(nextValue);
12630
+ setOpen(false);
12631
+ }
12632
+ }
12540
12633
  ) : /* @__PURE__ */ jsxRuntime.jsx(
12541
- DatePickerListbox,
12634
+ SDatePickerMonthListbox,
12542
12635
  {
12543
- mode,
12544
12636
  value,
12545
12637
  selectable,
12546
12638
  onValueChange: selectListboxValue,
12547
- onClose: () => setOpen(false)
12639
+ onMonthSelect: () => setOpen(false)
12548
12640
  }
12549
12641
  )
12550
12642
  }
@@ -12927,6 +13019,9 @@ function SDateRangePicker({
12927
13019
  icon: 20,
12928
13020
  minW: 104,
12929
13021
  fieldMinW: 260,
13022
+ // 상한 등급 xl — 값이 `YYYY-MM-DD ~ YYYY-MM-DD` 라 단일 날짜의 두 배가 넘는다.
13023
+ // 최소너비(260)가 이미 lg(240)를 넘으므로 상한도 한 등급 위여야 한다
13024
+ fieldMaxW: 480,
12930
13025
  fontSize: 14,
12931
13026
  lineHeight: 24,
12932
13027
  radius: 6
@@ -12936,6 +13031,8 @@ function SDateRangePicker({
12936
13031
  icon: 16,
12937
13032
  minW: 80,
12938
13033
  fieldMinW: 216,
13034
+ // 상한 등급 lg — 최소너비 216 이 md(160)를 넘어 md 로는 담기지 않는다
13035
+ fieldMaxW: 240,
12939
13036
  fontSize: 12,
12940
13037
  lineHeight: 20,
12941
13038
  radius: 4
@@ -12966,6 +13063,7 @@ function SDateRangePicker({
12966
13063
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12967
13064
  width,
12968
13065
  minWidth: dims.fieldMinW,
13066
+ maxWidth: dims.fieldMaxW,
12969
13067
  disabled,
12970
13068
  className,
12971
13069
  style,
@@ -13430,6 +13528,7 @@ function SKeyValueTable({
13430
13528
  values = {},
13431
13529
  search = false,
13432
13530
  radius = "default",
13531
+ bordered = true,
13433
13532
  onChange,
13434
13533
  onSearch,
13435
13534
  className,
@@ -13494,7 +13593,8 @@ function SKeyValueTable({
13494
13593
  });
13495
13594
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[8px] rounded-t-none" : "rounded-[8px]";
13496
13595
  const frameClass = cn(
13497
- "overflow-hidden border border-solid border-[color:var(--cmp-table-border-color)]",
13596
+ "overflow-hidden",
13597
+ bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
13498
13598
  radiusClass
13499
13599
  );
13500
13600
  const table = /* @__PURE__ */ jsxRuntime.jsxs(
@@ -14079,7 +14179,16 @@ var CHIPFILTER = {
14079
14179
  itemTextDefault: "var(--cmp-chipFilter-item-text-default)",
14080
14180
  itemTextSelected: "var(--cmp-chipFilter-item-text-selected)"
14081
14181
  };
14182
+ var DEFAULT_PERIOD_PRESETS = [
14183
+ { value: "day", label: "\uC77C\uBCC4" },
14184
+ { value: "month", label: "\uC6D4\uBCC4" },
14185
+ { value: "quarter", label: "\uBD84\uAE30\uBCC4" },
14186
+ { value: "half", label: "\uBC18\uAE30\uBCC4" },
14187
+ { value: "year", label: "\uC5F0\uB3C4\uBCC4" },
14188
+ { value: "custom", label: "\uC0AC\uC6A9\uC790 \uC9C0\uC815", custom: true }
14189
+ ];
14082
14190
  var isKeywordValue = (value) => typeof value === "object" && value != null && !Array.isArray(value) && "keywords" in value;
14191
+ var isPeriodValue = (value) => typeof value === "object" && value != null && !Array.isArray(value) && "unit" in value;
14083
14192
  var keywordEntries = (value) => {
14084
14193
  if (isKeywordValue(value)) return value.keywords;
14085
14194
  if (Array.isArray(value)) {
@@ -14103,12 +14212,33 @@ var todayIso = () => {
14103
14212
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
14104
14213
  };
14105
14214
  var isInlineDateField = (field) => field.type === "date" && field.radioButton === true && (field.presets?.length ?? 0) > 0;
14215
+ var getFieldPresets = (field) => {
14216
+ if (field.type === "period") return field.presets ?? DEFAULT_PERIOD_PRESETS;
14217
+ return field.presets ?? [];
14218
+ };
14219
+ var getDefaultPeriodInputValue = (unit) => {
14220
+ const today = todayIso();
14221
+ if (unit === "day") return today;
14222
+ if (unit === "month") return today.slice(0, 7);
14223
+ if (unit === "quarter") return 1;
14224
+ if (unit === "half") return "first";
14225
+ if (unit === "year") return today.slice(0, 4);
14226
+ return [today, today];
14227
+ };
14228
+ var createPeriodValue = (unit) => ({
14229
+ unit,
14230
+ value: getDefaultPeriodInputValue(unit)
14231
+ });
14106
14232
  var resolveFieldDefault = (field) => {
14107
14233
  if (field.defaultValue !== void 0) return field.defaultValue;
14108
14234
  if (isInlineDateField(field)) return todayIso();
14109
14235
  if (!field.required) return void 0;
14110
14236
  if (field.type === "single") return field.options?.[0]?.value;
14111
14237
  if (field.type === "date") return todayIso();
14238
+ if (field.type === "period") {
14239
+ const unit = getFieldPresets(field).find((preset) => !preset.custom)?.value;
14240
+ return unit ? createPeriodValue(unit) : void 0;
14241
+ }
14112
14242
  return void 0;
14113
14243
  };
14114
14244
  var valuesEqual = (a, b) => {
@@ -14132,6 +14262,8 @@ var isAtDefaultValue = (field, value) => {
14132
14262
  return def !== void 0 && valuesEqual(value, def);
14133
14263
  };
14134
14264
  var isInlineField = (field) => isInlineDateField(field) || field.type === "custom";
14265
+ var isFixedField = (field) => field.fix === true || field.required === true;
14266
+ var isAddableField = (field) => !isFixedField(field);
14135
14267
  var isGroupedFields = (fields) => fields.length > 0 && "fields" in fields[0];
14136
14268
  var isGroupSatisfied = (group, values) => {
14137
14269
  if (!group.rule) return true;
@@ -14158,9 +14290,10 @@ var defaultGroupTooltipMessage = (rule, fields) => {
14158
14290
  };
14159
14291
  var matchPresetId = (presets, value) => {
14160
14292
  if (isEmptyValue(value)) return null;
14293
+ if (isPeriodValue(value)) return value.unit;
14161
14294
  for (const preset of presets) {
14162
- if (preset.custom || !preset.resolve) continue;
14163
- const resolved = preset.resolve();
14295
+ if (preset.custom) continue;
14296
+ const resolved = preset.resolve ? preset.resolve() : preset.value;
14164
14297
  if (Array.isArray(resolved) && Array.isArray(value)) {
14165
14298
  if (resolved[0] === value[0] && resolved[1] === value[1]) return preset.value;
14166
14299
  } else if (!Array.isArray(resolved) && !Array.isArray(value) && resolved === value) {
@@ -14176,18 +14309,36 @@ var formatValueList = (labels) => {
14176
14309
  };
14177
14310
  var displayValue = (field, value) => {
14178
14311
  if (isEmptyValue(value)) return "";
14179
- if (field.type === "multi")
14180
- return formatValueList(toArrayValue(value).map((v) => optionLabel(field, v)));
14312
+ if (field.type === "multi") {
14313
+ const values = toArrayValue(value);
14314
+ const selectableValues = (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value);
14315
+ if (selectableValues.length > 0 && selectableValues.every((selectableValue) => values.includes(selectableValue))) {
14316
+ return "\uC804\uCCB4";
14317
+ }
14318
+ return formatValueList(values.map((v) => optionLabel(field, v)));
14319
+ }
14181
14320
  if (field.type === "single") return optionLabel(field, value);
14182
14321
  if (field.type === "date")
14183
14322
  return Array.isArray(value) ? `${value[0]} ~ ${value[1]}` : String(value);
14323
+ if (field.type === "period") {
14324
+ if (!isPeriodValue(value)) {
14325
+ const preset2 = getFieldPresets(field).find((p) => p.value === value);
14326
+ return preset2?.label ?? String(value);
14327
+ }
14328
+ const preset = getFieldPresets(field).find((p) => p.value === value.unit);
14329
+ const label = preset?.label ?? value.unit;
14330
+ if (value.unit === "quarter") return `${label}(${value.value ?? 1}\uBD84\uAE30)`;
14331
+ if (value.unit === "half") return `${label}(${value.value === "second" ? "\uD558\uBC18\uAE30" : "\uC0C1\uBC18\uAE30"})`;
14332
+ if (Array.isArray(value.value)) return `${value.value[0]} ~ ${value.value[1]}`;
14333
+ return value.value != null && value.value !== "" ? String(value.value) : label;
14334
+ }
14184
14335
  if (field.type === "keyword") return formatValueList(keywordEntries(value));
14185
14336
  return String(value);
14186
14337
  };
14187
14338
  var chipDisplayValue = (field, value) => {
14188
14339
  const text = displayValue(field, value);
14189
14340
  if (field.type !== "date" || isEmptyValue(value)) return text;
14190
- const presets = field.presets ?? [];
14341
+ const presets = getFieldPresets(field);
14191
14342
  if (presets.length === 0) return text;
14192
14343
  const preset = presets.find((p) => p.value === matchPresetId(presets, value));
14193
14344
  return preset && !preset.custom ? `${preset.label}(${text})` : text;
@@ -14204,7 +14355,6 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14204
14355
  onAddFilter,
14205
14356
  label = "\uAC80\uC0C9 \uD544\uD130",
14206
14357
  showLabel = false,
14207
- showAddButton = true,
14208
14358
  showReset = true,
14209
14359
  disabled = false,
14210
14360
  className,
@@ -14219,7 +14369,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14219
14369
  const values = normalizeValues(value);
14220
14370
  const portalContainer = usePortalContainer();
14221
14371
  const [internalActiveKeys, setInternalActiveKeys] = react.useState(
14222
- () => fields.filter((field) => field.defaultActive !== false).map((field) => field.key)
14372
+ () => fields.filter((field) => isFixedField(field) || field.defaultActive !== false).map((field) => field.key)
14223
14373
  );
14224
14374
  const [openKey, setOpenKey] = react.useState(null);
14225
14375
  const [addOpen, setAddOpen] = react.useState(false);
@@ -14261,7 +14411,9 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14261
14411
  setInternalActiveKeys((current) => {
14262
14412
  const known = new Set(fields.map((field) => field.key));
14263
14413
  const kept = current.filter((key) => known.has(key));
14264
- const additions = fields.filter((field) => field.defaultActive !== false && !kept.includes(field.key)).map((field) => field.key);
14414
+ const additions = fields.filter(
14415
+ (field) => (isFixedField(field) || field.defaultActive !== false) && !kept.includes(field.key)
14416
+ ).map((field) => field.key);
14265
14417
  return [...kept, ...additions];
14266
14418
  });
14267
14419
  }, [activeKeys, fields]);
@@ -14282,25 +14434,31 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14282
14434
  });
14283
14435
  if (changed) onValueChange?.({ ...values, ...patch });
14284
14436
  }, [fields, values, onValueChange]);
14285
- const resolvedActiveKeys = activeKeys ?? internalActiveKeys;
14437
+ const fixedKeys = fields.filter(isFixedField).map((field) => field.key);
14438
+ const resolvedActiveKeys = Array.from(
14439
+ /* @__PURE__ */ new Set([...activeKeys ?? internalActiveKeys, ...fixedKeys])
14440
+ );
14286
14441
  const activeFields = fields.filter((field) => resolvedActiveKeys.includes(field.key));
14287
- const inactiveFields = fields.filter((field) => !resolvedActiveKeys.includes(field.key));
14442
+ const inactiveFields = fields.filter(
14443
+ (field) => isAddableField(field) && !resolvedActiveKeys.includes(field.key)
14444
+ );
14288
14445
  const groupIndexByKey = /* @__PURE__ */ new Map();
14289
14446
  groups.forEach((group, groupIndex) => {
14290
14447
  group.fields.forEach((field) => groupIndexByKey.set(field.key, groupIndex));
14291
14448
  });
14292
14449
  const hasValue = fields.some((field) => !isEmptyValue(values[field.key]));
14293
- const hasRightActions = showAddButton || showReset;
14450
+ const hasAddButton = fields.some(isAddableField);
14451
+ const hasRightActions = hasAddButton || showReset;
14294
14452
  const currentOpenField = fields.find((field) => field.key === openKey);
14295
14453
  const editorPopoverStyle = react.useMemo(() => {
14296
14454
  if (currentOpenField?.type === "keyword") {
14297
14455
  return { width: CHIPFILTER_LISTBOX_WIDTH };
14298
14456
  }
14299
- if (currentOpenField?.type === "date" && (currentOpenField.presets?.length ?? 0) > 0) {
14457
+ if ((currentOpenField?.type === "date" || currentOpenField?.type === "period") && getFieldPresets(currentOpenField).length > 0) {
14300
14458
  return { width: CHIPFILTER_DATE_PRESET_LISTBOX_WIDTH };
14301
14459
  }
14302
14460
  return { width: CHIPFILTER_LISTBOX_WIDTH };
14303
- }, [currentOpenField?.type, currentOpenField?.presets?.length]);
14461
+ }, [currentOpenField]);
14304
14462
  const setActive = (next) => {
14305
14463
  if (!activeKeys) setInternalActiveKeys(next);
14306
14464
  onActiveKeysChange?.(next);
@@ -14319,6 +14477,12 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14319
14477
  const def = field ? resolveFieldDefault(field) : void 0;
14320
14478
  runSearch(emitValue(key, def !== void 0 ? def : null));
14321
14479
  };
14480
+ const removeField = (key) => {
14481
+ const nextValues = emitValue(key, null);
14482
+ setActive(resolvedActiveKeys.filter((activeKey) => activeKey !== key));
14483
+ if (openKey === key) setOpenKey(null);
14484
+ runSearch(nextValues);
14485
+ };
14322
14486
  const reset = react.useCallback(() => {
14323
14487
  const next = fields.reduce((acc, field) => {
14324
14488
  const def = resolveFieldDefault(field);
@@ -14409,7 +14573,15 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14409
14573
  flushRun();
14410
14574
  runGroupIndex = groupIndex;
14411
14575
  }
14412
- const showClear = !isAtDefaultValue(field, values[field.key]);
14576
+ const addable = isAddableField(field);
14577
+ const showClear = addable || !isAtDefaultValue(field, values[field.key]);
14578
+ const handleClear = () => {
14579
+ if (addable) {
14580
+ removeField(field.key);
14581
+ return;
14582
+ }
14583
+ clearValue(field.key);
14584
+ };
14413
14585
  const node = isInlineDateField(field) ? /* @__PURE__ */ jsxRuntime.jsx(
14414
14586
  InlineDatePresetFilter,
14415
14587
  {
@@ -14419,15 +14591,27 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14419
14591
  warning,
14420
14592
  showClear,
14421
14593
  onValueChange: (next) => emitValueAndSearch(field.key, next),
14422
- onClear: () => clearValue(field.key)
14594
+ onClear: handleClear
14423
14595
  },
14424
14596
  field.key
14425
- ) : field.type === "custom" ? /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: field.render?.({
14426
- value: values[field.key],
14427
- disabled: disabled || field.disabled,
14428
- warning,
14429
- onValueChange: (next) => emitValueAndSearch(field.key, next)
14430
- }) }, field.key) : /* @__PURE__ */ jsxRuntime.jsx(
14597
+ ) : field.type === "custom" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex shrink-0 items-center gap-[4px]", children: [
14598
+ field.render?.({
14599
+ value: values[field.key],
14600
+ disabled: disabled || field.disabled,
14601
+ warning,
14602
+ onValueChange: (next) => emitValueAndSearch(field.key, next)
14603
+ }),
14604
+ addable && !disabled && !field.disabled && showClear && /* @__PURE__ */ jsxRuntime.jsx(
14605
+ "button",
14606
+ {
14607
+ type: "button",
14608
+ "aria-label": `${field.label} \uD544\uD130 \uC81C\uAC70`,
14609
+ className: "inline-flex size-[16px] shrink-0 items-center justify-center rounded-[var(--cmp-ghostButton-radius-sm)] border-none bg-transparent p-0",
14610
+ onClick: handleClear,
14611
+ children: /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "close", size: 12, color: "var(--cmp-ghostButton-default-content)" })
14612
+ }
14613
+ )
14614
+ ] }, field.key) : /* @__PURE__ */ jsxRuntime.jsx(
14431
14615
  FilterChip,
14432
14616
  {
14433
14617
  field,
@@ -14437,9 +14621,10 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14437
14621
  disabled: disabled || field.disabled,
14438
14622
  warning,
14439
14623
  showClear,
14624
+ showClearWhenEmpty: addable,
14440
14625
  refCallback: (node2) => triggerRefs.current.set(field.key, node2),
14441
14626
  onOpen: () => setOpenKey(openKey === field.key ? null : field.key),
14442
- onClear: () => clearValue(field.key)
14627
+ onClear: handleClear
14443
14628
  },
14444
14629
  field.key
14445
14630
  );
@@ -14460,7 +14645,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14460
14645
  })(),
14461
14646
  hasRightActions && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
14462
14647
  /* @__PURE__ */ jsxRuntime.jsx(Divider, { inset: true }),
14463
- showAddButton && /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open: addOpen, onOpenChange: setAddOpen, children: [
14648
+ hasAddButton && /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open: addOpen, onOpenChange: setAddOpen, children: [
14464
14649
  /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
14465
14650
  "button",
14466
14651
  {
@@ -14507,7 +14692,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14507
14692
  "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",
14508
14693
  // 부모 flex gap(6px)은 칩 사이 간격 기준이라, 필터 추가 버튼과의 12px 간격을 맞추려면
14509
14694
  // 부족한 만큼(12px - gap)을 추가로 띄운다. 필터 추가가 없을 땐 divider와 이미 12px이라 불필요.
14510
- showAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
14695
+ hasAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
14511
14696
  ),
14512
14697
  style: {
14513
14698
  color: disabled || !hasValue ? "var(--cmp-textLink-content-disabled)" : "var(--cmp-textLink-content-default)"
@@ -14591,6 +14776,7 @@ function FilterChip({
14591
14776
  disabled,
14592
14777
  warning,
14593
14778
  showClear,
14779
+ showClearWhenEmpty,
14594
14780
  refCallback,
14595
14781
  onOpen,
14596
14782
  onClear
@@ -14660,7 +14846,7 @@ function FilterChip({
14660
14846
  }
14661
14847
  )
14662
14848
  ] }),
14663
- applied && !disabled && showClear && /* @__PURE__ */ jsxRuntime.jsx(
14849
+ !disabled && showClear && (applied || showClearWhenEmpty) && /* @__PURE__ */ jsxRuntime.jsx(
14664
14850
  "span",
14665
14851
  {
14666
14852
  role: "button",
@@ -14692,7 +14878,7 @@ function EditorPanel({
14692
14878
  onValueChange,
14693
14879
  onClose
14694
14880
  }) {
14695
- const wrapsInPaddedTop = field.type === "date";
14881
+ const wrapsInPaddedTop = field.type === "date" || field.type === "period";
14696
14882
  const isMulti = field.type === "multi";
14697
14883
  const selectableValues = isMulti ? (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value) : [];
14698
14884
  const selectedValues = isMulti ? toArrayValue(value) : [];
@@ -14771,6 +14957,17 @@ function FilterEditor({
14771
14957
  disabled,
14772
14958
  onValueChange
14773
14959
  }) {
14960
+ if (field.type === "period") {
14961
+ return /* @__PURE__ */ jsxRuntime.jsx(
14962
+ PeriodPresetEditor,
14963
+ {
14964
+ field,
14965
+ value,
14966
+ disabled,
14967
+ onValueChange
14968
+ }
14969
+ );
14970
+ }
14774
14971
  if (field.type === "date") {
14775
14972
  return /* @__PURE__ */ jsxRuntime.jsx(
14776
14973
  DatePresetEditor,
@@ -14810,7 +15007,7 @@ function DatePresetEditor({
14810
15007
  disabled,
14811
15008
  onValueChange
14812
15009
  }) {
14813
- const presets = field.presets ?? [];
15010
+ const presets = getFieldPresets(field);
14814
15011
  const [selectedId, setSelectedId] = react.useState(() => matchPresetId(presets, value));
14815
15012
  const syncedInitialValueRef = react.useRef(!isEmptyValue(value));
14816
15013
  react.useEffect(() => {
@@ -14821,9 +15018,9 @@ function DatePresetEditor({
14821
15018
  const selectedPreset = presets.find((preset) => preset.value === selectedId);
14822
15019
  const selectPreset = (preset) => {
14823
15020
  setSelectedId(preset.value);
14824
- if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : null);
15021
+ if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : preset.value);
14825
15022
  };
14826
- if (presets.length === 0) {
15023
+ if (field.type === "date" && presets.length === 0) {
14827
15024
  return /* @__PURE__ */ jsxRuntime.jsx(
14828
15025
  SDatePicker,
14829
15026
  {
@@ -14859,6 +15056,130 @@ function DatePresetEditor({
14859
15056
  )
14860
15057
  ] });
14861
15058
  }
15059
+ function PeriodPresetEditor({
15060
+ field,
15061
+ value,
15062
+ disabled,
15063
+ onValueChange
15064
+ }) {
15065
+ const presets = getFieldPresets(field);
15066
+ const firstUnit = presets.find((preset) => !preset.custom)?.value ?? "day";
15067
+ const [selectedId, setSelectedId] = react.useState(() => matchPresetId(presets, value) ?? firstUnit);
15068
+ const periodValue = isPeriodValue(value) ? value : createPeriodValue(selectedId ?? firstUnit);
15069
+ const selectedUnit = selectedId ?? periodValue.unit;
15070
+ react.useEffect(() => {
15071
+ if (isEmptyValue(value)) return;
15072
+ setSelectedId(matchPresetId(presets, value) ?? firstUnit);
15073
+ }, [firstUnit, presets, value]);
15074
+ const selectPreset = (preset) => {
15075
+ const nextUnit = preset.value;
15076
+ setSelectedId(nextUnit);
15077
+ onValueChange(createPeriodValue(nextUnit));
15078
+ };
15079
+ const updateInputValue = (nextValue) => {
15080
+ onValueChange({ unit: selectedUnit, value: nextValue });
15081
+ };
15082
+ const renderInput = () => {
15083
+ if (selectedUnit === "day") {
15084
+ return /* @__PURE__ */ jsxRuntime.jsx(
15085
+ SDatePicker,
15086
+ {
15087
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15088
+ onValueChange: updateInputValue,
15089
+ selectable: field.selectable,
15090
+ disabled,
15091
+ width: "100%"
15092
+ }
15093
+ );
15094
+ }
15095
+ if (selectedUnit === "month") {
15096
+ return /* @__PURE__ */ jsxRuntime.jsx(
15097
+ SDatePicker,
15098
+ {
15099
+ mode: "month",
15100
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15101
+ onValueChange: updateInputValue,
15102
+ selectable: field.selectable,
15103
+ disabled,
15104
+ width: "100%"
15105
+ }
15106
+ );
15107
+ }
15108
+ if (selectedUnit === "quarter") {
15109
+ return /* @__PURE__ */ jsxRuntime.jsx(
15110
+ SSelect2,
15111
+ {
15112
+ value: typeof periodValue.value === "number" ? periodValue.value : 1,
15113
+ onValueChange: updateInputValue,
15114
+ emitValue: true,
15115
+ disabled,
15116
+ width: "100%",
15117
+ options: [
15118
+ { value: 1, label: "1\uBD84\uAE30" },
15119
+ { value: 2, label: "2\uBD84\uAE30" },
15120
+ { value: 3, label: "3\uBD84\uAE30" },
15121
+ { value: 4, label: "4\uBD84\uAE30" }
15122
+ ]
15123
+ }
15124
+ );
15125
+ }
15126
+ if (selectedUnit === "half") {
15127
+ return /* @__PURE__ */ jsxRuntime.jsx(
15128
+ SSelect2,
15129
+ {
15130
+ value: typeof periodValue.value === "string" ? periodValue.value : "first",
15131
+ onValueChange: updateInputValue,
15132
+ emitValue: true,
15133
+ disabled,
15134
+ width: "100%",
15135
+ options: [
15136
+ { value: "first", label: "\uC0C1\uBC18\uAE30" },
15137
+ { value: "second", label: "\uD558\uBC18\uAE30" }
15138
+ ]
15139
+ }
15140
+ );
15141
+ }
15142
+ if (selectedUnit === "year") {
15143
+ return /* @__PURE__ */ jsxRuntime.jsx(
15144
+ SDatePicker,
15145
+ {
15146
+ mode: "year",
15147
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15148
+ onValueChange: updateInputValue,
15149
+ selectable: field.selectable,
15150
+ disabled,
15151
+ width: "100%"
15152
+ }
15153
+ );
15154
+ }
15155
+ return /* @__PURE__ */ jsxRuntime.jsx(
15156
+ SDateRangePicker,
15157
+ {
15158
+ value: Array.isArray(periodValue.value) && periodValue.value.length === 2 ? periodValue.value : null,
15159
+ onValueChange: updateInputValue,
15160
+ selectable: field.selectable,
15161
+ maxRange: field.maxRange,
15162
+ disabled,
15163
+ width: "100%"
15164
+ }
15165
+ );
15166
+ };
15167
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-[var(--cmp-chipFilter-listbox-top-gap)]", children: [
15168
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-[8px]", children: presets.map((preset) => /* @__PURE__ */ jsxRuntime.jsx(
15169
+ SRadio,
15170
+ {
15171
+ val: preset.value,
15172
+ value: selectedId,
15173
+ label: preset.label,
15174
+ disabled,
15175
+ onValueChange: () => selectPreset(preset)
15176
+ },
15177
+ preset.value
15178
+ )) }),
15179
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, className: "h-px w-full", style: { background: CHIPFILTER.listboxBorder } }),
15180
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full [&>*]:w-full", children: renderInput() })
15181
+ ] });
15182
+ }
14862
15183
  function InlineDatePresetFilter({
14863
15184
  field,
14864
15185
  value,
@@ -15863,7 +16184,9 @@ var TIMEPICKER_SIZE_CONFIG = {
15863
16184
  fontSize: 12,
15864
16185
  lineHeight: 20,
15865
16186
  minWidth: 80,
15866
- fieldMinWidth: 128
16187
+ fieldMinWidth: 128,
16188
+ // 상한 등급 md
16189
+ fieldMaxWidth: 160
15867
16190
  },
15868
16191
  md: {
15869
16192
  height: V3("md-height"),
@@ -15875,7 +16198,9 @@ var TIMEPICKER_SIZE_CONFIG = {
15875
16198
  fontSize: 14,
15876
16199
  lineHeight: 24,
15877
16200
  minWidth: 96,
15878
- fieldMinWidth: 160
16201
+ fieldMinWidth: 160,
16202
+ // 상한 등급 lg
16203
+ fieldMaxWidth: 240
15879
16204
  }
15880
16205
  };
15881
16206
  function clampTimePart(value, min, max) {
@@ -16199,6 +16524,7 @@ function STimePicker({
16199
16524
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
16200
16525
  width,
16201
16526
  minWidth: dims.fieldMinWidth,
16527
+ maxWidth: dims.fieldMaxWidth,
16202
16528
  disabled,
16203
16529
  className,
16204
16530
  style,
@@ -16315,6 +16641,10 @@ var MIDDAY_FIELD_MIN_WIDTH = {
16315
16641
  sm: 178,
16316
16642
  md: 216
16317
16643
  };
16644
+ var MIDDAY_FIELD_MAX_WIDTH = {
16645
+ sm: 240,
16646
+ md: 240
16647
+ };
16318
16648
  function RangeTimeSelector({
16319
16649
  value,
16320
16650
  useMeridiem,
@@ -16661,6 +16991,7 @@ function STimeRangePicker({
16661
16991
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
16662
16992
  width,
16663
16993
  minWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MIN_WIDTH[size] : dims.fieldMinWidth,
16994
+ maxWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MAX_WIDTH[size] : dims.fieldMaxWidth,
16664
16995
  disabled,
16665
16996
  className,
16666
16997
  style,
@@ -16769,6 +17100,7 @@ exports.GNB_RAIL_WIDTH = GNB_RAIL_WIDTH;
16769
17100
  exports.ICONS = ICONS;
16770
17101
  exports.IMAGE_FITS = IMAGE_FITS;
16771
17102
  exports.PAGE_BACKGROUNDS = PAGE_BACKGROUNDS;
17103
+ exports.PAGE_CONTENT_HEIGHTS = PAGE_CONTENT_HEIGHTS;
16772
17104
  exports.RangeCalendar = RangeCalendar;
16773
17105
  exports.SActionModal = SActionModal;
16774
17106
  exports.SBadge = SBadge;
@@ -16785,6 +17117,8 @@ exports.SChipInput = SChipInput;
16785
17117
  exports.SCircleProgress = SCircleProgress;
16786
17118
  exports.SConfirmModal = SConfirmModal;
16787
17119
  exports.SDatePicker = SDatePicker;
17120
+ exports.SDatePickerMonthListbox = SDatePickerMonthListbox;
17121
+ exports.SDatePickerYearListbox = SDatePickerYearListbox;
16788
17122
  exports.SDateRangePicker = SDateRangePicker;
16789
17123
  exports.SDivider = SDivider;
16790
17124
  exports.SDraggableGroup = SDraggableGroup;