sellmate-design-system-react 9.0.0-beta.2 → 9.0.0-beta.20

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 (130) hide show
  1. package/AGENTS.md +463 -89
  2. package/README.md +101 -0
  3. package/dist/components/SBadge/README.md +24 -0
  4. package/dist/components/SBadge/SBadge.d.ts +1 -1
  5. package/dist/components/SBarcodeInput/README.md +9 -1
  6. package/dist/components/SBarcodeInput/SBarcodeInput.d.ts +7 -1
  7. package/dist/components/SButton/README.md +36 -0
  8. package/dist/components/SCalendar/README.md +13 -0
  9. package/dist/components/SCallout/README.md +15 -0
  10. package/dist/components/SCard/SCard.d.ts +1 -1
  11. package/dist/components/SCheckbox/README.md +8 -0
  12. package/dist/components/SChipFilter/README.md +288 -5
  13. package/dist/components/SChipFilter/SChipFilter.d.ts +95 -40
  14. package/dist/components/SChipFilter/index.d.ts +1 -1
  15. package/dist/components/SChipInput/README.md +15 -1
  16. package/dist/components/SChipInput/SChipInput.d.ts +7 -1
  17. package/dist/components/SCircleProgress/README.md +8 -0
  18. package/dist/components/SConfirmModal/README.md +16 -0
  19. package/dist/components/SDatePicker/README.md +60 -4
  20. package/dist/components/SDatePicker/SDatePicker.d.ts +61 -5
  21. package/dist/components/SDatePicker/index.d.ts +1 -1
  22. package/dist/components/SDateRangePicker/README.md +17 -2
  23. package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +16 -3
  24. package/dist/components/SDivider/README.md +4 -0
  25. package/dist/components/SDraggableItem/README.md +37 -0
  26. package/dist/components/SDraggableItem/SDraggableItem.d.ts +2 -0
  27. package/dist/components/SDraggableList/README.md +29 -0
  28. package/dist/components/SDraggableList/SDraggableList.d.ts +10 -0
  29. package/dist/components/SDraggableList/index.d.ts +1 -1
  30. package/dist/components/SDrawer/README.md +8 -0
  31. package/dist/components/SDropdownButton/README.md +19 -0
  32. package/dist/components/SEditor/EditorBody.d.ts +40 -0
  33. package/dist/components/SEditor/EditorToolbar.d.ts +87 -0
  34. package/dist/components/SEditor/README.md +230 -0
  35. package/dist/components/SEditor/SEditor.d.ts +124 -0
  36. package/dist/components/SEditor/editor-icons.d.ts +59 -0
  37. package/dist/components/SEditor/editor.config.d.ts +85 -0
  38. package/dist/components/SEditor/index.d.ts +2 -0
  39. package/dist/components/SEditor/tiptap-api.d.ts +29 -0
  40. package/dist/components/SEditor/use-is-mobile.d.ts +14 -0
  41. package/dist/components/SExpansionItem/README.md +36 -0
  42. package/dist/components/SField/README.md +27 -2
  43. package/dist/components/SField/SField.d.ts +22 -4
  44. package/dist/components/SFilePicker/README.md +15 -1
  45. package/dist/components/SFilePicker/SFilePicker.d.ts +7 -1
  46. package/dist/components/SFooter/README.md +21 -0
  47. package/dist/components/SForm/README.md +11 -0
  48. package/dist/components/SGhostButton/README.md +20 -2
  49. package/dist/components/SGnb/README.md +45 -0
  50. package/dist/components/SGnb/gnb.config.d.ts +7 -0
  51. package/dist/components/SGuide/README.md +15 -0
  52. package/dist/components/SIcon/README.md +4 -0
  53. package/dist/components/SIcon/SIcon.d.ts +1 -1
  54. package/dist/components/SIcon/icons.gen.d.ts +2 -0
  55. package/dist/components/SImage/README.md +14 -0
  56. package/dist/components/SInput/README.md +3 -1
  57. package/dist/components/SInput/SInput.d.ts +7 -1
  58. package/dist/components/SKeyValueTable/README.md +87 -0
  59. package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +14 -3
  60. package/dist/components/SLayout/README.md +16 -0
  61. package/dist/components/SLinearProgress/README.md +8 -0
  62. package/dist/components/SList/README.md +5 -1
  63. package/dist/components/SList/SList.d.ts +0 -2
  64. package/dist/components/SListItem/README.md +41 -0
  65. package/dist/components/SLoadingModal/README.md +8 -0
  66. package/dist/components/SNumberInput/README.md +9 -1
  67. package/dist/components/SNumberInput/SNumberInput.d.ts +7 -1
  68. package/dist/components/SPage/README.md +41 -1
  69. package/dist/components/SPage/SPage.d.ts +24 -2
  70. package/dist/components/SPage/index.d.ts +1 -1
  71. package/dist/components/SPage/page.config.d.ts +8 -0
  72. package/dist/components/SPopover/README.md +15 -0
  73. package/dist/components/SPopup/README.md +19 -0
  74. package/dist/components/SPortal/README.md +14 -0
  75. package/dist/components/SRadio/README.md +20 -0
  76. package/dist/components/SRadioButton/README.md +18 -0
  77. package/dist/components/SScrollArea/README.md +14 -0
  78. package/dist/components/SSearchInput/README.md +61 -0
  79. package/dist/components/SSearchInput/SSearchInput.d.ts +52 -0
  80. package/dist/components/SSearchInput/index.d.ts +1 -0
  81. package/dist/components/SSectionHeaderCard/README.md +39 -20
  82. package/dist/components/SSectionHeaderCard/SSectionHeaderCard.d.ts +21 -14
  83. package/dist/components/SSectionHeaderCard/index.d.ts +1 -1
  84. package/dist/components/SSelect/README.md +25 -3
  85. package/dist/components/SSelect/SSelect.d.ts +13 -1
  86. package/dist/components/SSplitter/README.md +15 -0
  87. package/dist/components/SStepper/README.md +26 -0
  88. package/dist/components/SSwitch/README.md +13 -0
  89. package/dist/components/STable/README.md +72 -1
  90. package/dist/components/STable/STable.d.ts +129 -11
  91. package/dist/components/STable/index.d.ts +1 -1
  92. package/dist/components/STabs/README.md +12 -1
  93. package/dist/components/STabs/STabs.d.ts +2 -4
  94. package/dist/components/STabs/index.d.ts +1 -1
  95. package/dist/components/STabs/tabs.config.d.ts +3 -4
  96. package/dist/components/STag/README.md +47 -0
  97. package/dist/components/STextLink/README.md +17 -0
  98. package/dist/components/STextLink/STextLink.d.ts +2 -0
  99. package/dist/components/STextarea/README.md +1 -1
  100. package/dist/components/STextarea/STextarea.d.ts +7 -1
  101. package/dist/components/STimePicker/README.md +15 -1
  102. package/dist/components/STimePicker/STimePicker.d.ts +7 -1
  103. package/dist/components/STimePicker/timepicker.config.d.ts +7 -0
  104. package/dist/components/STimeRangePicker/README.md +27 -1
  105. package/dist/components/STimeRangePicker/STimeRangePicker.d.ts +7 -1
  106. package/dist/components/SToast/README.md +24 -0
  107. package/dist/components/SToggle/README.md +8 -0
  108. package/dist/components/STooltip/README.md +22 -0
  109. package/dist/components/STree/README.md +41 -0
  110. package/dist/components/STree/STree.d.ts +8 -0
  111. package/dist/components/STree/index.d.ts +1 -1
  112. package/dist/index.cjs +3987 -496
  113. package/dist/index.cjs.map +1 -1
  114. package/dist/index.d.ts +3 -0
  115. package/dist/index.js +3970 -496
  116. package/dist/index.js.map +1 -1
  117. package/dist/lib/field-width.d.ts +31 -0
  118. package/dist/lib/story-docs.d.ts +19 -3
  119. package/dist/lib/truncated-value-tooltip.d.ts +18 -0
  120. package/dist/llms-full.txt +2312 -198
  121. package/dist/llms.txt +465 -91
  122. package/dist/styles.css +672 -41
  123. package/dist/theme.css +22 -6
  124. package/eslint/index.mjs +10 -0
  125. package/eslint/lib/table-column.mjs +26 -0
  126. package/eslint/rules/field-width-grade.d.mts +41 -0
  127. package/eslint/rules/field-width-grade.mjs +311 -0
  128. package/eslint/rules/table-column-width.mjs +110 -0
  129. package/eslint/scale.gen.mjs +3 -0
  130. package/package.json +13 -1
