sellmate-design-system-react 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { clsx } from 'clsx';
3
3
  import { extendTailwindMerge } from 'tailwind-merge';
4
- import { forwardRef, useState, useId, useEffect, useRef, useCallback, createContext, useImperativeHandle, useMemo, useContext, useLayoutEffect, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
4
+ import { forwardRef, useState, useId, useEffect, useRef, useCallback, createContext, useImperativeHandle, useMemo, useContext, Fragment as Fragment$1, useLayoutEffect, useSyncExternalStore } from 'react';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  import { createPortal } from 'react-dom';
7
7
  import { createRoot } from 'react-dom/client';
@@ -4873,7 +4873,8 @@ function SConfirmModal({
4873
4873
  const mainStyle = MAIN_BUTTON_STYLE[mainButtonName ?? DEFAULT_MAIN_BUTTON[type]];
4874
4874
  const tagNode = tagSlot ?? (tagLabel ? /* @__PURE__ */ jsx(STag, { shape: tagShape, size: tagSize, color: tagColor, label: tagLabel }) : null);
4875
4875
  const optionNode = optionSlot ?? (slotLabel ? /* @__PURE__ */ jsx("span", { children: slotLabel }) : null);
4876
- const hasBody = topMessage.length > 0 || bottomMessage.length > 0 || tagNode != null || optionNode != null;
4876
+ const hasContentBox = tagNode != null || optionNode != null;
4877
+ const hasBody = topMessage.length > 0 || bottomMessage.length > 0 || hasContentBox;
4877
4878
  return /* @__PURE__ */ jsx(
4878
4879
  SModalContainer,
4879
4880
  {
@@ -4908,8 +4909,12 @@ function SConfirmModal({
4908
4909
  },
4909
4910
  i
4910
4911
  )) }),
4911
- tagNode != null && // tag 위치 content-box: 흰 박스 + 1px 테두리 + 패딩 + 14/700 (원본 sd-confirm-modal__content-box)
4912
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center gap-[8px] rounded-[8px] border border-solid border-[color:var(--color-grey-30)] bg-white px-[16px] py-[12px] text-[14px] font-bold leading-[22px] w-full", children: tagNode }),
4912
+ hasContentBox && // content-box: 흰 박스 + 1px 테두리 + 패딩 + 14/700 (원본 sd-confirm-modal__content-box)
4913
+ // tag 왼쪽, slotLabel/optionSlot 오른쪽에 gap 8 나란히 놓인다
4914
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-[8px] rounded-[8px] border border-solid border-[color:var(--color-grey-30)] bg-white px-[16px] py-[12px] text-[14px] font-bold leading-[22px] w-full", children: [
4915
+ tagNode,
4916
+ optionNode
4917
+ ] }),
4913
4918
  bottomMessage.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-[2px]", children: bottomMessage.map((m, i) => /* @__PURE__ */ jsx(
4914
4919
  "p",
4915
4920
  {
@@ -4917,8 +4922,7 @@ function SConfirmModal({
4917
4922
  dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(m) }
4918
4923
  },
4919
4924
  i
4920
- )) }),
4921
- optionNode != null && optionNode
4925
+ )) })
4922
4926
  ] })
4923
4927
  ] }),
