sellmate-design-system-react 9.0.0-beta.102 → 9.0.0-beta.103

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.
@@ -0,0 +1,26 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type SLocale, type SLocaleMessages, type SLocaleMessagesOverride } from './locale';
3
+ interface LocaleValue {
4
+ locale: SLocale;
5
+ messages: SLocaleMessages;
6
+ /** 로케일에 맞춘 숫자 포맷(천 단위 구분). 날짜 형식은 고정이라 여기서 다루지 않는다 */
7
+ formatNumber: (value: number) => string;
8
+ }
9
+ export interface SLocaleProviderProps {
10
+ /** `ko` | `en` | `ja` | `zh` (`en-US` 처럼 지역이 붙어도 된다). 모르는 값이면 `ko` 로 폴백한다 */
11
+ locale?: string;
12
+ /**
13
+ * 일부 문구만 덮어쓸 때. 그룹 단위로 얕게 병합되고, 값이 `undefined` 인 키는 무시한다.
14
+ * 렌더마다 새 객체를 만들면 하위 컴포넌트가 매번 다시 그려지므로 모듈 상수나 `useMemo` 로 안정시킨다.
15
+ */
16
+ messages?: SLocaleMessagesOverride;
17
+ children?: ReactNode;
18
+ }
19
+ /**
20
+ * SLocaleProvider — 컴포넌트 내장 문구의 로케일을 정한다. 앱 루트에 한 번 둔다.
21
+ * `SModalOutlet` 은 이 Provider 안쪽에 렌더해야 명령형 모달도 같은 로케일을 상속한다.
22
+ */
23
+ export declare function SLocaleProvider({ locale, messages, children }: SLocaleProviderProps): import("react").JSX.Element;
24
+ /** 현재 로케일·메시지·숫자 포맷터를 반환한다. Provider 밖이면 `ko` 기본값이다 */
25
+ export declare function useLocale(): LocaleValue;
26
+ export {};
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 컴포넌트 내장 문구(i18n)의 메시지 타입·번들·해석 로직.
3
+ *
4
+ * 외부 i18n 라이브러리 없이 타입 안전한 객체로 둔다. 어순·조사가 얽힌 문구는 문자열이
5
+ * 아니라 함수로 정의한다(예: `(n) => '외 ' + n + '개'`). React 에 의존하지 않는 순수 모듈이라
6
+ * Node 에서 그대로 테스트한다.
7
+ *
8
+ * 날짜 형식(YYYY-MM-DD)은 로케일과 무관하게 고정이고, 숫자 구분만 로케일을 따른다.
9
+ */
10
+ export declare const SLOCALES: readonly ["ko", "en", "ja", "zh"];
11
+ export type SLocale = (typeof SLOCALES)[number];
12
+ /** 기본 로케일 — Provider 가 없거나 모르는 로케일이면 이 값으로 폴백한다 */
13
+ export declare const DEFAULT_LOCALE: SLocale;
14
+ export interface SLocaleMessages {
15
+ pagination: {
16
+ firstPage: string;
17
+ prevPage: string;
18
+ nextPage: string;
19
+ lastPage: string;
20
+ prevGroup: string;
21
+ nextGroup: string;
22
+ };
23
+ select: {
24
+ placeholder: string;
25
+ allSelected: string;
26
+ /** 선택값 전체 지우기 버튼 aria-label */
27
+ clear: string;
28
+ /** 칩 × 버튼 aria-label — 어순이 언어마다 달라 함수로 둔다 */
29
+ deselect: (label: string) => string;
30
+ searchPlaceholder: string;
31
+ empty: string;
32
+ loading: string;
33
+ };
34
+ }
35
+ /** 소비 앱이 일부 문구만 덮어쓸 때 쓰는 부분 메시지 */
36
+ export type SLocaleMessagesOverride = {
37
+ [K in keyof SLocaleMessages]?: Partial<SLocaleMessages[K]>;
38
+ };
39
+ /**
40
+ * 로케일 문자열을 지원 로케일로 정규화한다. `en-US`·`ja_JP` 처럼 지역이 붙어도 언어 코드로 맞추고,
41
+ * 모르는 값이거나 비어 있으면 기본 로케일(ko)로 폴백한다 — 예외로 화면을 깨지 않는다.
42
+ * `zh` 는 간체 문구라 `zh-TW`·`zh-HK` 도 같은 번들로 연결된다(번체 번들은 아직 없다 — 필요하면 `messages` 로 덮어쓴다).
43
+ */
44
+ export declare function normalizeLocale(locale: string | undefined | null): SLocale;
45
+ /** 이미 정규화된 로케일의 번들 위에 소비 앱의 부분 override 를 그룹 단위로 얹어 최종 메시지를 만든다 */
46
+ export declare function resolveMessages(locale: string | undefined | null, override?: SLocaleMessagesOverride): SLocaleMessages;
@@ -147,6 +147,8 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
147
147
 
