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

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
@@ -1106,14 +1106,16 @@ const columns: STableColumn[] = [
1106
1106
 
1107
1107
  - **항목 하나의 속성을 나열하는 것은 목록이 아니다.** `라벨: 값` 이 세로로 쌓이는 것은 `SKeyValueTable` 이다 (§4-4).
1108
1108
  - `SList` 는 레이아웃만 담당한다. 펼침·단일 선택 동작이 필요하면 `SExpansionList` 다 (§3-7-7).
1109
- - **항목 사이 구분선은 리스트가 그린다.** `SListItem`·`SExpansionItem`·`SDraggableItem` 스스로 구분선을 그리지 않으므로, 목록을 감싸는 `SList`·`SExpansionList`·`SDraggableList` `separator` 준다 아이템에 `border-b` 직접 붙이지 않는다. 테두리형(`bordered`)은 테두리가 구분 역할을 하므로 `separator` 대신 `useGap` 으로 띄운다.
1109
+ - **`SList` 자식은 `SListItem` 권장한다.** 다른 자식도 그대로 렌더되지만, 펼치는 항목은 `SExpansionList` + `SExpansionItem` 이, 끌어서 순서를 바꾸는 항목은 `SDraggableList` + `SDraggableItem` 여닫힘·선택·정렬 동작까지 함께 관리하므로 그쪽을 쓴다 (§3-7-7).
1110
+ - **항목 사이 구분선은 리스트가 알아서 그린다.** `SListItem`·`SExpansionItem`·`SDraggableItem` 셋 다 스스로 구분선을 그리지 않는다. `SList`·`SExpansionList`·`SDraggableList` 가 자식 **사이에** 구분선을 넣으므로 아이템에 `border-b` 를 직접 붙이지 않고, 켜는 prop 도 따로 없다. 마지막 항목 아래에는 선이 남지 않는다. 테두리형(`bordered`)은 테두리가 구분 역할을 하므로 리스트가 구분선을 빼고, `useGap` 으로 띄운다 — `useGap` 을 준 목록에도 구분선은 들어가지 않는다.
1110
1111
  - **`SListItem` 은 기본이 표시 전용이다.** 눌러서 이동·선택하게 하려면 `clickable` 을 준다 — hover·`selected`·`interaction="chevron"` 표현이 전부 여기에 딸려 있어서, `clickable` 없이 `selected` 만 주면 선택 표시가 나오지 않는다. `SExpansionList` 는 선택을 자기가 관리하므로 자식 아이템을 알아서 클릭 가능하게 만든다.
1111
1112
 
1112
1113
  ```tsx
1113
- ✅ <SList separator><SListItem title="일반 문의" />…</SList> {/* 줄로 구분되는 목록 */}
1114
+ ✅ <SList><SListItem title="일반 문의" />…</SList> {/* 줄로 구분되는 목록 — 구분선은 자동 */}
1114
1115
  ✅ <SList useGap><SListItem title="일반 문의" bordered />…</SList> {/* 카드처럼 떨어진 목록 */}
1115
1116
  ✅ <SListItem title="일반 문의" clickable selected onClick={…} /> {/* 눌러서 고르는 목록 */}
1116
- ❌ <SList><SListItem title="일반 문의" className="border-b" />…</SList>
1117
+ ❌ <SList><SListItem title="일반 문의" className="border-b" />…</SList> {/* 구분선을 직접 붙이지 않는다 */}
1118
+ ❌ <SList><><SListItem title="일반 문의" /><SListItem title="결제 문의" /></></SList> {/* Fragment 로 묶으면 그 안쪽은 구분되지 않는다 */}
1117
1119
  ❌ <SListItem title="일반 문의" selected /> {/* clickable 없으면 선택 표시가 안 나온다 */}
1118
1120
  ```
1119
1121
 
@@ -1125,7 +1127,7 @@ const columns: STableColumn[] = [
1125
1127
  | **한 번에 하나만** 열려야 한다 (아코디언) | `SExpansionList` + `SExpansionItem` |
1126
1128
  | **부모-자식 계층 자체**를 보여줘야 한다 (2단 이상, 연결선) | `STree` |
1127
1129
 
1128
- `SExpansionList` 는 depth 별 단일 확장·선택을 관리하는 wrapper 다. 직접 `useState` 로 "열린 항목 하나"를 들고 있지 않는다. 항목 사이 구분선은 여기서도 리스트가 그린다 — `separator` 준다 (§3-7-6).
1130
+ `SExpansionList` 는 depth 별 단일 확장·선택을 관리하는 wrapper 다. 직접 `useState` 로 "열린 항목 하나"를 들고 있지 않는다. 항목 사이 구분선은 여기서도 리스트가 알아서 그린다 — 따로 prop 이 없다 (§3-7-6).
1129
1131
 
1130
1132
  #### 3-7-8. SCard vs SSectionHeaderCard
1131
1133
 
@@ -3,5 +3,5 @@ export interface SCardProps extends HTMLAttributes<HTMLDivElement> {
3
3
  /** 테두리 표시 여부 */
4
4
  bordered?: boolean;
5
5
  }
6
- /** SCard — sd-card 포팅. 라운드 8px, 흰 배경, 옵션 테두리. */
6
+ /** SCard — sd-card 포팅. 라운드 8px, 흰 배경, 옵션 테두리. 콘텐츠는 라운드 경계에서 잘린다. */
7
7
  export declare const SCard: import("react").ForwardRefExoticComponent<SCardProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -16,6 +16,7 @@
16
16
 
17
17
  - [SCalendar](../SCalendar)
18
18
  - [SDateRangePicker](../SDateRangePicker)
19
+ - [SList](../SList)
19
20
  - [STableBar](../STableBar)
20
21
 
21
22
  ### Graph
@@ -24,6 +25,7 @@
24
25
  graph TD;
25
26
  SCalendar --> SDivider
26
27
  SDateRangePicker --> SDivider
28
+ SList --> SDivider
27
29
  STableBar --> SDivider
28
30
  style SDivider fill:#f9f,stroke:#333,stroke-width:4px
29
31
  ```
@@ -162,3 +162,8 @@ export declare function findGnbRailValue(items: SGnbMenuItem[], value: string):
162
162
  export declare function findGnbSiblingValues(items: SGnbMenuItem[], value: string): string[];
163
163
  /** value 로 선택된 아이템의 조상 value 목록(자기 자신 제외)을 반환 → 자동 펼침용 */
164
164
  export declare function findGnbAncestors(items: SGnbMenuItem[], value: string): string[];
165
+ /**
166
+ * value 를 가진 아이템 아래의 **하위 가지**(children 을 가진 자손) value 목록을 반환.
167
+ * 부모를 펼칠 때 그 아래 가지도 전부 함께 펼치는 용도 — 리프는 펼침 대상이 아니라 제외한다.
168
+ */
169
+ export declare function findGnbDescendantBranchValues(items: SGnbMenuItem[], value: string): string[];
@@ -19,10 +19,15 @@
19
19
  - [SDraggableList](../SDraggableList)
20
20
  - [SExpansionList](../SExpansionList)
21
21
 
22
+ ### Depends on
23
+
24
+ - [SDivider](../SDivider)
25
+
22
26
  ### Graph
23
27
 
24
28
  ```mermaid
25
29
  graph TD;
30
+ SList --> SDivider
26
31
  SDraggableList --> SList
27
32
  SExpansionList --> SList
28
33
  style SList fill:#f9f,stroke:#333,stroke-width:4px
package/dist/index.cjs CHANGED
@@ -3373,7 +3373,11 @@ var SCard = /* @__PURE__ */ react.forwardRef(function SCard2({ bordered = false,
3373
3373
  "div",
3374
3374
  {
3375
3375
  ref,
3376
- className: cn("h-fit rounded-[8px] bg-white", bordered && "border border-grey-30", className),
3376
+ className: cn(
3377
+ "h-fit overflow-hidden rounded-[8px] bg-white",
3378
+ bordered && "border border-grey-30",
3379
+ className
3380
+ ),
3377
3381
  ...rest,
3378
3382
  children
3379
3383
  }
@@ -3654,7 +3658,49 @@ var SDraggableItemImpl = /* @__PURE__ */ react.forwardRef(function SDraggableIte
3654
3658
  ] });
3655
3659
  });
3656
3660
  var SDraggableItem2 = /* @__PURE__ */ withDisplayName(SDraggableItemImpl, "SDraggableItem");
3661
+ var SDivider = /* @__PURE__ */ react.forwardRef(function SDivider2({ vertical = false, className, style, ...rest }, ref) {
3662
+ return /* @__PURE__ */ jsxRuntime.jsx(
3663
+ "hr",
3664
+ {
3665
+ ref,
3666
+ "aria-orientation": vertical ? "vertical" : "horizontal",
3667
+ className: cn(
3668
+ "shrink-0 border-0",
3669
+ vertical ? "inline-block h-auto w-px self-stretch" : "block h-px w-full",
3670
+ className
3671
+ ),
3672
+ style: { backgroundColor: "var(--sys-color-divider-default, #E1E1E1)", ...style },
3673
+ ...rest
3674
+ }
3675
+ );
3676
+ });
3677
+ var dividerStyle = { backgroundColor: "var(--cmp-listItem-border-default)" };
3678
+ function hasBorderedChild(children) {
3679
+ let bordered = false;
3680
+ react.Children.forEach(children, (child) => {
3681
+ if (!react.isValidElement(child)) return;
3682
+ if (child.props.bordered === true) {
3683
+ bordered = true;
3684
+ return;
3685
+ }
3686
+ react.Children.forEach(child.props.children, (wrapped) => {
3687
+ if (react.isValidElement(wrapped) && wrapped.props.bordered === true) {
3688
+ bordered = true;
3689
+ }
3690
+ });
3691
+ });
3692
+ return bordered;
3693
+ }
3694
+ function withDividers(children) {
3695
+ return react.Children.toArray(children).flatMap(
3696
+ (child, index) => index === 0 ? [child] : [
3697
+ /* @__PURE__ */ jsxRuntime.jsx(SDivider, { "aria-hidden": "true", style: dividerStyle }, `s-list-divider-${index}`),
3698
+ child
3699
+ ]
3700
+ );
3701
+ }
3657
3702
  var SListImpl = /* @__PURE__ */ react.forwardRef(function SList({ className, children, useGap = false, usePadding = false, ...rest }, ref) {
3703
+ const showDivider = !useGap && !hasBorderedChild(children);
3658
3704
  return /* @__PURE__ */ jsxRuntime.jsx(
3659
3705
  "div",
3660
3706
  {
@@ -3662,11 +3708,10 @@ var SListImpl = /* @__PURE__ */ react.forwardRef(function SList({ className, chi
3662
3708
  className: cn(
3663
3709
  useGap && "flex flex-col gap-(--cmp-list-group-gap)",
3664
3710
  usePadding && "p-(--cmp-list-group-padding)",
3665
- "[&>*]:border-b [&>*]:border-(--cmp-listItem-border-default)",
3666
3711
  className
3667
3712
  ),
3668
3713
  ...rest,
3669
- children
3714
+ children: showDivider ? withDividers(children) : children
3670
3715
  }
3671
3716
  );
3672
3717
  });
@@ -5381,22 +5426,6 @@ var SSectionHeaderCardRootImpl = /* @__PURE__ */ react.forwardRef(
5381
5426
  }
5382
5427
  );
5383
5428
  var SSectionHeaderCard2 = /* @__PURE__ */ withDisplayName(SSectionHeaderCardRootImpl, "SSectionHeaderCard");
5384
- var SDivider = /* @__PURE__ */ react.forwardRef(function SDivider2({ vertical = false, className, style, ...rest }, ref) {
5385
- return /* @__PURE__ */ jsxRuntime.jsx(
5386
- "hr",
5387
- {
5388
- ref,
5389
- "aria-orientation": vertical ? "vertical" : "horizontal",
5390
- className: cn(
5391
- "shrink-0 border-0",
5392
- vertical ? "inline-block h-auto w-px self-stretch" : "block h-px w-full",
5393
- className
5394
- ),
5395
- style: { backgroundColor: "var(--sys-color-divider-default, #E1E1E1)", ...style },
5396
- ...rest
5397
- }
5398
- );
5399
- });
5400
5429
 
5401
5430
  // src/lib/scrollbar.ts
5402
5431
  var FIREFOX_SCROLLBAR_CLASS = "supports-[not_selector(::-webkit-scrollbar)]:[scrollbar-width:thin] supports-[not_selector(::-webkit-scrollbar)]:[scrollbar-color:var(--color-grey-45)_transparent]";
@@ -5479,7 +5508,7 @@ var SSplitterRoot = /* @__PURE__ */ react.forwardRef(function SSplitter({
5479
5508
  vertical = false,
5480
5509
  disabled = false,
5481
5510
  dividerClassName,
5482
- dividerStyle,
5511
+ dividerStyle: dividerStyle2,
5483
5512
  className,
5484
5513
  style,
5485
5514
  children,
@@ -5604,7 +5633,7 @@ var SSplitterRoot = /* @__PURE__ */ react.forwardRef(function SSplitter({
5604
5633
  ),
5605
5634
  style: {
5606
5635
  ...vertical ? { bottom: HIT_END, height: HIT } : { right: HIT_END, width: HIT },
5607
- ...dividerStyle
5636
+ ...dividerStyle2
5608
5637
  },
5609
5638
  children: /* @__PURE__ */ jsxRuntime.jsx(
5610
5639
  "span",
@@ -8304,6 +8333,29 @@ function findGnbAncestors(items, value) {
8304
8333
  };
8305
8334
  return walk(items, []) ?? [];
8306
8335
  }
8336
+ function findGnbDescendantBranchValues(items, value) {
8337
+ const find = (list) => {
8338
+ for (const item of list) {
8339
+ if (item.value === value) return item;
8340
+ if (item.children) {
8341
+ const found = find(item.children);
8342
+ if (found) return found;
8343
+ }
8344
+ }
8345
+ return void 0;
8346
+ };
8347
+ const collect = (list, acc) => {
8348
+ for (const item of list) {
8349
+ if (item.children && item.children.length > 0) {
8350
+ acc.push(item.value);
8351
+ collect(item.children, acc);
8352
+ }
8353
+ }
8354
+ return acc;
8355
+ };
8356
+ const target = find(items);
8357
+ return target?.children ? collect(target.children, []) : [];
8358
+ }
8307
8359
  var LABEL_TIP_HIDE_DELAY_MS = 100;
8308
8360
  var DEPTH_PADDING_LEFT = (M, depth) => depth === 1 ? M.depth1PaddingLeft : depth === 2 ? M.depth2PaddingLeft : M.depth3PaddingLeft;
8309
8361
  var SGnb = /* @__PURE__ */ react.forwardRef(function SGnb2({
@@ -8344,8 +8396,12 @@ var SGnb = /* @__PURE__ */ react.forwardRef(function SGnb2({
8344
8396
  const H = GNB_HEADER[header];
8345
8397
  const M = GNB_MENU[type];
8346
8398
  const C = GNB_COLORS[color];
8399
+ const branchWithDescendants = (key) => [
8400
+ key,
8401
+ ...findGnbDescendantBranchValues(items, key)
8402
+ ];
8347
8403
  const [expandedKeys, setExpandedKeys] = react.useState(
8348
- () => new Set(findGnbAncestors(items, value))
8404
+ () => new Set(findGnbAncestors(items, value).flatMap(branchWithDescendants))
8349
8405
  );
8350
8406
  const [hoveredKey, setHoveredKey] = react.useState(null);
8351
8407
  const [labelTip, setLabelTip] = react.useState(null);
@@ -8392,12 +8448,15 @@ var SGnb = /* @__PURE__ */ react.forwardRef(function SGnb2({
8392
8448
  () => findGnbRailValue(items, value) ?? items[0]?.value
8393
8449
  );
8394
8450
  react.useEffect(() => {
8395
- const ancestors = findGnbAncestors(items, value);
8396
- if (ancestors.length === 0) return;
8451
+ const keys = findGnbAncestors(items, value).flatMap((ancestor) => [
8452
+ ancestor,
8453
+ ...findGnbDescendantBranchValues(items, ancestor)
8454
+ ]);
8455
+ if (keys.length === 0) return;
8397
8456
  setExpandedKeys((prev) => {
8398
- if (ancestors.every((a) => prev.has(a))) return prev;
8457
+ if (keys.every((key) => prev.has(key))) return prev;
8399
8458
  const next = new Set(prev);
8400
- ancestors.forEach((a) => next.add(a));
8459
+ keys.forEach((key) => next.add(key));
8401
8460
  return next;
8402
8461
  });
8403
8462
  }, [items, value]);
@@ -8414,10 +8473,12 @@ var SGnb = /* @__PURE__ */ react.forwardRef(function SGnb2({
8414
8473
  setExpandedKeys((prev) => {
8415
8474
  const next = new Set(prev);
8416
8475
  if (next.has(key)) {
8417
- next.delete(key);
8476
+ branchWithDescendants(key).forEach((descendant) => next.delete(descendant));
8418
8477
  } else {
8419
- findGnbSiblingValues(items, key).forEach((sibling) => next.delete(sibling));
8420
- next.add(key);
8478
+ findGnbSiblingValues(items, key).forEach(
8479
+ (sibling) => branchWithDescendants(sibling).forEach((descendant) => next.delete(descendant))
8480
+ );
8481
+ branchWithDescendants(key).forEach((descendant) => next.add(descendant));
8421
8482
  }
8422
8483
  return next;
8423
8484
  });