4924
4928
  /* @__PURE__ */ jsxs("div", { className: "flex flex-shrink-0 gap-[8px]", children: [
@@ -7352,8 +7356,10 @@ var STable = forwardRef(function STable2({
7352
7356
  stickyColumn,
7353
7357
  radius = "default",
7354
7358
  noDataLabel = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
7359
+ noDataSlot,
7355
7360
  isLoading = false,
7356
7361
  dense = false,
7362
+ noHover = false,
7357
7363
  pagination,
7358
7364
  onPageChange,
7359
7365
  useInternalPagination = false,
@@ -7439,7 +7445,10 @@ var STable = forwardRef(function STable2({
7439
7445
  );
7440
7446
  const handleScroll = useCallback(() => {
7441
7447
  const el = scrollRef.current;
7442
- if (!el) return;
7448
+ if (!el) {
7449
+ setHeaderGutter(0);
7450
+ return;
7451
+ }
7443
7452
  const header = headerScrollRef.current;
7444
7453
  if (header) header.scrollLeft = el.scrollLeft;
7445
7454
  setHeaderGutter(el.offsetWidth - el.clientWidth);
@@ -7571,6 +7580,7 @@ var STable = forwardRef(function STable2({
7571
7580
  bodyRows = rows.slice(pageInfo.startIndex, pageInfo.endIndex);
7572
7581
  }
7573
7582
  const isNoData = rowCount === 0 && !isLoading;
7583
+ const showNoDataSlot = isNoData && noDataSlot != null;
7574
7584
  const showPagination = !useVirtualScroll && (pagination != null || useInternalPagination);
7575
7585
  const lastPage = useInternalPagination ? Math.max(1, Math.ceil(rowCount / (innerRowsPerPage || 1))) : pagination?.lastPage ?? Math.max(1, Math.ceil(rowCount / (pagination?.rowsPerPage ?? 10)));
7576
7586
  const displayPage = useInternalPagination ? currentPage : pagination?.page ?? 1;
@@ -7632,6 +7642,12 @@ var STable = forwardRef(function STable2({
7632
7642
  isRightEdge && scrolledRight && "shadow-[-5px_0_8px_0_rgba(34,34,34,0.1)]"
7633
7643
  );
7634
7644
  };
7645
+ const bodyScrollClass = cn(
7646
+ "min-h-0 flex-1 overflow-auto border-t border-solid border-[color:var(--cmp-table-border-color)]",
7647
+ SCROLLBAR_CLASS,
7648
+ SCROLLBAR_TRACK_BG_CLASS,
7649
+ SCROLLBAR_TRACK_BORDER_CLASS
7650
+ );
7635
7651
  const colgroup = /* @__PURE__ */ jsxs("colgroup", { children: [
7636
7652
  selectable && /* @__PURE__ */ jsx("col", { style: { width: SELECTABLE_COLUMN_WIDTH } }),
7637
7653
  visibleCols.map((col, i) => /* @__PURE__ */ jsx("col", { style: col.autoWidth ? void 0 : { width: columnWidths[i] } }, col.name))
@@ -7753,97 +7769,106 @@ var STable = forwardRef(function STable2({
7753
7769
  )
7754
7770
  }
7755
7771
  ),
7756
- /* @__PURE__ */ jsx(
7757
- "div",
7758
- {
7759
- ref: scrollRef,
7760
- onScroll: handleScroll,
7761
- className: cn(
7762
- // 헤더 하단 구분선은 body 상단 border로 그린다. body 래퍼는 스크롤바를 포함한
7763
- // 전체 폭을 차지하므로(스크롤바는 border 안쪽) 헤더 거터의 border-right와 미터가
7764
- // 생기지 않아 색·길이가 균일한 한 줄이 된다.
7765
- "min-h-0 flex-1 overflow-auto border-t border-solid border-[color:var(--cmp-table-border-color)]",
7766
- SCROLLBAR_CLASS,
7767
- SCROLLBAR_TRACK_BG_CLASS,
7768
- SCROLLBAR_TRACK_BORDER_CLASS,
7769
- // 원본 sd-table__scroll-container--loading/--no-data: 로딩·데이터 없음 시 스크롤 차단
7770
- (isLoading || rowCount === 0) && "overflow-hidden"
7771
- ),
7772
- children: /* @__PURE__ */ jsxs(
7773
- "table",
7774
- {
7775
- className: "w-full table-fixed border-separate border-spacing-0 text-left",
7776
- style: { fontSize: 12 },
7777
- children: [
7778
- colgroup,
7779
- /* @__PURE__ */ jsxs("tbody", { children: [
7780
- useVirtualScroll && topSpacer > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": true, style: { height: topSpacer }, children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0" }) }),
7781
- rowCount === 0 ? null : bodyRows.map((row, ri) => {
7782
- const isSel = selectedKeys.has(row[rowKey]);
7783
- const rowBg = "bg-white";
7784
- return /* @__PURE__ */ jsxs(
7785
- "tr",
7786
- {
7787
- onClick: () => onRowClick?.(row),
7788
- className: cn(
7789
- "group/table-row hover:bg-[var(--color-grey-05)]",
7790
- onRowClick && "cursor-pointer"
7791
- ),
7792
- style: { height: useVirtualScroll ? effectiveRowHeight : rowH },
7793
- children: [
7794
- selectable && /* @__PURE__ */ jsx(
7795
- "td",
7796
- {
7797
- className: cn(
7798
- // 원본 .td--selected: 48px 고정폭, 항상 left sticky, 체크박스 중앙정렬
7799
- "sticky left-0 z-[2] border-b border-solid border-[color:var(--cmp-table-border-color)] align-middle group-hover/table-row:bg-[var(--color-grey-05)]",
7800
- rowBg,
7801
- stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
7802
- ),
7803
- style: {
7804
- width: SELECTABLE_COLUMN_WIDTH,
7805
- minWidth: SELECTABLE_COLUMN_WIDTH,
7806
- maxWidth: SELECTABLE_COLUMN_WIDTH
7807
- },
7808
- onClick: (e) => e.stopPropagation(),
7809
- children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(SCheckbox, { value: isSel, onValueChange: () => toggleRow(row) }) })
7810
- }
7772
+ showNoDataSlot ? /* @__PURE__ */ jsx("div", { className: cn(bodyScrollClass, "pointer-events-auto bg-white"), children: /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-[60px] w-full items-center justify-center", children: noDataSlot }) }) : (
7773
+ /* 바디: 세로 스크롤은 이 컨테이너에만 생긴다 (헤더 제외) */
7774
+ /* @__PURE__ */ jsx(
7775
+ "div",
7776
+ {
7777
+ ref: scrollRef,
7778
+ onScroll: handleScroll,
7779
+ className: cn(
7780
+ bodyScrollClass,
7781
+ // 원본 sd-table__scroll-container--loading/--no-data: 로딩·데이터 없음 스크롤 차단
7782
+ (isLoading || rowCount === 0) && "overflow-hidden"
7783
+ ),
7784
+ children: /* @__PURE__ */ jsxs(
7785
+ "table",
7786
+ {
7787
+ className: "w-full table-fixed border-separate border-spacing-0 text-left",
7788
+ style: { fontSize: 12 },
7789
+ children: [
7790
+ colgroup,
7791
+ /* @__PURE__ */ jsxs("tbody", { children: [
7792
+ useVirtualScroll && topSpacer > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": true, style: { height: topSpacer }, children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0" }) }),
7793
+ rowCount === 0 ? null : bodyRows.map((row, ri) => {
7794
+ const isSel = selectedKeys.has(row[rowKey]);
7795
+ const rowBg = "bg-white";
7796
+ return /* @__PURE__ */ jsxs(
7797
+ "tr",
7798
+ {
7799
+ onClick: () => onRowClick?.(row),
7800
+ className: cn(
7801
+ "group/table-row",
7802
+ !noHover && "hover:bg-[var(--color-grey-05)]",
7803
+ onRowClick && "cursor-pointer"
7811
7804
  ),
7812
- visibleCols.map((col, i) => /* @__PURE__ */ jsx(
7813
- "td",
7814
- {
7815
- className: cn(
7805
+ style: { height: useVirtualScroll ? effectiveRowHeight : rowH },
7806
+ children: [
7807
+ selectable && /* @__PURE__ */ jsx(
7808
+ "td",
7809
+ {
7810
+ className: cn(
7811
+ // 원본 .td--selected: 48px 고정폭, 항상 left sticky, 체크박스 중앙정렬
7812
+ "sticky left-0 z-[2] border-b border-solid border-[color:var(--cmp-table-border-color)] align-middle",
7813
+ rowBg,
7814
+ !noHover && "group-hover/table-row:bg-[var(--color-grey-05)]",
7815
+ stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
7816
+ ),
7817
+ style: {
7818
+ width: SELECTABLE_COLUMN_WIDTH,
7819
+ minWidth: SELECTABLE_COLUMN_WIDTH,
7820
+ maxWidth: SELECTABLE_COLUMN_WIDTH
7821
+ },
7822
+ onClick: (e) => e.stopPropagation(),
7823
+ children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(SCheckbox, { value: isSel, onValueChange: () => toggleRow(row) }) })
7824
+ }
7825
+ ),
7826
+ visibleCols.map((col, i) => {
7827
+ const tdClassName = cn(
7816
7828
  // 원본 .td: 컬럼 폭에 맞춰 내용 말줄임(overflow hidden + ellipsis) + 행 구분선(border-b)
7817
7829
  // 셀 배경은 흰색으로 채워 뒤 배경이 비치지 않게 하고, hover 시 행 배경(grey_05)을 따른다.
7818
7830
  "overflow-hidden text-ellipsis whitespace-nowrap border-b border-solid border-[color:var(--cmp-table-border-color)] text-[12px] leading-[20px] text-[color:var(--sys-color-fg-primary)]",
7819
7831
  rowBg,
7820
- "group-hover/table-row:bg-[var(--color-grey-05)]",
7832
+ !noHover && "group-hover/table-row:bg-[var(--color-grey-05)]",
7821
7833
  stickyShadowClass(i),
7822
7834
  typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
7823
- ),
7824
- style: {
7835
+ );
7836
+ const tdStyle = {
7825
7837
  paddingLeft: cellPadX,
7826
7838
  paddingRight: cellPadX,
7827
7839
  paddingTop: cellPadY,
7828
7840
  paddingBottom: cellPadY,
7829
7841
  textAlign: col.align ?? "left",
7830
7842
  ...cellStyle(i)
7831
- },
7832
- children: col.render ? col.render(row, cellValue(col, row)) : cellValue(col, row)
7833
- },
7834
- col.name
7835
- ))
7836
- ]
7837
- },
7838
- row[rowKey] ?? ri
7839
- );
7840
- }),
7841
- useVirtualScroll && bottomSpacer > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": true, style: { height: bottomSpacer }, children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0" }) })
7842
- ] })
7843
- ]
7844
- }
7845
- )
7846
- }
7843
+ };
7844
+ if (col.renderCell) {
7845
+ const cell = col.renderCell({
7846
+ row,
7847
+ value: cellValue(col, row),
7848
+ column: col,
7849
+ colIndex: i,
7850
+ rowIndex: ri,
7851
+ isSelected: isSel,
7852
+ className: tdClassName,
7853
+ style: tdStyle
7854
+ });
7855
+ if (cell == null || cell === false) return null;
7856
+ return /* @__PURE__ */ jsx(Fragment$1, { children: cell }, col.name);
7857
+ }
7858
+ return /* @__PURE__ */ jsx("td", { className: tdClassName, style: tdStyle, children: col.render ? col.render(row, cellValue(col, row)) : cellValue(col, row) }, col.name);
7859
+ })
7860
+ ]
7861
+ },
7862
+ row[rowKey] ?? ri
7863
+ );
7864
+ }),
7865
+ useVirtualScroll && bottomSpacer > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": true, style: { height: bottomSpacer }, children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0" }) })
7866
+ ] })
7867
+ ]
7868
+ }
7869
+ )
7870
+ }
7871
+ )
7847
7872
  ),
