sellmate-design-system-react 9.0.0-beta.14 → 9.0.0-beta.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/AGENTS.md +244 -45
  2. package/README.md +45 -12
  3. package/dist/components/SBarcodeInput/README.md +1 -1
  4. package/dist/components/SBarcodeInput/SBarcodeInput.d.ts +7 -1
  5. package/dist/components/SChipInput/README.md +1 -1
  6. package/dist/components/SChipInput/SChipInput.d.ts +7 -1
  7. package/dist/components/SDatePicker/README.md +1 -1
  8. package/dist/components/SDatePicker/SDatePicker.d.ts +7 -1
  9. package/dist/components/SDateRangePicker/README.md +1 -1
  10. package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +7 -1
  11. package/dist/components/SField/README.md +3 -3
  12. package/dist/components/SField/SField.d.ts +12 -6
  13. package/dist/components/SFilePicker/README.md +1 -1
  14. package/dist/components/SFilePicker/SFilePicker.d.ts +7 -1
  15. package/dist/components/SGhostButton/README.md +2 -0
  16. package/dist/components/SInput/README.md +1 -1
  17. package/dist/components/SInput/SInput.d.ts +7 -1
  18. package/dist/components/SKeyValueTable/README.md +4 -1
  19. package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +6 -2
  20. package/dist/components/SNumberInput/README.md +1 -1
  21. package/dist/components/SNumberInput/SNumberInput.d.ts +7 -1
  22. package/dist/components/SPage/README.md +1 -1
  23. package/dist/components/SPage/SPage.d.ts +7 -1
  24. package/dist/components/SSearchInput/README.md +1 -1
  25. package/dist/components/SSearchInput/SSearchInput.d.ts +8 -2
  26. package/dist/components/SSelect/README.md +1 -1
  27. package/dist/components/SSelect/SSelect.d.ts +8 -1
  28. package/dist/components/SSwitch/README.md +13 -0
  29. package/dist/components/STable/README.md +28 -67
  30. package/dist/components/STable/STable.d.ts +70 -6
  31. package/dist/components/STable/index.d.ts +1 -1
  32. package/dist/components/STextarea/README.md +1 -1
  33. package/dist/components/STextarea/STextarea.d.ts +7 -1
  34. package/dist/components/STimePicker/README.md +1 -1
  35. package/dist/components/STimePicker/STimePicker.d.ts +7 -1
  36. package/dist/components/STimePicker/timepicker.config.d.ts +4 -2
  37. package/dist/components/STimeRangePicker/README.md +1 -1
  38. package/dist/components/STimeRangePicker/STimeRangePicker.d.ts +7 -1
  39. package/dist/components/STooltip/README.md +2 -0
  40. package/dist/index.cjs +143 -97
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.ts +1 -0
  43. package/dist/index.js +141 -98
  44. package/dist/index.js.map +1 -1
  45. package/dist/lib/field-width.d.ts +31 -0
  46. package/dist/llms-full.txt +301 -131
  47. package/dist/llms.txt +244 -45
  48. package/dist/styles.css +10 -6
  49. package/eslint/index.mjs +5 -0
  50. package/eslint/lib/table-column.mjs +17 -0
  51. package/eslint/rules/field-width-grade.mjs +27 -10
  52. package/eslint/rules/table-column-width.mjs +109 -0
  53. package/eslint/scale.gen.mjs +3 -0
  54. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -260,6 +260,16 @@ function resolveColor(input, fallback) {
260
260
  if (input === null || input === void 0 || input === "") return fallback;
261
261
  return isColorKey(input) ? `var(--color-${input.replace(/_/g, "-")})` : input;
262
262
  }
