@simplysm/sd-claude 14.0.41 → 14.0.43

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 (67) hide show
  1. package/claude/references/sd-simplysm14/angular/docs/directives.md +74 -3
  2. package/claude/references/sd-simplysm14/angular/docs/features.md +64 -14
  3. package/claude/references/sd-simplysm14/angular/docs/plugins.md +2 -90
  4. package/claude/references/sd-simplysm14/angular/docs/providers.md +2 -2
  5. package/claude/references/sd-simplysm14/angular/docs/type-utilities.md +1 -2
  6. package/claude/references/sd-simplysm14/angular/docs/ui-data.md +103 -23
  7. package/claude/references/sd-simplysm14/angular/docs/ui-form.md +173 -28
  8. package/claude/references/sd-simplysm14/angular/docs/ui-layout.md +19 -4
  9. package/claude/references/sd-simplysm14/angular/docs/ui-navigation.md +20 -2
  10. package/claude/references/sd-simplysm14/angular/docs/ui-overlay.md +23 -14
  11. package/claude/references/sd-simplysm14/angular/docs/ui-visual.md +15 -7
  12. package/claude/references/sd-simplysm14/angular/docs/utils.md +1 -1
  13. package/claude/references/sd-simplysm14/angular/usage.md +16 -15
  14. package/claude/references/sd-simplysm14/capacitor-plugin-auto-update/usage.md +1 -1
  15. package/claude/references/sd-simplysm14/capacitor-plugin-file-system/docs/file-operations.md +154 -0
  16. package/claude/references/sd-simplysm14/capacitor-plugin-file-system/docs/permissions.md +84 -0
  17. package/claude/references/sd-simplysm14/capacitor-plugin-file-system/docs/storage-paths.md +107 -0
  18. package/claude/references/sd-simplysm14/capacitor-plugin-file-system/docs/types.md +83 -0
  19. package/claude/references/sd-simplysm14/capacitor-plugin-file-system/usage.md +83 -128
  20. package/claude/references/sd-simplysm14/capacitor-plugin-usb-storage/usage.md +99 -1
  21. package/claude/references/sd-simplysm14/core-node/docs/child-process.md +182 -0
  22. package/claude/references/sd-simplysm14/core-node/docs/features.md +1 -1
  23. package/claude/references/sd-simplysm14/core-node/docs/file-system.md +509 -0
  24. package/claude/references/sd-simplysm14/core-node/docs/file-watching.md +139 -0
  25. package/claude/references/sd-simplysm14/core-node/docs/logging.md +180 -0
  26. package/claude/references/sd-simplysm14/core-node/docs/path.md +176 -0
  27. package/claude/references/sd-simplysm14/core-node/docs/worker-threads.md +334 -0
  28. package/claude/references/sd-simplysm14/core-node/usage.md +192 -96
  29. package/claude/references/sd-simplysm14/excel/docs/core-classes.md +33 -14
  30. package/claude/references/sd-simplysm14/excel/usage.md +47 -45
  31. package/claude/references/sd-simplysm14/lint/usage.md +3 -2
  32. package/claude/references/sd-simplysm14/orm-common/docs/queryable-executable.md +30 -35
  33. package/claude/references/sd-simplysm14/orm-common/usage.md +9 -8
  34. package/claude/references/sd-simplysm14/sd-claude/docs/assets.md +43 -34
  35. package/claude/references/sd-simplysm14/sd-claude/docs/cli.md +1 -1
  36. package/claude/references/sd-simplysm14/sd-claude/docs/hooks.md +20 -2
  37. package/claude/references/sd-simplysm14/sd-claude/docs/scripts.md +5 -18
  38. package/claude/references/sd-simplysm14/sd-claude/usage.md +6 -5
  39. package/claude/references/sd-simplysm14/sd-cli/usage.md +176 -1
  40. package/claude/references/sd-simplysm14/service-client/usage.md +126 -61
  41. package/claude/references/sd-simplysm14/service-common/usage.md +28 -28
  42. package/claude/references/sd-simplysm14/storage/usage.md +123 -30
  43. package/claude/references/sd-testing.md +100 -4
  44. package/claude/rules/sd-claude-rules.md +19 -4
  45. package/claude/sd-check-write.py +1 -1
  46. package/claude/skills/sd-check/SKILL.md +7 -4
  47. package/claude/skills/sd-claude-docs/SKILL.md +7 -4
  48. package/claude/skills/sd-claude-docs/references/package-doc-gen.md +30 -7
  49. package/claude/skills/sd-commit/SKILL.md +2 -0
  50. package/claude/skills/sd-debug/SKILL.md +1 -1
  51. package/claude/skills/sd-deliverable/SKILL.md +2 -0
  52. package/claude/skills/sd-dev/SKILL.md +1 -1
  53. package/claude/skills/sd-doc-extract/SKILL.md +2 -0
  54. package/claude/{references/sd-debug.md → skills/sd-inner-debug/SKILL.md} +16 -20
  55. package/claude/{references/sd-review.md → skills/sd-inner-review/SKILL.md} +9 -4
  56. package/claude/skills/sd-issue/SKILL.md +2 -0
  57. package/claude/skills/sd-outlook/SKILL.md +2 -0
  58. package/claude/skills/sd-plan/SKILL.md +1 -1
  59. package/claude/skills/sd-prompt/SKILL.md +2 -2
  60. package/claude/skills/sd-refactor/SKILL.md +2 -2
  61. package/claude/skills/sd-review/SKILL.md +1 -1
  62. package/claude/skills/sd-tdd/SKILL.md +7 -7
  63. package/claude/skills/sd-use/SKILL.md +2 -0
  64. package/claude/skills/sd-wbs/SKILL.md +41 -18
  65. package/package.json +1 -1
  66. /package/claude/{rules → references}/sd-simplysm14.md +0 -0
  67. /package/claude/{references → rules}/sd-clarify.md +0 -0
