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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4729,7 +4729,7 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4729
4729
  "aria-disabled": node.disabled || void 0,
4730
4730
  onClick: handleRowClick,
4731
4731
  className: cn(
4732
- "group/tree-item flex min-h-[32px] w-full items-center gap-(--cmp-tree-item-rowGap) bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left",
4732
+ "group/tree-item box-border flex h-[32px] w-full items-center gap-(--cmp-tree-item-rowGap) overflow-hidden bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left",
4733
4733
  border && "border-b border-(--cmp-tree-item-border-default)",
4734
4734
  !node.disabled && "hover:bg-(--cmp-tree-item-bg-hover)",
4735
4735
  node.disabled && "cursor-not-allowed opacity-60",
@@ -4748,7 +4748,7 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4748
4748
  ancestorLast
4749
4749
  }
4750
4750
  ),
4751
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap) py-(--cmp-tree-item-paddingY)", children: [
4751
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "box-border flex h-full min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap) py-(--cmp-tree-item-paddingY)", children: [
4752
4752
  hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
4753
4753
  SGhostButton,
4754
4754
  {
@@ -4784,25 +4784,17 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4784
4784
  /* @__PURE__ */ jsxRuntime.jsx(
4785
4785
  "span",
4786
4786
  {
4787
+ onClick: (event) => {
4788
+ if (!selectable) return;
4789
+ event.stopPropagation();
4790
+ if (!node.disabled && selectable) onTreeSelect?.(node, event);
4791
+ },
4787
4792
  className: cn(
4788
- "min-w-0 flex-1 text-[14px] leading-[24px]",
4789
- selected || indeterminate ? "font-bold text-(--cmp-tree-item-text-selected)" : "font-medium text-(--cmp-tree-item-text-default)"
4793
+ "block min-w-0 flex-1 truncate text-[14px] leading-[24px]",
4794
+ selected || indeterminate ? "font-bold text-(--cmp-tree-item-text-selected)" : "font-medium text-(--cmp-tree-item-text-default)",
4795
+ node.disabled ? "cursor-not-allowed" : selectable ? "cursor-pointer" : void 0
4790
4796
  ),
4791
- children: /* @__PURE__ */ jsxRuntime.jsx(
4792
- "span",
4793
- {
4794
- onClick: (event) => {
4795
- if (!selectable) return;
4796
- event.stopPropagation();
4797
- if (!node.disabled && selectable) onTreeSelect?.(node, event);
4798
- },
4799
- className: cn(
4800
- "inline-block max-w-full truncate",
4801
- node.disabled ? "cursor-not-allowed" : selectable ? "cursor-pointer" : void 0
4802
- ),
4803
- children: node.label
4804
- }
4805
- )
4797
+ children: node.label
4806
4798
  }
4807
4799
  ),
4808
4800
  trailingNode
@@ -5148,6 +5140,10 @@ var BODY_PADDING_CLASS = {
5148
5140
  wide: "p-(--sys-space-panel-padding-wide)",
5149
5141
  none: ""
5150
5142
  };
5143
+ var BODY_BACKGROUND_CLASS = {
5144
+ frame: "",
5145
+ neutral: "bg-(--sys-color-bg-neutralLight)"
5146
+ };
5151
5147
  var topBorderColorClass = {
5152
5148
  default: "bg-(--cmp-sectionHeaderCard-topBorder-color-default)",
5153
5149
  accent: "bg-(--cmp-sectionHeaderCard-topBorder-color-accent)"
@@ -5157,8 +5153,8 @@ var titleSizeClass = {
5157
5153
  sm: "typo-heading-sm"
5158
5154
  };
5159
5155
  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 });
5156
+ function SSectionHeaderCardBody({ padding = "default", background = "frame", children }, ref) {
5157
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn(BODY_PADDING_CLASS[padding], BODY_BACKGROUND_CLASS[background]), children });
5162
5158
  }
5163
5159
  );
5164
5160
  var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
@@ -5171,8 +5167,7 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
5171
5167
  subtitle,
5172
5168
  slot,
5173
5169
  thickness = false,
5174
- className,
5175
- ...rest
5170
+ className
5176
5171
  }, ref) {
5177
5172
  const hasHelp = helpText != null && helpText.length > 0;
5178
5173
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -5193,7 +5188,6 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
5193
5188
  "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
5189
  className
5195
5190
  ),
5196
- ...rest,
5197
5191
  children: [
5198
5192
  marker && /* @__PURE__ */ jsxRuntime.jsx(SBadge, { color: "darkblue", className: "shrink-0" }),
5199
5193
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 shrink-0 items-center gap-[8px]", children: [
@@ -5237,8 +5231,24 @@ var SSectionHeaderCardHeaderImpl = /* @__PURE__ */ react.forwardRef(
5237
5231
  }
5238
5232
  );