263
+
264
+ // src/lib/field-width.ts
265
+ var FIELD_WIDTH_GRADES = ["xs", "sm", "md", "lg", "xl"];
266
+ var GRADE_SET = new Set(FIELD_WIDTH_GRADES);
267
+ var isFieldWidthGrade = (v) => typeof v === "string" && GRADE_SET.has(v);
268
+ var fieldWidthValue = (v) => {
269
+ if (v === void 0 || v === "" || v === 0) return void 0;
270
+ if (isFieldWidthGrade(v)) return `var(--cmp-field-width-${v})`;
271
+ return typeof v === "number" ? `${v}px` : v;
272
+ };
263
273
  var Icon_add = (p) => {
264
274
  const { size = 24, ...rest } = p;
265
275
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -3197,7 +3207,9 @@ var SCircleProgress = /* @__PURE__ */ react.forwardRef(
3197
3207
  {
3198
3208
  ref,
3199
3209
  className: cn(
3200
- "inline-flex flex-col items-center gap-[var(--cmp-progress-circular-gap)]",
3210
+ // inline-flex 유지: 폭이 곧 내용 폭이라 블록으로 바꾸면 부모 폭까지 늘어나
3211
+ // 가운데 정렬이 깨진다. 대신 align-middle 로 baseline 아래 descender 여백을 없앤다.
3212
+ "inline-flex flex-col items-center align-middle gap-[var(--cmp-progress-circular-gap)]",
3201
3213
  className
3202
3214
  ),
3203
3215
  style,
@@ -3448,7 +3460,7 @@ var SDraggableItemView = /* @__PURE__ */ react.forwardRef(
3448
3460
  titleTypo,
3449
3461
  disabled ? "bg-(--cmp-listDraggableItem-bg-default) text-(--sys-color-disabled-text)" : selected ? "bg-(--cmp-listDraggableItem-bg-selected) text-(--cmp-listDraggableItem-text-selected)" : "bg-(--cmp-listDraggableItem-bg-default) text-(--cmp-listDraggableItem-text-default)",
3450
3462
  !disabled && !selected && !dragging && "hover:bg-(--cmp-listDraggableItem-bg-hover)",
3451
- hasSupportingText && supportingTextPosition === "bottom" ? dense ? "min-h-[52px]" : "min-h-[60px]" : dense ? "h-[32px]" : "h-[40px]",
3463
+ // 높이는 세로 패딩과 내용이 정한다 — 항목에 높이를 못 박지 않는다 (SListItem 과 동일)
3452
3464
  paddingYClass,
3453
3465
  // 항목 사이 구분선은 리스트가 그린다.
3454
3466
  bordered && cn(
@@ -4115,7 +4127,8 @@ var SExpansionItem = /* @__PURE__ */ react.forwardRef(
4115
4127
  titleTypo,
4116
4128
  isExpanded && "border-b border-(--cmp-listExpansionItem-title-border)",
4117
4129
  hasChevronInteraction ? "hover:bg-(--sys-color-bg-neutralBright)" : !isExpanded && "hover:bg-(--cmp-listExpansionItem-title-bg-hover)",
4118
- hasSupportingText && supportingTextPosition === "bottom" ? dense ? "min-h-[52px] py-(--cmp-listExpansionItem-title-paddingY-dense)" : "min-h-[60px] py-(--cmp-listExpansionItem-title-paddingY-normal)" : dense ? "h-[32px] py-(--cmp-listExpansionItem-title-paddingY-dense)" : "h-[40px] py-(--cmp-listExpansionItem-title-paddingY-normal)",
4130
+ // 높이는 세로 패딩과 내용이 정한다 — 제목 줄에 높이를 못 박지 않는다 (SListItem 과 동일)
4131
+ dense ? "py-(--cmp-listExpansionItem-title-paddingY-dense)" : "py-(--cmp-listExpansionItem-title-paddingY-normal)",
4119
4132
  "gap-(--cmp-listExpansionItem-title-gap)",
4120
4133
  "pr-(--cmp-listExpansionItem-title-paddingX)",
4121
4134
  depth === 2 ? "pl-[36px]" : depth === 3 ? "pl-[56px]" : "pl-[16px]",
@@ -4238,7 +4251,10 @@ var SListItemImpl = /* @__PURE__ */ react.forwardRef(function SListItem({
4238
4251
  "cursor-pointer",
4239
4252
  hasChevronInteraction ? "hover:bg-(--sys-color-bg-neutralBright)" : "hover:bg-(--cmp-listItem-bg-hover)"
4240
4253
  ) : "cursor-default",
4241
- hasSupportingText && supportingTextPosition === "bottom" ? dense ? "min-h-[52px] py-(--cmp-listItem-paddingY-dense)" : "min-h-[60px] py-(--cmp-listItem-paddingY-normal)" : dense ? "h-[32px] py-(--cmp-listItem-paddingY-dense)" : "h-[40px] py-(--cmp-listItem-paddingY-normal)",
4254
+ // 높이는 세로 패딩과 내용이 정한다 — 항목에 높이를 못 박지 않는다.
4255
+ // 슬롯(leading/trailing)이 글줄보다 크면 그만큼 행이 커져야 하고,
4256
+ // 고정 높이를 두면 그 슬롯이 잘리거나 상자 밖으로 넘친다.
4257
+ dense ? "py-(--cmp-listItem-paddingY-dense)" : "py-(--cmp-listItem-paddingY-normal)",
4242
4258
  "gap-(--cmp-listItem-gap)",
4243
4259
  "pr-(--cmp-listItem-paddingX)",
4244
4260
  depth === 2 ? "pl-[36px]" : depth === 3 ? "pl-[56px]" : "pl-[16px]",
@@ -4305,40 +4321,17 @@ function getInitialOpenKeys(children) {
4305
4321
  });
4306
4322
  return openKeys;
4307
4323
  }
4308
- function getInitialSelectedKeys(children) {
4309
- const selectedKeys = {};
4310
- react.Children.forEach(children, (child, index) => {
4311
- if (!isListItemElement(child) || !child.props.selected) return;
4312
- const depth = child.props.depth ?? 1;
4313
- if (selectedKeys[depth] == null) selectedKeys[depth] = String(child.key ?? index);
4314
- });
4315
- return selectedKeys;
4316
- }
4317
4324
  var SExpansionListImpl = /* @__PURE__ */ react.forwardRef(function SExpansionList({ children, ...rest }, ref) {
4318
4325
  const [openKeys, setOpenKeys] = react.useState(() => getInitialOpenKeys(children));
4319
- const [selectedKeys, setSelectedKeys] = react.useState(
4320
- () => getInitialSelectedKeys(children)
4321
- );
4322
4326
  const listChildren = react.Children.map(children, (child, index) => {
4323
4327
  if (isListItemElement(child)) {
4324
- const depth2 = child.props.depth ?? 1;
4325
- const itemKey2 = String(child.key ?? index);
4326
- return react.cloneElement(child, {
4327
- // 선택을 리스트가 관리하는 이상 아이템은 클릭 가능해야 한다 — SListItem 의 기본값(false)을 뒤집는다
4328
- clickable: child.props.clickable ?? true,
4329
- selected: selectedKeys[depth2] === itemKey2,
4330
- onClick: (event) => {
4331
- setSelectedKeys((current) => ({ ...current, [depth2]: itemKey2 }));
4332
- child.props.onClick?.(event);
4333
- }
4334
- });
4328
+ return react.cloneElement(child, { clickable: child.props.clickable ?? true });
4335
4329
  }
4336
4330
  if (!isExpansionItemElement(child)) return child;
4337
4331
  const depth = child.props.depth ?? 1;
4338
4332
  const itemKey = String(child.key ?? index);
4339
4333
  return react.cloneElement(child, {
4340
4334
  expanded: openKeys[depth] === itemKey,
4341
- selected: void 0,
4342
4335
  onToggle: (next, event) => {
4343
4336
  setOpenKeys((current) => ({
4344
4337
  ...current,
@@ -5945,7 +5938,7 @@ var SLinearProgress = /* @__PURE__ */ react.forwardRef(
5945
5938
  "div",
5946
5939
  {
5947
5940
  ref,
5948
- className: cn("inline-flex w-full flex-col gap-[12px]", className),
5941
+ className: cn("flex w-full flex-col gap-[12px]", className),
5949
5942
  style,
5950
5943
  children: [
5951
5944
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-[20px] w-full overflow-hidden rounded-full bg-[var(--cmp-progress-color-track)]", children: [
@@ -9202,7 +9195,7 @@ function SPageOverlayScrollbars({ scrollRef }) {
9202
9195
  }
9203
9196
  var SPage = /* @__PURE__ */ react.forwardRef(function SPage2({
9204
9197
  background = "frame",
9205
- scrollEndSpacing = true,
9198
+ scrollEndSpacing = false,
9206
9199
  overlayScrollbar = false,
9207
9200
  contentHeight = "auto",
9208
9201
  header,
@@ -10152,9 +10145,11 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
10152
10145
  const showError = resolvedError && errorMessage !== "";
10153
10146
  const interactive = !disabled && !readOnly;
10154
10147
  const isPercentWidth = typeof width === "string" && width.trim().endsWith("%");
10155
- const hug = cssLen(width) !== void 0 && !isPercentWidth;
10148
+ const resolvedWidth = fieldWidthValue(width);
10149
+ const hug = resolvedWidth !== void 0 && !isPercentWidth;
10156
10150
  const addonWidth = effectiveAddonLabel !== "" ? cssLen(labelWidth) : void 0;
10157
- const resolvedMaxWidth = cssLen(maxWidth) === void 0 || effectiveAddonLabel === "" ? cssLen(maxWidth) : addonWidth ? `calc(${cssLen(maxWidth)} + ${addonWidth})` : void 0;
10151
+ const maxWidthValue = fieldWidthValue(maxWidth);
10152
+ const resolvedMaxWidth = maxWidthValue === void 0 || effectiveAddonLabel === "" ? maxWidthValue : addonWidth ? `calc(${maxWidthValue} + ${addonWidth})` : void 0;
10158
10153
  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)";
10159
10154
  const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
10160
10155
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
@@ -10212,8 +10207,8 @@ var SField = /* @__PURE__ */ react.forwardRef(function SField2({
10212
10207
  {
10213
10208
  className: cn("flex min-w-0 flex-col", !hug && !isPercentWidth && "flex-1"),
10214
10209
  style: {
10215
- width: cssLen(width),
10216
- minWidth: cssLen(minWidth),
10210
+ width: resolvedWidth,
10211
+ minWidth: fieldWidthValue(minWidth),
10217
10212
  maxWidth: resolvedMaxWidth
10218
10213
  },
10219
10214
  children: [
@@ -11056,10 +11051,16 @@ var DEFAULT_ROWS_PER_PAGE_OPTION = [
11056
11051
  { label: "100\uAC1C\uC529 \uBCF4\uAE30", value: 100 }
11057
11052
  ];
11058
11053
  var cellValue = (col, row) => {
11059
- const raw = typeof col.field === "function" ? col.field(row) : row[col.field];
11054
+ const raw = col.field == null ? void 0 : typeof col.field === "function" ? col.field(row) : row[col.field];
11060
11055
  return col.format ? col.format(raw, row) : raw;
11061
11056
  };
11062
- var colWidthFromDef = (c) => c.autoWidth ? 0 : parseInt(c.width || "120", 10);
11057
+ var DEFAULT_COLUMN_WIDTH = 120;
11058
+ var colWidthFromDef = (c) => {
11059
+ if (c.autoWidth) return 0;
11060
+ if (c.width == null) return DEFAULT_COLUMN_WIDTH;
11061
+ const px = typeof c.width === "number" ? c.width : parseInt(c.width, 10);
11062
+ return Number.isFinite(px) ? px : DEFAULT_COLUMN_WIDTH;
11063
+ };
11063
11064
  var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11064
11065
  columns = [],
11065
11066
  rows = [],
@@ -11067,6 +11068,8 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11067
11068
  selectable = false,
11068
11069
  selected = [],
11069
11070
  onSelectedChange,
11071
+ sort = null,
11072
+ onSortChange,
11070
11073
  resizable = false,
11071
11074
  width,
11072
11075
  height,
@@ -11366,6 +11369,14 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11366
11369
  SCROLLBAR_TRACK_BG_CLASS,
11367
11370
  SCROLLBAR_TRACK_BORDER_CLASS
11368
11371
  );
11372
+ const toggleSort = (col) => {
11373
+ if (!onSortChange) return;
11374
+ if (sort?.name !== col.name) {
11375
+ onSortChange({ name: col.name, dir: "asc" });
11376
+ return;
11377
+ }
11378
+ onSortChange(sort.dir === "asc" ? { name: col.name, dir: "desc" } : null);
11379
+ };
11369
11380
  const colgroup = /* @__PURE__ */ jsxRuntime.jsxs("colgroup", { children: [
11370
11381
  selectable && /* @__PURE__ */ jsxRuntime.jsx("col", { style: { width: SELECTABLE_COLUMN_WIDTH } }),
11371
11382
  visibleCols.map((col, i) => /* @__PURE__ */ jsxRuntime.jsx("col", { style: col.autoWidth ? void 0 : { width: columnWidths[i] } }, col.name))
@@ -11443,49 +11454,81 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
11443
11454
  ]
11444
11455
  }
11445
11456
  ),
11446
- visibleCols.map((col, i) => /* @__PURE__ */ jsxRuntime.jsxs(
11447
- "th",
11448
- {
11449
- className: cn(
11450
- // 하단 border는 헤더 래퍼가 연속으로 그린다 (거터 영역까지 이어짐).
11451
- "relative bg-[var(--cmp-table-header-bg)] px-[16px] text-[12px] font-medium leading-[20px] text-[color:var(--sys-color-fg-primary)]",
11452
- stickyInfo(i).isSticky && "z-[6]",
11453
- stickyShadowClass(i),
11454
- col.thClass
11455
- ),
11456
- style: { textAlign: col.align ?? "left", ...cellStyle(i), ...col.thStyle },
11457
- children: [
11458
- /* @__PURE__ */ jsxRuntime.jsx(
11459
- "span",
11460
- {
11461
- className: cn(
11462
- // 원본 th__content: align에 따라 justify-content 적용 (th도 정렬 반영)
11463
- "inline-flex w-full min-w-0 items-center gap-[4px]",
11464
- col.align === "center" && "justify-center",
11465
- col.align === "right" && "justify-end"
11466
- ),
11467
- children: col.renderHeader ? col.renderHeader(col, i) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11468
- col.icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: col.icon, size: 16, color: col.iconColor }),
11469
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: col.label })
11470
- ] })
11471
- }
11457
+ visibleCols.map((col, i) => {
11458
+ const isSortable = !!col.sortable && !col.renderHeader;
11459
+ const sortDir = isSortable && sort?.name === col.name ? sort.dir : void 0;
11460
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11461
+ "th",
11462
+ {
11463
+ className: cn(
11464
+ // 하단 border는 헤더 래퍼가 연속으로 그린다 (거터 영역까지 이어짐).
11465
+ "relative bg-[var(--cmp-table-header-bg)] px-[16px] text-[12px] font-medium leading-[20px] text-[color:var(--sys-color-fg-primary)]",
11466
+ stickyInfo(i).isSticky && "z-[6]",
11467
+ stickyShadowClass(i),
11468
+ col.thClass
11472
11469
  ),
11473
- resizable && !col.autoWidth && i < visibleCols.length - 1 && // 원본 th__resizer: 셀 우측 경계 안쪽 4px 그립(좌우 1px #CCCCCC 라인, 세로 16px).
11474
- // 셀 밖으로 넘치면 다음 셀에 가려져 클릭이 안 먹으므로 translate 없이 셀 내부에 배치.
11475
- // 겉보기 그립은 4px(원본과 동일), 클릭 영역은 9px로 넓혀 사용성 확보.
11476
- /* @__PURE__ */ jsxRuntime.jsx(
11477
- "span",
11478
- {
11479
- onMouseDown: (e) => startResize(i, e),
11480
- className: "absolute right-0 top-1/2 z-[5] flex h-[16px] w-[9px] -translate-y-1/2 cursor-col-resize items-center justify-end",
11481
- "aria-hidden": true,
11482
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-full w-[4px] border-x border-solid border-[color:var(--cmp-table-header-resizingBar-color)]" })
11483
- }
11484
- )
11485
- ]
11486
- },
11487
- col.name
11488
- ))
11470
+ style: { textAlign: col.align ?? "left", ...cellStyle(i), ...col.thStyle },
11471
+ "aria-sort": isSortable ? sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : "none" : void 0,
11472
+ children: [
11473
+ /* @__PURE__ */ jsxRuntime.jsx(
11474
+ "span",
11475
+ {
11476
+ className: cn(
11477
+ // 원본 th__content: align에 따라 justify-content 적용 (th도 정렬 반영)
11478
+ "inline-flex w-full min-w-0 items-center gap-[4px]",
11479
+ col.align === "center" && "justify-center",
11480
+ col.align === "right" && "justify-end"
11481
+ ),
11482
+ children: col.renderHeader ? col.renderHeader(col, i) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11483
+ col.icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: col.icon, size: 16, color: col.iconColor }),
11484
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: col.label }),
11485
+ isSortable && // 미정렬은 updown 을 default(회색)로 — 정렬할 수 있다는 것 자체가
11486
+ // 헤더에 보여야 한다. 정렬 중이면 action 색으로 올려 활성임을 알린다.
11487
+ // 라벨이 truncate 돼도 버튼은 남도록 shrink-0.
11488
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
11489
+ SGhostButton,
11490
+ {
11491
+ size: "xxs",
11492
+ intent: sortDir ? "action" : "default",
11493
+ icon: sortDir === "asc" ? "arrowUp" : sortDir === "desc" ? "arrowDown" : "updown",
11494
+ ariaLabel: `${col.label} \uC815\uB82C`,
11495
+ onClick: () => toggleSort(col)
11496
+ }
11497
+ ) }),
11498
+ col.helpText && col.helpText.length > 0 && // 라벨이 좁아져 truncate 되더라도 아이콘까지 함께 줄어들면 안 되므로
11499
+ // shrink-0 래퍼로 감싼다 (STooltip 의 className 은 본문 쪽으로 간다).
11500
+ // 아이콘·크기·색은 SKeyValueTable 헤더 도움말과 동일하게 맞춘다.
11501
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
11502
+ STooltip2,
11503
+ {
11504
+ trigger: "hover",
11505
+ icon: "helpOutline",
11506
+ iconSize: 12,
11507
+ color: "var(--sys-color-fg-success)",
11508
+ placement: "top",
11509
+ message: col.helpText
11510
+ }
11511
+ ) })
11512
+ ] })
11513
+ }
11514
+ ),
11515
+ resizable && !col.autoWidth && i < visibleCols.length - 1 && // 원본 th__resizer: 셀 우측 경계 안쪽 4px 그립(좌우 1px #CCCCCC 라인, 세로 16px).
11516
+ // 셀 밖으로 넘치면 다음 셀에 가려져 클릭이 안 먹으므로 translate 없이 셀 내부에 배치.
11517
+ // 겉보기 그립은 4px(원본과 동일), 클릭 영역은 9px로 넓혀 사용성 확보.
11518
+ /* @__PURE__ */ jsxRuntime.jsx(
11519
+ "span",
11520
+ {
11521
+ onMouseDown: (e) => startResize(i, e),
11522
+ className: "absolute right-0 top-1/2 z-[5] flex h-[16px] w-[9px] -translate-y-1/2 cursor-col-resize items-center justify-end",
11523
+ "aria-hidden": true,
11524
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-full w-[4px] border-x border-solid border-[color:var(--cmp-table-header-resizingBar-color)]" })
11525
+ }
11526
+ )
11527
+ ]
11528
+ },
11529
+ col.name
11530
+ );
11531
+ })
11489
11532
  ] }) })