@@ -6,7 +6,7 @@
6
6
 
7
7
  ```typescript
8
8
  @Directive({
9
- selector: `[click.capture], [scroll.passive], [sdResize], [sdSaveCommand], ...`,
9
+ selector: `[click.capture], [scroll.passive], ...`,
10
10
  })
11
11
  class SdEvents {
12
12
  // 클릭: click.capture, click.once, click.capture.once
@@ -19,11 +19,11 @@ class SdEvents {
19
19
  // 터치: touchstart.passive, touchstart.capture.passive, touchmove.passive, touchmove.capture.passive, touchend.passive
20
20
  // 드래그: dragover.capture, dragenter.capture, dragleave.capture, drop.capture
21
21
  // 애니메이션: transitionend.once, animationend.once
22
- // 커스텀: sdResize (SdResizeEvent)
23
- // 커맨드: sdRefreshCommand, sdSaveCommand, sdInsertCommand (KeyboardEvent)
24
22
  }
25
23
  ```
26
24
 
25
+ > **NOTE:** `sdResize`, `sdSaveCommand`, `sdRefreshCommand`, `sdInsertCommand`는 `SdEvents`에서 분리되어 각각 `SdResizeDirective`, `SdIntersectionDirective`, `SdCommandDirective`로 독립 디렉티브로 제공된다.
26
+
27
27
  ## `SdRipple`
28
28
 
29
29
  호스트 요소에 리플 효과를 추가하는 디렉티브.