148
148
  콤마 없는 `39000` 은 자릿수를 세어야 읽히지만 `39,000` 은 한눈에 읽힌다. 숫자를 그대로 출력하는 코드는 미완성으로 본다.
149
149
 
150
+ **앱이 `SLocaleProvider` 로 로케일을 바꾸는 경우(§4-1)에는 `useLocale().formatNumber(v)` 로 쓴다** — 선택한 로케일의 숫자 구분을 따른다. 로케일을 바꾸지 않는 앱은 `toLocaleString()` 그대로 쓴다. **날짜는 로케일을 바꿔도 아래 `YYYY-MM-DD` 표기가 그대로다.**
151
+
150
152
  **번호·코드는 제외한다.** 전화번호·사업자번호·송장번호·상품코드처럼 대상을 가리키는 값은 크기를 비교하는 숫자가 아니라 **서식이 정해진 문자열**이다. 여기에 콤마를 넣으면 송장번호 `123456789` 가 `123,456,789` 로 보여 값 자체가 달라진다.
151
153
 
152
154
  **날짜는 `YYYY-MM-DD` 로 쓴다.** 자릿수를 채우고 하이픈으로 구분한다 — `2026-08-13`.
@@ -1567,6 +1569,7 @@ tableRef.current.scrollToRow(row); // 복원
1567
1569
  - **태그의 색·이름을 밖에서 직접 주지 않는다** (`status`). `option.color`·`option.label` 은 없다 — 지금 상태가 가리키는 `options` 칸이 곧 태그다. 색이 상태의 뜻이라 둘이 어긋나면 안 되기 때문이다.
1568
1570
  - **상태 메뉴의 색은 칸마다 준다** — 점과 글자가 그 상태의 색으로 함께 물든다. 상태는 색 자체가 뜻이라 고르는 자리에서도 색을 보고 고른다.
1569
1571
  - `switch` 는 메뉴를 갖지 않는다 — 우측 문구가 "지금 값"이 아니라 보조 조작 문구라, 고를 목록이 있으면 그것은 `subSelect` 다.
1572
+ - **라벨 옆에 뱃지·카운트를 붙일 때는 `labelSlot` 을 쓴다** (`account` `select` `switch` `subSelect`). 라벨이 길면 라벨만 잘리고 슬롯은 밀리지 않는다. 행 전체가 버튼이라 **슬롯에 버튼·링크를 넣지 않는다**(button 중첩). `status` 는 태그가 라벨 자리를 차지해 받지 않는다. 텍스트를 넘기면 라벨과 같은 서체·색을 상속하고, 색을 바꿀 때는 토큰(`--sys-color-*`)으로 덮는다(고정 색은 어두운 면에서 묻힌다). 라벨 자체의 색은 바꾸지 못한다.
1570
1573
  - 높이·라운드·아이콘 크기·좌우 여백은 전부 토큰이 넣는다. 직접 주지 않는다.
1571
1574
 
1572
1575
  #### 3-5-8. `SAccountListBox` — 계정 행을 눌러 뜨는 계정 패널
@@ -2465,6 +2468,23 @@ import { SModalOutlet } from 'sellmate-design-system-react';
2465
2468
 
2466
2469
  `SModal.confirm/loading/create` 로 띄운 모달이 그려지는 자리다. outlet 이 앱 트리 안에 있어야 모달이 앱의 Context(QueryClient·Router·Theme 등)를 상속한다 — outlet 이 없으면 모달은 뜨지만 별도 React 루트라 Provider 가 닿지 않는다(§3-3-4). 모달은 언제나 `body` 로 portal 되므로 outlet 을 어디에 두든 레이아웃에는 영향이 없고, **셸 안에 넣을 필요도 없다.** 토스트를 쓴다면 `SToastContainer` 도 같은 자리에 둔다.