11490
11533
  ]
11491
11534
  }
@@ -13039,9 +13082,10 @@ function SDatePicker({
13039
13082
  icon: 20,
13040
13083
  minW: 104,
13041
13084
  fieldMinW: 168,
13042
- // 상한 등급 lg — 값 길이가 YYYY-MM-DD 로 정해져 있어, width="100%" 를 받아도
13043
- // 행 전체로 늘어나지 않게 막는다 (SKeyValueTable 이 모든 컨트롤에 100% 를 넘긴다)
13044
- fieldMaxW: 240,
13085
+ // 값 길이가 YYYY-MM-DD 로 정해져 있어, width="100%" 를 받아도 행 전체로
13086
+ // 늘어나지 않게 상한을 건다 (SKeyValueTable 이 모든 컨트롤에 100% 를 넘긴다).
13087
+ // px 가 아니라 등급이라 --cmp-field-width-* 가 바뀌면 함께 따라간다
13088
+ fieldMaxW: "lg",
13045
13089
  fontSize: 14,
13046
13090
  lineHeight: 24,
13047
13091
  radius: 6
@@ -13051,8 +13095,7 @@ function SDatePicker({
13051
13095
  icon: 16,
13052
13096
  minW: 80,
13053
13097
  fieldMinW: 128,
13054
- // 상한 등급 md
13055
- fieldMaxW: 160,
13098
+ fieldMaxW: "md",
13056
13099
  fontSize: 12,
13057
13100
  lineHeight: 20,
13058
13101
  radius: 4
@@ -13544,9 +13587,9 @@ function SDateRangePicker({
13544
13587
  icon: 20,
13545
13588
  minW: 104,
13546
13589
  fieldMinW: 260,
13547
- // 상한 등급 xl — 값이 `YYYY-MM-DD ~ YYYY-MM-DD` 라 단일 날짜의 두 배가 넘는다.
13548
- // 최소너비(260)가 이미 lg(240)를 넘으므로 상한도 한 등급 위여야 한다
13549
- fieldMaxW: 480,
13590
+ // xl — 값이 `YYYY-MM-DD ~ YYYY-MM-DD` 라 단일 날짜의 두 배가 넘는다.
13591
+ // 최소너비(260)가 이미 lg 를 넘으므로 상한도 한 등급 위여야 한다
13592
+ fieldMaxW: "xl",
13550
13593
  fontSize: 14,
13551
13594
  lineHeight: 24,
13552
13595
  radius: 6
@@ -13556,8 +13599,8 @@ function SDateRangePicker({
13556
13599
  icon: 16,
13557
13600
  minW: 80,
13558
13601
  fieldMinW: 216,
13559
- // 상한 등급 lg — 최소너비 216 이 md(160)를 넘어 md 로는 담기지 않는다
13560
- fieldMaxW: 240,
13602
+ // lg — 최소너비 216 이 md 를 넘어 md 로는 담기지 않는다
13603
+ fieldMaxW: "lg",
13561
13604
  fontSize: 12,
13562
13605
  lineHeight: 20,
13563
13606
  radius: 4
@@ -14085,6 +14128,8 @@ function SKeyValueTable({
14085
14128
  return /* @__PURE__ */ jsxRuntime.jsx(SRadioGroup, { direction: "horizontal", ...opt, onValueChange: up });
14086
14129
  case "checkbox":
14087
14130
  return /* @__PURE__ */ jsxRuntime.jsx(SCheckbox, { ...opt, onValueChange: up });
14131
+ case "switch":
14132
+ return /* @__PURE__ */ jsxRuntime.jsx(SSwitch, { ...opt, onValueChange: up });
14088
14133
  case "date-picker":
14089
14134
  return /* @__PURE__ */ jsxRuntime.jsx(SDatePicker, { width: "100%", ...opt, onValueChange: up });
14090
14135
  case "date-range-picker":
@@ -14141,6 +14186,7 @@ function SKeyValueTable({
14141
14186
  const cellBorder = r === 0 ? "" : "border-t border-solid border-[color:var(--cmp-table-border-color)]";
14142
14187
  return /* @__PURE__ */ jsxRuntime.jsx("tr", { children: row.map((f, c) => {
14143
14188
  const showTh = !f.hideTh && !skip[r][c];
14189
+ const isReadOnlyCell = f.render == null && (f.type == null || f.type === "text");
14144
14190
  return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
14145
14191
  showTh && /* @__PURE__ */ jsxRuntime.jsx(
14146
14192
  "th",
@@ -14184,9 +14230,8 @@ function SKeyValueTable({
14184
14230
  colSpan: f.tdColSpan && f.tdColSpan > 1 ? f.tdColSpan : void 0,
14185
14231
  className: cn(
14186
14232
  "box-border bg-[var(--sys-color-bg-frame)] align-middle",
14187
- // text 타입(및 미지정 기본값)은 읽기 전용 콘텐츠라 넓은 paddingX(wide),
14188
- // 폼 필드 셀은 default paddingX 유지
14189
- f.type == null || f.type === "text" ? "px-[var(--cmp-table-keyValue-body-paddingX-wide)]" : "px-[var(--cmp-table-keyValue-body-paddingX-default)]",
14233
+ // 읽기 전용 콘텐츠는 넓은 paddingX(wide), 폼 필드 셀은 default paddingX 유지
14234
+ isReadOnlyCell ? "px-[var(--cmp-table-keyValue-body-paddingX-wide)]" : "px-[var(--cmp-table-keyValue-body-paddingX-default)]",
14190
14235
  cellBorder,
14191
14236
  f.tdClass
14192
14237
  ),
@@ -16714,8 +16759,7 @@ var TIMEPICKER_SIZE_CONFIG = {
16714
16759
  lineHeight: 20,
16715
16760
  minWidth: 80,
16716
16761
  fieldMinWidth: 128,
16717
- // 상한 등급 md
16718
- fieldMaxWidth: 160
16762
+ fieldMaxWidth: "md"
16719
16763
  },
16720
16764
  md: {
16721
16765
  height: V3("md-height"),
@@ -16728,8 +16772,7 @@ var TIMEPICKER_SIZE_CONFIG = {
16728
16772
  lineHeight: 24,
16729
16773
  minWidth: 96,
16730
16774
  fieldMinWidth: 160,
16731
- // 상한 등급 lg
16732
- fieldMaxWidth: 240
16775
+ fieldMaxWidth: "lg"
16733
16776
  }
16734
16777
  };
16735
16778
  function clampTimePart(value, min, max) {
@@ -17171,8 +17214,8 @@ var MIDDAY_FIELD_MIN_WIDTH = {
17171
17214
  md: 216
17172
17215
  };
17173
17216
  var MIDDAY_FIELD_MAX_WIDTH = {
17174
- sm: 240,
17175
- md: 240
17217
+ sm: "lg",
17218
+ md: "lg"
17176
17219
  };
17177
17220
  function RangeTimeSelector({
17178
17221
  value,
@@ -17617,6 +17660,7 @@ exports.BADGE_COLORS = BADGE_COLORS;
17617
17660
  exports.BUTTON_COLORS = BUTTON_COLORS;
17618
17661
  exports.BUTTON_SIZES = BUTTON_SIZES;
17619
17662
  exports.COLOR_KEYS = COLOR_KEYS;
17663
+ exports.FIELD_WIDTH_GRADES = FIELD_WIDTH_GRADES;
17620
17664
  exports.GNB_COLORS = GNB_COLORS;
17621
17665
  exports.GNB_COMMON = GNB_COMMON;
17622
17666
  exports.GNB_FOLD_MS = GNB_FOLD_MS;
@@ -17718,9 +17762,11 @@ exports.TIMEPICKER_SIZES = TIMEPICKER_SIZES;
17718
17762
  exports.TIMEPICKER_SIZE_CONFIG = TIMEPICKER_SIZE_CONFIG;
17719
17763
  exports.buttonPreset = buttonPreset;
17720
17764
  exports.cn = cn;
17765
+ exports.fieldWidthValue = fieldWidthValue;
17721
17766
  exports.findGnbAncestors = findGnbAncestors;
17722
17767
  exports.findGnbRailValue = findGnbRailValue;
17723
17768
  exports.isColorKey = isColorKey;
17769
+ exports.isFieldWidthGrade = isFieldWidthGrade;
17724
17770
  exports.loading = loading;
17725
17771
  exports.resolveColor = resolveColor;
17726
17772
  exports.useSLayout = useSLayout;