@@ -133,6 +133,77 @@ interface SdItemOfTemplateContext<TItem> {
133
133
  </ng-template>
134
134
  ```
135
135
 
136
+ ## `SdCommandDirective`
137
+
138
+ 키보드 단축키를 output 이벤트로 제공하는 디렉티브. `document` keydown을 감지하며, `shouldProcessCommandEvent()`로 최상위 모달만 이벤트 처리.
139
+
140
+ ```typescript
141
+ @Directive({ selector: "[sdRefreshCommand],[sdSaveCommand],[sdInsertCommand]" })
142
+ class SdCommandDirective {
143
+ sdRefreshCommand = output<KeyboardEvent>(); // Ctrl+Alt+L
144
+ sdSaveCommand = output<KeyboardEvent>(); // Ctrl+S
145
+ sdInsertCommand = output<KeyboardEvent>(); // Ctrl+Insert
146
+ }
147
+ ```
148
+
149
+ 사용법: `<div (sdSaveCommand)="onSave($event)" (sdRefreshCommand)="onRefresh($event)">`
150
+
151
+ ## `SdResizeDirective`
152
+
153
+ ResizeObserver 기반 resize output 이벤트 디렉티브. `requestAnimationFrame`으로 디바운스.
154
+
155
+ ```typescript
156
+ @Directive({ selector: "[sdResize]" })
157
+ class SdResizeDirective {
158
+ sdResize = output<SdResizeEvent>();
159
+ }
160
+ ```
161
+
162
+ 사용법: `<div (sdResize)="onResize($event)">`
163
+
164
+ ## `SdResizeEvent`
165
+
166
+ ```typescript
167
+ interface SdResizeEvent {
168
+ heightChanged: boolean;
169
+ widthChanged: boolean;
170
+ target: HTMLElement;
171
+ contentRect: DOMRectReadOnly;
172
+ }
173
+ ```
174
+
175
+ | Field | Type | Description |
176
+ |-------|------|-------------|
177
+ | `heightChanged` | `boolean` | 높이 변경 여부 |
178
+ | `widthChanged` | `boolean` | 너비 변경 여부 |
179
+ | `target` | `HTMLElement` | 대상 요소 |
180
+ | `contentRect` | `DOMRectReadOnly` | 컨텐츠 영역 크기 |
181
+
182
+ ## `SdIntersectionDirective`
183
+
184
+ IntersectionObserver 기반 intersection output 이벤트 디렉티브.
185
+
186
+ ```typescript
187
+ @Directive({ selector: "[sdIntersection]" })
188
+ class SdIntersectionDirective {
189
+ sdIntersection = output<SdIntersectionEvent>();
190
+ }
191
+ ```
192
+
193
+ 사용법: `<div (sdIntersection)="onIntersect($event)">`
194
+
195
+ ## `SdIntersectionEvent`
196
+
197
+ ```typescript
198
+ interface SdIntersectionEvent {
199
+ entry: IntersectionObserverEntry;
200
+ }
201
+ ```
202
+
203
+ | Field | Type | Description |
204
+ |-------|------|-------------|
205
+ | `entry` | `IntersectionObserverEntry` | 마지막 IntersectionObserver 엔트리 |
206
+
136
207
  ## `SdRouterLink`
137
208
 
138
209
  라우터 네비게이션 디렉티브. 일반 클릭은 라우터 네비게이션, Ctrl/Shift+클릭은 새 창, 팝업 윈도우에서는 팝업 형태로 열린다.
@@ -71,13 +71,19 @@ interface Address {
71
71
 
72
72
  ```typescript
73
73
  @Component({ selector: "sd-permission-table" })
74
- class SdPermissionTable {
75
- permissions = input.required<SdPermission[]>();
76
- permRecord = model<Record<string, boolean>>({});
74
+ class SdPermissionTable<TModule = unknown> {
75
+ value = model<Record<string, boolean>>({});
76
+ items = input<SdPermission<TModule>[]>([]);
77
77
  disabled = input(false, { transform: booleanAttribute });
78
78
  }
79
79
  ```
80
80
 
81
+ | Input | Type | Default | Description |
82
+ |-------|------|---------|-------------|
83
+ | `value` | `Record<string, boolean>` | `{}` | 권한 레코드 (two-way). 키는 `codeChain.join(".") + ".use"` 또는 `".edit"` 형태 |
84
+ | `items` | `SdPermission<TModule>[]` | `[]` | 권한 트리 |
85
+ | `disabled` | `boolean` | `false` | 비활성화 |
86
+
81
87
  ## Data View Abstractions
82
88
 
83
89
  ### `SdDataSheetBase`
@@ -250,6 +256,15 @@ abstract class SdDataSelectButtonBase<TItem extends object, TKey, TMode extends
250
256
  }
251
257
  ```
252
258
 
259
+ | Input/Model | Type | Default | Description |
260
+ |-------------|------|---------|-------------|
261
+ | `value` | `SelectModeValue<TKey>[TMode]` | - | 선택된 값 (two-way) |
262
+ | `disabled` | `boolean` | `false` | 비활성화 |
263
+ | `required` | `boolean` | `false` | 필수 |
264
+ | `inset` | `boolean` | `false` | 삽입 스타일 |
265
+ | `size` | `"sm" \| "lg" \| undefined` | `undefined` | 크기 |
266
+ | `selectMode` | `TMode` | `"single"` | 선택 모드 |
267
+
253
268
  ### `SdDataSelectButton`
254
269
 
255
270
  선택 버튼 presentation 컴포넌트.
@@ -267,15 +282,25 @@ class SdDataSelectButton { }
267
282
 
268
283
  ```typescript
269
284
  @Component({ selector: "sd-shared-data-select" })
270
- class SdSharedDataSelect<TItem extends SharedDataBase<string | number>, TMode extends keyof SelectModeValue<...>> {
285
+ class SdSharedDataSelect<TItem extends SharedDataBase<string | number>, TMode extends keyof SelectModeValue<...>, TModal extends SdSelectModal<any>> {
286
+ value = model<SelectModeValue<TItem["__valueKey"] | undefined>[TMode]>();
271
287
  items = input.required<TItem[]>();
272
- value = model<SelectModeValue<...>[TMode]>();
273
- selectMode = input<TMode>("single" as TMode);
274
288
  disabled = input(false, { transform: booleanAttribute });
275
289
  required = input(false, { transform: booleanAttribute });
290
+ useUndefined = input(false, { transform: booleanAttribute });
276
291
  inset = input(false, { transform: booleanAttribute });
292
+ inline = input(false, { transform: booleanAttribute });
277
293
  size = input<"sm" | "lg">();
278
- // 기타 옵션...
294
+ selectMode = input("single" as TMode);
295
+ filterFn = input<(item: TItem, index: number, ...params: any[]) => boolean>();
296
+ filterFnParams = input<any[]>();
297
+ modal = input<SdSelectModalInfo<TModal>>();
298
+ editModal = input<SdModalInfo<SdModalContentDef<boolean>>>();
299
+ selectClass = input<string>();
300
+ multiSelectionDisplayDirection = input<"vertical">();
301
+ getIsHiddenFn = input<(item: TItem, index: number) => boolean>();
302
+ getSearchTextFn = input<(item: TItem, index: number) => string>();
303
+ displayOrderKeyProp = input<string>();
279
304
  }
280
305
  ```
281
306
 
@@ -285,7 +310,10 @@ class SdSharedDataSelect<TItem extends SharedDataBase<string | number>, TMode ex
285
310
 
286
311
  ```typescript
287
312
  @Component({ selector: "sd-shared-data-select-button" })
288
- class SdSharedDataSelectButton<TItem extends SharedDataBase<...>, TMode extends keyof SelectModeValue<...>> { }
313
+ class SdSharedDataSelectButton<TItem extends SharedDataBase<...>, TMode extends keyof SelectModeValue<...>, TModal extends SdSelectModal<any>> {
314
+ items = input<TItem[]>([]);
315
+ modal = input.required<SdSelectModalInfo<TModal>>();
316
+ }
289
317
  ```
290
318
 
291
319
  ### `SdSharedDataSelectList`
@@ -294,15 +322,37 @@ class SdSharedDataSelectButton<TItem extends SharedDataBase<...>, TMode extends
294
322
 
295
323
  ```typescript
296
324
  @Component({ selector: "sd-shared-data-select-list" })
297
- class SdSharedDataSelectList<TItem extends SharedDataBase<...>> {
325
+ class SdSharedDataSelectList<TItem extends SharedDataBase<string | number>, TModal extends SdSelectModal<any>> {
326
+ selectedItem = model<TItem>();
327
+ canChangeFn = input<(item: TItem | undefined) => boolean | Promise<boolean>>(() => true);
298
328
  items = input.required<TItem[]>();
299
- value = model<(string | number)[]>();
300
- disabled = input(false, { transform: booleanAttribute });
301
- pageLength = input(30);
302
- // 기타 옵션...
329
+ selectedIcon = input<string>();
330
+ useUndefined = input(false, { transform: booleanAttribute });
331
+ filterFn = input<(item: TItem, index: number) => boolean>();
332
+ modal = input<SdSelectModalInfo<TModal>>();
333
+ header = input<string>();
334
+ pageItemCount = input<number>();
303
335
  }
304
336
  ```
305
337
 
338
+ | Input | Type | Default | Description |
339
+ |-------|------|---------|-------------|
340
+ | `selectedItem` | `TItem \| undefined` | - | 선택된 항목 (two-way) |
341
+ | `canChangeFn` | `(item) => boolean \| Promise<boolean>` | `() => true` | 선택 변경 가능 여부 함수 |
342
+ | `items` | `TItem[]` | required | 공유 데이터 항목 |
343
+ | `selectedIcon` | `string \| undefined` | `undefined` | 선택됨 아이콘 |
344
+ | `useUndefined` | `boolean` | `false` | undefined 항목 포함 |
345
+ | `filterFn` | `((item, index) => boolean) \| undefined` | `undefined` | 필터 함수 |
346
+ | `modal` | `SdSelectModalInfo<TModal> \| undefined` | `undefined` | 모달 선택 정보 |
347
+ | `header` | `string \| undefined` | `undefined` | 헤더 텍스트 |
348
+ | `pageItemCount` | `number \| undefined` | `undefined` | 페이지당 항목 수 |
349
+
350
+ Content children:
351
+ - `#headerTpl`: 헤더 커스텀 템플릿
352
+ - `#filterTpl`: 필터 커스텀 템플릿
353
+ - `SdItemOfTemplate`: 항목 커스텀 템플릿
354
+ - `#undefinedTpl`: undefined 항목 커스텀 템플릿
355
+
306
356
  ### `matchesSearchText`
307
357
 
308
358
  공백 구분 AND 조건 텍스트 검색 매칭 함수.
@@ -323,7 +373,7 @@ function getOrmDataEditToastErrorMessage(err: unknown): string
323
373
 
324
374
  | 감지 조건 | 반환 메시지 |
325
375
  |-----------|-------------|
326
- | FK 제약 위반 (`a parent row: a foreign key constraint` 또는 `conflicted with the REFERENCE`) | `"경위! 연결된 작업에 의한 처리 거부. 후속작업 확인 요망"` |
376
+ | FK 제약 위반 (`a parent row: a foreign key constraint` 또는 `conflicted with the REFERENCE`) | `"경고! 연결된 작업에 의한 처리 거부. 후속작업 확인 요망"` |
327
377
  | 그 외 | `err.message` (또는 `String(err)`) |
328
378
 
329
379
  `SdDataSheetBase`, `SdDataDetailBase` 내부에서 사용되며, 소비 코드에서 직접 사용할 수도 있다.
@@ -1,96 +1,8 @@
1
1
  # Plugins
2
2
 
3
- 모든 플러그인은 `provideSdAngular()`에서 자동 등록된다. Angular의 `EventManagerPlugin`을 확장하여 커스텀 이벤트를 지원한다.
3
+ Angular의 `EventManagerPlugin`을 확장하여 커스텀 이벤트 옵션을 지원한다. `provideSdAngular()`에서 `SdOptionEventPlugin`만 자동 등록된다.
4
4
 
5
- ## Command Plugins
6
-
7
- 커맨드 플러그인은 `document` 레벨에서 keydown을 감지하며, `findTopOpenModalEl()`로 최상위 모달만 이벤트를 수신한다.
8
-
9
- ### `SdSaveCommandEventPlugin`
10
-
11
- Ctrl+S 키 조합 이벤트. 이벤트명: `sdSaveCommand`
12
-
13
- ```typescript
14
- class SdSaveCommandEventPlugin extends EventManagerPlugin {
15
- supports(eventName: string): boolean; // eventName === "sdSaveCommand"
16
- }
17
- ```
18
-
19
- 사용법: `<div (sdSaveCommand)="onSave($event)">`
20
-
21
- ### `SdRefreshCommandEventPlugin`
22
-
23
- Ctrl+Alt+L 키 조합 이벤트. 이벤트명: `sdRefreshCommand`
24
-
25
- ```typescript
26
- class SdRefreshCommandEventPlugin extends EventManagerPlugin {
27
- supports(eventName: string): boolean; // eventName === "sdRefreshCommand"
28
- }
29
- ```
30
-
31
- ### `SdInsertCommandEventPlugin`
32
-
33
- Ctrl+Insert 키 조합 이벤트. 이벤트명: `sdInsertCommand`
34
-
35
- ```typescript
36
- class SdInsertCommandEventPlugin extends EventManagerPlugin {
37
- supports(eventName: string): boolean; // eventName === "sdInsertCommand"
38
- }
39
- ```
40
-
41
- ## Observer Plugins
42
-
43
- ### `SdResizeEventPlugin`
44
-
45
- ResizeObserver 기반 이벤트. 이벤트명: `sdResize`. `requestAnimationFrame`으로 디바운스.
46
-
47
- ```typescript
48
- class SdResizeEventPlugin extends EventManagerPlugin {
49
- supports(eventName: string): boolean; // eventName === "sdResize"
50
- }
51
- ```
52
-
53
- 사용법: `<div (sdResize)="onResize($event)">`
54
-
55
- ### `SdResizeEvent`
56
-
57
- ```typescript
58
- interface SdResizeEvent {
59
- heightChanged: boolean;
60
- widthChanged: boolean;
61
- target: Element;
62
- contentRect: DOMRectReadOnly;
63
- }
64
- ```
65
-
66
- | Field | Type | Description |
67
- |-------|------|-------------|
68
- | `heightChanged` | `boolean` | 높이 변경 여부 |
69
- | `widthChanged` | `boolean` | 너비 변경 여부 |
70
- | `target` | `Element` | 대상 요소 |
71
- | `contentRect` | `DOMRectReadOnly` | 컨텐츠 영역 크기 |
72
-
73
- ### `SdIntersectionEventPlugin`
74
-
75
- IntersectionObserver 기반 이벤트. 이벤트명: `sdIntersection`
76
-
77
- ```typescript
78
- class SdIntersectionEventPlugin extends EventManagerPlugin {
79
- supports(eventName: string): boolean; // eventName === "sdIntersection"
80
- }
81
- ```
82
-
83
- ### `SdIntersectionEvent`
84
-
85
- ```typescript
86
- interface SdIntersectionEvent {
87
- entry: IntersectionObserverEntry;
88
- }
89
- ```
90
-
91
- | Field | Type | Description |
92
- |-------|------|-------------|
93
- | `entry` | `IntersectionObserverEntry` | 마지막 IntersectionObserver 엔트리 |
5
+ > **NOTE:** 이전 버전의 커맨드 플러그인(`sdSaveCommand`, `sdRefreshCommand`, `sdInsertCommand`)과 옵저버 플러그인(`sdResize`, `sdIntersection`)은 디렉티브로 대체되었다. 자세한 내용은 [directives.md](./directives.md)의 `SdCommandDirective`, `SdResizeDirective`, `SdIntersectionDirective` 항목을 참고한다.
94
6
 
95
7
  ## Option Plugin
96
8
 
@@ -256,7 +256,7 @@ class SdNavigateWindowProvider {
256
256
  class SdActivatedModalProvider<T extends SdModalContentDef<any> = SdModalContentDef<any>> {
257
257
  modalComponent = signal<any>(undefined);
258
258
  contentComponent = signal<T | undefined>(undefined);
259
- canDeactiveFn: () => boolean;
259
+ canDeactivateFn: () => boolean;
260
260
  }
261
261
  ```
262
262
 
@@ -264,7 +264,7 @@ class SdActivatedModalProvider<T extends SdModalContentDef<any> = SdModalContent
264
264
  |-------|------|-------------|
265
265
  | `modalComponent` | `WritableSignal<any>` | SdModal 인스턴스 |
266
266
  | `contentComponent` | `WritableSignal<T \| undefined>` | 컨텐츠 컴포넌트 인스턴스 |
267
- | `canDeactiveFn` | `() => boolean` | 모달 닫기 가능 여부 판별 함수 (기본: `() => true`) |
267
+ | `canDeactivateFn` | `() => boolean` | 모달 닫기 가능 여부 판별 함수 (기본: `() => true`) |
268
268
 
269
269
  ## `SdToastProvider`
270
270
 
@@ -143,9 +143,8 @@ select mode별 value 타입 매핑. `SdSelect`에서 export됨.
143
143
 
144
144
  ```typescript
145
145
  type SelectModeValue<T> = {
146
- single: T | undefined;
147
146
  multi: T[];
148
- "multi-with-header": T[];
147
+ single: T;
149
148
  }
150
149
  ```
151
150
 
@@ -45,53 +45,59 @@ class SdListItem {
45
45
  ```typescript
46
46
  @Component({ selector: "sd-sheet" })
47
47
  class SdSheet<T> {
48
- items = input.required<T[]>();
49
- currentPage = model(0);
48
+ key = input<string>();
49
+ items = input<T[]>([]);
50
+ trackByFn = input<(item: T, index: number) => unknown>();
51
+ selectMode = input<"single" | "multi">();
52
+ getItemSelectableFn = input<(item: T) => boolean | string>();
53
+ getChildrenFn = input<(item: T, index: number) => T[] | undefined>();
54
+ useAutoSort = input(false, { transform: booleanAttribute });
55
+ visiblePageCount = input(10);
50
56
  totalPageCount = input(0);
51
57
  itemsPerPage = input(0);
52
- visiblePageCount = input(10);
53
- useAutoSort = input(false, { transform: booleanAttribute });
54
58
  inset = input(false, { transform: booleanAttribute });
55
59
  hideConfigBar = input(false, { transform: booleanAttribute });
56
- sorts = model<SortingDef[]>([]);
57
- selectedItems = model<T[]>([]);
58
- selectMode = input<"single" | "multi">();
59
- expandedItems = model<T[]>([]);
60
- getChildrenFn = input<(item: T, index: number) => T[] | undefined>();
61
- getItemSelectableFn = input<(item: T) => boolean | string>();
62
- configKey = input<string>();
63
- trackByFn = input<(index: number, item: T) => any>();
64
60
 
65
61
  itemKeydown = output<SdSheetItemKeydownEventParam<T>>();
66
62
  cellKeydown = output<SdSheetCellKeydownEventParam<T>>();
63
+
64
+ selectedItems = model<T[]>([]);
65
+ expandedItems = model<T[]>([]);
66
+ sorts = model<SortingDef[]>([]);
67
+ currentPage = model(0);
67
68
  }
68
69
  ```
69
70
 
70
71
  | Input | Type | Default | Description |
71
72
  |-------|------|---------|-------------|
72
- | `items` | `T[]` | required | 표시할 항목 |
73
- | `currentPage` | `number` | `0` | 현재 페이지 (two-way) |
74
- | `totalPageCount` | `number` | `0` | 페이지 |
73
+ | `key` | `string \| undefined` | `undefined` | 설정 저장 |
74
+ | `items` | `T[]` | `[]` | 표시할 항목 |
75
+ | `trackByFn` | `((item, index) => unknown) \| undefined` | `undefined` | 트랙킹 함수 |
76
+ | `selectMode` | `"single" \| "multi" \| undefined` | `undefined` | 선택 모드 |
77
+ | `getItemSelectableFn` | `((item) => boolean \| string) \| undefined` | `undefined` | 선택 가능 여부 함수. string은 비활성 사유 |
78
+ | `getChildrenFn` | `((item, index) => T[] \| undefined) \| undefined` | `undefined` | 트리 구조 자식 반환 함수 |
75
79
  | `useAutoSort` | `boolean` | `false` | 클라이언트 측 자동 정렬 |
80
+ | `visiblePageCount` | `number` | `10` | 한 번에 표시할 페이지 수 |
81
+ | `totalPageCount` | `number` | `0` | 총 페이지 수 |
82
+ | `itemsPerPage` | `number` | `0` | 페이지당 항목 수 |
76
83
  | `inset` | `boolean` | `false` | 삽입 스타일 |
77
84
  | `hideConfigBar` | `boolean` | `false` | 설정 바 숨김 |
85
+ | `currentPage` | `number` | `0` | 현재 페이지 (two-way) |
78
86
  | `sorts` | `SortingDef[]` | `[]` | 정렬 설정 (two-way) |
79
87
  | `selectedItems` | `T[]` | `[]` | 선택된 항목 (two-way) |
80
- | `selectMode` | `"single" \| "multi" \| undefined` | - | 선택 모드 |
81
88
  | `expandedItems` | `T[]` | `[]` | 확장된 항목 (two-way) |
82
- | `getChildrenFn` | `((item, index) => T[] \| undefined) \| undefined` | - | 트리 구조 자식 반환 함수 |
83
- | `getItemSelectableFn` | `((item) => boolean \| string) \| undefined` | - | 선택 가능 여부 함수. string은 비활성 사유 |
84
- | `configKey` | `string \| undefined` | - | 설정 저장 키 |
85
89
 
86
90
  ### `SdSheetColumn`
87
91
 
88
- 시트 컬럼 정의 디렉티브.
92
+ 시트 컬럼 정의 디렉티브. 컬럼의 헤더, 너비, 고정, 정렬 등을 설정한다. 셀 내용은 `SdSheetColumnCellTemplate`으로 정의한다.
89
93
 
90
94
  ```typescript
91
95
  @Directive({ selector: "sd-sheet-column" })
92
- class SdSheetColumn {
96
+ class SdSheetColumn<T = unknown> {
93
97
  key = input.required<string>();
94
- header = input<string | string[]>();
98
+ header = input<string | string[]>("");
99
+ headerStyle = input<string>();
100
+ tooltip = input<string>();
95
101
  width = input<string>();
96
102
  fixed = input(false, { transform: booleanAttribute });
97
103
  hidden = input(false, { transform: booleanAttribute });
@@ -99,13 +105,19 @@ class SdSheetColumn {
99
105
  disableSorting = input(false, { transform: booleanAttribute });
100
106
  disableResizing = input(false, { transform: booleanAttribute });
101
107
  ordering = input(0);
108
+
109
+ cellTplRef = contentChild.required(SdSheetColumnCellTemplate, { read: TemplateRef });
110
+ headerTplRef = contentChild<TemplateRef<void>>("headerTpl");
111
+ summaryTplRef = contentChild<TemplateRef<void>>("summaryTpl");
102
112
  }
103
113
  ```
104
114
 
105
115
  | Input | Type | Default | Description |
106
116
  |-------|------|---------|-------------|
107
117
  | `key` | `string` | required | 컬럼 식별 키 |
108
- | `header` | `string \| string[]` | - | 헤더 텍스트 (배열이면 멀티 행 헤더) |
118
+ | `header` | `string \| string[]` | `""` | 헤더 텍스트 (배열이면 멀티 행 헤더) |
119
+ | `headerStyle` | `string \| undefined` | - | 헤더 셀 인라인 스타일 |
120
+ | `tooltip` | `string \| undefined` | - | 헤더 툴팁 텍스트 |
109
121
  | `width` | `string \| undefined` | - | 컬럼 너비 (예: `"100px"`) |
110
122
  | `fixed` | `boolean` | `false` | 고정 컬럼 |
111
123
  | `hidden` | `boolean` | `false` | 숨김 |
@@ -114,6 +126,58 @@ class SdSheetColumn {
114
126
  | `disableResizing` | `boolean` | `false` | 리사이즈 비활성화 |
115
127
  | `ordering` | `number` | `0` | 순서 (낮을수록 앞) |
116
128
 
129
+ Content children:
130
+ - `SdSheetColumnCellTemplate` (required): 셀 렌더링 템플릿
131
+ - `#headerTpl`: 커스텀 헤더 템플릿
132
+ - `#summaryTpl`: 요약 행 템플릿
133
+
134
+ ### `SdSheetColumnCellTemplate`
135
+
136
+ 시트 컬럼 셀 내용을 정의하는 디렉티브. `ng-template[cell]` 셀렉터를 사용하며, `SdSheetCellContext` 타입 가드를 제공한다.
137
+
138
+ ```typescript
139
+ @Directive({ selector: "ng-template[cell]" })
140
+ class SdSheetColumnCellTemplate<T> {
141
+ cell = input.required<T[]>();
142
+
143
+ static ngTemplateContextGuard<TContextItem>(
144
+ _dir: SdSheetColumnCellTemplate<TContextItem>,
145
+ _ctx: unknown,
146
+ ): _ctx is SdSheetCellContext<TContextItem>;
147
+ }
148
+ ```
149
+
150
+ 사용법:
151
+ ```html
152
+ <sd-sheet-column key="name" header="이름">
153
+ <ng-template [cell]="items()" let-item>
154
+ {{ item.name }}
155
+ </ng-template>
156
+ </sd-sheet-column>
157
+ ```
158
+
159
+ ### `SdSheetCellContext`
160
+
161
+ 시트 셀 템플릿 컨텍스트.
162
+
163
+ ```typescript
164
+ interface SdSheetCellContext<T = unknown> {
165
+ $implicit: T;
166
+ item: T;
167
+ index: number;
168
+ depth: number;
169
+ edit: boolean;
170
+ }
171
+ ```
172
+
173
+ | Field | Type | Description |
174
+ |-------|------|-------------|
175
+ | `$implicit` | `T` | 현재 항목 (let-item으로 접근) |
176
+ | `item` | `T` | 현재 항목 (명시적 접근) |
177
+ | `index` | `number` | 행 인덱스 |
178
+ | `depth` | `number` | 트리 깊이 |
179
+ | `edit` | `boolean` | 편집 모드 여부 (SdDataSheetColumn의 edit input에 의해 설정) |
180
+
117
181
  ### `SdSheetConfigModal`
118
182
 
119
183
  시트 설정 모달. 컬럼 표시/숨김, 고정, 너비 등을 설정한다.
@@ -134,6 +198,8 @@ class SdSheetConfigModal implements SdModalContentDef<SdSheetConfig> {
134
198
  interface SdSheetColumnDef {
135
199
  key: string;
136
200
  header: string | string[];
201
+ headerStyle: string | undefined;
202
+ tooltip: string | undefined;
137
203
  width: string | undefined;
138
204
  fixed: boolean;
139
205
  hidden: boolean;
@@ -144,6 +210,20 @@ interface SdSheetColumnDef {
144
210
  }
145
211
  ```
146
212
 
213
+ | Field | Type | Description |
214
+ |-------|------|-------------|
215
+ | `key` | `string` | 컬럼 식별 키 |
216
+ | `header` | `string \| string[]` | 헤더 텍스트 |
217
+ | `headerStyle` | `string \| undefined` | 헤더 셀 인라인 스타일 |
218
+ | `tooltip` | `string \| undefined` | 헤더 툴팁 텍스트 |
219
+ | `width` | `string \| undefined` | 컬럼 너비 |
220
+ | `fixed` | `boolean` | 고정 컬럼 여부 |
221
+ | `hidden` | `boolean` | 숨김 여부 |
222
+ | `collapse` | `boolean` | 접힘 여부 |
223
+ | `disableSorting` | `boolean` | 정렬 비활성화 여부 |
224
+ | `disableResizing` | `boolean` | 리사이즈 비활성화 여부 |
225
+ | `ordering` | `number` | 순서 |
226
+
147
227
  ### `SdSheetConfig`
148
228
 
149
229
  ```typescript