package/README.md CHANGED
@@ -186,6 +186,8 @@ export default [
186
186
  | `sellmate/component-group-gap` | warn | 같은 컴포넌트를 나열할 때의 그룹 간격 — 배열 방향에 따라 값이 다르다(체크박스 가로 24 / 세로 8) |
187
187
  | `sellmate/table-numeric-align` | warn | 수량 컬럼(금액·수량 등)에 `align: 'right'` 누락 — **`--fix` 로 자동 교정** |
188
188
  | `sellmate/require-locale-number` | warn | 수량 컬럼의 `toLocaleString()` 누락 — 세 자리 콤마 |
189
+ | `sellmate/field-width-grade` | warn | 필드 폭이 `maxLength`(스키마 상한)와 어긋남 — 등급 미지정 · 등급 밖 폭 · 상한 대비 과부족 · 등급 px 직접 지정(**`--fix` 로 등급 이름 치환**) |
190
+ | `sellmate/table-column-width` | warn | 컬럼 폭 미지정(기본 120px 이 조용히 들어감) · px 아닌 폭(`%` `clamp()`) · 값을 그리는 열의 `autoWidth` 오용 |
189
191
  | `sellmate/no-arbitrary-class` | **off** | 토큰이 있는 속성(색·타이포·간격·모서리)의 임의 값 — `text-[14px]`, `bg-[#eee]`. 앱 고유 화면에는 정당한 사용이 많아 기본값은 끕니다 |
190
192
 
191
193
  `className` 뿐 아니라 `cn()`/`clsx()` 인자, 템플릿 리터럴, 객체 키 안까지 검사합니다.
@@ -237,6 +239,105 @@ export default [
237
239
  'sellmate/table-numeric-align': ['error', { allow: ['rank'] }],
238
240
  ```
239
241
 
242
+ ### 필드 폭 등급 (`field-width-grade`)
243
+
244
+ 필드 너비는 **`maxLength`(= 스키마 상한)로 정합니다.** 상한이 등급 안에 들어오면 그 등급으로 고정하고,
245
+ 등급 상한(`xl`)을 넘거나 상한이 아예 없으면 행 전체(`width="100%"` 또는 생략)로 둡니다.
246
+
247
+ **폭은 등급 이름으로 줍니다** — `width="md"` 처럼 씁니다. 등급은 `--cmp-field-width-*` 토큰으로
248
+ 풀리므로 토큰이 바뀌면 화면이 따라갑니다. 같은 값을 px 로 적으면 그 화면만 옛 값에 남습니다.
249
+
250
+ | 등급 | 쓰는 곳 |
251
+ | ---- | ------------- |
252
+ | `xs` | 숫자 필드 전용 |
253
+ | `sm` | |
254
+ | `md` | |
255
+ | `lg` | |
256
+ | `xl` | 정책상 상한 |
257
+
258
+ ```tsx
259
+ // 상한이 등급 안에 들어온다 → 그 등급으로 고정
260
+ <SInput name="code" label="코드" maxLength={10} width="md" />
261
+
262
+ // 상한이 xl 로도 안 담긴다 → 행 전체
263
+ <SInput name="desc" label="설명" maxLength={100} width="100%" />
264
+
265
+ // 숫자 필드의 상한은 maxLength 가 아니라 max 다 (천단위 콤마도 폭을 먹는다)
266
+ <SNumberInput name="qty" label="수량" max={99} width="xs" />
267
+
268
+ // 등급 값과 같은 px 를 적으면 등급 이름으로 자동 수정됩니다 (--fix)
269
+ <SInput name="code" label="코드" maxLength={10} width={160} /> // → width="md"
270
+ ```
271
+
272
+ 대상은 `SInput` · `SNumberInput` · `SBarcodeInput` 입니다. `STextarea` 는 여러 줄로 접혀 상한이 폭을
273
+ 정하지 않고, 셀렉트 계열은 상한 개념이 없어 목록 최장값으로 폭을 정하며, 날짜·시간 계열은 컴포넌트가
274
+ 자체 상한을 갖습니다.
275
+
276
+ **상한도 폭도 변수로 넘기면 검사하지 않습니다.** 정적으로 알 수 없는 것을 위반으로 보고하지 않습니다.
277
+
278
+ ```tsx
279
+ <SInput maxLength={LIMIT} width={fieldWidth} /> // 통과
280
+ <SInput maxLength={10} {...rest} /> // 통과 (spread 안에 width 가 있을 수 있다)
281
+ ```
282
+
283
+ #### 글자수 환산
284
+
285
+ 등급이 몇 글자를 담는지는 토큰 값(글꼴 크기 · 좌우 패딩 · 테두리 · 스테퍼)에서 계산합니다.
286
+ 한글은 전각이라 영숫자의 약 두 배를 먹으므로 **두 기준 사이에는 아무것도 보고하지 않습니다** —
287
+ 영숫자로 채워도 넘칠 때만 "좁다", 한글로 채워도 남을 때만 "넓다" 로 판정합니다.
288
+
289
+ | 등급 | `SInput` (sm) | `SInput` (md) | `SNumberInput` (sm) | `SNumberInput` (sm, `useButton`) |
290
+ | ---- | ------------- | ------------- | ------------------- | -------------------------------- |
291
+ | `xs` | — | — | 영숫자 8 | 영숫자 2 |
292
+ | `sm` | 영숫자 14 / 한글 7 | 영숫자 11 / 한글 6 | 영숫자 14 | 영숫자 8 |
293
+ | `md` | 영숫자 20 / 한글 11 | 영숫자 16 / 한글 9 | 영숫자 20 | 영숫자 14 |
294
+ | `lg` | 영숫자 32 / 한글 17 | 영숫자 26 / 한글 14 | 영숫자 32 | 영숫자 26 |
295
+ | `xl` | 영숫자 68 / 한글 37 | 영숫자 57 / 한글 31 | 영숫자 68 | 영숫자 62 |
296
+
297
+ `SNumberInput` 은 `useButton` 여부로 글자 자리가 크게 갈립니다 — 스테퍼가 없으면 좌우 패딩이
298
+ 두 배가 되고(기본값), 있으면 버튼 두 개와 간격이 폭을 먹습니다. 규칙은 JSX 의 `useButton` 을 읽어
299
+ 둘을 구분합니다.
300
+
301
+ 등급 폭은 `--cmp-field-width-*` 토큰에서 읽으므로 규칙과 컴포넌트가 같은 값을 봅니다.
302
+ 글자수 환산 비율이 팀 기준과 다르면 옵션으로 덮습니다(등급 폭도 덮을 수 있지만, 토큰과 어긋나게 됩니다).
303
+
304
+ ```js
305
+ 'sellmate/field-width-grade': ['warn', {
306
+ charRatio: { narrow: 0.55, wide: 1 },
307
+ }],
308
+ ```
309
+
310
+ ### 컬럼 폭 (`table-column-width`)
311
+
312
+ `STable` 의 컬럼 폭은 `<colgroup>` 의 `<col width>` 로 들어가고 테이블이 `table-fixed` 라,
313
+ **px 이 아닌 값은 에러 없이 화면만 틀어집니다** — `'30%'` 는 `30`(px)으로 읽히고, `clamp()` 같은
314
+ 함수형 값은 폭이 통째로 무효가 된 뒤 auto 로 떨어집니다.
315
+
316
+ ```tsx
317
+ // 내용이 들어가는 열은 전부 폭을 명시한다 (생략하면 기본 120px 이 조용히 들어간다)
318
+ { name: 'name', label: '이름', field: 'name', width: 160 }
319
+ { name: 'code', label: '코드', field: 'code', width: '120px' }
320
+
321
+ // autoWidth 는 남은 폭을 흡수하는 스페이서 열 하나에만 (값을 그리지 않으므로 field 도 없다)
322
+ { name: 'spacer', label: '', autoWidth: true }
323
+
324
+ // ❌ 조용히 깨진다
325
+ { name: 'ratio', label: '비율', field: 'ratio', width: '30%' }
326
+ // ❌ 값을 그리는 열의 autoWidth — 폭이 다른 열에 좌우돼 화면마다 달라진다
327
+ { name: 'title', label: '상품명', field: 'title', autoWidth: true, render: r => r.title }
328
+ ```
329
+
330
+ 검사 대상은 `name`(문자열 리터럴) · `label` 에 더해 **`field` 또는 `autoWidth` 를 가진 객체**입니다.
331
+ 검색 필터 필드나 상세 모달의 키-값 필드가 컬럼과 같은 `{ name, label, ... }` 모양이라, 그 둘만으로
332
+ 판정하면 표와 무관한 객체가 대부분 걸리기 때문입니다. 대신 `{ ...base, name, label }` 처럼 `field` 가
333
+ 스프레드로 들어오는 컬럼은 보고하지 않습니다 — 정적으로 필터 필드와 구분되지 않습니다.
334
+
335
+ 예외는 컬럼 `name` 을 `allow` 로 지정해 뺍니다.
336
+
337
+ ```js
338
+ 'sellmate/table-column-width': ['warn', { allow: ['spacer'] }],
339
+ ```
340
+
240
341
  ### 더 엄격하게 / 더 느슨하게
241
342
 
242
343
  `configs.strict` 는 전 규칙을 error 로 올리고, `<ul>` `<ol>` `<li>` `<svg>` `<label>` 까지 검사하며, 간격 유틸리티에 `sd-` 접두를 강제합니다(`requirePrefix`). 디자인 시스템 규칙을 처음부터 전면 적용하는 신규 프로젝트용입니다.
@@ -10,6 +10,30 @@
10
10
  |------|------|---------|-------------|
11
11
  | `color?` | `SBadgeColor` | `'blue'` | 뱃지 색상 |
12
12
 
13
+ ## Types
14
+
15
+ ### SBadgeColor
16
+
17
+ ```ts
18
+ export type SBadgeColor = (typeof BADGE_COLORS)[number];
19
+ ```
20
+
21
+ ### BADGE_COLORS
22
+
23
+ ```ts
24
+ export const BADGE_COLORS = [
25
+ 'red',
26
+ 'orange',
27
+ 'yellow',
28
+ 'green',
29
+ 'lightblue',
30
+ 'blue',
31
+ 'darkblue',
32
+ 'indigo',
33
+ 'grey',
34
+ ] as const;
35
+ ```
36
+
13
37
  ## Dependencies
14
38
 
15
39
  ### Used by
@@ -1,5 +1,5 @@
1
1
  import { type HTMLAttributes } from 'react';
2
- export declare const BADGE_COLORS: readonly ["red", "orange", "yellow", "green", "blue", "darkblue", "indigo", "grey"];
2
+ export declare const BADGE_COLORS: readonly ["red", "orange", "yellow", "green", "lightblue", "blue", "darkblue", "indigo", "grey"];
3
3
  export type SBadgeColor = (typeof BADGE_COLORS)[number];
4
4
  export interface SBadgeProps extends HTMLAttributes<HTMLSpanElement> {
5
5
  /** 뱃지 색상 */
@@ -35,7 +35,7 @@
35
35
  | `hint?` | `string` | — | |
36
36
  | `error?` | `boolean` | — | |
37
37
  | `errorMessage?` | `string` | — | |
38
- | `width?` | `number \| string` | — | |
38
+ | `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리며 `maxLength`(= 스키마 상한)로 고른다. 상한이 `xl` 을 넘거나 상한이 없으면 `"100%"`. (`sellmate/field-width-grade` 가 검사한다) |
39
39
  | `className?` | `string` | — | |
40
40
  | `style?` | `CSSProperties` | — | |
41
41
 
@@ -47,6 +47,14 @@
47
47
  | `onFocus` | `() => void` | |
48
48
  | `onBlur` | `() => void` | |
49
49
 
50
+ ## Types
51
+
52
+ ### SBarcodeInputSize
53
+
54
+ ```ts
55
+ export type SBarcodeInputSize = SFieldSize;
56
+ ```
57
+
50
58
  ## Dependencies
51
59
 
52
60
  ### Depends on
@@ -4,6 +4,7 @@ import { type SIconName } from '../SIcon';
4
4
  import { type SColor } from '../../lib/color';
5
5
  import { type Rule } from '../../lib/form';
6
6
  import { type STooltipProps } from '../STooltip';
7
+ import { type SFieldWidth } from '../../lib/field-width';
7
8
  export type SBarcodeInputSize = SFieldSize;
8
9
  export interface SBarcodeInputProps {
9
10
  value?: string | number | null;
@@ -39,7 +40,12 @@ export interface SBarcodeInputProps {
39
40
  hint?: string;
40
41
  error?: boolean;
41
42
  errorMessage?: string;
42
- width?: number | string;
43
+ /**
44
+ * 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이.
45
+ * 등급은 `--cmp-field-width-*` 토큰으로 풀리며 `maxLength`(= 스키마 상한)로 고른다.
46
+ * 상한이 `xl` 을 넘거나 상한이 없으면 `"100%"`. (`sellmate/field-width-grade` 가 검사한다)
47
+ */
48
+ width?: SFieldWidth;
43
49
  className?: string;
44
50
  style?: CSSProperties;
45
51
  }
@@ -15,12 +15,47 @@
15
15
  | `rightIcon?` | `SIconName` | — | 레이블 오른쪽 아이콘 |
16
16
  | `label?` | `string` | — | 버튼 텍스트 (문자열만 — 아이콘은 icon/rightIcon 사용) |
17
17
 
18
+ ## Types
19
+
20
+ ### SButtonColor
21
+
22
+ ```ts
23
+ export type SButtonColor = (typeof BUTTON_COLORS)[number];
24
+ ```
25
+
26
+ ### SButtonSize
27
+
28
+ ```ts
29
+ export type SButtonSize = (typeof BUTTON_SIZES)[number];
30
+ ```
31
+
32
+ ### BUTTON_COLORS
33
+
34
+ ```ts
35
+ /**
36
+ * SButton 색상/사이즈 설정 — sd-button(component.button 토큰) 충실 포팅.
37
+ * Stencil `name`(예: primary_sm)의 preset을 color + outline(boolean) + size 로 분리.
38
+ * - primary / danger : solid·outline 모두 지원
39
+ * - secondary : solid 전용 (outline 스타일 없음 → outline 무시)
40
+ * - neutral : 흰 배경 고정, outline 은 회색 테두리만 추가(solid = 테두리 없는 흰 버튼)
41
+ * 색상은 theme.css의 `--cmp-button-*` CSS 변수를 참조한다.
42
+ */
43
+ export const BUTTON_COLORS = ['primary', 'secondary', 'neutral', 'danger'] as const;
44
+ ```
45
+
46
+ ### BUTTON_SIZES
47
+
48
+ ```ts
49
+ export const BUTTON_SIZES = ['xs', 'sm', 'md', 'lg'] as const;
50
+ ```
51
+
18
52
  ## Dependencies
19
53
 
20
54
  ### Used by
21
55
 
22
56
  - [SConfirmModal](../SConfirmModal)
23
57
  - [SDropdownButton](../SDropdownButton)
58
+ - [SEditor](../SEditor)
24
59
  - [SFooter](../SFooter)
25
60
  - [SKeyValueTable](../SKeyValueTable)
26
61
  - [SLoadingModal](../SLoadingModal)
@@ -38,6 +73,7 @@ graph TD;
38
73
  SButton --> SIcon
39
74
  SConfirmModal --> SButton
40
75
  SDropdownButton --> SButton
76
+ SEditor --> SButton
41
77
  SFooter --> SButton
42
78
  SKeyValueTable --> SButton
43
79
  SLoadingModal --> SButton
@@ -22,6 +22,19 @@
22
22
  | `onValueChange` | `(date: string) => void` | 선택 변경 (sdUpdate) |
23
23
  | `onViewChange` | `(v: { year: number; month: number }) => void` | 보이는 연·월 변경 (sdViewChange) |
24
24
 
25
+ ## Types
26
+
27
+ ### SCalendarEventGroup
28
+
29
+ ```ts
30
+ export interface SCalendarEventGroup {
31
+ /** 도트 색상. 팔레트 키(`grey_65`, `red_95` …) 또는 임의 CSS 색상 */
32
+ color: SColor;
33
+ label: string;
34
+ dates: string[];
35
+ }
36
+ ```
37
+
25
38
  ## Dependencies
26
39
 
27
40
  ### Used by
@@ -14,6 +14,21 @@
14
14
  | `className?` | `string` | — | |
15
15
  | `style?` | `CSSProperties` | — | |
16
16
 
17
+ ## Types
18
+
19
+ ### SCalloutType
20
+
21
+ ```ts
22
+ export type SCalloutType = 'default' | 'danger';
23
+ ```
24
+
25
+ ### SCalloutMessage
26
+
27
+ ```ts
28
+ /** 중첩 메시지: 문자열 또는 (한 단계 더 들어간) 문자열 배열 */
29
+ export type SCalloutMessage = string | SCalloutMessage[];
30
+ ```
31
+
17
32
  ## Dependencies
18
33
 
19
34
  ### Depends on
@@ -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>>;
@@ -22,6 +22,14 @@
22
22
  |-------|------|-------------|
23
23
  | `onValueChange` | `(value: boolean \| unknown[]) => void` | 값 변경 (sdUpdate) |
24
24
 
25
+ ## Types
26
+
27
+ ### SCheckboxValue
28
+
29
+ ```ts
30
+ export type SCheckboxValue = boolean | unknown[] | null;
31
+ ```
32
+
25
33
  ## Dependencies
26
34
 
27
35
  ### Used by
@@ -8,12 +8,11 @@
8
8
 
9
9
  | Prop | Type | Default | Description |
10
10
  |------|------|---------|-------------|
11
- | `fields?` | `SChipFilterField[] \| SChipFilterGroup[]` | — | 필터 정의 목록. 그룹으로 묶으려면 SChipFilterGroup[]을 넘긴다 — 그룹이 시작될 때마다 앞에 구분선이 자동으로 붙는다(showLabel·인접 그룹·인라인 date 필터와 중첩되지 않도록 처리됨). |
11
+ | `fields?` | `SChipFilterGroup[]` | — | 필터 정의 목록. 묶을 것이 없어도 한 그룹으로 감싸 넘긴다 — `[{ fields: [...] }]`. 그룹은 rule로 함께 검증하거나 divider로 갈라 놓을 때 나눈다. |
12
12
  | `value?` | `SChipFilterValueMap` | — | 필터 값 맵 |
13
- | `activeKeys?` | `string[]` | — | 노출 필터 key 목록. 지정하면 제어 컴포넌트로 동작 — "필터 추가"로 고른 필드를 이 배열에 직접 넣어줘야 칩이 나타난다(onActiveKeysChange에서 받은 keys로 갱신). 지정하지 않으면 필드별 defaultActive 값을 기준으로 컴포넌트가 내부 상태로 관리하는 비제어 방식으로 동작한다 |
13
+ | `activeKeys?` | `string[]` | — | 노출 필터 key 목록. 지정하면 제어 컴포넌트로 동작 — "필터 추가"로 고른 필드를 이 배열에 직접 넣어줘야 칩이 나타난다(onActiveKeysChange에서 받은 keys로 갱신). 지정하지 않으면 fixed·required 필드만 노출된 상태로 시작해 컴포넌트가 내부 상태로 관리하는 비제어 방식으로 동작한다. fixed·required 필드는 이 배열에 없어도 항상 노출된다 |
14
14
  | `label?` | `string` | `'검색 필터'` | 좌측 태그 텍스트 |
15
15
  | `showLabel?` | `boolean` | `false` | 좌측 태그(label)·구분선 표시 여부 |
16
- | `showAddButton?` | `boolean` | `true` | 필터 추가 버튼 표시 여부 |
17
16
  | `showReset?` | `boolean` | `true` | 검색 초기화 링크 표시 여부 |
18
17
  | `disabled?` | `boolean` | `false` | 바 비활성 상태 |
19
18
  | `className?` | `string` | — | |
@@ -25,7 +24,7 @@
25
24
  |-------|------|-------------|
26
25
  | `onValueChange` | `(value: SChipFilterValueMap) => void` | 전체 값 변경 — 편집 중인 값이 바뀔 때마다(선택할 때마다) 호출된다. 실제 검색 실행은 onSearch를 쓴다 |
27
26
  | `onFilterChange` | `(detail: SChipFilterChangeDetail) => void` | 개별 필터 값 변경 |
28
- | `onSearch` | `(value: SChipFilterValueMap) => void` | 실제 검색을 실행할 시점 — 편집 팝오버의 "검색" 버튼을 누르거나 팝오버가 닫힐 때(바깥 클릭·Esc·다른 칩으로 전환 포함) 그 시점의 전체 값 맵과 함께 호출된다. 팝오버가 없는 필드(인라인 date 프리셋·custom, clearable ×, 검색 초기화)는 값이 바뀌는 즉시 호출된다. dirty 체크가 기본 적용되어 있어 — 마지막으로 실제 검색이 실행된 값 맵과 비교해 하나라도 달라진 게 없으면(예: 팝오버를 열었다 아무것도 안 바꾸고 닫는 경우) 호출되지 않는다. fields를 그룹으로 넘겼다면 rule을 만족하지 못한 그룹이 있는 동안엔 onSearch가 호출되지 않는다 — 해당 그룹의 경고 툴팁은 이 시점과 무관하게 값이 비어 있는 동안 항상 실시간으로 떠 있다(별도로 validate()를 호출할 필요 없음) |
27
+ | `onSearch` | `(value: SChipFilterValueMap) => void` | 실제 검색을 실행할 시점 — 편집 팝오버의 "검색" 버튼을 누르거나 팝오버가 닫힐 때(바깥 클릭·Esc·다른 칩으로 전환 포함) 그 시점의 전체 값 맵과 함께 호출된다. 팝오버가 없는 필드(인라인 date 프리셋·custom, clearable ×, 검색 초기화)는 값이 바뀌는 즉시 호출된다. keyword 필터에서 Enter 로 키워드를 추가할 때도 그 즉시 호출된다 — 팝오버는 열린 채라 키워드를 이어서 더 넣을 수 있고, 넣을 때마다 조회가 갱신된다. dirty 체크가 기본 적용되어 있어 — 마지막으로 실제 검색이 실행된 값 맵과 비교해 하나라도 달라진 게 없으면(예: 팝오버를 열었다 아무것도 안 바꾸고 닫는 경우) 호출되지 않는다. fields를 그룹으로 넘겼다면 rule을 만족하지 못한 그룹이 있는 동안엔 onSearch가 호출되지 않는다 — 해당 그룹의 경고 툴팁은 이 시점과 무관하게 값이 비어 있는 동안 항상 실시간으로 떠 있다(별도로 validate()를 호출할 필요 없음) |
29
28
  | `onActiveKeysChange` | `(keys: string[]) => void` | 노출 필터 key 변경(칩 추가·제거) — 비제어 방식에서도 참고용으로 호출된다. activeKeys를 직접 제어할 때는 이 값을 그대로 activeKeys에 반영해야 한다 |
30
29
  | `onReset` | `() => void` | "검색 초기화" 클릭 — 모든 필드가 기본값(또는 null)으로 리셋된 뒤 호출된다 |
31
30
  | `onAddFilter` | `(key: string) => void` | "필터 추가" 목록에서 항목을 골랐을 때 — activeKeys를 직접 제어 중이면 이 콜백에서 (또는 onActiveKeysChange에서) key를 activeKeys에 추가해줘야 칩이 실제로 나타난다. activeKeys를 넘기지 않았다면(비제어) 별도 처리 없이도 컴포넌트가 알아서 칩을 노출한다 |
@@ -36,7 +35,291 @@
36
35
  |--------|------|-------------|
37
36
  | `open` | `(key: string) => void` | 특정 필터 편집 팝오버를 엽니다. |
38
37
  | `reset` | `() => void` | 모든 필터 값을 초기화합니다. |
39
- | `validate` | `() => boolean` | fields를 그룹(SChipFilterGroup[])으로 넘겼을 때, 현재 값 기준으로 각 그룹의 rule을 만족하는지 반환합니다. 경고 툴팁은 이 호출과 무관하게 rule 위반 상태인 동안 항상 실시간으로 떠 있으므로, 이 메서드는 그 상태를 그대로 읽어오는 용도다(예: 검색 버튼을 눌러도 되는지 사전 확인) |
38
+ | `validate` | `() => boolean` | 현재 값 기준으로 각 그룹의 rule을 만족하는지 반환합니다. rule을 준 그룹이 없으면 항상 true. 경고 툴팁은 이 호출과 무관하게 rule 위반 상태인 동안 항상 실시간으로 떠 있으므로, 이 메서드는 그 상태를 그대로 읽어오는 용도다(예: 검색 버튼을 눌러도 되는지 사전 확인) |
39
+
40
+ ## Types
41
+
42
+ ### SChipFilterGroup
43
+
44
+ ```ts
45
+ /** fields를 이루는 단위. 묶을 것이 없어도 한 그룹으로 감싸 넘긴다 — `[{ fields: [...] }]` */
46
+ export interface SChipFilterGroup {
47
+ fields: SChipFilterField[];
48
+ /** 지정하면 이 규칙으로 그룹을 검증한다. values가 바뀔 때마다 즉시 재평가되는 실시간 검증이라 —
49
+ * 그룹이 rule을 만족하지 못하면 검색 시도 여부와 무관하게 그 즉시 그룹 중앙에 경고 툴팁이 뜬다.
50
+ * 지정 안 하면 검증하지 않는다. */
51
+ rule?: SChipFilterGroupRule;
52
+ /** rule을 만족하지 않을 때 그룹 중앙에 띄울 툴팁 메시지. 지정 안 하면 rule 종류에 따른 기본 문구를 쓴다 */
53
+ tooltipMessage?: string;
54
+ /** 이 그룹 앞에 구분선을 넣을지. 검증(rule)과 구분선은 별개라 — 묶어서 검증만 하고 싶으면
55
+ * 주지 않는다. 첫 그룹에는 앞에 가를 것이 없으므로 무시된다(showLabel의 구분선이 이미 있다) */
56
+ divider?: boolean;
57
+ }
58
+ ```
59
+
60
+ ### SChipFilterValueMap
61
+
62
+ ```ts
63
+ export type SChipFilterValueMap = Record<string, SChipFilterValue>;
64
+ ```
65
+
66
+ ### SChipFilterChangeDetail
67
+
68
+ ```ts
69
+ export interface SChipFilterChangeDetail {
70
+ key: string;
71
+ value: SChipFilterValue;
72
+ values: SChipFilterValueMap;
73
+ }
74
+ ```
75
+
76
+ ### SChipFilterField
77
+
78
+ ```ts
79
+ /** 필터 하나의 정의. type에 따라 쓸 수 있는 속성이 달라진다 —
80
+ * options는 single·multi·keyword, presets·selectable·maxRange는 date·period,
81
+ * render는 custom 에만 있다 */
82
+ export type SChipFilterField =
83
+ | SChipFilterSingleField
84
+ | SChipFilterMultiField
85
+ | SChipFilterKeywordField
86
+ | SChipFilterDateField
87
+ | SChipFilterPeriodField
88
+ | SChipFilterCustomField;
89
+ ```
90
+
91
+ ### SChipFilterGroupRule
92
+
93
+ ```ts
94
+ /** 필터 그룹 검증 규칙 */
95
+ export type SChipFilterGroupRule =
96
+ | { type: 'requireKey'; key: string }
97
+ | { type: 'requireAll' }
98
+ | { type: 'requireAny'; dataGroupName?: string };
99
+ ```
100
+
101
+ ### SChipFilterValue
102
+
103
+ ```ts
104
+ export type SChipFilterValue =
105
+ | SChipFilterOptionValue
106
+ | SChipFilterOptionValue[]
107
+ | SDateRangeValue
108
+ | SChipFilterKeywordValue
109
+ | SChipFilterPeriodValue
110
+ | SChipFilterCustomValue
111
+ | null
112
+ | undefined;
113
+ ```
114
+
115
+ ### SChipFilterSingleField
116
+
117
+ ```ts
118
+ /** 후보 하나를 고른다 */
119
+ export interface SChipFilterSingleField extends SChipFilterOptionsField {
120
+ type: 'single';
121
+ }
122
+ ```
123
+
124
+ ### SChipFilterMultiField
125
+
126
+ ```ts
127
+ /** 후보 여럿을 고른다 */
128
+ export interface SChipFilterMultiField extends SChipFilterOptionsField {
129
+ type: 'multi';
130
+ }
131
+ ```
132
+
133
+ ### SChipFilterKeywordField
134
+
135
+ ```ts
136
+ /** 키워드를 입력해 누적한다. options는 입력 중 후보로만 뜬다 */
137
+ export interface SChipFilterKeywordField extends SChipFilterOptionsField {
138
+ type: 'keyword';
139
+ /** 입력 placeholder */
140
+ placeholder?: string;
141
+ /** 입력 방식. 기본 'tag' — Enter 로 하나씩 추가한다.
142
+ * 'csv' 는 쉼표도 구분자로 인정해, 쉼표를 치거나 쉼표가 섞인 텍스트를 붙여넣으면 그 자리에서
143
+ * 여러 개로 쪼개져 목록에 쌓인다(엑셀에서 복사한 코드 목록을 한 번에 넣는 용도).
144
+ * 쌓이는 목록도 값 형태도 'tag' 와 같다 — 구분자만 늘어난다. */
145
+ input?: SChipFilterKeywordInput;
146
+ /** 검색조건(포함/일치) 토글 표시 여부 */
147
+ matchModes?: boolean;
148
+ /** matchModes 활성 시 "미포함"까지 포함해 3개(포함/일치/미포함)로 노출할지.
149
+ * 기본 false — 2개(포함/일치)만 */
150
+ excludeMode?: boolean;
151
+ }
152
+ ```
153
+
154
+ ### SChipFilterDateField
155
+
156
+ ```ts
157
+ /** 날짜 하나 또는 기간을 고른다 */
158
+ export interface SChipFilterDateField extends SChipFilterPresetsField {
159
+ type: 'date';
160
+ /** presets 없이 단일 캘린더 트리거로 동작할 때의 placeholder */
161
+ placeholder?: string;
162
+ /** presets를 필터 바에 세그먼트 라디오로 펼쳐 놓는다(팝오버 없음).
163
+ * 기본 false — 칩 클릭 시 팝오버 안에 세로 라디오 목록(+사용자 지정 선택 시 기간 피커) */
164
+ radioButton?: boolean;
165
+ }
166
+ ```
167
+
168
+ ### SChipFilterPeriodField
169
+
170
+ ```ts
171
+ /** 집계 단위(일·월·분기·반기·연)와 그 단위의 값을 함께 고른다 */
172
+ export interface SChipFilterPeriodField extends SChipFilterPresetsField {
173
+ type: 'period';
174
+ }
175
+ ```
176
+
177
+ ### SChipFilterCustomField
178
+
179
+ ```ts
180
+ /** 칩+팝오버를 거치지 않고 바에 놓을 노드를 앱이 직접 그린다 */
181
+ export interface SChipFilterCustomField extends SChipFilterFieldBase {
182
+ type: 'custom';
183
+ /** 필터 바의 이 필드 자리에 놓일 노드를 직접 그린다. 반환한 노드가 그대로 바에 노출된다 —
184
+ * SSelect를 그대로 놓거나 SInput을 바로 노출하는 식으로 렌더 방식을 자유롭게 구성한다. */
185
+ render?: (ctx: {
186
+ value: SChipFilterValue;
187
+ disabled?: boolean;
188
+ /** 속한 그룹이 rule을 위반하는 동안 true — 직접 그린 노드에도 경고 표시를 맞추라는 신호다 */
189
+ warning?: boolean;
190
+ onValueChange: (value: SChipFilterValue) => void;
191
+ }) => ReactNode;
192
+ }
193
+ ```
194
+
195
+ ### SChipFilterOptionValue
196
+
197
+ ```ts
198
+ export type SChipFilterOptionValue = string | number;
199
+ ```
200
+
201
+ ### SChipFilterKeywordValue
202
+
203
+ ```ts
204
+ /** keyword 필드에서 matchModes 활성 시 사용하는 값 형태 — 입력해 추가한 키워드 목록 */
205
+ export interface SChipFilterKeywordValue {
206
+ keywords: string[];
207
+ mode: SChipFilterMatchMode;
208
+ }
209
+ ```
210
+
211
+ ### SChipFilterPeriodValue
212
+
213
+ ```ts
214
+ /** period 필드 값 — 선택 단위(unit)와 그 단위의 입력값(value)을 함께 보관한다 */
215
+ export interface SChipFilterPeriodValue {
216
+ unit: SChipFilterPeriodUnit;
217
+ value?: string | number | SDateRangeValue | null;
218
+ }
219
+ ```
220
+
221
+ ### SChipFilterCustomValue
222
+
223
+ ```ts
224
+ /** custom 필드가 자유롭게 담는 값. 형태를 강제하지 않는다 — render에서 직접 정의한 그대로 읽고 쓴다 */
225
+ export type SChipFilterCustomValue = Record<string, unknown>;
226
+ ```
227
+
228
+ ### SChipFilterOptionsField
229
+
230
+ ```ts
231
+ /** 후보 목록에서 고르는 필터 — single·multi·keyword */
232
+ export interface SChipFilterOptionsField extends SChipFilterFieldBase {
233
+ /** 고를 수 있는 후보 목록 */
234
+ options?: SChipFilterOption[];
235
+ }
236
+ ```
237
+
238
+ ### SChipFilterKeywordInput
239
+
240
+ ```ts
241
+ /** keyword 필드의 입력 방식 — 무엇을 키워드 하나의 끝으로 볼지 */
242
+ export type SChipFilterKeywordInput = 'tag' | 'csv';
243
+ ```
244
+
245
+ ### SChipFilterPresetsField
246
+
247
+ ```ts
248
+ /** 프리셋으로 기간을 고르는 필터 — date·period */
249
+ export interface SChipFilterPresetsField extends SChipFilterFieldBase {
250
+ /** 프리셋 라디오 목록. date에서 지정하지 않으면 단일 캘린더 트리거로 동작하고,
251
+ * period에서 지정하지 않으면 일별·월별·분기별·반기별·연도별·사용자 지정 기본 목록을 쓴다 */
252
+ presets?: SChipFilterDatePreset[];
253
+ /** 선택 가능 범위 */
254
+ selectable?: [string, string];
255
+ /** "사용자 지정" 프리셋으로 기간을 고를 때의 최대 선택 일수 */
256
+ maxRange?: number;
257
+ }
258
+ ```
259
+
260
+ ### SChipFilterFieldBase
261
+
262
+ ```ts
263
+ /** 타입과 무관하게 모든 필터가 갖는 속성 */
264
+ export interface SChipFilterFieldBase {
265
+ /** 필터 식별자 */
266
+ key: string;
267
+ /** 칩에 표시할 레이블 */
268
+ label: string;
269
+ /** 필수 필터 표시. true면 값이 비어 있을 때 기본값(defaultValue 또는 타입별 내장 기본값)이
270
+ * 자동으로 채워지고, clearable은 현재 값이 기본값과 같을 땐 숨겨지며 클릭 시 기본값으로 되돌아간다 */
271
+ required?: boolean;
272
+ /** 고정 필터. true면 activeKeys와 무관하게 항상 노출되고 "필터 추가" 목록에는 나타나지 않는다.
273
+ * required도 같은 효과를 낸다 — 처음부터 바에 보이는 것은 fixed이거나 required인 필드뿐이고,
274
+ * 나머지는 전부 "필터 추가"에서 골라야 나타난다 */
275
+ fixed?: boolean;
276
+ /** 초기값 및 clearable 클릭 시 되돌아갈 값. required 여부와 무관하게 적용된다 — 값이 비어 있으면
277
+ * 마운트(또는 "필터 추가"로 활성화) 시 이 값이 자동으로 채워진다. required인데 지정하지 않으면
278
+ * 타입별 내장 기본값(single: 첫 번째 옵션, date: 오늘 날짜)을 대신 쓴다 */
279
+ defaultValue?: SChipFilterValue;
280
+ /** 이 필터만 비활성. 바에 남아 있되 팝오버가 열리지 않고 clearable도 눌리지 않는다.
281
+ * 바 전체를 잠그려면 SChipFilterProps.disabled를 쓴다 — 둘은 OR로 합쳐진다 */
282
+ disabled?: boolean;
283
+ }
284
+ ```
285
+
286
+ ### SChipFilterMatchMode
287
+
288
+ ```ts
289
+ export type SChipFilterMatchMode = 'contains' | 'exact' | 'excludes';
290
+ ```
291
+
292
+ ### SChipFilterPeriodUnit
293
+
294
+ ```ts
295
+ export type SChipFilterPeriodUnit = 'day' | 'month' | 'quarter' | 'half' | 'year' | 'custom';
296
+ ```
297
+
298
+ ### SChipFilterOption
299
+
300
+ ```ts
301
+ export interface SChipFilterOption {
302
+ value: SChipFilterOptionValue;
303
+ label: string;
304
+ disabled?: boolean;
305
+ }
306
+ ```
307
+
308
+ ### SChipFilterDatePreset
309
+
310
+ ```ts
311
+ /** date/period 필드의 프리셋 라디오 항목 (오늘/지난 7일/일별/월별/사용자 지정 등) */
312
+ export interface SChipFilterDatePreset {
313
+ /** 프리셋 식별자 */
314
+ value: string;
315
+ /** 라벨 */
316
+ label: string;
317
+ /** true면 "사용자 지정" — 선택 시 날짜/기간 피커가 추가로 노출된다. resolve는 무시된다. */
318
+ custom?: boolean;
319
+ /** custom이 아닐 때 실제 값을 계산한다. 단일 날짜(string) 또는 기간([start,end]) 모두 가능 */
320
+ resolve?: () => string | SDateRangeValue;
321
+ }
322
+ ```
40
323
 
41
324
  ## Dependencies
42
325