5239
5233
  var SSectionHeaderCardRootImpl = /* @__PURE__ */ react.forwardRef(
5240
- function SSectionHeaderCard({ className, children, ...rest }, ref) {
5241
- return /* @__PURE__ */ jsxRuntime.jsx(
5234
+ function SSectionHeaderCard({
5235
+ title,
5236
+ titleSize,
5237
+ marker,
5238
+ required,
5239
+ helpText,
5240
+ subtitle,
5241
+ slot,
5242
+ thickness,
5243
+ headerClassName,
5244
+ padding,
5245
+ background,
5246
+ className,
5247
+ children,
5248
+ ...rest
5249
+ }, ref) {
5250
+ const hasHeader = title != null;
5251
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5242
5252
  "div",
5243
5253
  {
5244
5254
  ref,
@@ -5247,24 +5257,28 @@ var SSectionHeaderCardRootImpl = /* @__PURE__ */ react.forwardRef(
5247
5257
  className
5248
5258
  ),
5249
5259
  ...rest,
5250
- children
5260
+ children: [
5261
+ hasHeader && /* @__PURE__ */ jsxRuntime.jsx(
5262
+ SSectionHeaderCardHeaderImpl,
5263
+ {
5264
+ title,
5265
+ titleSize,
5266
+ marker,
5267
+ required,
5268
+ helpText,
5269
+ subtitle,
5270
+ slot,
5271
+ thickness,
5272
+ className: headerClassName
5273
+ }
5274
+ ),
5275
+ /* @__PURE__ */ jsxRuntime.jsx(SSectionHeaderCardBodyImpl, { padding, background, children })
5276
+ ]
5251
5277
  }
5252
5278
  );
5253
5279
  }
5254
5280
  );
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
- });
5281
+ var SSectionHeaderCard2 = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5268
5282
  var SDivider = /* @__PURE__ */ react.forwardRef(function SDivider2({ vertical = false, className, style, ...rest }, ref) {
5269
5283
  return /* @__PURE__ */ jsxRuntime.jsx(
5270
5284
  "hr",
@@ -6895,7 +6909,7 @@ function SModalContainer({
6895
6909
  "div",
6896
6910
  {
6897
6911
  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",
6912
+ "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
6913
  shaking && "animate-modal-shake",
6900
6914
  className
6901
6915
  ),
@@ -7152,7 +7166,7 @@ function SActionModal({
7152
7166
  showClose: true,
7153
7167
  width,
7154
7168
  height,
7155
- className: "w-fit min-w-[min(480px,calc(100dvw-48px))] min-h-[min(320px,calc(100dvh-48px))]",
7169
+ className: "w-fit min-w-[min(480px,calc(100dvw-48px))] min-h-[min(320px,85dvh)]",
7156
7170
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-auto flex-col", children: [
7157
7171
  /* @__PURE__ */ jsxRuntime.jsx(SOverlayHeader, { title: modalTitle, description, reserveCloseSpace: true }),
7158
7172
  /* @__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 +8888,7 @@ var BACKGROUND_CLASS = {
8874
8888
  var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8875
8889
  background = "frame",
8876
8890
  scrollEndSpacing = true,
8891
+ contentHeight = "auto",
8877
8892
  header,
8878
8893
  className,
8879
8894
  style,
@@ -8888,13 +8903,14 @@ var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8888
8903
  onScroll?.(e);
8889
8904
  if (header != null && !header.fix) setScrolled(e.currentTarget.scrollTop > 0);
8890
8905
  };
8906
+ const fill = contentHeight === "fill";
8891
8907
  const main = /* @__PURE__ */ jsxRuntime.jsx(
8892
8908
  "main",
8893
8909
  {
8894
8910
  ref,
8895
8911
  className: cn(
8896
8912
  "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)]",
8913
+ !fill && scrollEndSpacing && "pb-[var(--cmp-pageBody-padding-scrollEnd)]",
8898
8914
  SCROLLBAR_CLASS,
8899
8915
  BACKGROUND_CLASS[background],
8900
8916
  className
@@ -8902,7 +8918,7 @@ var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8902
8918
  style: header == null ? { ...gridStyle, ...style } : style,
8903
8919
  onScroll: handleScroll,
8904
8920
  ...rest,
8905
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { minWidth: MIN_CONTENT_WIDTH }, children })
8921
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(fill && "h-full"), style: { minWidth: MIN_CONTENT_WIDTH }, children })
8906
8922
  }
8907
8923
  );
8908
8924
  if (header == null) return main;
@@ -8926,6 +8942,7 @@ var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
8926
8942
 
8927
8943
  // src/components/SPage/page.config.ts
8928
8944
  var PAGE_BACKGROUNDS = ["frame", "neutral", "screen"];
8945
+ var PAGE_CONTENT_HEIGHTS = ["auto", "fill"];
8929
8946
 
8930
8947
  // src/components/STabs/tabs.config.ts
8931
8948
  var TAB_MAIN_SIZE = {
@@ -9743,6 +9760,7 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9743
9760
  readOnly = false,
9744
9761
  width,
9745
9762
  minWidth,
9763
+ maxWidth,
9746
9764
  multiline = false,
9747
9765
  borderless = false,
9748
9766
  children,
@@ -9778,7 +9796,10 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9778
9796
  const resolvedSuccess = status === "pass";
9779
9797
  const showError = resolvedError && errorMessage !== "";
9780
9798
  const interactive = !disabled && !readOnly;
9781
- const hug = cssLen(width) !== void 0;
9799
+ const isPercentWidth = typeof width === "string" && width.trim().endsWith("%");
9800
+ const hug = cssLen(width) !== void 0 && !isPercentWidth;
9801
+ const addonWidth = effectiveAddonLabel !== "" ? cssLen(labelWidth) : void 0;
9802
+ const resolvedMaxWidth = cssLen(maxWidth) === void 0 || effectiveAddonLabel === "" ? cssLen(maxWidth) : addonWidth ? `calc(${cssLen(maxWidth)} + ${addonWidth})` : void 0;
9782
9803
  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
9804
  const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
9784
9805
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
@@ -9834,8 +9855,12 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
9834
9855
  /* @__PURE__ */ jsxRuntime.jsxs(
9835
9856
  "div",
9836
9857
  {
9837
- className: cn("flex min-w-0 flex-col", !hug && "flex-1"),
9838
- style: { width: cssLen(width), minWidth: cssLen(minWidth) },
9858
+ className: cn("flex min-w-0 flex-col", !hug && !isPercentWidth && "flex-1"),
9859
+ style: {
9860
+ width: cssLen(width),
9861
+ minWidth: cssLen(minWidth),
9862
+ maxWidth: resolvedMaxWidth
9863
+ },
9839
9864
  children: [
9840
9865
  /* @__PURE__ */ jsxRuntime.jsxs(
9841
9866
  "div",
@@ -10590,6 +10615,7 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10590
10615
  height,
10591
10616
  stickyColumn,
10592
10617
  radius = "default",
10618
+ bordered = true,
10593
10619
  noDataLabel = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
10594
10620
  noDataSlot,
10595
10621
  isLoading = false,
@@ -10848,7 +10874,7 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10848
10874
  };
10849
10875
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[var(--cmp-table-radius)] rounded-t-none" : "rounded-[var(--cmp-table-radius)]";
10850
10876
  const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-body-paddingY-default)";
10851
- const cellPadX = "var(--cmp-table-body-paddingX-wide)";
10877
+ const cellPadXOf = (col) => col.contentType === "control" ? "var(--cmp-table-body-paddingX-default)" : "var(--cmp-table-body-paddingX-wide)";
10852
10878
  const rowH = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-body-default-height)";
10853
10879
  const totalColSpan = visibleCols.length + (selectable ? 1 : 0);
10854
10880
  const stickyInfo = (i) => {
@@ -10902,7 +10928,8 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
10902
10928
  // 만으로는 스태킹 컨텍스트가 생기지 않아, 모달 안에 들어가면 30·31이 모달 Content
10903
10929
  // 레벨까지 올라가 같은 컨텍스트의 floating portal 밴드(z-2)를 덮는다.
10904
10930
  // 자손은 이미 overflow-hidden으로 클리핑되므로 테이블 내부 순서는 달라지지 않는다.
10905
- "bg-white relative isolate flex flex-col overflow-hidden border border-solid border-[color:var(--cmp-table-border-color)]",
10931
+ "bg-white relative isolate flex flex-col overflow-hidden",
10932
+ bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
10906
10933
  // height가 없으면 루트가 받은 높이를 끝까지 채운다. 호스트가 className으로
10907
10934
  // `flex-1 min-h-0` 등을 줘 루트 높이가 확정된 경우, 행이 적어도 테두리 박스가
10908
10935
  // 남은 영역을 그대로 차지한다. basis는 auto로 둬 높이가 확정되지 않은
@@ -11072,9 +11099,10 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11072
11099
  stickyShadowClass(i),
11073
11100
  typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
11074
11101
  );
11102
+ const tdPadX = cellPadXOf(col);
11075
11103
  const tdStyle = {
11076
- paddingLeft: cellPadX,
11077
- paddingRight: cellPadX,
11104
+ paddingLeft: tdPadX,
11105
+ paddingRight: tdPadX,
11078
11106
  paddingTop: cellPadY,
11079
11107
  paddingBottom: cellPadY,
11080
11108
  textAlign: col.align ?? "left",
@@ -11127,19 +11155,29 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11127
11155
  // SPagination은 전체 폭 기준 중앙, rows-per-page SSelect는 우측에 absolute 배치해 중앙 정렬을 유지한다.
11128
11156
  // 루트 높이가 확정된 경우(테이블이 남은 영역을 채우는 배치) 페이지네이션 바가
11129
11157
  // 눌리지 않도록 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
- ] })
11158
+ /* @__PURE__ */ jsxRuntime.jsxs(
11159
+ "div",
11160
+ {
11161
+ className: cn(
11162
+ "relative flex h-[48px] shrink-0 items-center justify-center rounded-[var(--cmp-table-radius)] bg-[var(--color-grey-05)]",
11163
+ // 테두리를 끄면 겹칠 대상이 없어진다 — -mt-px 를 남기면 1px 어긋남만 생긴다.
11164
+ bordered && "-mt-px border border-solid border-[color:var(--cmp-table-border-color)]"
11165
+ ),
11166
+ children: [
11167
+ /* @__PURE__ */ jsxRuntime.jsx(SPagination, { currentPage: displayPage, lastPage, onPageChange: changePage }),
11168
+ useRowsPerPageSelect && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-[10px] top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsxRuntime.jsx(
11169
+ SSelect2,
11170
+ {
11171
+ options: rowsPerPageOption,
11172
+ value: useInternalPagination ? innerRowsPerPage : pagination?.rowsPerPage,
11173
+ emitValue: true,
11174
+ onValueChange: (v) => changeRowsPerPage(Number(v)),
11175
+ width: 128
11176
+ }
11177
+ ) })
11178
+ ]
11179
+ }
11180
+ )
11143
11181
  ]
11144
11182
  }