7848
7873
  isNoData && /* @__PURE__ */ jsxs(Fragment, { children: [
7849
7874
  /* @__PURE__ */ jsx(
@@ -7853,7 +7878,7 @@ var STable = forwardRef(function STable2({
7853
7878
  className: "pointer-events-none absolute left-0 right-0 top-0 z-[31] h-[var(--cmp-table-header-height)] bg-white/60"
7854
7879
  }
7855
7880
  ),
7856
- /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-0 left-0 right-0 top-[var(--cmp-table-header-height)] z-[30] flex items-center justify-center bg-white/60 text-[12px] text-[color:var(--color-grey-65)]", children: /* @__PURE__ */ jsx("div", { className: "flex min-h-[60px] w-full items-center justify-center", children: noDataLabel }) })
7881
+ !showNoDataSlot && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-0 left-0 right-0 top-[var(--cmp-table-header-height)] z-[30] flex items-center justify-center bg-white/60 text-[12px] text-[color:var(--color-grey-65)]", children: /* @__PURE__ */ jsx("div", { className: "flex min-h-[60px] w-full items-center justify-center", children: noDataLabel }) })
7857
7882
  ] }),
7858
7883
  isLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-white/60", children: /* @__PURE__ */ jsx(SCircleProgress, { indeterminate: true }) })
7859
7884
  ]