2467
2470
 
2471
+ **컴포넌트가 내장한 문구(aria-label·placeholder·"전체" 등)의 언어는 `SLocaleProvider` 가 정한다 — 앱 루트에 한 번 두고, `SModalOutlet` 은 그 안쪽에 둔다.** 안쪽에 두어야 명령형 모달도 같은 로케일을 상속한다. Provider 가 없으면 한국어다.
2472
+
2473
+ ```tsx
2474
+ import { SLocaleProvider, SModalOutlet } from 'sellmate-design-system-react';
2475
+
2476
+ <SLocaleProvider locale={userLocale}>
2477
+ <RouterProvider router={router} />
2478
+ <SModalOutlet />
2479
+ </SLocaleProvider>;
2480
+ ```
2481
+
2482
+ - 지원 로케일은 `SLOCALES` 에 있다. `en-US` 처럼 지역이 붙어도 되고, **지원하지 않는 값은 예외 없이 한국어로 폴백한다.** `zh` 는 간체 문구라 `zh-TW` 도 간체로 나온다.
2483
+ - **`ja`·`zh` 문구는 원어민 검수 전 초안이다.** 사용자에게 노출하기 전에 검수한다.
2484
+ - 일부 문구만 바꾸려면 `messages` 로 덮어쓴다 — 나머지는 로케일 문구를 유지한다. `messages` 는 모듈 상수나 `useMemo` 로 안정시킨다(렌더마다 새 객체를 만들면 하위 컴포넌트가 매번 다시 그려진다).
2485
+ - 개별 prop(`placeholder` 등)을 직접 주면 로케일 문구보다 prop 이 우선한다. **내장 문구를 앱이 직접 번역해 prop 으로 넘기지 않는다** — Provider 가 이미 처리한다.
2486
+ - 아직 로케일을 따르는 컴포넌트는 `SPagination`·`SSelect` 뿐이고, 나머지는 순차로 옮기는 중이다. 로케일을 바꿔도 그 밖의 컴포넌트 문구는 한국어로 남는다.
2487
+
2468
2488
  **최소 너비는 `SLayout` 이 보장한다 — 앱이 `min-w-*` 를 직접 주지 않는다.** 창이 최소 너비(`SLAYOUT_MIN_WIDTH`, GNB 포함한 전체 기준)보다 좁아지면 GNB·상단바는 제자리에 남고 **`SPage` 안에만 가로 스크롤이 생긴다.** 문서(브라우저 창)에는 가로 스크롤이 생기지 않는다. 그래서 셸을 감싸는 요소에 `min-width` 나 `overflow-x` 를 걸지 않는다 — 걸면 창 전체가 스크롤되어 GNB 가 화면 밖으로 밀린다.
2469
2489
 
2470
2490
  **창이 최소 너비보다 넓을 때 본문을 어떻게 세울지는 `SPage` 의 `contentWidth` 가 정한다 — 폭을 직접 계산하지 않는다.** 넓은 모니터에서 본문이 끝까지 늘어나 성기어지는 화면이 있으므로 세 가지 중 하나를 고른다.