11145
11183
  );
@@ -12277,6 +12315,44 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
12277
12315
  }
12278
12316
  ) });
12279
12317
  }
12318
+ function SDatePickerYearListbox({
12319
+ value,
12320
+ anchorYear,
12321
+ selectable,
12322
+ onValueChange,
12323
+ className,
12324
+ style
12325
+ }) {
12326
+ const today = getTodayParts();
12327
+ const resolvedAnchorYear = anchorYear ?? today.year;
12328
+ const [year, setYear] = react.useState(() => parseYear(value) ?? resolvedAnchorYear);
12329
+ react.useEffect(() => {
12330
+ setYear(parseYear(value) ?? resolvedAnchorYear);
12331
+ }, [value, resolvedAnchorYear]);
12332
+ const commitYear = (nextYear) => {
12333
+ setYear(nextYear);
12334
+ onValueChange?.(String(nextYear));
12335
+ };
12336
+ return /* @__PURE__ */ jsxRuntime.jsx(
12337
+ "div",
12338
+ {
12339
+ className: cn(
12340
+ "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",
12341
+ className
12342
+ ),
12343
+ style,
12344
+ children: /* @__PURE__ */ jsxRuntime.jsx(
12345
+ YearWheel,
12346
+ {
12347
+ value: year,
12348
+ anchorYear: resolvedAnchorYear,
12349
+ selectable,
12350
+ onCommit: commitYear
12351
+ }
12352
+ )
12353
+ }
12354
+ );
12355
+ }
12280
12356
  function MonthWheel({ year, value, selectable, onCommit }) {
12281
12357
  const listRef = react.useRef(null);
12282
12358
  const initialValueRef = react.useRef(value);
@@ -12323,66 +12399,59 @@ function MonthWheel({ year, value, selectable, onCommit }) {
12323
12399
  }
12324
12400
  ) });
12325
12401
  }
12326
- function DatePickerListbox({
12327
- mode,
12402
+ function SDatePickerMonthListbox({
12328
12403
  value,
12404
+ anchorYear,
12329
12405
  selectable,
12330
12406
  onValueChange,
12331
- onClose
12407
+ onMonthSelect,
12408
+ className,
12409
+ style
12332
12410
  }) {
12333
12411
  const today = getTodayParts();
12334
- const [year, setYear] = react.useState(() => parseYear(value) ?? today.year);
12412
+ const resolvedAnchorYear = anchorYear ?? today.year;
12413
+ const [year, setYear] = react.useState(() => parseYear(value) ?? resolvedAnchorYear);
12335
12414
  const [month, setMonth] = react.useState(() => parseMonth(value) ?? today.month);
12415
+ react.useEffect(() => {
12416
+ setYear(parseYear(value) ?? resolvedAnchorYear);
12417
+ setMonth(parseMonth(value) ?? today.month);
12418
+ }, [value, resolvedAnchorYear, today.month]);
12336
12419
  const commitYear = (nextYear) => {
12337
12420
  setYear(nextYear);
12338
- if (mode === "year") {
12339
- onValueChange?.(String(nextYear));
12340
- onClose();
12341
- return;
12342
- }
12343
12421
  onValueChange?.(formatMonthValue(nextYear, month));
12344
12422
  };
12345
12423
  const commitMonth = (nextMonth) => {
12346
12424
  setMonth(nextMonth);
12347
- onValueChange?.(formatMonthValue(year, nextMonth));
12348
- onClose();
12425
+ const nextValue = formatMonthValue(year, nextMonth);
12426
+ onValueChange?.(nextValue);
12427
+ onMonthSelect?.(nextValue);
12349
12428
  };
12350
12429
  return /* @__PURE__ */ jsxRuntime.jsxs(
12351
12430
  "div",
12352
12431
  {
12353
12432
  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)]"
12433
+ "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",
12434
+ className
12356
12435
  ),
12436
+ style,
12357
12437
  children: [
12438
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 w-[var(--cmp-datepicker-mode-yearMonth-column-width)]", children: /* @__PURE__ */ jsxRuntime.jsx(
12439
+ YearWheel,
12440
+ {
12441
+ value: year,
12442
+ anchorYear: resolvedAnchorYear,
12443
+ selectable,
12444
+ onCommit: commitYear
12445
+ }
12446
+ ) }),
12358
12447
  /* @__PURE__ */ jsxRuntime.jsx(
12359
12448
  "div",
12360
12449
  {
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
- )
12450
+ "aria-hidden": true,
12451
+ className: "w-[var(--border-width-1)] self-stretch bg-[var(--sys-color-divider-default)]"
12374
12452
  }
