sellmate-design-system-react 9.0.0-beta.49 → 9.0.0-beta.50

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/AGENTS.md CHANGED
@@ -45,7 +45,7 @@
45
45
  | **표·목록** | `SChatMessage`(대화의 메시지 한 건) `SChatAttachedFile`(입력창 위, 아직 보내지 않은 첨부 한 칸) `SChatFile`(대화 흐름에 선, 보낸 파일 한 칸) `SChatSystemMessage`(대화 흐름 가운데 서는 시스템 안내) `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SListItem` `SExpansionList` `SDraggableList` `SDraggableItem` `STree` |
46
46
  | **레이아웃** | `SLayout` `SGnb` `SGnbSystem` `SSystemActionButton`(GNB system 패널에 한 줄씩 쌓는 액션 행 — 버튼 고르기는 §3-5) `SAccountListBox`(계정 행을 눌러 뜨는 계정 패널) `SPage` `SPageHeader`(페이지 제목 영역 — `SLayout` 안에서 `SPage` 앞에 둔다) `SSectionHeaderCard` `SCard` `SLoginCard`(통합 계정 로그인 화면의 카드) `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
47
47
  | **내비게이션** | `STabs` `SPagination` `SStepper` |
48
- | **차트** | `SBarChart`(막대 그래프 — 항목끼리 크기를 견준다) |
48
+ | **차트** | `SBarChart`(막대 그래프 — 항목끼리 크기를 견준다. `stacked` 로 항목 안의 구성까지) |
49
49
  | **표시·상태** | `STag` `SBadge` `SIcon` `SImage` `SCallout` `SGuide` |
50
50
  | **진행·로딩** | `SLinearProgress` `SCircleProgress` `SLoadingContainer` `SLoadingModal` |
51
51
  | **오버레이** | `STooltip` `SPopover` `SPopup` `SDrawer` `SPortal` |
@@ -542,6 +542,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
542
542
  | 사용자가 순서를 드래그로 바꾸게 한다 | `SDraggableList` + `SDraggableItem` | §3-7-6 |
543
543
  | 날짜별 일정을 한 달치 격자로 펼쳐 보여준다 | `SCalendarBoard` | §3-7-4 |
544
544
  | 항목끼리 크기를 눈으로 견주게 한다 (기간별 추이·채널별 비중) | `SBarChart` | 정확한 값을 읽어야 하면 `STable` — 그래프는 "어느 쪽이 큰가" 를 답하지 "얼마인가" 를 답하지 않는다 |
545
+ | 항목의 크기와 **그 안의 구성**을 함께 보여준다 (주문 상태별 내역, 유입 경로별 몫) | `SBarChart stacked` | 계열끼리 견주는 것이 목적이면 `stacked` 없이 나란히 세운다. 더해서 뜻이 없는 값(비율·평균)은 쌓지 않는다 — 합계 라벨이 거짓말이 된다 |
545
546
  | 표 위에 건수 요약과 액션을 얹는다 | `STableBar` | §4-2 |
546
547
  | 상태·분류를 라벨로 찍는다 | `STag` | §3-1 |
547
548
  | 색 점만으로 상태를 찍는다 | `SBadge` | §3-1 |
@@ -2153,6 +2154,10 @@ export default function ProductCreatePage() {
2153
2154
  - 조회 값은 `type: 'text'` 행으로 표시한다. **상태·분류 태그도 별도 영역이 아니라 표의 한 행**으로 넣는다 (`render` 에 `STag`).
2154
2155
  - 행이 많아지면 **유형별로 섹션을 나누고, 각 섹션을 `SSectionHeaderCard` 로 감싼다.**
2155
2156
  섹션 제목은 `title` prop 으로, 바디 여백은 `padding` prop 으로 준다.
2157
+ - **한 화면에 행이 많아 훑어보기가 목적이면 `SKeyValueTable` 에 `dense` 를 켠다** — 셀 상하 여백과
2158
+ 최소 높이가 함께 내려가 행이 좁아진다. 다만 **읽기 전용(`text`) 행에서만 실제로 좁아진다**:
2159
+ 폼 필드가 든 행은 필드 자체의 높이가 행 높이를 정하므로 거의 변하지 않는다. 그래서 필터(§4-2)나
2160
+ 폼(§4-3) 표에 켜 봐야 눈에 띄는 변화가 없다.
2156
2161
  - **수정·삭제 버튼은 하단에 둔다.** 내용이 짧아 우측 상단에 두는 변형도 있으나 기본은 하단이다.
2157
2162
  - **상세 페이지도 `contentHeight="fill"` 이 기본이다**(§2-2). 섹션이 많아 페이지가 실제로 스크롤되는 화면에서만 `auto` + `scrollEndSpacing` 을 켠다.
2158
2163
  - **섹션마다 독립 인라인 폼이 있는 형태**도 상세 페이지의 변형이다. 섹션 안에서 바로 수정·저장하게 하는 화면인데, 이때 버튼 강조는 **섹션 단위가 아니라 페이지 단위로 판단한다** — §3-5-1 의 "`secondary` 연속 배치 금지"는 섹션이 다르면 적용되지 않는다. 그 섹션 안에서 닫히는 저장·취소는 `SSectionHeaderCard` 의 `button`·`footerLeft` 로 넘긴다 (§3-7-8). 아래 예처럼 **페이지 전체를 확정하는 버튼은 카드 밖 하단 줄**에 둔다 — 둘을 섞지 않는다.
@@ -11,6 +11,7 @@
11
11
  | `categories` | `string[]` | — | 가로축(가로 막대면 세로축)에 놓일 항목 이름 |
12
12
  | `series` | `SBarChartSeries[]` | — | 계열 목록. 각 `data` 는 `categories` 와 같은 순서다 |
13
13
  | `direction?` | `SBarChartDirection` | `'vertical'` | 막대가 자라는 방향 |
14
+ | `stacked?` | `boolean` | `false` | 계열을 나란히 놓는 대신 **한 막대에 쌓는다**. 항목의 크기와 그 안의 구성을 함께 볼 때 쓴다. 첫 계열이 기준선에 붙고, 값 라벨은 조각마다가 아니라 **항목의 합계**를 한 번 적는다. 음수는 0 아래로 따로 쌓이며, 값 축도 개별 값이 아닌 항목별 합계로 잡힌다 |
14
15
  | `palette?` | `SBarChartPalette` | `'default'` | 색 팔레트. 계열이 하나면 계열 대신 **항목**마다 색이 바뀐다. `custom` 일 때만 `colors` 를 본다 |
15
16
  | `colors?` | `SBarChartColor[]` | — | `palette='custom'` 에서 쓸 색 이름을 계열 순서대로. 계열보다 짧으면 앞에서부터 순환한다 |
16
17
  | `max?` | `number` | — | 값 축의 최댓값. 주지 않으면 데이터에서 읽기 좋은 값으로 정한다 |
@@ -39,6 +39,12 @@ export interface SBarChartProps {
39
39
  series: SBarChartSeries[];
40
40
  /** 막대가 자라는 방향 */
41
41
  direction?: SBarChartDirection;
42
+ /**
43
+ * 계열을 나란히 놓는 대신 **한 막대에 쌓는다**. 항목의 크기와 그 안의 구성을 함께 볼 때 쓴다.
44
+ * 첫 계열이 기준선에 붙고, 값 라벨은 조각마다가 아니라 **항목의 합계**를 한 번 적는다.
45
+ * 음수는 0 아래로 따로 쌓이며, 값 축도 개별 값이 아닌 항목별 합계로 잡힌다
46
+ */
47
+ stacked?: boolean;
42
48
  /** 색 팔레트. 계열이 하나면 계열 대신 **항목**마다 색이 바뀐다. `custom` 일 때만 `colors` 를 본다 */
43
49
  palette?: SBarChartPalette;
44
50
  /** `palette='custom'` 에서 쓸 색 이름을 계열 순서대로. 계열보다 짧으면 앞에서부터 순환한다 */
@@ -116,7 +116,8 @@ export interface SChatMessageProps extends Omit<HTMLAttributes<HTMLDivElement>,
116
116
  `direction` 하나로 받은 메시지(왼쪽)와
117
117
  * 보낸 메시지(오른쪽)가 갈리고, 그에 따라 정렬과 말풍선 색이 함께 뒤집힌다.
118
118
  *
119
- * **말풍선 폭은 내용이 정한다** — 짧은 메시지는 글자만큼만 차지하고, 길면 부모 폭에서 접힌다.
120
- * 대화 폭을 제한하려면 컴포넌트가 아니라 담는 쪽을 좁힌다.
119
+ * **말풍선 폭은 내용이 정한다** — 짧은 메시지는 글자만큼만 차지하고, 길면 접힌다. 다만 반대편
120
+ * 끝까지 가지는 않는다: 받은 메시지는 오른쪽, 보낸 메시지는 왼쪽에 여백이 남아 어느 쪽이 말한
121
+ * 것인지가 폭으로도 읽힌다. 대화 폭을 더 좁히려면 이 컴포넌트가 아니라 담는 쪽을 좁힌다.
121
122
  */
122
123
  export declare const SChatMessage: import("react").ForwardRefExoticComponent<SChatMessageProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -28,6 +28,7 @@ export declare const ICONS: {
28
28
  readonly caretLeft: (p: IconRenderProps) => import("react").JSX.Element;
29
29
  readonly caretRight: (p: IconRenderProps) => import("react").JSX.Element;
30
30
  readonly caretUp: (p: IconRenderProps) => import("react").JSX.Element;
31
+ readonly chat: (p: IconRenderProps) => import("react").JSX.Element;
31
32
  readonly check: (p: IconRenderProps) => import("react").JSX.Element;
32
33
  readonly checkFill: (p: IconRenderProps) => import("react").JSX.Element;
33
34
  readonly checkOutline: (p: IconRenderProps) => import("react").JSX.Element;
@@ -54,6 +55,7 @@ export declare const ICONS: {
54
55
  readonly edit: (p: IconRenderProps) => import("react").JSX.Element;
55
56
  readonly etc: (p: IconRenderProps) => import("react").JSX.Element;
56
57
  readonly event: (p: IconRenderProps) => import("react").JSX.Element;
58
+ readonly focus: (p: IconRenderProps) => import("react").JSX.Element;
57
59
  readonly global: (p: IconRenderProps) => import("react").JSX.Element;
58
60
  readonly helpFill: (p: IconRenderProps) => import("react").JSX.Element;
59
61
  readonly helpOutline: (p: IconRenderProps) => import("react").JSX.Element;
@@ -64,6 +66,7 @@ export declare const ICONS: {
64
66
  readonly launcher: (p: IconRenderProps) => import("react").JSX.Element;
65
67
  readonly leftRight: (p: IconRenderProps) => import("react").JSX.Element;
66
68
  readonly line: (p: IconRenderProps) => import("react").JSX.Element;
69
+ readonly link: (p: IconRenderProps) => import("react").JSX.Element;
67
70
  readonly location: (p: IconRenderProps) => import("react").JSX.Element;
68
71
  readonly logout: (p: IconRenderProps) => import("react").JSX.Element;
69
72
  readonly minus: (p: IconRenderProps) => import("react").JSX.Element;
@@ -10,6 +10,7 @@
10
10
  |------|------|---------|-------------|
11
11
  | `fields?` | `SKeyValueField[][]` | `[]` | 2차원 배열. row → [th|td] 필드들 |
12
12
  | `values?` | `Record<string, unknown>` | `{}` | field name을 key로 하는 값 객체 (`{ [name]: value }`). 지정 시 해당 field의 값으로 사용되며, field별 `options.value`보다 우선합니다. `onChange`의 `detail.values`와 함께 controlled 패턴으로 사용합니다. |
13
+ | `dense?` | `boolean` | `false` | 행을 좁게 그린다 — 셀의 상하 패딩(8px → 6px)과 짝이 되는 최소 높이(44px → 32px)를 함께 내린다. 한 화면에 행이 많아 훑어보기가 목적인 상세 조회 표에 쓴다. 좌우 패딩과 글자 크기는 그대로다. **`STable` 과 달리 밀도 토글이 붙지 않는 고정값이다** — 준 값 그대로 그린다. 읽기 전용(`text`) 행에서만 실제로 좁아진다: 폼 필드 행은 필드 높이가 행 높이를 정한다 |
13
14
  | `search?` | `boolean` | `false` | 우측 검색 패널 |
14
15
  | `radius?` | `'default' \| 'useTop' \| 'full'` | `'default'` | border-radius 제어 |
15
16
  | `bordered?` | `boolean` | `true` | 바깥 테두리. 기본은 `true`. `SSectionHeaderCard` 의 `padding="none"` 안에 넣어 카드 가장자리까지 채울 때 `false` 로 끈다 — 카드가 이미 바깥 테두리를 그리므로, 켜 두면 1px 두 개가 나란히 놓여 그 변만 2px 로 보인다. |
@@ -104,6 +104,13 @@ export interface SKeyValueTableProps {
104
104
  * `onChange`의 `detail.values`와 함께 controlled 패턴으로 사용합니다.
105
105
  */
106
106
  values?: Record<string, unknown>;
107
+ /**
108
+ * 행을 좁게 그린다 — 셀의 상하 패딩(8px → 6px)과 짝이 되는 최소 높이(44px → 32px)를 함께
109
+ * 내린다. 한 화면에 행이 많아 훑어보기가 목적인 상세 조회 표에 쓴다. 좌우 패딩과 글자 크기는
110
+ * 그대로다. **`STable` 과 달리 밀도 토글이 붙지 않는 고정값이다** — 준 값 그대로 그린다.
111
+ * 읽기 전용(`text`) 행에서만 실제로 좁아진다: 폼 필드 행은 필드 높이가 행 높이를 정한다
112
+ */
113
+ dense?: boolean;
107
114
  /** 우측 검색 패널 */
108
115
  search?: boolean;
109
116
  /** border-radius 제어 */
@@ -123,5 +130,5 @@ export interface SKeyValueTableProps {
123
130
  style?: CSSProperties;
124
131
  }
125
132
  /** SKeyValueTable — sd-key-value-table 포팅. th/td 폼 레이아웃 + 필드 렌더러. */
126
- export declare function SKeyValueTable({ fields, values, search, radius, bordered, onChange, onSearch, className, style, }: SKeyValueTableProps): import("react").JSX.Element | null;
133
+ export declare function SKeyValueTable({ fields, values, dense, search, radius, bordered, onChange, onSearch, className, style, }: SKeyValueTableProps): import("react").JSX.Element | null;
127
134
  export {};
package/dist/index.cjs CHANGED
@@ -967,6 +967,29 @@ var Icon_caretUp = (p) => {
967
967
  }
968
968
  );
969
969
  };
970
+ var Icon_chat = (p) => {
971
+ const { size = 24, ...rest } = p;
972
+ return /* @__PURE__ */ jsxRuntime.jsx(
973
+ "svg",
974
+ {
975
+ width: size,
976
+ height: size,
977
+ viewBox: "0 0 24 24",
978
+ fill: "none",
979
+ xmlns: "http://www.w3.org/2000/svg",
980
+ ...rest,
981
+ children: /* @__PURE__ */ jsxRuntime.jsx(
982
+ "path",
983
+ {
984
+ fillRule: "evenodd",
985
+ clipRule: "evenodd",
986
+ d: "M12.2961 2.25C15.6684 2.25008 18.4344 4.83725 18.7228 8.13477C21.4143 9.74397 22.7011 13.2749 21.1398 16.3975C20.6005 17.4762 20.4853 18.7183 20.8166 19.8779L21.048 20.6885C21.2975 21.5618 20.3398 22.2835 19.5695 21.8027L16.2853 19.75H12.0002C9.43974 19.75 7.24026 18.2099 6.27457 16.0059L4.16129 17.3037C3.38392 17.7807 2.42724 17.0507 2.6818 16.1748L2.92301 15.3428C3.27732 14.1243 3.1547 12.8165 2.57926 11.6855C0.381511 7.36716 3.51808 2.25032 8.36344 2.25H12.2961ZM8.36344 3.75C4.63792 3.75032 2.22559 7.6845 3.9152 11.0049C4.60038 12.3512 4.79173 13.8901 4.46598 15.3564L6.89567 13.8643L7.06656 13.7744C7.24277 13.6973 7.43434 13.6572 7.62809 13.6572H12.2961C15.0317 13.6571 17.2499 11.4397 17.2502 8.7041C17.2502 5.9683 15.0318 3.75008 12.2961 3.75H8.36344Z",
987
+ fill: "currentColor"
988
+ }
989
+ )
990
+ }
991
+ );
992
+ };
970
993
  var Icon_check = (p) => {
971
994
  const { size = 24, ...rest } = p;
972
995
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1678,6 +1701,59 @@ var Icon_event = (p) => {
1678
1701
  }
1679
1702
  );
1680
1703
  };
1704
+ var Icon_focus = (p) => {
1705
+ const { size = 24, ...rest } = p;
1706
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1707
+ "svg",
1708
+ {
1709
+ width: size,
1710
+ height: size,
1711
+ viewBox: "0 0 24 24",
1712
+ fill: "none",
1713
+ xmlns: "http://www.w3.org/2000/svg",
1714
+ ...rest,
1715
+ children: [
1716
+ /* @__PURE__ */ jsxRuntime.jsx(
1717
+ "path",
1718
+ {
1719
+ d: "M3 16.25C3.41421 16.25 3.75 16.5858 3.75 17V20C3.75 20.1381 3.86193 20.25 4 20.25H7C7.41421 20.25 7.75 20.5858 7.75 21C7.75 21.4142 7.41421 21.75 7 21.75H4C3.0335 21.75 2.25 20.9665 2.25 20V17C2.25 16.5858 2.58579 16.25 3 16.25Z",
1720
+ fill: "currentColor"
1721
+ }
1722
+ ),
1723
+ /* @__PURE__ */ jsxRuntime.jsx(
1724
+ "path",
1725
+ {
1726
+ d: "M21 16.25C21.4142 16.25 21.75 16.5858 21.75 17V20C21.75 20.9665 20.9665 21.75 20 21.75H17C16.5858 21.75 16.25 21.4142 16.25 21C16.25 20.5858 16.5858 20.25 17 20.25H20C20.1381 20.25 20.25 20.1381 20.25 20V17C20.25 16.5858 20.5858 16.25 21 16.25Z",
1727
+ fill: "currentColor"
1728
+ }
1729
+ ),
1730
+ /* @__PURE__ */ jsxRuntime.jsx(
1731
+ "path",
1732
+ {
1733
+ fillRule: "evenodd",
1734
+ clipRule: "evenodd",
1735
+ d: "M12 8.25C14.0711 8.25 15.75 9.92893 15.75 12C15.75 14.0711 14.0711 15.75 12 15.75C9.92893 15.75 8.25 14.0711 8.25 12C8.25 9.92893 9.92893 8.25 12 8.25ZM12 9.75C10.7574 9.75 9.75 10.7574 9.75 12C9.75 13.2426 10.7574 14.25 12 14.25C13.2426 14.25 14.25 13.2426 14.25 12C14.25 10.7574 13.2426 9.75 12 9.75Z",
1736
+ fill: "currentColor"
1737
+ }
1738
+ ),
1739
+ /* @__PURE__ */ jsxRuntime.jsx(
1740
+ "path",
1741
+ {
1742
+ d: "M7 2.25C7.41421 2.25 7.75 2.58579 7.75 3C7.75 3.41421 7.41421 3.75 7 3.75H4C3.86193 3.75 3.75 3.86193 3.75 4V7C3.75 7.41421 3.41421 7.75 3 7.75C2.58579 7.75 2.25 7.41421 2.25 7V4C2.25 3.0335 3.0335 2.25 4 2.25H7Z",
1743
+ fill: "currentColor"
1744
+ }
1745
+ ),
1746
+ /* @__PURE__ */ jsxRuntime.jsx(
1747
+ "path",
1748
+ {
1749
+ d: "M20 2.25C20.9665 2.25 21.75 3.0335 21.75 4V7C21.75 7.41421 21.4142 7.75 21 7.75C20.5858 7.75 20.25 7.41421 20.25 7V4C20.25 3.86193 20.1381 3.75 20 3.75H17C16.5858 3.75 16.25 3.41421 16.25 3C16.25 2.58579 16.5858 2.25 17 2.25H20Z",
1750
+ fill: "currentColor"
1751
+ }
1752
+ )
1753
+ ]
1754
+ }
1755
+ );
1756
+ };
1681
1757
  var Icon_global = (p) => {
1682
1758
  const { size = 24, ...rest } = p;
1683
1759
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -2038,6 +2114,43 @@ var Icon_line = (p) => {
2038
2114
  }
2039
2115
  );
2040
2116
  };
2117
+ var Icon_link = (p) => {
2118
+ const { size = 24, ...rest } = p;
2119
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2120
+ "svg",
2121
+ {
2122
+ width: size,
2123
+ height: size,
2124
+ viewBox: "0 0 24 24",
2125
+ fill: "none",
2126
+ xmlns: "http://www.w3.org/2000/svg",
2127
+ ...rest,
2128
+ children: [
2129
+ /* @__PURE__ */ jsxRuntime.jsx(
2130
+ "path",
2131
+ {
2132
+ d: "M10 6.25C10.4142 6.25 10.75 6.58579 10.75 7C10.75 7.41421 10.4142 7.75 10 7.75H7C4.65279 7.75 2.75 9.65279 2.75 12C2.75 14.3472 4.65279 16.25 7 16.25H10C10.4142 16.25 10.75 16.5858 10.75 17C10.75 17.4142 10.4142 17.75 10 17.75H7C3.82436 17.75 1.25 15.1756 1.25 12C1.25 8.82436 3.82436 6.25 7 6.25H10Z",
2133
+ fill: "currentColor"
2134
+ }
2135
+ ),
2136
+ /* @__PURE__ */ jsxRuntime.jsx(
2137
+ "path",
2138
+ {
2139
+ d: "M17 6.25C20.1756 6.25 22.75 8.82436 22.75 12C22.75 15.1756 20.1756 17.75 17 17.75H14C13.5858 17.75 13.25 17.4142 13.25 17C13.25 16.5858 13.5858 16.25 14 16.25H17C19.3472 16.25 21.25 14.3472 21.25 12C21.25 9.65279 19.3472 7.75 17 7.75H14C13.5858 7.75 13.25 7.41421 13.25 7C13.25 6.58579 13.5858 6.25 14 6.25H17Z",
2140
+ fill: "currentColor"
2141
+ }
2142
+ ),
2143
+ /* @__PURE__ */ jsxRuntime.jsx(
2144
+ "path",
2145
+ {
2146
+ d: "M16 11.25C16.4142 11.25 16.75 11.5858 16.75 12C16.75 12.4142 16.4142 12.75 16 12.75H8C7.58579 12.75 7.25 12.4142 7.25 12C7.25 11.5858 7.58579 11.25 8 11.25H16Z",
2147
+ fill: "currentColor"
2148
+ }
2149
+ )
2150
+ ]
2151
+ }
2152
+ );
2153
+ };
2041
2154
  var Icon_location = (p) => {
2042
2155
  const { size = 24, ...rest } = p;
2043
2156
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -3249,6 +3362,7 @@ var ICONS = {
3249
3362
  caretLeft: Icon_caretLeft,
3250
3363
  caretRight: Icon_caretRight,
3251
3364
  caretUp: Icon_caretUp,
3365
+ chat: Icon_chat,
3252
3366
  check: Icon_check,
3253
3367
  checkFill: Icon_checkFill,
3254
3368
  checkOutline: Icon_checkOutline,
@@ -3275,6 +3389,7 @@ var ICONS = {
3275
3389
  edit: Icon_edit,
3276
3390
  etc: Icon_etc,
3277
3391
  event: Icon_event,
3392
+ focus: Icon_focus,
3278
3393
  global: Icon_global,
3279
3394
  helpFill: Icon_helpFill,
3280
3395
  helpOutline: Icon_helpOutline,
@@ -3285,6 +3400,7 @@ var ICONS = {
3285
3400
  launcher: Icon_launcher,
3286
3401
  leftRight: Icon_leftRight,
3287
3402
  line: Icon_line,
3403
+ link: Icon_link,
3288
3404
  location: Icon_location,
3289
3405
  logout: Icon_logout,
3290
3406
  minus: Icon_minus,
@@ -5024,6 +5140,11 @@ var SChatMessageImpl = /* @__PURE__ */ react.forwardRef(function SChatMessage({
5024
5140
  // items-start 라 이름이 있든 없든 아바타는 언제나 첫 줄에 붙는다.
5025
5141
  "flex items-start gap-(--cmp-chatMessage-avatarGap)",
5026
5142
  outgoing && "flex-row-reverse",
5143
+ // 반대편은 비워 둔다 — 받은 메시지는 오른쪽, 보낸 메시지는 왼쪽. 긴 메시지가 화면 폭을
5144
+ // 다 차지하면 어느 쪽이 말한 것인지가 자리로 읽히지 않으므로, 말풍선이 반대편 끝까지
5145
+ // 가지 못하게 막는다. 논리 속성(ps/pe)을 쓰지 않는 이유: flex-row-reverse 는 글 방향을
5146
+ // 뒤집지 않아 보낸 메시지에서 여백이 같은 쪽에 남는다.
5147
+ outgoing ? "pl-(--cmp-chatMessage-row-paddingInset)" : "pr-(--cmp-chatMessage-row-paddingInset)",
5027
5148
  className
5028
5149
  ),
5029
5150
  ...rest,
@@ -17720,6 +17841,7 @@ var cssLen2 = (v) => v == null ? void 0 : typeof v === "number" ? `${v}px` : v;
17720
17841
  function SKeyValueTable({
17721
17842
  fields = [],
17722
17843
  values = {},
17844
+ dense = false,
17723
17845
  search = false,
17724
17846
  radius = "default",
17725
17847
  bordered = true,
@@ -17734,8 +17856,8 @@ function SKeyValueTable({
17734
17856
  value,
17735
17857
  values: { ...values, [name]: value }
17736
17858
  });
17737
- const cellHeight = "var(--cmp-table-keyValue-height)";
17738
- const cellPadY = "var(--cmp-table-keyValue-body-paddingY)";
17859
+ const cellHeight = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-keyValue-height)";
17860
+ const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-keyValue-body-paddingY)";
17739
17861
  const renderField = (f) => {
17740
17862
  if (f.render) return f.render;
17741
17863
  const opt = { ...f.options ?? {} };
@@ -18253,6 +18375,7 @@ var SBarChart = /* @__PURE__ */ react.forwardRef(function SBarChart2({
18253
18375
  categories,
18254
18376
  series,
18255
18377
  direction = "vertical",
18378
+ stacked = false,
18256
18379
  palette = "default",
18257
18380
  colors: customColors,
18258
18381
  max,
@@ -18272,6 +18395,7 @@ var SBarChart = /* @__PURE__ */ react.forwardRef(function SBarChart2({
18272
18395
  const { ref: plotRef, size } = useElementSize();
18273
18396
  const { ref: axisTextRef, size: axisText } = useElementSize();
18274
18397
  const { ref: axisSampleRef, size: axisSample } = useElementSize();
18398
+ const clipId = react.useId();
18275
18399
  const [hovered, setHovered] = react.useState(null);
18276
18400
  const hoveredCategory = hovered?.category ?? null;
18277
18401
  const perCategory = series.length === 1;
@@ -18279,23 +18403,37 @@ var SBarChart = /* @__PURE__ */ react.forwardRef(function SBarChart2({
18279
18403
  (seriesIndex, categoryIndex) => paletteColor(palette, perCategory ? categoryIndex : seriesIndex, customColors),
18280
18404
  [palette, customColors, perCategory]
18281
18405
  );
18406
+ const stackTotals = react.useMemo(
18407
+ () => categories.map(
18408
+ (_, categoryIndex) => series.reduce(
18409
+ (acc, s) => {
18410
+ const value = s.data[categoryIndex];
18411
+ if (value == null || !Number.isFinite(value)) return acc;
18412
+ return value < 0 ? { positive: acc.positive, negative: acc.negative + value } : { positive: acc.positive + value, negative: acc.negative };
18413
+ },
18414
+ { positive: 0, negative: 0 }
18415
+ )
18416
+ ),
18417
+ [categories, series]
18418
+ );
18282
18419
  const scale = react.useMemo(
18283
18420
  () => niceScale(
18284
- series.flatMap((s) => s.data),
18421
+ stacked ? stackTotals.flatMap((total) => [total.positive, total.negative]) : series.flatMap((s) => s.data),
18285
18422
  tickCount,
18286
18423
  max
18287
18424
  ),
18288
- [series, tickCount, max]
18425
+ [stacked, stackTotals, series, tickCount, max]
18289
18426
  );
18427
+ const barSlots = stacked ? 1 : series.length;
18290
18428
  const axisLength = vertical ? size.width : size.height;
18291
18429
  const layout = react.useMemo(
18292
- () => bandLayout(axisLength, categories.length, series.length, barSize),
18293
- [axisLength, categories.length, series.length, barSize]
18430
+ () => bandLayout(axisLength, categories.length, barSlots, barSize),
18431
+ [axisLength, categories.length, barSlots, barSize]
18294
18432
  );
18295
18433
  const zeroRatio = ratioOf(0, scale);
18296
- const minPlotWidth = vertical ? categories.length * minBand(series.length) : 0;
18434
+ const minPlotWidth = vertical ? categories.length * minBand(barSlots) : 0;
18297
18435
  const totalHeight = height ?? (vertical ? 320 : void 0);
18298
- const plotHeight = totalHeight == null ? Math.max(1, categories.length) * minBand(series.length) : typeof totalHeight === "number" ? Math.max(0, totalHeight - axisText.height) : `calc(${totalHeight} - ${axisText.height}px)`;
18436
+ const plotHeight = totalHeight == null ? Math.max(1, categories.length) * minBand(barSlots) : typeof totalHeight === "number" ? Math.max(0, totalHeight - axisText.height) : `calc(${totalHeight} - ${axisText.height}px)`;
18299
18437
  const legend = showLegend ? /* @__PURE__ */ jsxRuntime.jsx(ChartLegend, { items: series.map((s, i) => ({ name: s.name, color: colorAt(i, 0) })) }) : null;
18300
18438
  const handleMove = (event) => {
18301
18439
  if (!showTooltip || categories.length === 0 || series.length === 0) return;
@@ -18352,7 +18490,7 @@ var SBarChart = /* @__PURE__ */ react.forwardRef(function SBarChart2({
18352
18490
  }
18353
18491
  );
18354
18492
  const bars = react.useMemo(
18355
- () => size.width > 0 && size.height > 0 ? series.flatMap(
18493
+ () => !stacked && size.width > 0 && size.height > 0 ? series.flatMap(
18356
18494
  (s, seriesIndex) => categories.map((_, categoryIndex) => {
18357
18495
  const value = s.data[categoryIndex];
18358
18496
  if (value == null || !Number.isFinite(value)) return null;
@@ -18429,9 +18567,105 @@ var SBarChart = /* @__PURE__ */ react.forwardRef(function SBarChart2({
18429
18567
  formatValue,
18430
18568
  onBarClick,
18431
18569
  colorAt,
18432
- hoveredCategory
18570
+ hoveredCategory,
18571
+ stacked
18433
18572
  ]
18434
18573
  );
18574
+ const stackedBars = react.useMemo(() => {
18575
+ if (!stacked || size.width <= 0 || size.height <= 0) return [];
18576
+ const pxOf = (value) => vertical ? size.height - ratioOf(value, scale) * size.height : ratioOf(value, scale) * size.width;
18577
+ return categories.map((_, categoryIndex) => {
18578
+ const dimmed = hoveredCategory != null && hoveredCategory !== categoryIndex;
18579
+ const barStart = layout.offset + categoryIndex * layout.band + (layout.band - layout.bar) / 2;
18580
+ let positive = 0;
18581
+ let negative = 0;
18582
+ const segments = series.flatMap((s, seriesIndex) => {
18583
+ const value = s.data[categoryIndex];
18584
+ if (value == null || !Number.isFinite(value) || value === 0) return [];
18585
+ const from = value < 0 ? negative + value : positive;
18586
+ const to = value < 0 ? negative : positive + value;
18587
+ if (value < 0) negative += value;
18588
+ else positive += value;
18589
+ return [{ seriesIndex, name: s.name, value, from, to }];
18590
+ });
18591
+ if (segments.length === 0) return null;
18592
+ const boxOf = (from, to) => {
18593
+ const a = pxOf(from);
18594
+ const b = pxOf(to);
18595
+ return vertical ? { x: barStart, y: Math.min(a, b), width: layout.bar, height: Math.abs(a - b) } : { x: Math.min(a, b), y: barStart, width: Math.abs(a - b), height: layout.bar };
18596
+ };
18597
+ const outlineOf = (extent, negativeSide) => {
18598
+ const box3 = negativeSide ? boxOf(extent, 0) : boxOf(0, extent);
18599
+ return barPath(box3.x, box3.y, box3.width, box3.height, direction, negativeSide);
18600
+ };
18601
+ const positiveClip = `${clipId}-p-${categoryIndex}`;
18602
+ const negativeClip = `${clipId}-n-${categoryIndex}`;
18603
+ const total = positive + negative;
18604
+ const outer = pxOf(total >= 0 ? positive : negative);
18605
+ const labelX = vertical ? barStart + layout.bar / 2 : total >= 0 ? outer + VALUE_LABEL_GAP : outer - VALUE_LABEL_GAP;
18606
+ const labelY = vertical ? total >= 0 ? outer - VALUE_LABEL_GAP : outer + VALUE_LABEL_GAP + 10 : barStart + layout.bar / 2;
18607
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
18608
+ /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
18609
+ positive > 0 && /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: positiveClip, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: outlineOf(positive, false) }) }),
18610
+ negative < 0 && /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: negativeClip, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: outlineOf(negative, true) }) })
18611
+ ] }),
18612
+ segments.map((segment) => {
18613
+ const box3 = boxOf(segment.from, segment.to);
18614
+ return /* @__PURE__ */ jsxRuntime.jsx(
18615
+ "rect",
18616
+ {
18617
+ x: box3.x,
18618
+ y: box3.y,
18619
+ width: box3.width,
18620
+ height: box3.height,
18621
+ clipPath: `url(#${segment.value < 0 ? negativeClip : positiveClip})`,
18622
+ style: {
18623
+ fill: colorAt(segment.seriesIndex, categoryIndex),
18624
+ // 흐림은 조각에만 건다 — 묶음에 걸면 합계 라벨까지 같이 흐려진다.
18625
+ opacity: dimmed ? BAR_OPACITY_DIMMED : BAR_OPACITY_DEFAULT,
18626
+ cursor: onBarClick ? "pointer" : void 0
18627
+ },
18628
+ onClick: onBarClick ? () => onBarClick({
18629
+ seriesIndex: segment.seriesIndex,
18630
+ categoryIndex,
18631
+ value: segment.value
18632
+ }) : void 0,
18633
+ children: /* @__PURE__ */ jsxRuntime.jsx("title", { children: `${categories[categoryIndex]} ${segment.name} ${formatValue(segment.value)}` })
18634
+ },
18635
+ segment.seriesIndex
18636
+ );
18637
+ }),
18638
+ showValueLabel && /* @__PURE__ */ jsxRuntime.jsx(
18639
+ "text",
18640
+ {
18641
+ x: labelX,
18642
+ y: labelY,
18643
+ className: "typo-body-sm-medium",
18644
+ style: { fill: BAR_VALUE_COLOR },
18645
+ textAnchor: vertical ? "middle" : total >= 0 ? "start" : "end",
18646
+ dominantBaseline: vertical ? "auto" : "central",
18647
+ children: formatValue(total)
18648
+ }
18649
+ )
18650
+ ] }, categoryIndex);
18651
+ });
18652
+ }, [
18653
+ stacked,
18654
+ size.width,
18655
+ size.height,
18656
+ series,
18657
+ categories,
18658
+ layout,
18659
+ scale,
18660
+ vertical,
18661
+ direction,
18662
+ showValueLabel,
18663
+ formatValue,
18664
+ onBarClick,
18665
+ colorAt,
18666
+ hoveredCategory,
18667
+ clipId
18668
+ ]);
18435
18669
  const axisLabels = /* @__PURE__ */ jsxRuntime.jsx(
18436
18670
  ChartAxisLabels,
18437
18671
  {
@@ -18465,7 +18699,7 @@ var SBarChart = /* @__PURE__ */ react.forwardRef(function SBarChart2({
18465
18699
  width: size.width,
18466
18700
  height: size.height,
18467
18701
  role: "img",
18468
- "aria-label": `${categories.length}\uAC1C \uD56D\uBAA9, ${series.length}\uAC1C \uACC4\uC5F4 \uB9C9\uB300 \uADF8\uB798\uD504`,
18702
+ "aria-label": `${categories.length}\uAC1C \uD56D\uBAA9, ${series.length}\uAC1C \uACC4\uC5F4 ${stacked ? "\uB204\uC801 " : ""}\uB9C9\uB300 \uADF8\uB798\uD504`,
18469
18703
  children: [
18470
18704
  /* @__PURE__ */ jsxRuntime.jsx(
18471
18705
  ChartGrid,
@@ -18490,7 +18724,7 @@ var SBarChart = /* @__PURE__ */ react.forwardRef(function SBarChart2({
18490
18724
  }
18491
18725
  }
18492
18726
  ),
18493
- bars
18727
+ stacked ? stackedBars : bars
18494
18728
  ]
18495
18729
  }
18496
18730
  ),