@@ -11487,8 +11507,8 @@ export interface SSectionNavItem {
11487
11507
  | `valueDisplay?` | `SSelectValueDisplay` | `'text'` | 트리거에 선택값을 보여주는 방식. - `text`: 선택 라벨을 쉼표로 이어 한 줄로 보여준다. 넘치면 말줄임한다. - `chip`: 선택값마다 칩을 낸다. 칩의 × 를 누르면 그 값만 선택에서 빠지고, 드롭다운은 열리지 않는다. 칩이 한 줄을 넘으면 줄바꿈하고 트리거가 그만큼 높아진다. `chip` 은 `multi`·`multi_depth`·`multi_tree` 에서만 동작한다. 단일 선택 type 에서는 무시하고 `text` 로 보여준다. 전체 선택이면 칩 대신 `allSelectedLabel` 을 보여준다. `multi_depth`·`multi_tree` 에서 그룹의 하위 리프를 전부 고르면 리프 칩 대신 그룹명 칩 하나로 묶고, 그 칩의 × 는 그룹의 리프를 한꺼번에 뺀다. 비활성(select 자체나 해당 옵션)인 칩에는 × 를 내지 않는다. |
11488
11508
  | `showSearch?` | `boolean` | `false` | 검색 입력 사용 |
11489
11509
  | `showSelectAll?` | `boolean` | `false` | 전체선택 옵션 사용 (multi 전용) |
11490
- | `allSelectedLabel?` | `string` | `'전체'` | 전체선택 레이블 |
11491
- | `placeholder?` | `string` | `'선택'` | placeholder |
11510
+ | `allSelectedLabel?` | `string` | — | 전체선택 레이블. 생략하면 `SLocaleProvider` 로케일의 기본 문구(ko: "전체")를 쓴다 |
11511
+ | `placeholder?` | `string` | — | placeholder. 생략하면 `SLocaleProvider` 로케일의 기본 문구(ko: "선택")를 쓴다 |
11492
11512
  | `disabled?` | `boolean` | `false` | 비활성 |
11493
11513
  | `clearable?` | `boolean` | `false` | 선택값 지우기(×) 버튼 노출. 선택값이 있고 비활성이 아닐 때만 나타나며, 누르면 드롭다운을 열지 않고 값만 비운다. |
11494
11514
  | `error?` | `boolean` | `false` | 에러 상태 |
@@ -11908,6 +11928,7 @@ export const STEPPER_SIZES = ['sm', 'lg'] as const;
11908
11928
  |------|------|---------|-------------|
11909
11929
  | `type` | `'account'` | — | 계정 행 |
11910
11930
  | `label?` | `string` | — | 라벨 텍스트 |
11931
+ | `labelSlot?` | `ReactNode` | — | 라벨 오른쪽에 붙는 표시용 요소(뱃지·카운트 등). 라벨이 길면 라벨만 잘리고 이 요소는 밀리지 않는다. 텍스트를 넘기면 라벨과 같은 서체·색을 상속한다 — 색을 바꾸려면 넘기는 쪽에서 토큰(`--sys-color-*`)으로 덮고, 고정 색은 `dark`·`darker` 면에서 묻히니 세 색 모두에서 확인한다. 라벨 자체의 색은 바꾸지 못한다. 행 전체가 버튼이라 **버튼·링크 같은 조작 요소는 넣지 않는다**(button 중첩). |
11911
11932
  | `option` | `SSystemActionButtonAccountOption` | — | 누르면 뜨는 계정 리스트박스. **필수다** — 계정 행은 늘 이 패널의 트리거이고, 눌러도 아무것도 뜨지 않는 계정 행은 없다. 아이콘은 `user` 로 고정이라 여기서 받지 않는다. |
11912
11933
 
11913
11934
  ### SSystemActionButtonBase
@@ -11927,6 +11948,7 @@ export const STEPPER_SIZES = ['sm', 'lg'] as const;
11927
11948
  |------|------|---------|-------------|
11928
11949
  | `type` | `'account' \| 'select' \| 'switch' \| 'subSelect' \| 'status'` | — | 계정 행 |
11929
11950
  | `label?` | `string` | — | 라벨 텍스트 |
11951
+ | `labelSlot?` | `ReactNode` | — | 라벨 오른쪽에 붙는 표시용 요소(뱃지·카운트 등). 라벨이 길면 라벨만 잘리고 이 요소는 밀리지 않는다. 텍스트를 넘기면 라벨과 같은 서체·색을 상속한다 — 색을 바꾸려면 넘기는 쪽에서 토큰(`--sys-color-*`)으로 덮고, 고정 색은 `dark`·`darker` 면에서 묻히니 세 색 모두에서 확인한다. 라벨 자체의 색은 바꾸지 못한다. 행 전체가 버튼이라 **버튼·링크 같은 조작 요소는 넣지 않는다**(button 중첩). |
11930
11952
  | `option?` | `<union 5종>` | — | 누르면 뜨는 계정 리스트박스. **필수다** — 계정 행은 늘 이 패널의 트리거이고, 눌러도 아무것도 뜨지 않는 계정 행은 없다. 아이콘은 `user` 로 고정이라 여기서 받지 않는다. |
11931
11953
 
11932
11954
  ### SSystemActionButtonSelect
@@ -11937,6 +11959,7 @@ export const STEPPER_SIZES = ['sm', 'lg'] as const;
11937
11959
  |------|------|---------|-------------|
11938
11960
  | `type` | `'select'` | — | 화살표만 붙는 행 |
11939
11961
  | `label?` | `string` | — | 라벨 텍스트 |
11962
+ | `labelSlot?` | `ReactNode` | — | 라벨 오른쪽에 붙는 표시용 요소(뱃지·카운트 등). 라벨이 길면 라벨만 잘리고 이 요소는 밀리지 않는다. 텍스트를 넘기면 라벨과 같은 서체·색을 상속한다 — 색을 바꾸려면 넘기는 쪽에서 토큰(`--sys-color-*`)으로 덮고, 고정 색은 `dark`·`darker` 면에서 묻히니 세 색 모두에서 확인한다. 라벨 자체의 색은 바꾸지 못한다. 행 전체가 버튼이라 **버튼·링크 같은 조작 요소는 넣지 않는다**(button 중첩). |
11940
11963
  | `option?` | `SSystemActionButtonIconOption` | — | 선행 아이콘. 아이콘 없이 라벨만 두는 행이면 통째로 생략한다 |
11941
11964
 
11942
11965
  ### SSystemActionButtonStatus
@@ -11947,6 +11970,7 @@ export const STEPPER_SIZES = ['sm', 'lg'] as const;
11947
11970
  |------|------|---------|-------------|
11948
11971
  | `type` | `'status'` | — | 라벨 자리에 상태 태그가 들어가는 행 |
11949
11972
  | `label?` | `never` | — | 태그가 라벨 자리를 차지하므로 받지 않는다 |
11973
+ | `labelSlot?` | `never` | — | 태그가 라벨 자리를 차지하므로 받지 않는다 |
11950
11974
  | `option` | `SSystemActionButtonStatusOption` | — | 상태 태그 + 고를 목록. **필수다** — 상태 행은 늘 그 상태를 바꾸는 메뉴의 트리거다. |
11951
11975
 
11952
11976
  ### SSystemActionButtonSubSelect
@@ -11957,6 +11981,7 @@ export const STEPPER_SIZES = ['sm', 'lg'] as const;
11957
11981
  |------|------|---------|-------------|
11958
11982
  | `type` | `'subSelect'` | — | 우측에 지금 선택된 값이 붙는 행 |
11959
11983
  | `label?` | `string` | — | 라벨 텍스트 |
11984
+ | `labelSlot?` | `ReactNode` | — | 라벨 오른쪽에 붙는 표시용 요소(뱃지·카운트 등). 라벨이 길면 라벨만 잘리고 이 요소는 밀리지 않는다. 텍스트를 넘기면 라벨과 같은 서체·색을 상속한다 — 색을 바꾸려면 넘기는 쪽에서 토큰(`--sys-color-*`)으로 덮고, 고정 색은 `dark`·`darker` 면에서 묻히니 세 색 모두에서 확인한다. 라벨 자체의 색은 바꾸지 못한다. 행 전체가 버튼이라 **버튼·링크 같은 조작 요소는 넣지 않는다**(button 중첩). |
11960
11985
  | `option` | `SSystemActionButtonSubSelectOption` | — | 선행 아이콘 + 우측 값 + 고를 목록 |
11961
11986
 
11962
11987
  ### SSystemActionButtonSwitch
@@ -11967,6 +11992,7 @@ export const STEPPER_SIZES = ['sm', 'lg'] as const;
11967
11992
  |------|------|---------|-------------|
11968
11993
  | `type` | `'switch'` | — | 우측에 보조 조작 문구가 붙는 행 |
11969
11994
  | `label?` | `string` | — | 라벨 텍스트 |
11995
+ | `labelSlot?` | `ReactNode` | — | 라벨 오른쪽에 붙는 표시용 요소(뱃지·카운트 등). 라벨이 길면 라벨만 잘리고 이 요소는 밀리지 않는다. 텍스트를 넘기면 라벨과 같은 서체·색을 상속한다 — 색을 바꾸려면 넘기는 쪽에서 토큰(`--sys-color-*`)으로 덮고, 고정 색은 `dark`·`darker` 면에서 묻히니 세 색 모두에서 확인한다. 라벨 자체의 색은 바꾸지 못한다. 행 전체가 버튼이라 **버튼·링크 같은 조작 요소는 넣지 않는다**(button 중첩). |
11970
11996
  | `option?` | `SSystemActionButtonTextOption` | — | 선행 아이콘 + 우측 액션 텍스트. 둘 다 두지 않으면 통째로 생략한다 |
11971
11997
 
11972
11998
  ### SSystemActionButtonText
@@ -11977,6 +12003,7 @@ export const STEPPER_SIZES = ['sm', 'lg'] as const;
11977
12003
  |------|------|---------|-------------|
11978
12004
  | `type` | `'switch' \| 'subSelect'` | — | 우측에 보조 조작 문구가 붙는 행 |
11979
12005
  | `label?` | `string` | — | 라벨 텍스트 |
12006
+ | `labelSlot?` | `ReactNode` | — | 라벨 오른쪽에 붙는 표시용 요소(뱃지·카운트 등). 라벨이 길면 라벨만 잘리고 이 요소는 밀리지 않는다. 텍스트를 넘기면 라벨과 같은 서체·색을 상속한다 — 색을 바꾸려면 넘기는 쪽에서 토큰(`--sys-color-*`)으로 덮고, 고정 색은 `dark`·`darker` 면에서 묻히니 세 색 모두에서 확인한다. 라벨 자체의 색은 바꾸지 못한다. 행 전체가 버튼이라 **버튼·링크 같은 조작 요소는 넣지 않는다**(button 중첩). |
11980
12007
  | `option?` | `SSystemActionButtonTextOption \| SSystemActionButtonSubSelectOption` | — | 선행 아이콘 + 우측 액션 텍스트. 둘 다 두지 않으면 통째로 생략한다 |
11981
12008
 
11982
12009
  ## Types
package/dist/llms.txt CHANGED
@@ -148,6 +148,8 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
148
148
 
149
149
  콤마 없는 `39000` 은 자릿수를 세어야 읽히지만 `39,000` 은 한눈에 읽힌다. 숫자를 그대로 출력하는 코드는 미완성으로 본다.
150
150
 
151
+ **앱이 `SLocaleProvider` 로 로케일을 바꾸는 경우(§4-1)에는 `useLocale().formatNumber(v)` 로 쓴다** — 선택한 로케일의 숫자 구분을 따른다. 로케일을 바꾸지 않는 앱은 `toLocaleString()` 그대로 쓴다. **날짜는 로케일을 바꿔도 아래 `YYYY-MM-DD` 표기가 그대로다.**
152
+
151
153
  **번호·코드는 제외한다.** 전화번호·사업자번호·송장번호·상품코드처럼 대상을 가리키는 값은 크기를 비교하는 숫자가 아니라 **서식이 정해진 문자열**이다. 여기에 콤마를 넣으면 송장번호 `123456789` 가 `123,456,789` 로 보여 값 자체가 달라진다.
152
154
 
153
155
  **날짜는 `YYYY-MM-DD` 로 쓴다.** 자릿수를 채우고 하이픈으로 구분한다 — `2026-08-13`.
@@ -1568,6 +1570,7 @@ tableRef.current.scrollToRow(row); // 복원
1568
1570
  - **태그의 색·이름을 밖에서 직접 주지 않는다** (`status`). `option.color`·`option.label` 은 없다 — 지금 상태가 가리키는 `options` 칸이 곧 태그다. 색이 상태의 뜻이라 둘이 어긋나면 안 되기 때문이다.
1569
1571
  - **상태 메뉴의 색은 칸마다 준다** — 점과 글자가 그 상태의 색으로 함께 물든다. 상태는 색 자체가 뜻이라 고르는 자리에서도 색을 보고 고른다.
1570
1572
  - `switch` 는 메뉴를 갖지 않는다 — 우측 문구가 "지금 값"이 아니라 보조 조작 문구라, 고를 목록이 있으면 그것은 `subSelect` 다.
1573
+ - **라벨 옆에 뱃지·카운트를 붙일 때는 `labelSlot` 을 쓴다** (`account` `select` `switch` `subSelect`). 라벨이 길면 라벨만 잘리고 슬롯은 밀리지 않는다. 행 전체가 버튼이라 **슬롯에 버튼·링크를 넣지 않는다**(button 중첩). `status` 는 태그가 라벨 자리를 차지해 받지 않는다. 텍스트를 넘기면 라벨과 같은 서체·색을 상속하고, 색을 바꿀 때는 토큰(`--sys-color-*`)으로 덮는다(고정 색은 어두운 면에서 묻힌다). 라벨 자체의 색은 바꾸지 못한다.
1571
1574
  - 높이·라운드·아이콘 크기·좌우 여백은 전부 토큰이 넣는다. 직접 주지 않는다.
1572
1575
 
1573
1576
  #### 3-5-8. `SAccountListBox` — 계정 행을 눌러 뜨는 계정 패널
@@ -2466,6 +2469,23 @@ import { SModalOutlet } from 'sellmate-design-system-react';
2466
2469
 
2467
2470
  `SModal.confirm/loading/create` 로 띄운 모달이 그려지는 자리다. outlet 이 앱 트리 안에 있어야 모달이 앱의 Context(QueryClient·Router·Theme 등)를 상속한다 — outlet 이 없으면 모달은 뜨지만 별도 React 루트라 Provider 가 닿지 않는다(§3-3-4). 모달은 언제나 `body` 로 portal 되므로 outlet 을 어디에 두든 레이아웃에는 영향이 없고, **셸 안에 넣을 필요도 없다.** 토스트를 쓴다면 `SToastContainer` 도 같은 자리에 둔다.
2468
2471
 
2472
+ **컴포넌트가 내장한 문구(aria-label·placeholder·"전체" 등)의 언어는 `SLocaleProvider` 가 정한다 — 앱 루트에 한 번 두고, `SModalOutlet` 은 그 안쪽에 둔다.** 안쪽에 두어야 명령형 모달도 같은 로케일을 상속한다. Provider 가 없으면 한국어다.
2473
+
2474
+ ```tsx
2475
+ import { SLocaleProvider, SModalOutlet } from 'sellmate-design-system-react';
2476
+
2477
+ <SLocaleProvider locale={userLocale}>
2478
+ <RouterProvider router={router} />
2479
+ <SModalOutlet />
2480
+ </SLocaleProvider>;
2481
+ ```
2482
+
2483
+ - 지원 로케일은 `SLOCALES` 에 있다. `en-US` 처럼 지역이 붙어도 되고, **지원하지 않는 값은 예외 없이 한국어로 폴백한다.** `zh` 는 간체 문구라 `zh-TW` 도 간체로 나온다.
2484
+ - **`ja`·`zh` 문구는 원어민 검수 전 초안이다.** 사용자에게 노출하기 전에 검수한다.
2485
+ - 일부 문구만 바꾸려면 `messages` 로 덮어쓴다 — 나머지는 로케일 문구를 유지한다. `messages` 는 모듈 상수나 `useMemo` 로 안정시킨다(렌더마다 새 객체를 만들면 하위 컴포넌트가 매번 다시 그려진다).
2486
+ - 개별 prop(`placeholder` 등)을 직접 주면 로케일 문구보다 prop 이 우선한다. **내장 문구를 앱이 직접 번역해 prop 으로 넘기지 않는다** — Provider 가 이미 처리한다.
2487
+ - 아직 로케일을 따르는 컴포넌트는 `SPagination`·`SSelect` 뿐이고, 나머지는 순차로 옮기는 중이다. 로케일을 바꿔도 그 밖의 컴포넌트 문구는 한국어로 남는다.
2488
+
2469
2489
  **최소 너비는 `SLayout` 이 보장한다 — 앱이 `min-w-*` 를 직접 주지 않는다.** 창이 최소 너비(`SLAYOUT_MIN_WIDTH`, GNB 포함한 전체 기준)보다 좁아지면 GNB·상단바는 제자리에 남고 **`SPage` 안에만 가로 스크롤이 생긴다.** 문서(브라우저 창)에는 가로 스크롤이 생기지 않는다. 그래서 셸을 감싸는 요소에 `min-width` 나 `overflow-x` 를 걸지 않는다 — 걸면 창 전체가 스크롤되어 GNB 가 화면 밖으로 밀린다.
2470
2490
 
2471
2491
  **창이 최소 너비보다 넓을 때 본문을 어떻게 세울지는 `SPage` 의 `contentWidth` 가 정한다 — 폭을 직접 계산하지 않는다.** 넓은 모니터에서 본문이 끝까지 늘어나 성기어지는 화면이 있으므로 세 가지 중 하나를 고른다.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sellmate-design-system-react",
3
- "version": "9.0.0-beta.102",
3
+ "version": "9.0.0-beta.103",
4
4
  "description": "Sellmate Design System — React (TypeScript + Tailwind v4) port",
5
5
  "license": "UNLICENSED",
6
6
  "keywords": [