12375
12453
  ),
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
- ] })
12454
+ /* @__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
12455
  ]
12387
12456
  }
12388
12457
  );
@@ -12437,6 +12506,9 @@ function SDatePicker({
12437
12506
  icon: 20,
12438
12507
  minW: 104,
12439
12508
  fieldMinW: 168,
12509
+ // 상한 등급 lg — 값 길이가 YYYY-MM-DD 로 정해져 있어, width="100%" 를 받아도
12510
+ // 행 전체로 늘어나지 않게 막는다 (SKeyValueTable 이 모든 컨트롤에 100% 를 넘긴다)
12511
+ fieldMaxW: 240,
12440
12512
  fontSize: 14,
12441
12513
  lineHeight: 24,
12442
12514
  radius: 6
@@ -12446,6 +12518,8 @@ function SDatePicker({
12446
12518
  icon: 16,
12447
12519
  minW: 80,
12448
12520
  fieldMinW: 128,
12521
+ // 상한 등급 md
12522
+ fieldMaxW: 160,
12449
12523
  fontSize: 12,
12450
12524
  lineHeight: 20,
12451
12525
  radius: 4
@@ -12478,6 +12552,7 @@ function SDatePicker({
12478
12552
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12479
12553
  width,
12480
12554
  minWidth: dims.fieldMinW,
12555
+ maxWidth: dims.fieldMaxW,
12481
12556
  disabled,
12482
12557
  className,
12483
12558
  style,
@@ -12522,7 +12597,7 @@ function SDatePicker({
12522
12597
  RPopover9__namespace.Content,
12523
12598
  {
12524
12599
  side: "bottom",
12525
- align: "start",
12600
+ align: mode === "date" ? "start" : "center",
12526
12601
  sideOffset: 4,
12527
12602
  className: cn(
12528
12603
  "z-50 max-w-[var(--radix-popover-content-available-width)]",
@@ -12537,14 +12612,23 @@ function SDatePicker({
12537
12612
  onValueChange: select,
12538
12613
  onViewChange
12539
12614
  }
12615
+ ) : mode === "year" ? /* @__PURE__ */ jsxRuntime.jsx(
12616
+ SDatePickerYearListbox,
12617
+ {
12618
+ value,
12619
+ selectable,
12620
+ onValueChange: (nextValue) => {
12621
+ selectListboxValue(nextValue);
12622
+ setOpen(false);
12623
+ }
12624
+ }
12540
12625
  ) : /* @__PURE__ */ jsxRuntime.jsx(
12541
- DatePickerListbox,
12626
+ SDatePickerMonthListbox,
12542
12627
  {
12543
- mode,
12544
12628
  value,
12545
12629
  selectable,
12546
12630
  onValueChange: selectListboxValue,
12547
- onClose: () => setOpen(false)
12631
+ onMonthSelect: () => setOpen(false)
12548
12632
  }
12549
12633
  )
12550
12634
  }
@@ -12927,6 +13011,9 @@ function SDateRangePicker({
12927
13011
  icon: 20,
12928
13012
  minW: 104,
12929
13013
  fieldMinW: 260,
13014
+ // 상한 등급 xl — 값이 `YYYY-MM-DD ~ YYYY-MM-DD` 라 단일 날짜의 두 배가 넘는다.
13015
+ // 최소너비(260)가 이미 lg(240)를 넘으므로 상한도 한 등급 위여야 한다
13016
+ fieldMaxW: 480,
12930
13017
  fontSize: 14,
12931
13018
  lineHeight: 24,
12932
13019
  radius: 6
@@ -12936,6 +13023,8 @@ function SDateRangePicker({
12936
13023
  icon: 16,
12937
13024
  minW: 80,
12938
13025
  fieldMinW: 216,
13026
+ // 상한 등급 lg — 최소너비 216 이 md(160)를 넘어 md 로는 담기지 않는다
13027
+ fieldMaxW: 240,
12939
13028
  fontSize: 12,
12940
13029
  lineHeight: 20,
12941
13030
  radius: 4
@@ -12966,6 +13055,7 @@ function SDateRangePicker({
12966
13055
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
12967
13056
  width,
12968
13057
  minWidth: dims.fieldMinW,
13058
+ maxWidth: dims.fieldMaxW,
12969
13059
  disabled,
12970
13060
  className,
12971
13061
  style,
@@ -13430,6 +13520,7 @@ function SKeyValueTable({
13430
13520
  values = {},
13431
13521
  search = false,
13432
13522
  radius = "default",
13523
+ bordered = true,
13433
13524
  onChange,
13434
13525
  onSearch,
13435
13526
  className,
@@ -13494,7 +13585,8 @@ function SKeyValueTable({
13494
13585
  });
13495
13586
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[8px] rounded-t-none" : "rounded-[8px]";
13496
13587
  const frameClass = cn(
13497
- "overflow-hidden border border-solid border-[color:var(--cmp-table-border-color)]",
13588
+ "overflow-hidden",
13589
+ bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
13498
13590
  radiusClass
13499
13591
  );
13500
13592
  const table = /* @__PURE__ */ jsxRuntime.jsxs(
@@ -14079,7 +14171,16 @@ var CHIPFILTER = {
14079
14171
  itemTextDefault: "var(--cmp-chipFilter-item-text-default)",
14080
14172
  itemTextSelected: "var(--cmp-chipFilter-item-text-selected)"
14081
14173
  };
14174
+ var DEFAULT_PERIOD_PRESETS = [
14175
+ { value: "day", label: "\uC77C\uBCC4" },
14176
+ { value: "month", label: "\uC6D4\uBCC4" },
14177
+ { value: "quarter", label: "\uBD84\uAE30\uBCC4" },
14178
+ { value: "half", label: "\uBC18\uAE30\uBCC4" },
14179
+ { value: "year", label: "\uC5F0\uB3C4\uBCC4" },
14180
+ { value: "custom", label: "\uC0AC\uC6A9\uC790 \uC9C0\uC815", custom: true }
14181
+ ];
14082
14182
  var isKeywordValue = (value) => typeof value === "object" && value != null && !Array.isArray(value) && "keywords" in value;
14183
+ var isPeriodValue = (value) => typeof value === "object" && value != null && !Array.isArray(value) && "unit" in value;
14083
14184
  var keywordEntries = (value) => {
14084
14185
  if (isKeywordValue(value)) return value.keywords;
14085
14186
  if (Array.isArray(value)) {
@@ -14103,12 +14204,33 @@ var todayIso = () => {
14103
14204
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
14104
14205
  };
14105
14206
  var isInlineDateField = (field) => field.type === "date" && field.radioButton === true && (field.presets?.length ?? 0) > 0;
14207
+ var getFieldPresets = (field) => {
14208
+ if (field.type === "period") return field.presets ?? DEFAULT_PERIOD_PRESETS;
14209
+ return field.presets ?? [];
14210
+ };
14211
+ var getDefaultPeriodInputValue = (unit) => {
14212
+ const today = todayIso();
14213
+ if (unit === "day") return today;
14214
+ if (unit === "month") return today.slice(0, 7);
14215
+ if (unit === "quarter") return 1;
14216
+ if (unit === "half") return "first";
14217
+ if (unit === "year") return today.slice(0, 4);
14218
+ return [today, today];
14219
+ };
14220
+ var createPeriodValue = (unit) => ({
14221
+ unit,
14222
+ value: getDefaultPeriodInputValue(unit)
14223
+ });
14106
14224
  var resolveFieldDefault = (field) => {
14107
14225
  if (field.defaultValue !== void 0) return field.defaultValue;
14108
14226
  if (isInlineDateField(field)) return todayIso();
14109
14227
  if (!field.required) return void 0;
14110
14228
  if (field.type === "single") return field.options?.[0]?.value;
14111
14229
  if (field.type === "date") return todayIso();
14230
+ if (field.type === "period") {
14231
+ const unit = getFieldPresets(field).find((preset) => !preset.custom)?.value;
14232
+ return unit ? createPeriodValue(unit) : void 0;
14233
+ }
14112
14234
  return void 0;
14113
14235
  };
14114
14236
  var valuesEqual = (a, b) => {
@@ -14132,6 +14254,8 @@ var isAtDefaultValue = (field, value) => {
14132
14254
  return def !== void 0 && valuesEqual(value, def);
14133
14255
  };
14134
14256
  var isInlineField = (field) => isInlineDateField(field) || field.type === "custom";
14257
+ var isFixedField = (field) => field.fix === true || field.required === true;
14258
+ var isAddableField = (field) => !isFixedField(field);
14135
14259
  var isGroupedFields = (fields) => fields.length > 0 && "fields" in fields[0];
14136
14260
  var isGroupSatisfied = (group, values) => {
14137
14261
  if (!group.rule) return true;
@@ -14158,9 +14282,10 @@ var defaultGroupTooltipMessage = (rule, fields) => {
14158
14282
  };
14159
14283
  var matchPresetId = (presets, value) => {
14160
14284
  if (isEmptyValue(value)) return null;
14285
+ if (isPeriodValue(value)) return value.unit;
14161
14286
  for (const preset of presets) {
14162
- if (preset.custom || !preset.resolve) continue;
14163
- const resolved = preset.resolve();
14287
+ if (preset.custom) continue;
14288
+ const resolved = preset.resolve ? preset.resolve() : preset.value;
14164
14289
  if (Array.isArray(resolved) && Array.isArray(value)) {
14165
14290
  if (resolved[0] === value[0] && resolved[1] === value[1]) return preset.value;
14166
14291
  } else if (!Array.isArray(resolved) && !Array.isArray(value) && resolved === value) {
@@ -14176,18 +14301,36 @@ var formatValueList = (labels) => {
14176
14301
  };
14177
14302
  var displayValue = (field, value) => {
14178
14303
  if (isEmptyValue(value)) return "";
14179
- if (field.type === "multi")
14180
- return formatValueList(toArrayValue(value).map((v) => optionLabel(field, v)));
14304
+ if (field.type === "multi") {
14305
+ const values = toArrayValue(value);
14306
+ const selectableValues = (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value);
14307
+ if (selectableValues.length > 0 && selectableValues.every((selectableValue) => values.includes(selectableValue))) {
14308
+ return "\uC804\uCCB4";
14309
+ }
14310
+ return formatValueList(values.map((v) => optionLabel(field, v)));
14311
+ }
14181
14312
  if (field.type === "single") return optionLabel(field, value);
14182
14313
  if (field.type === "date")
14183
14314
  return Array.isArray(value) ? `${value[0]} ~ ${value[1]}` : String(value);
14315
+ if (field.type === "period") {
14316
+ if (!isPeriodValue(value)) {
14317
+ const preset2 = getFieldPresets(field).find((p) => p.value === value);
14318
+ return preset2?.label ?? String(value);
14319
+ }
14320
+ const preset = getFieldPresets(field).find((p) => p.value === value.unit);
14321
+ const label = preset?.label ?? value.unit;
14322
+ if (value.unit === "quarter") return `${label}(${value.value ?? 1}\uBD84\uAE30)`;
14323
+ if (value.unit === "half") return `${label}(${value.value === "second" ? "\uD558\uBC18\uAE30" : "\uC0C1\uBC18\uAE30"})`;
14324
+ if (Array.isArray(value.value)) return `${value.value[0]} ~ ${value.value[1]}`;
14325
+ return value.value != null && value.value !== "" ? String(value.value) : label;
14326
+ }
14184
14327
  if (field.type === "keyword") return formatValueList(keywordEntries(value));
14185
14328
  return String(value);
14186
14329
  };
14187
14330
  var chipDisplayValue = (field, value) => {
14188
14331
  const text = displayValue(field, value);
14189
14332
  if (field.type !== "date" || isEmptyValue(value)) return text;
14190
- const presets = field.presets ?? [];
14333
+ const presets = getFieldPresets(field);
14191
14334
  if (presets.length === 0) return text;
14192
14335
  const preset = presets.find((p) => p.value === matchPresetId(presets, value));
14193
14336
  return preset && !preset.custom ? `${preset.label}(${text})` : text;
@@ -14204,7 +14347,6 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14204
14347
  onAddFilter,
14205
14348
  label = "\uAC80\uC0C9 \uD544\uD130",
14206
14349
  showLabel = false,
14207
- showAddButton = true,
14208
14350
  showReset = true,
14209
14351
  disabled = false,
14210
14352
  className,
@@ -14219,7 +14361,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14219
14361
  const values = normalizeValues(value);
14220
14362
  const portalContainer = usePortalContainer();
14221
14363
  const [internalActiveKeys, setInternalActiveKeys] = react.useState(
14222
- () => fields.filter((field) => field.defaultActive !== false).map((field) => field.key)
14364
+ () => fields.filter((field) => isFixedField(field) || field.defaultActive !== false).map((field) => field.key)
14223
14365
  );
14224
14366
  const [openKey, setOpenKey] = react.useState(null);
14225
14367
  const [addOpen, setAddOpen] = react.useState(false);
@@ -14261,7 +14403,9 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14261
14403
  setInternalActiveKeys((current) => {
14262
14404
  const known = new Set(fields.map((field) => field.key));
14263
14405
  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);
14406
+ const additions = fields.filter(
14407
+ (field) => (isFixedField(field) || field.defaultActive !== false) && !kept.includes(field.key)
14408
+ ).map((field) => field.key);
14265
14409
  return [...kept, ...additions];
14266
14410
  });
14267
14411
  }, [activeKeys, fields]);
@@ -14282,25 +14426,31 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14282
14426
  });
14283
14427
  if (changed) onValueChange?.({ ...values, ...patch });
14284
14428
  }, [fields, values, onValueChange]);
14285
- const resolvedActiveKeys = activeKeys ?? internalActiveKeys;
14429
+ const fixedKeys = fields.filter(isFixedField).map((field) => field.key);
14430
+ const resolvedActiveKeys = Array.from(
14431
+ /* @__PURE__ */ new Set([...activeKeys ?? internalActiveKeys, ...fixedKeys])
14432
+ );
14286
14433
  const activeFields = fields.filter((field) => resolvedActiveKeys.includes(field.key));
14287
- const inactiveFields = fields.filter((field) => !resolvedActiveKeys.includes(field.key));
14434
+ const inactiveFields = fields.filter(
14435
+ (field) => isAddableField(field) && !resolvedActiveKeys.includes(field.key)
14436
+ );
14288
14437
  const groupIndexByKey = /* @__PURE__ */ new Map();
14289
14438
  groups.forEach((group, groupIndex) => {
14290
14439
  group.fields.forEach((field) => groupIndexByKey.set(field.key, groupIndex));
14291
14440
  });
14292
14441
  const hasValue = fields.some((field) => !isEmptyValue(values[field.key]));
14293
- const hasRightActions = showAddButton || showReset;
14442
+ const hasAddButton = fields.some(isAddableField);
14443
+ const hasRightActions = hasAddButton || showReset;
14294
14444
  const currentOpenField = fields.find((field) => field.key === openKey);
14295
14445
  const editorPopoverStyle = react.useMemo(() => {
14296
14446
  if (currentOpenField?.type === "keyword") {
14297
14447
  return { width: CHIPFILTER_LISTBOX_WIDTH };
14298
14448
  }
14299
- if (currentOpenField?.type === "date" && (currentOpenField.presets?.length ?? 0) > 0) {
14449
+ if ((currentOpenField?.type === "date" || currentOpenField?.type === "period") && getFieldPresets(currentOpenField).length > 0) {
14300
14450
  return { width: CHIPFILTER_DATE_PRESET_LISTBOX_WIDTH };
14301
14451
  }
14302
14452
  return { width: CHIPFILTER_LISTBOX_WIDTH };
14303
- }, [currentOpenField?.type, currentOpenField?.presets?.length]);
14453
+ }, [currentOpenField]);
14304
14454
  const setActive = (next) => {
14305
14455
  if (!activeKeys) setInternalActiveKeys(next);
14306
14456
  onActiveKeysChange?.(next);
@@ -14319,6 +14469,12 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14319
14469
  const def = field ? resolveFieldDefault(field) : void 0;
14320
14470
  runSearch(emitValue(key, def !== void 0 ? def : null));
14321
14471
  };
14472
+ const removeField = (key) => {
14473
+ const nextValues = emitValue(key, null);
14474
+ setActive(resolvedActiveKeys.filter((activeKey) => activeKey !== key));
14475
+ if (openKey === key) setOpenKey(null);
14476
+ runSearch(nextValues);
14477
+ };
14322
14478
  const reset = react.useCallback(() => {
14323
14479
  const next = fields.reduce((acc, field) => {
14324
14480
  const def = resolveFieldDefault(field);
@@ -14409,7 +14565,15 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14409
14565
  flushRun();
14410
14566
  runGroupIndex = groupIndex;
14411
14567
  }
14412
- const showClear = !isAtDefaultValue(field, values[field.key]);
14568
+ const addable = isAddableField(field);
14569
+ const showClear = addable || !isAtDefaultValue(field, values[field.key]);
14570
+ const handleClear = () => {
14571
+ if (addable) {
14572
+ removeField(field.key);
14573
+ return;
14574
+ }
14575
+ clearValue(field.key);
14576
+ };
14413
14577
  const node = isInlineDateField(field) ? /* @__PURE__ */ jsxRuntime.jsx(
14414
14578
  InlineDatePresetFilter,
14415
14579
  {
@@ -14419,15 +14583,27 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14419
14583
  warning,
14420
14584
  showClear,
14421
14585
  onValueChange: (next) => emitValueAndSearch(field.key, next),
14422
- onClear: () => clearValue(field.key)
14586
+ onClear: handleClear
14423
14587
  },
14424
14588
  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(
14589
+ ) : field.type === "custom" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex shrink-0 items-center gap-[4px]", children: [
14590
+ field.render?.({
14591
+ value: values[field.key],
14592
+ disabled: disabled || field.disabled,
14593
+ warning,
14594
+ onValueChange: (next) => emitValueAndSearch(field.key, next)
14595
+ }),
14596
+ addable && !disabled && !field.disabled && showClear && /* @__PURE__ */ jsxRuntime.jsx(
14597
+ "button",
14598
+ {
14599
+ type: "button",
14600
+ "aria-label": `${field.label} \uD544\uD130 \uC81C\uAC70`,
14601
+ className: "inline-flex size-[16px] shrink-0 items-center justify-center rounded-[var(--cmp-ghostButton-radius-sm)] border-none bg-transparent p-0",
14602
+ onClick: handleClear,
14603
+ children: /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "close", size: 12, color: "var(--cmp-ghostButton-default-content)" })
14604
+ }
14605
+ )
14606
+ ] }, field.key) : /* @__PURE__ */ jsxRuntime.jsx(
14431
14607
  FilterChip,
14432
14608
  {
14433
14609
  field,
@@ -14437,9 +14613,10 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14437
14613
  disabled: disabled || field.disabled,
14438
14614
  warning,
14439
14615
  showClear,
14616
+ showClearWhenEmpty: addable,
14440
14617
  refCallback: (node2) => triggerRefs.current.set(field.key, node2),
14441
14618
  onOpen: () => setOpenKey(openKey === field.key ? null : field.key),
14442
- onClear: () => clearValue(field.key)
14619
+ onClear: handleClear
14443
14620
  },
14444
14621
  field.key
14445
14622
  );
@@ -14460,7 +14637,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14460
14637
  })(),
14461
14638
  hasRightActions && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
14462
14639
  /* @__PURE__ */ jsxRuntime.jsx(Divider, { inset: true }),
14463
- showAddButton && /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open: addOpen, onOpenChange: setAddOpen, children: [
14640
+ hasAddButton && /* @__PURE__ */ jsxRuntime.jsxs(RPopover9__namespace.Root, { open: addOpen, onOpenChange: setAddOpen, children: [
14464
14641
  /* @__PURE__ */ jsxRuntime.jsx(RPopover9__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
14465
14642
  "button",
14466
14643
  {
@@ -14507,7 +14684,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
14507
14684
  "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
14685
  // 부모 flex gap(6px)은 칩 사이 간격 기준이라, 필터 추가 버튼과의 12px 간격을 맞추려면
14509
14686
  // 부족한 만큼(12px - gap)을 추가로 띄운다. 필터 추가가 없을 땐 divider와 이미 12px이라 불필요.
14510
- showAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
14687
+ hasAddButton && "ml-[calc(12px-var(--cmp-chipFilter-chip-group))]"
14511
14688
  ),
14512
14689
  style: {
14513
14690
  color: disabled || !hasValue ? "var(--cmp-textLink-content-disabled)" : "var(--cmp-textLink-content-default)"
@@ -14591,6 +14768,7 @@ function FilterChip({
14591
14768
  disabled,
14592
14769
  warning,
14593
14770
  showClear,
14771
+ showClearWhenEmpty,
14594
14772
  refCallback,
14595
14773
  onOpen,
14596
14774
  onClear
@@ -14660,7 +14838,7 @@ function FilterChip({
14660
14838
  }
14661
14839
  )
14662
14840
  ] }),
14663
- applied && !disabled && showClear && /* @__PURE__ */ jsxRuntime.jsx(
14841
+ !disabled && showClear && (applied || showClearWhenEmpty) && /* @__PURE__ */ jsxRuntime.jsx(
14664
14842
  "span",
14665
14843
  {
14666
14844
  role: "button",
@@ -14692,7 +14870,7 @@ function EditorPanel({
14692
14870
  onValueChange,
14693
14871
  onClose
14694
14872
  }) {
14695
- const wrapsInPaddedTop = field.type === "date";
14873
+ const wrapsInPaddedTop = field.type === "date" || field.type === "period";
14696
14874
  const isMulti = field.type === "multi";
14697
14875
  const selectableValues = isMulti ? (field.options ?? []).filter((option) => !option.disabled).map((option) => option.value) : [];
14698
14876
  const selectedValues = isMulti ? toArrayValue(value) : [];
@@ -14771,6 +14949,17 @@ function FilterEditor({
14771
14949
  disabled,
14772
14950
  onValueChange
14773
14951
  }) {
14952
+ if (field.type === "period") {
14953
+ return /* @__PURE__ */ jsxRuntime.jsx(
14954
+ PeriodPresetEditor,
14955
+ {
14956
+ field,
14957
+ value,
14958
+ disabled,
14959
+ onValueChange
14960
+ }
14961
+ );
14962
+ }
14774
14963
  if (field.type === "date") {
14775
14964
  return /* @__PURE__ */ jsxRuntime.jsx(
14776
14965
  DatePresetEditor,
@@ -14810,7 +14999,7 @@ function DatePresetEditor({
14810
14999
  disabled,
14811
15000
  onValueChange
14812
15001
  }) {
14813
- const presets = field.presets ?? [];
15002
+ const presets = getFieldPresets(field);
14814
15003
  const [selectedId, setSelectedId] = react.useState(() => matchPresetId(presets, value));
14815
15004
  const syncedInitialValueRef = react.useRef(!isEmptyValue(value));
14816
15005
  react.useEffect(() => {
@@ -14821,9 +15010,9 @@ function DatePresetEditor({
14821
15010
  const selectedPreset = presets.find((preset) => preset.value === selectedId);
14822
15011
  const selectPreset = (preset) => {
14823
15012
  setSelectedId(preset.value);
14824
- if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : null);
15013
+ if (!preset.custom) onValueChange(preset.resolve ? preset.resolve() : preset.value);
14825
15014
  };
14826
- if (presets.length === 0) {
15015
+ if (field.type === "date" && presets.length === 0) {
14827
15016
  return /* @__PURE__ */ jsxRuntime.jsx(
14828
15017
  SDatePicker,
14829
15018
  {
@@ -14859,6 +15048,130 @@ function DatePresetEditor({
14859
15048
  )
14860
15049
  ] });
14861
15050
  }
15051
+ function PeriodPresetEditor({
15052
+ field,
15053
+ value,
15054
+ disabled,
15055
+ onValueChange
15056
+ }) {
15057
+ const presets = getFieldPresets(field);
15058
+ const firstUnit = presets.find((preset) => !preset.custom)?.value ?? "day";
15059
+ const [selectedId, setSelectedId] = react.useState(() => matchPresetId(presets, value) ?? firstUnit);
15060
+ const periodValue = isPeriodValue(value) ? value : createPeriodValue(selectedId ?? firstUnit);
15061
+ const selectedUnit = selectedId ?? periodValue.unit;
15062
+ react.useEffect(() => {
15063
+ if (isEmptyValue(value)) return;
15064
+ setSelectedId(matchPresetId(presets, value) ?? firstUnit);
15065
+ }, [firstUnit, presets, value]);
15066
+ const selectPreset = (preset) => {
15067
+ const nextUnit = preset.value;
15068
+ setSelectedId(nextUnit);
15069
+ onValueChange(createPeriodValue(nextUnit));
15070
+ };
15071
+ const updateInputValue = (nextValue) => {
15072
+ onValueChange({ unit: selectedUnit, value: nextValue });
15073
+ };
15074
+ const renderInput = () => {
15075
+ if (selectedUnit === "day") {
15076
+ return /* @__PURE__ */ jsxRuntime.jsx(
15077
+ SDatePicker,
15078
+ {
15079
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15080
+ onValueChange: updateInputValue,
15081
+ selectable: field.selectable,
15082
+ disabled,
15083
+ width: "100%"
15084
+ }
15085
+ );
15086
+ }
15087
+ if (selectedUnit === "month") {
15088
+ return /* @__PURE__ */ jsxRuntime.jsx(
15089
+ SDatePicker,
15090
+ {
15091
+ mode: "month",
15092
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15093
+ onValueChange: updateInputValue,
15094
+ selectable: field.selectable,
15095
+ disabled,
15096
+ width: "100%"
15097
+ }
15098
+ );
15099
+ }
15100
+ if (selectedUnit === "quarter") {
15101
+ return /* @__PURE__ */ jsxRuntime.jsx(
15102
+ SSelect2,
15103
+ {
15104
+ value: typeof periodValue.value === "number" ? periodValue.value : 1,
15105
+ onValueChange: updateInputValue,
15106
+ emitValue: true,
15107
+ disabled,
15108
+ width: "100%",
15109
+ options: [
15110
+ { value: 1, label: "1\uBD84\uAE30" },
15111
+ { value: 2, label: "2\uBD84\uAE30" },
15112
+ { value: 3, label: "3\uBD84\uAE30" },
15113
+ { value: 4, label: "4\uBD84\uAE30" }
15114
+ ]
15115
+ }
15116
+ );
15117
+ }
15118
+ if (selectedUnit === "half") {
15119
+ return /* @__PURE__ */ jsxRuntime.jsx(
15120
+ SSelect2,
15121
+ {
15122
+ value: typeof periodValue.value === "string" ? periodValue.value : "first",
15123
+ onValueChange: updateInputValue,
15124
+ emitValue: true,
15125
+ disabled,
15126
+ width: "100%",
15127
+ options: [
15128
+ { value: "first", label: "\uC0C1\uBC18\uAE30" },
15129
+ { value: "second", label: "\uD558\uBC18\uAE30" }
15130
+ ]
15131
+ }
15132
+ );
15133
+ }
15134
+ if (selectedUnit === "year") {
15135
+ return /* @__PURE__ */ jsxRuntime.jsx(
15136
+ SDatePicker,
15137
+ {
15138
+ mode: "year",
15139
+ value: typeof periodValue.value === "string" ? periodValue.value : null,
15140
+ onValueChange: updateInputValue,
15141
+ selectable: field.selectable,
15142
+ disabled,
15143
+ width: "100%"
15144
+ }
15145
+ );
15146
+ }
15147
+ return /* @__PURE__ */ jsxRuntime.jsx(
15148
+ SDateRangePicker,
15149
+ {
15150
+ value: Array.isArray(periodValue.value) && periodValue.value.length === 2 ? periodValue.value : null,
15151
+ onValueChange: updateInputValue,
15152
+ selectable: field.selectable,
15153
+ maxRange: field.maxRange,
15154
+ disabled,
15155
+ width: "100%"
15156
+ }
15157
+ );
15158
+ };
15159
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-[var(--cmp-chipFilter-listbox-top-gap)]", children: [
15160
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-[8px]", children: presets.map((preset) => /* @__PURE__ */ jsxRuntime.jsx(
15161
+ SRadio,
15162
+ {
15163
+ val: preset.value,
15164
+ value: selectedId,
15165
+ label: preset.label,
15166
+ disabled,
15167
+ onValueChange: () => selectPreset(preset)
15168
+ },
15169
+ preset.value
15170
+ )) }),
15171
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, className: "h-px w-full", style: { background: CHIPFILTER.listboxBorder } }),
15172
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full [&>*]:w-full", children: renderInput() })
15173
+ ] });
15174
+ }
14862
15175
  function InlineDatePresetFilter({
14863
15176
  field,
14864
15177
  value,
@@ -15863,7 +16176,9 @@ var TIMEPICKER_SIZE_CONFIG = {
15863
16176
  fontSize: 12,
15864
16177
  lineHeight: 20,
15865
16178
  minWidth: 80,
15866
- fieldMinWidth: 128
16179
+ fieldMinWidth: 128,
16180
+ // 상한 등급 md
16181
+ fieldMaxWidth: 160
15867
16182
  },
15868
16183
  md: {
15869
16184
  height: V3("md-height"),
@@ -15875,7 +16190,9 @@ var TIMEPICKER_SIZE_CONFIG = {
15875
16190
  fontSize: 14,
15876
16191
  lineHeight: 24,
15877
16192
  minWidth: 96,
15878
- fieldMinWidth: 160
16193
+ fieldMinWidth: 160,
16194
+ // 상한 등급 lg
16195
+ fieldMaxWidth: 240
15879
16196
  }
15880
16197
  };
15881
16198
  function clampTimePart(value, min, max) {
@@ -16199,6 +16516,7 @@ function STimePicker({
16199
16516
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
16200
16517
  width,
16201
16518
  minWidth: dims.fieldMinWidth,
16519
+ maxWidth: dims.fieldMaxWidth,
16202
16520
  disabled,
16203
16521
  className,
16204
16522
  style,
@@ -16315,6 +16633,10 @@ var MIDDAY_FIELD_MIN_WIDTH = {
16315
16633
  sm: 178,
16316
16634
  md: 216
16317
16635
  };
16636
+ var MIDDAY_FIELD_MAX_WIDTH = {
16637
+ sm: 240,
16638
+ md: 240
16639
+ };
16318
16640
  function RangeTimeSelector({
16319
16641
  value,
16320
16642
  useMeridiem,
@@ -16661,6 +16983,7 @@ function STimeRangePicker({
16661
16983
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
16662
16984
  width,
16663
16985
  minWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MIN_WIDTH[size] : dims.fieldMinWidth,
16986
+ maxWidth: effectiveUseMeridiem ? MIDDAY_FIELD_MAX_WIDTH[size] : dims.fieldMaxWidth,
16664
16987
  disabled,
16665
16988
  className,
16666
16989
  style,
@@ -16769,6 +17092,7 @@ exports.GNB_RAIL_WIDTH = GNB_RAIL_WIDTH;
16769
17092
  exports.ICONS = ICONS;
16770
17093
  exports.IMAGE_FITS = IMAGE_FITS;
16771
17094
  exports.PAGE_BACKGROUNDS = PAGE_BACKGROUNDS;
17095
+ exports.PAGE_CONTENT_HEIGHTS = PAGE_CONTENT_HEIGHTS;
16772
17096
  exports.RangeCalendar = RangeCalendar;
16773
17097
  exports.SActionModal = SActionModal;
16774
17098
  exports.SBadge = SBadge;
@@ -16785,6 +17109,8 @@ exports.SChipInput = SChipInput;
16785
17109
  exports.SCircleProgress = SCircleProgress;
16786
17110
  exports.SConfirmModal = SConfirmModal;
16787
17111
  exports.SDatePicker = SDatePicker;
17112
+ exports.SDatePickerMonthListbox = SDatePickerMonthListbox;
17113
+ exports.SDatePickerYearListbox = SDatePickerYearListbox;
16788
17114
  exports.SDateRangePicker = SDateRangePicker;
16789
17115
  exports.SDivider = SDivider;
16790
17116
  exports.SDraggableGroup = SDraggableGroup;