@simplysm/sd-claude 14.0.97 → 14.0.99

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 (77) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -16
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +81 -153
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +179 -205
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +71 -57
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +49 -109
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +58 -86
  7. package/claude/references/sd-simplysm14/apis/angular/kanban.md +32 -40
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +38 -52
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +86 -110
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -86
  11. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +82 -74
  12. package/claude/references/sd-simplysm14/apis/angular/sheet.md +56 -80
  13. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +15 -15
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -21
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +79 -53
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +9 -11
  17. package/claude/references/sd-simplysm14/apis/core-browser/README.md +15 -15
  18. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +20 -20
  19. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +18 -18
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +20 -49
  21. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +66 -55
  22. package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +83 -56
  23. package/claude/references/sd-simplysm14/apis/core-common/errors.md +32 -21
  24. package/claude/references/sd-simplysm14/apis/core-common/obj.md +57 -39
  25. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +36 -30
  26. package/claude/references/sd-simplysm14/apis/core-common/value-types.md +69 -41
  27. package/claude/references/sd-simplysm14/apis/core-node/README.md +4 -4
  28. package/claude/references/sd-simplysm14/apis/core-node/consola.md +15 -13
  29. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +11 -7
  30. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +8 -8
  31. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +29 -20
  32. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +14 -6
  33. package/claude/references/sd-simplysm14/apis/core-node/worker.md +3 -3
  34. package/claude/references/sd-simplysm14/apis/excel/README.md +3 -3
  35. package/claude/references/sd-simplysm14/apis/excel/cell.md +32 -32
  36. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +23 -24
  37. package/claude/references/sd-simplysm14/apis/excel/style.md +24 -30
  38. package/claude/references/sd-simplysm14/apis/excel/utils.md +20 -23
  39. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +60 -71
  40. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +36 -36
  41. package/claude/references/sd-simplysm14/apis/lint/README.md +7 -9
  42. package/claude/references/sd-simplysm14/apis/lint/recommended.md +59 -37
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +81 -74
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -6
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +112 -78
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +131 -75
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +126 -82
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +170 -113
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +102 -48
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +12 -13
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +3 -3
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +5 -5
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +67 -65
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +130 -123
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +63 -63
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +22 -22
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +30 -26
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +8 -8
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +13 -6
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +1 -1
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +43 -47
  62. package/claude/references/sd-simplysm14/apis/service-server/built-in-services.md +35 -0
  63. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +20 -19
  64. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +23 -25
  65. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +9 -9
  66. package/claude/references/sd-simplysm14/apis/storage/README.md +26 -26
  67. package/claude/references/sd-simplysm14/manuals/client-component.md +9 -1
  68. package/claude/references/sd-simplysm14/manuals/client-crud.md +1 -1
  69. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -0
  70. package/claude/references/sd-simplysm14/manuals/client-service.md +1 -0
  71. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +1 -0
  72. package/claude/references/sd-simplysm14/manuals/client-ssg.md +1 -0
  73. package/claude/sd-system-prompt.md +11 -26
  74. package/claude/skills/sd-docs/references/subagent-prompt.md +4 -3
  75. package/claude/skills/sd-spec/SKILL.md +87 -18
  76. package/claude/skills/sd-spec/references/format.md +2 -2
  77. package/package.json +1 -1
@@ -1,136 +1,104 @@
1
1
  # @simplysm/angular — 라우팅·메뉴·권한(app-structure)
2
2
 
3
- 라우터 링크·현재 페이지 식별·뷰 컨텍스트(page/control/modal)·이탈 가드, 그리고 앱 구조 트리에서 메뉴·권한을 파생하는 군. 화면 컴포넌트의 표준 시그널 `viewType`, 권한 가드 `injectPermsSignal`, 사이드바/탑바 메뉴가 이 군에 의존.
3
+ 라우터 링크·현재 페이지 식별·뷰 컨텍스트(page/control/modal)·이탈 가드, 그리고 앱 구조 트리에서 메뉴·권한을 파생하는 군. 화면 컴포넌트의 표준 시그널 `viewType`, 권한 가드 `injectPermsSignal`, 사이드바/탑바 메뉴가 이 군에 의존. 메뉴·권한 정의 절차는 [client-app-structure.md](../manuals/client-app-structure.md) 참조.
4
4
 
5
5
  ## 라우팅 디렉티브·프로바이더
6
6
 
7
- ### SdRouterLink — `[sdRouterLink]`
7
+ ### `SdRouterLink` — `[sdRouterLink]`
8
8
 
9
- ```ts
10
- option = input<{ link: string; params?: Record<string,string>;
11
- window?: { width?: number; height?: number };
12
- outletName?: string; queryParams?: Record<string,string> } | undefined>(undefined, { alias: "sdRouterLink" });
13
- ```
9
+ - `option: input<{ link: string; params?: Record<string,string>; window?: { width?: number; height?: number }; outletName?: string; queryParams?: Record<string,string> } | undefined>({ alias: "sdRouterLink" })` — 이동 대상. `link` = 라우트 경로, `params` = 라우트 파라미터, `outletName` 지정 시 named outlet, `window` 지정 시 팝업 창 크기.
10
+ - 클릭 동작: `Alt+click` 무시. 팝업 창 모드면 새 창; `Ctrl/Shift+click` 이면 새 탭/창; `outletName` 없으면 `Router.navigate([link, ...params])`; 있으면 outlet navigate.
14
11
 
15
- - 클릭 시 라우터 내비게이트. `option.link`=경로, `params`=matrix 파라미터, `queryParams`=쿼리, `outletName`=명명 outlet. Ctrl/Shift 클릭 또는 윈도우 모드면 새 창으로(`window.width/height`). Alt 클릭은 무시.
16
-
17
- ```html
18
- <sd-list-item [sdRouterLink]="{ link: '/home/order/list' }">주문</sd-list-item>
19
- ```
20
-
21
- ### SdNavigateWindowProvider
22
-
23
- ```ts
24
- @Injectable({ providedIn: "root" }) class SdNavigateWindowProvider {
25
- get isWindow: boolean;
26
- open(navigate: string, params?: Record<string,string>, features?: string): void;
27
- }
28
- ```
12
+ ### `SdNavigateWindowProvider`
29
13
 
30
- - `isWindow` — 현재가 팝업 창 모드인지(hash `window=true`). `open(navigate, params, features)` — 새 창/탭으로 화면을 염(features 있으면 팝업, 없으면 새 탭). 부모 종료 시 자식 창 일괄 닫힘.
14
+ `@Injectable({ providedIn: "root" })`.
31
15
 
32
- ## 페이지 식별·뷰 컨텍스트 시그널
16
+ - `get isWindow(): boolean` — 현재 hash query 에 `window=true` 가 있는지(팝업 창 컨텍스트 여부).
17
+ - `open(navigate: string, params?: Record<string,string>, features?: string): void` — 이미 팝업 창이거나 `features` 지정 시 새 브라우저 창(`window.open`), 아니면 `_blank` 탭으로 `#{navigate};{params}` 열기.
33
18
 
34
- 주입 컨텍스트(컴포넌트 생성자)에서 호출하는 헬퍼.
19
+ ## 현재 페이지·뷰 식별
35
20
 
36
- ### injectFullPageCodeSignal / injectCurrentPageCodeSignal
21
+ ### `injectCurrentPageCodeSignal` / `injectFullPageCodeSignal`
37
22
 
38
23
  ```ts
39
- injectFullPageCodeSignal(): Signal<string>;
40
- injectCurrentPageCodeSignal(): Signal<string> | undefined;
24
+ function injectCurrentPageCodeSignal(): Signal<string> | undefined
25
+ function injectFullPageCodeSignal(): Signal<string>
41
26
  ```
42
27
 
43
- - `injectFullPageCodeSignal` — 현재 URL 전체 페이지 코드(`a.b.c`, `/home/` 이후 세그먼트를 `.` 결합). 메뉴 선택 판정에 사용.
44
- - `injectCurrentPageCodeSignal` — 현재 라우트 컴포넌트 기준 페이지 코드(중첩 라우트의 자기 위치). `ActivatedRoute` 없으면 undefined.
28
+ - `injectCurrentPageCodeSignal` — `ActivatedRoute` 없으면 `undefined`; 있으면 활성 라우트 URL 세그먼트(앞 2개 제외)를 `"."` 이은 코드.
29
+ - `injectFullPageCodeSignal` — `Router` URL(`NavigationEnd` 추적)을 `/` 분리, 2개 제외, `;`/`?` 접미 제거 후 `"."` 이은 페이지 코드. 메뉴 선택 판정 등에 사용.
45
30
 
46
- ### injectViewTitleSignal / injectViewTypeSignal
31
+ ### `injectViewTitleSignal`
47
32
 
48
33
  ```ts
49
- injectViewTitleSignal(): Signal<string>;
50
- injectViewTypeSignal(): Signal<SdViewType>; // "page" | "modal" | "control"
34
+ function injectViewTitleSignal(): Signal<string>
51
35
  ```
52
36
 
53
- - `injectViewTitleSignal` 현재 뷰 제목. 모달이면 모달 title, 페이지면 app-structure 에서 코드로 제목 조회. `sd-base-container` 사용.
54
- - `injectViewTypeSignal` — 현재 뷰 컨텍스트. `"modal"`=모달 안, `"page"`=라우팅 진입 단위(컴포넌트 selector 가 라우트 컴포넌트와 일치+풀코드 매칭), 그 외 `"control"`(임베드). crud 골격의 `viewType` 입력에 그대로 전달.
37
+ - 활성 모달 안이면 모달 `title`, 아니면 `SdAppStructureProvider.findTitleByFullCode(...)` 화면 제목. 엑셀 파일명·탑바 제목 등에.
55
38
 
56
- ### setupCanDeactivate
39
+ ### `injectViewTypeSignal` / `SdViewType`
57
40
 
58
41
  ```ts
59
- setupCanDeactivate(fn: () => boolean): void;
42
+ function injectViewTypeSignal(): Signal<SdViewType>
43
+ type SdViewType = "page" | "modal" | "control"
60
44
  ```
61
45
 
62
- - 화면 이탈 가드 등록. 모달 안이면 `SdActivatedModalProvider.canDeactivateFn` 에, 페이지면 라우트 `canDeactivate` 연결(파괴 해제). `fn` false 이탈 차단(미저장 변경 보호).
46
+ - `"modal"` = 활성 모달 안에서 렌더; `"page"` = 라우트가 있고 컴포넌트 selector·코드가 일치하는 최상위 페이지; `"control"` = 그 외(라우트 없음·임베드 재사용). 화면 컴포넌트가 `viewType = injectViewTypeSignal()` 으로 받아 `sd-crud-*`/`sd-base-container` 전달.
63
47
 
64
- ### 메뉴 유틸
48
+ ### `setupCanDeactivate`
65
49
 
66
50
  ```ts
67
- getMenuRouterLinkOption(menu: SdMenu): { link: string; queryParams: Record<string,string>|undefined } | undefined;
68
- getIsMenuSelected(menu: SdMenu, fullPageCode: string|undefined, customFn?: (menu: SdMenu)=>boolean): boolean;
51
+ function setupCanDeactivate(fn: () => boolean): void
69
52
  ```
70
53
 
71
- - `getMenuRouterLinkOption` 리프 메뉴(자식·url 없음)면 `/home/<codeChain>` 링크 옵션 반환, 그룹/외부url 이면 undefined(클릭 비내비게이트). `sdRouterLink` 전달.
72
- - `getIsMenuSelected` — 메뉴가 현재 페이지인지. `customFn` 있으면 그 결과, 없으면 코드 일치. 메뉴 컴포넌트가 선택 강조에 사용.
54
+ - 이탈 가드 등록. 모달 컨텍스트면 `SdActivatedModalProvider.canDeactivateFn = fn`; 라우트 페이지면 route config `canDeactivate` `fn()` 반환 가드를 push(파괴 시 제거). `fn()` false 면 이탈 차단. detail 의 미저장 변경 가드에 사용([client-component.md](../manuals/client-component.md)).
73
55
 
74
- ## 구조(메뉴·권한)
56
+ ## 메뉴 유틸
75
57
 
76
- ### SdAppStructureProvider
58
+ ### `getMenuRouterLinkOption` / `getIsMenuSelected`
77
59
 
78
60
  ```ts
79
- @Injectable({ providedIn: "root" }) class SdAppStructureProvider<TModule = unknown> {
80
- usableModules: WritableSignal<TModule[] | undefined>;
81
- permRecord: WritableSignal<Record<string, boolean> | undefined>;
82
- items: WritableSignal<AppStructureItem<TModule>[]>;
83
- initialize(items): void;
84
- usableMenus = computed<SdMenu[]>(...);
85
- usableFlatMenus = computed<SdFlatMenu<TModule>[]>(...);
86
- getPermissionsByStructure(items, codeChain?): SdPermission<TModule>[];
87
- getTitleByFullCode(fullCode): string; // 못 찾으면 throw
88
- findTitleByFullCode(fullCode): string | undefined; // 결측 보존
89
- getItemChainByFullCode(fullCode): AppStructureItem<TModule>[];
90
- getPermsByFullCode<K>(fullCodes: string[], permKeys: K[]): K[];
91
- }
61
+ function getMenuRouterLinkOption(menu: SdMenu): { link: string; queryParams: Record<string,string> | undefined } | undefined
62
+ function getIsMenuSelected(menu: SdMenu, fullPageCode: string | undefined, customFn?: (menu: SdMenu) => boolean): boolean
92
63
  ```
93
64
 
94
- - `initialize(items)` 구조 트리 주입. `usableModules`/`permRecord` 인증 채우면 메뉴/권한이 그에 맞게 필터됨.
95
- - `usableMenus` — 모듈·권한으로 필터된 메뉴 트리(사이드바/탑바용). `usableFlatMenus` 평탄화된 메뉴(검색 등).
96
- - `getTitleByFullCode` 는 못 찾으면 throw, `findTitleByFullCode` 는 undefined(결측 보존) — 상황에 맞게 선택. `getPermsByFullCode` 는 주어진 코드들에 대해 보유한 권한 키만 반환.
65
+ - `getMenuRouterLinkOption` — 그룹 메뉴(`children != null`)·외부 링크(`url != null`)면 `undefined`; leaf `link = "/home/" + codeChain.join("/")`(`?query` `queryParams` 분리). 사이드바/탑바 메뉴가 라우터 링크 생성에 사용.
66
+ - `getIsMenuSelected` — `customFn` 있으면 결과, 아니면 `fullPageCode === menu.codeChain.join(".")`.
67
+
68
+ ## 앱 구조 프로바이더
97
69
 
98
- ### injectPermsSignal
70
+ ### `injectPermsSignal`
99
71
 
100
72
  ```ts
101
- injectPermsSignal<K extends string>(viewCodes: string[], keys: K[]): Signal<K[]>;
73
+ function injectPermsSignal<K extends string>(viewCodes: string[], keys: K[]): Signal<K[]>
102
74
  ```
103
75
 
104
- - 화면 권한 가드. `viewCodes`(보통 현재 화면 코드)에 대해 `keys`(`["use","edit"]` 등) 보유한 것만 반환하는 시그널. `perms().includes("use")` 로 분기.
76
+ - 화면 fullCode()에 대해 사용자가 보유한 권한 action 배열을 `computed` 로 반환(`SdAppStructureProvider.getPermsByFullCode` 위임).
105
77
 
106
78
  ```ts
107
- perms = injectPermsSignal(["base.role"], ["use", "edit"]);
79
+ perms = injectPermsSignal(["inventory.outbound-instruction"], ["use", "edit"]);
80
+ // this.perms().includes("use")
108
81
  ```
109
82
 
110
- ### SdAppStructureUtils
83
+ ### `SdAppStructureProvider<TModule>`
111
84
 
112
- ```ts
113
- abstract class SdAppStructureUtils {
114
- static getTitleByFullCode(items, fullCode): string;
115
- static findTitleByFullCode(items, fullCode): string | undefined;
116
- static getPermsByFullCode(items, fullCodes, permKeys, permRecord): K[];
117
- static getItemChainByFullCode(items, fullCode): AppStructureItem[];
118
- static getMenus(items, codeChain, usableModules, permRecord): SdMenu[];
119
- static getFlatMenus(items, usableModules, permRecord): SdFlatMenu[];
120
- static getPermissions(items, codeChain, usableModules): SdPermission[];
121
- static getFlatPermissions(items, usableModules);
122
- }
123
- ```
85
+ `@Injectable({ providedIn: "root" })`. 앱 메뉴·권한 트리의 단일 소스. 부트스트랩에서 `initialize(items)` 로 연결.
124
86
 
125
- - 트리에서 메뉴/권한/제목/체인을 계산하는 순수 정적 유틸(`SdAppStructureProvider` 위임). provider 없이 트리만으로 파생할 때 직접 호출.
87
+ - 시그널: `usableModules: WritableSignal<TModule[] | undefined>` / `permRecord: WritableSignal<Record<string, boolean> | undefined>` / `items: WritableSignal<AppStructureItem<TModule>[]>`.
88
+ - computed: `usableMenus: Signal<SdMenu[]>` / `usableFlatMenus: Signal<SdFlatMenu<TModule>[]>`.
89
+ - 메서드:
90
+ - `initialize(items): void` — `items` set.
91
+ - `getPermissionsByStructure(items, codeChain?): SdPermission<TModule>[]` — 권한 테이블용 권한 트리.
92
+ - `getTitleByFullCode(fullCode): string`(없으면 throw) / `findTitleByFullCode(fullCode): string | undefined`.
93
+ - `getItemChainByFullCode(fullCode): AppStructureItem<TModule>[]`.
94
+ - `getPermsByFullCode<K extends string>(fullCodes, permKeys): K[]`.
126
95
 
127
- ### 타입
96
+ ### `SdAppStructureUtils`
128
97
 
129
- ```ts
130
- SdMenu { title: string; codeChain: string[]; url?: string; icon?: string; children?: SdMenu[] }
131
- SdFlatMenu<TModule> { titleChain: string[]; codeChain: string[]; modulesChain: TModule[][] }
132
- SdPermission<TModule> { title; codeChain; modules?; perms?: ("use"|"edit")[]; children? }
133
- SdViewType = "page" | "modal" | "control"
134
- ```
98
+ static 메서드만 가진 abstract class(`SdAppStructureProvider` 의 순수 함수 형태). `getTitleByFullCode`/`findTitleByFullCode`/`getPermsByFullCode`/`getItemChainByFullCode`/`getMenus`/`getFlatMenus`/`getPermissions`/`getFlatPermissions` — 각각 `items: AppStructureItem[]` 을 첫 인자로 받아 메뉴·권한·제목을 파생. provider 가 이를 시그널로 래핑.
99
+
100
+ ## 타입
135
101
 
136
- - `SdMenu` — 메뉴 트리 노드(레이아웃 메뉴 컴포넌트 입력). `SdFlatMenu` 평탄화 메뉴. `SdPermission` 권한 트리 노드(`SdPermissionTable` 입력, features.md). `SdViewType` — crud/뷰 컨텍스트.
102
+ - `SdMenu` — `{ title: string; codeChain: string[]; url?: string; icon?: string; children?: SdMenu[] }`. 메뉴 트리 노드. 사이드바/탑바 메뉴가 소비.
103
+ - `SdFlatMenu<TModule>` — `{ titleChain: string[]; codeChain: string[]; modulesChain: TModule[][] }`. leaf 메뉴를 평탄화한 형태.
104
+ - `SdPermission<TModule>` — `{ title: string; codeChain: string[]; modules: TModule[] | undefined; perms: ("use"|"edit")[] | undefined; children: SdPermission<TModule>[] | undefined }`. 권한 트리 노드(`perms`: `"use"`=조회·`"edit"`=편집). `SdPermissionTable` 의 `items` 입력 타입([crud.md](./crud.md)).
@@ -1,107 +1,115 @@
1
- # @simplysm/angular — 공유 마스터 데이터
1
+ # @simplysm/angular — 공유 마스터 데이터·선택 매니저
2
2
 
3
- 고객사·품목 등 자주 참조하는 마스터 데이터를 한 번 등록해 어느 화면에서든 공유 시그널로 쓰고, 그 데이터를 선택하는 드롭다운/버튼/리스트 컨트롤을 제공하는 군. 등록·항목 추가 절차는 [client-shared-data.md](../manuals/client-shared-data.md) 참조.
3
+ 고객사·품목 등 자주 참조하는 마스터 데이터를 한 번 등록해 어느 화면에서든 공유 시그널로 쓰고, 그 데이터를 선택하는 드롭다운/버튼/리스트 컨트롤과, 시트·리스트의 선택/정렬/펼침 상태를 관리하는 composable 매니저를 제공하는 군. 등록·항목 추가·선택 모달·좌측목록+우측상세 절차는 [client-shared-data.md](../manuals/client-shared-data.md) 참조.
4
4
 
5
- ## SdSharedDataProvider
5
+ ## `SdSharedDataProvider<T>`
6
6
 
7
- ```ts
8
- @Injectable() abstract class SdSharedDataProvider<T extends Record<string, SharedDataBase<string|number>>> {
9
- loadingCount: WritableSignal<number>;
10
- abstract initialize(): void;
11
- register<K>(name: K, info: SharedDataInfo<T[K]>): void;
12
- getHandle<K>(name: K): SharedDataHandle<T[K]>;
13
- emitAsync<K>(name: K, changeKeys?: (string|number)[]): Promise<void>;
14
- wait(): Promise<void>;
15
- }
16
- ```
7
+ `@Injectable()` **abstract**. 앱이 상속해 `initialize()` 에서 데이터를 `register` (root 별칭 등록 필요). `T extends Record<string, SharedDataBase<string|number>>`.
17
8
 
18
- - 추상 클래스 — 앱에서 상속(`AppSharedDataProvider`)해 `initialize()` 안에서 `register`. `T` 는 이름→항목타입 매핑.
19
- - `register(name, info)` — 마스터 데이터 등록. 재등록 시 이전 리스너 정리 + generation 증가로 이전 이벤트 무시 후 재로드.
20
- - `getHandle(name)` — 핸들 반환( 접근 lazy 로드 + 변경 이벤트 리스너 등록). 미등록이면 throw.
21
- - `emitAsync(name, changeKeys?)`변경 통지 이벤트 발생. `changeKeys` 지정 해당 키만 부분 갱신, 미지정 전체 리로드. CRUD 저장/삭제 후 호출해 다른 화면을 동기화.
22
- - `wait()`진행 중인 로드(`loadingCount`)가 끝날 때까지 대기. `sd-base-container` ready 게이트가 사용.
9
+ - `loadingCount: WritableSignal<number>` (초기 0) 로드 카운트.
10
+ - `abstract initialize(): void` — 하위에서 `register` 호출.
11
+ - `register<K>(name: K, info: SharedDataInfo<T[K]>): void` — 데이터 항목 등록(lazy; 즉시 로드 함). 재등록 기존 리스너 제거·generation 증가로 stale 이벤트 무시.
12
+ - `getHandle<K>(name: K): SharedDataHandle<T[K]>` 핸들 반환(첫 접근lazy 로드·리스너 등록). 미등록throw.
13
+ - `emitAsync<K>(name: K, changeKeys?: (string|number)[]): Promise<void>` 변경 통지. `changeKeys` 생략 = 전체 재로드, 지정 = 키들만 부분 갱신. 저장·삭제 후 호출해 공유데이터를 최신화.
14
+ - `wait(): Promise<void>` — `loadingCount <= 0` 까지 대기.
23
15
 
24
- ### 관련 타입
16
+ 타입:
17
+ - `SharedDataBase<TKey extends string|number>` — 항목 매직 필드: `__valueKey: TKey`(키) / `__searchText: string`(검색용) / `__isHidden: boolean`(숨김) / `__parentKey?: TKey`(트리 부모). getter select 결과에 포함 필수.
18
+ - `SharedDataInfo<T>` — `{ serviceKey: string; getter: (changeKeys?) => Promise<T[]>; filter?: unknown; orderBy?: (item) => string|number|DateOnly|DateTime|Time|undefined }`. `getter(changeKeys)` 는 changeKeys 주어지면 그 키만 재조회(incremental).
19
+ - `SharedDataHandle<T>` — `{ items: Signal<T[]>; get(key): T | undefined }`. 화면이 `useSharedSignal(name)` 으로 받아 `items()`·`get(id)` 사용.
20
+ - `SdSharedDataChangeEvent` — `defineEvent<{ name; filter }, (string|number)[] | undefined>("SdSharedDataChange")`. provider 내부 통지 이벤트.
25
21
 
26
- ```ts
27
- SharedDataBase<TKey> { __valueKey: TKey; __searchText: string; __isHidden: boolean; __parentKey?: TKey }
28
- SharedDataInfo<T> { serviceKey: string; getter: (changeKeys?) => Promise<T[]>; filter?; orderBy?: (item) => ...|undefined }
29
- SharedDataHandle<T> { items: Signal<T[]>; get(key): T | undefined }
30
- SdSharedDataChangeEvent // defineEvent — 변경 통지 이벤트 정의
31
- ```
22
+ ## `SdSharedDataSelect<TItem, TMode, TModal>` — `<sd-shared-data-select>`
32
23
 
33
- - `SharedDataBase` 모든 공유 항목이 가져야 할 매직 필드: `__valueKey`(키), `__searchText`(검색 텍스트), `__isHidden`(숨김 여부), `__parentKey`(트리 부모, 선택).
34
- - `SharedDataInfo.getter(changeKeys)` — DB 조회 함수. changeKeys 주어지면 그 키만 재조회(incremental refresh). `orderBy` 는 정렬 키 반환. `SharedDataHandle.get(key)` 로 단건 O(1) 조회.
24
+ 검색·트리(`__parentKey`) 가능한 공유데이터 드롭다운(`sd-select` 래핑). 입력의 공유 데이터 선택지에 사용.
35
25
 
36
- ```ts
37
- sharedProducts = useSharedSignal("품목"); // 헬퍼
38
- // sharedProducts.items() / sharedProducts.get(id)
26
+ - `items: input.required<TItem[]>` — 소스 목록(`sharedX.items()`).
27
+ - `value: model<...>` single 이면 `키|undefined`, multi 면 `키[]`.
28
+ - `selectMode: TMode` (기본 `"single"`) `"single"`/`"multi"`.
29
+ - `required: boolean` — true(single)면 "미지정" 옵션 숨김.
30
+ - `useUndefined: boolean` — multi 에서 "미지정" 항목 표시.
31
+ - `disabled`/`inset`/`inline`/`size: "sm"|"lg"`/`selectClass`/`multiSelectionDisplayDirection: "vertical"`.
32
+ - `filterFn: (item, index, ...params) => boolean` + `filterFnParams: any[]` — 표시 전 필터.
33
+ - `getIsHiddenFn` (기본 `(item) => item.__isHidden`) / `getSearchTextFn` (기본 `(item) => item.__searchText`) / `displayOrderByFn` — 숨김/검색텍스트/정렬 커스터마이즈.
34
+ - `modal: SdSelectModalInfo<TModal>` — 설정 시 검색 모달 버튼(선택 갱신). `editModal: SdModalInfo<SdModalContentDef<boolean>>` — 관리 전용 모달 버튼(선택 안 바꿈). (선택/관리 모달 규약은 [client-shared-data.md](../manuals/client-shared-data.md))
35
+ - 콘텐츠: `[itemOf]` 항목 템플릿, `#undefinedTpl`("미지정" 라벨).
36
+
37
+ ```html
38
+ <sd-shared-data-select [items]="sharedCustomers.items()" [(value)]="data().customerId" (valueChange)="mark(data)">
39
+ <ng-template [itemOf]="sharedCustomers.items()" let-item="item">{{ item.name }}</ng-template>
40
+ </sd-shared-data-select>
39
41
  ```
40
42
 
41
- ## 선택 컨트롤
43
+ ## `SdSharedDataSelectButton<TItem, TMode, TModal>` — `<sd-shared-data-select-button>`
42
44
 
43
- ### SdSharedDataSelect — `<sd-shared-data-select>`
45
+ 모달로 선택하는 버튼(`sd-modal-select-button` 래핑).
44
46
 
45
- ```ts
46
- value = model<SelectModeValue<TItem["__valueKey"] | undefined>[TMode]>();
47
- items = input.required<TItem[]>();
48
- disabled; required; useUndefined; inset; inline;
49
- size = input<"sm"|"lg">(); selectMode = input<TMode>("single"); // "single" | "multi"
50
- filterFn = input<(item, index, ...params) => boolean>(); filterFnParams = input<any[]>();
51
- modal = input<SdSelectModalInfo<TModal>>(); editModal = input<SdModalInfo<SdModalContentDef<boolean>>>();
52
- selectClass; multiSelectionDisplayDirection = input<"vertical">();
53
- getIsHiddenFn = input(item => item.__isHidden); getSearchTextFn = input(item => item.__searchText);
54
- displayOrderByFn = input<(item) => ...|undefined>();
55
- // 콘텐츠: [itemOf] 템플릿(항목 렌더), #undefinedTpl(미지정 표시)
56
- ```
47
+ - `items: TItem[]` (기본 `[]`) — 선택 항목 표시 해석용.
48
+ - `modal: input.required<SdSelectModalInfo<TModal>>` 선택 모달(필수).
49
+ - `value: model<SelectModeValue<string|number>[TMode]>` / `selectMode: TMode`(기본 `"single"`).
50
+ - `disabled`/`required`/`inset`/`size: "sm"|"lg"`.
51
+ - 콘텐츠: `[itemOf]` 항목 템플릿(필수).
52
+
53
+ ## `SdSharedDataSelectList<TItem, TModal>` `<sd-shared-data-select-list>`
54
+
55
+ 검색·페이지 가능한 단일 선택 리스트. 좌측목록+우측상세 레이아웃의 마스터로 사용.
57
56
 
58
- - 공유데이터 드롭다운 선택. `value` 선택된 `__valueKey`(single) 또는 키 배열(multi). `items` `sharedX.items()` 전달.
59
- - `selectMode` — `"single"`/`"multi"`. `useUndefined`=multi 에서 "미지정" 항목 노출, `required=false`+single 이면 미지정 선택 가능. 내부 검색바로 `__searchText` 필터(부모키 트리면 자식 매칭 포함).
60
- - `modal` — 관리·선택 모달(`selectMode:"single"`+현재 주입, 결과로 선택 갱신). `editModal`관리 전용(선택 변경 없음). `<sd-select-button>` 아이콘으로 노출.
61
- - `filterFn`/`displayOrderByFn` — 표시 필터/정렬. `getIsHiddenFn`/`getSearchTextFn` — 숨김·검색텍스트 추출 커스텀(기본 매직필드). `__parentKey` 있으면 트리(`getChildrenFn` 자동).
57
+ - `selectedItem: model<TItem>` 현재 선택(항목 객체). `canChangeFn` 가드(`setupModelHook`). `items` 변경 `__valueKey` 로 재해석 자동 동기.
58
+ - `items: input.required<TItem[]>`.
59
+ - `canChangeFn: (item: TItem | undefined) => boolean | Promise<boolean>` (기본 `() => true`) — 선택 변경 가드(다른 마스터로 전환 미저장 변경 확인 등).
60
+ - `selectedIcon: string` — 선택 항목 아이콘. `useUndefined: boolean` — "미지정" 항목. `header: string` — 헤더 텍스트. `pageItemCount: number` `>0` 이면 그 크기로 페이지네이션.
61
+ - `filterFn: (item, index) => boolean`.
62
+ - `modal: SdSelectModalInfo<TModal>` — 설정 시 외부 링크 모달 버튼(목록 관리·선택).
63
+ - 콘텐츠: `#headerTpl` / `#filterTpl`(기본 검색 필드 대체) / `[itemOf]` / `#undefinedTpl`.
64
+ - 메서드: `select(item)` / `toggle(item)` / `onModalButtonClick()`.
62
65
 
63
66
  ```html
64
- <sd-shared-data-select [items]="sharedProducts.items()" [(value)]="productId" [required]="true">
65
- <ng-template [itemOf]="sharedProducts.items()" let-item="item">{{ item.name }}</ng-template>
66
- </sd-shared-data-select>
67
+ <sd-shared-data-select-list class="flex-min" [items]="sharedRoles.items()" [(selectedItem)]="selectedRole" [header]="'역할'" [modal]="{ type: RoleList, title: '역할', inputs: {} }">
68
+ <ng-template [itemOf]="sharedRoles.items()" let-item="item">{{ item.name }}</ng-template>
69
+ </sd-shared-data-select-list>
67
70
  ```
68
71
 
69
- ### SdSharedDataSelectButton — `<sd-shared-data-select-button>`
72
+ ## `matchesSearchText`
70
73
 
71
74
  ```ts
72
- value = model<SelectModeValue<string|number>[TMode]>();
73
- items = input<TItem[]>([]); modal = input.required<SdSelectModalInfo<TModal>>();
74
- selectMode = input<TMode>("single"); disabled; required; inset; size = input<"sm"|"lg">();
75
- itemTplRef = contentChild.required(SdItemOfTemplate); // [itemOf] 템플릿(필수)
75
+ function matchesSearchText(itemText: string, searchQuery: string | undefined): boolean
76
76
  ```
77
77
 
78
- - 모달로만 선택하는 버튼형(`SdModalSelectButton` 래핑). 선택된 항목을 `[itemOf]` 템플릿으로 표시(multi 콤마 구분). 항목이 많아 드롭다운보다 모달 검색이 나을 때.
78
+ - `searchQuery` 공백으로 분리한 모든 단어가 `itemText` (대소문자 무시) 부분 포함되면 `true`(AND 매칭). 단어 없으면 `true`. 공유데이터 select 검색 매칭에 사용.
79
79
 
80
- ### SdSharedDataSelectList `<sd-shared-data-select-list>`
80
+ ## 선택/정렬/펼침 매니저 (composable)
81
+
82
+ 시트·리스트가 내부적으로 쓰는 상태 매니저. 직접 사용은 커스텀 그리드를 만들 때.
83
+
84
+ ### `useSelectionManager<TItem, TKey>`
81
85
 
82
86
  ```ts
83
- selectedItem = model<TItem>();
84
- canChangeFn = input<(item: TItem | undefined) => boolean | Promise<boolean>>(() => true);
85
- items = input.required<TItem[]>(); selectedIcon = input<string>(); useUndefined;
86
- filterFn = input<(item, index) => boolean>(); modal = input<SdSelectModalInfo<TModal>>();
87
- header = input<string>(); pageItemCount = input<number>();
88
- // 콘텐츠: [itemOf](항목) #headerTpl #filterTpl #undefinedTpl
87
+ useSelectionManager(options: {
88
+ displayItems: Signal<TItem[]>; selectedKeys: WritableSignal<TKey[]>;
89
+ selectMode: Signal<"single"|"multi"|undefined>;
90
+ getItemSelectableFn: Signal<((item) => boolean|string) | undefined>;
91
+ trackByFn: Signal<(item, index) => TKey>;
92
+ })
89
93
  ```
90
94
 
91
- - 좌측 선택 목록형(master-detail 좌측 패널). `selectedItem` 항목 객체(키 아님). `canChangeFn` 으로 선택 전환 가드(미저장 변경 보호), Promise 가능.
92
- - `pageItemCount` 지정 시 페이징. `header`/`#headerTpl`=상단 제목, `#filterTpl`=검색 대체, `modal`=목록 관리 모달. 검색은 `__searchText` 매칭, `__isHidden` 항목 제외.
95
+ 반환: `hasSelectable`/`isAllSelected: Signal<boolean>`, `getSelectable(item): true | string | undefined`(불가=undefined, 가능=true, 사유=string), `getCanChangeFn(item)`, `select`/`deselect`/`toggle`/`toggleAll`/`isSelected`. single 교체, multi 는 추가(`obj.equal` 중복 제거).
93
96
 
94
- ```html
95
- <sd-shared-data-select-list [items]="sharedRoles.items()" [(selectedItem)]="selectedRole"
96
- [canChangeFn]="checkCanLeave" [header]="'역할'">
97
- <ng-template [itemOf]="sharedRoles.items()" let-item="item">{{ item.name }}</ng-template>
98
- </sd-shared-data-select-list>
97
+ ### `useSortingManager`
98
+
99
+ ```ts
100
+ useSortingManager(options: { sorts: WritableSignal<SortingDef[]> })
99
101
  ```
100
102
 
101
- ### matchesSearchText
103
+ 반환: `defMap: Signal<Map<string, { indexText?: string; desc: boolean }>>`(다중 정렬 시 `indexText` "1"…"n"), `toggle(key, multiple)`(none→asc→desc→제거; `multiple=false` 면 전체 교체), `sort<T>(items): T[]`(안정 다중 키 정렬; null < non-null). `SortingDef` = `{ key: string; desc: boolean }`.
104
+
105
+ ### `useExpandingManager<T>`
102
106
 
103
107
  ```ts
104
- matchesSearchText(itemText: string, searchQuery: string | undefined): boolean;
108
+ useExpandingManager(binding: {
109
+ items: Signal<T[]>; expandedItems: WritableSignal<T[]>;
110
+ getChildrenFn: Signal<((item, index) => T[] | undefined) | undefined>;
111
+ sort: (items: T[]) => T[];
112
+ })
105
113
  ```
106
114
 
107
- - 공백 분리 AND 부분일치(대소문자 무시). 쿼리면 true. 선택 컨트롤이 내부 검색에 사용하며, 커스텀 필터에서 재사용 가능.
115
+ 반환: `displayItems: Signal<T[]>`(평탄화·부모→자식 순), `hasExpandable`/`isAllExpanded: Signal<boolean>`, `toggle(item)`/`toggleAll()`/`isVisible(item)`(모든 조상 펼침 true)/`def(item): ExpandItemDef<T>`. `ExpandItemDef<T>` = `{ item: T; parentDef: ExpandItemDef<T> | undefined; hasChildren: boolean; depth: number }`.
@@ -1,100 +1,76 @@
1
1
  # @simplysm/angular — 시트(sd-sheet)
2
2
 
3
- 다건 목록·편집 표(그리드). 컬럼 디렉티브 + 셀 템플릿으로 구성하며, 선택·정렬·페이지·트리펼침·셀 편집·컬럼 고정/리사이즈/설정저장을 내장. `sd-crud-list` 이 시트를 감싼 표준 골격([crud.md](./crud.md)). 셀은 항상 `<ng-template [cell]="items()" let-item="item">` 안에 렌더.
4
-
5
- ## SdSheet — `<sd-sheet>`
6
-
7
- ```ts
8
- key = input<string>(); items = input<TItem[]>([]);
9
- trackByFn = input<(item: TItem, index: number) => unknown>((item) => item);
10
- selectMode = input<"single" | "multi">();
11
- autoSelect = input<"click" | "focus">();
12
- getItemSelectableFn = input<(item: TItem) => boolean | string>();
13
- getChildrenFn = input<(item: TItem, index: number) => TItem[] | undefined>();
14
- useAutoSort = input(false);
15
- visiblePageCount = input(10); totalPageCount = input(0); itemsPerPage = input(0);
16
- focusMode = input<"row" | "cell">("cell");
17
- inset = input(false); contentStyle = input<string>(); hideConfigBar = input(false);
18
- getItemCellClassFn = input<(item, colKey) => string>();
19
- getItemCellStyleFn = input<(item, colKey) => string | undefined>();
20
-
21
- // outputs
22
- itemKeydown = output<SdSheetItemKeydownEventParam<TItem>>(); // { item, event }
23
- cellKeydown = output<SdSheetCellKeydownEventParam<TItem>>(); // { item, key, event }
24
- // models
25
- selectedKeys = model<unknown[]>([]); expandedItems = model<TItem[]>([]);
26
- sorts = model<SortingDef[]>([]); currentPage = model(0);
27
- columnControlsInput = input<readonly SdSheetColumn[]>([]); // 외부에서 컬럼 주입(crud 가 사용)
28
- ```
29
-
30
- - `key` — 지정 시 사용자 컬럼 설정(폭/고정/숨김/순서)을 `injectSdSystemConfigResource` 로 영속(설정 버튼·설정 모달 노출). 미지정이면 설정 저장 없음.
31
- - `trackByFn` — 행 키 함수. `selectedKeys` 가 이 키 기준으로 동작. 서버 키가 있으면 그 키 반환.
32
- - `selectMode` — `"single"`=단일(좌측 화살표), `"multi"`=다중(좌측 체크박스+전체선택). undefined=선택 비활성. `autoSelect` `"click"`=행/셀 클릭 시 선택, `"focus"`=셀 포커스만으로 선택(키보드 위주).
33
- - `getItemSelectableFn` — 행별 선택 가능. `true`=가능, `false`=불가, `string`=불가+사유(툴팁). 본인 계정 삭제 차단 등.
34
- - `getChildrenFn` — 자식 반환(트리). 지정 시 펼침 컬럼·들여쓰기 표시, `expandedItems` 로 펼친 항목 관리.
35
- - `useAutoSort` — true 면 `sorts` 변경 시 시트가 직접 `items` 클라이언트 정렬. 서버측 정렬/페이징이면 false 로 두고 외부에서 재조회. `itemsPerPage>0`+`useAutoSort` 면 클라이언트 페이징, `totalPageCount>0` 이면 서버 페이징.
36
- - `focusMode` — `"cell"`=셀 단위 포커스 이동(편집·복사), `"row"`=행 단위(셀 인디케이터 숨김). `hideConfigBar`=상단 설정/페이징 바 숨김, `inset`=테두리 제거.
37
- - `getItemCellClassFn`/`getItemCellStyleFn` — 행+컬럼키별 클래스/스타일(예: 삭제행 취소선). `columnControlsInput` 은 래퍼(crud)가 자식 컬럼을 시트로 위임 투영할 때 사용.
3
+ 다건 목록·편집 표(그리드). 컬럼 디렉티브 + 셀 템플릿으로 구성하며 선택·정렬·페이지·트리펼침·셀 편집·컬럼 고정/리사이즈/설정저장을 내장. `sd-crud-list` 이 시트를 감싼 표준 골격([crud.md](./crud.md)). 본문·요약 작성 규약은 [client-component.md](../manuals/client-component.md) '시트 컬럼·셀 표준' 참조.
4
+
5
+ ## `SdSheet<TItem>` — `<sd-sheet>`
6
+
7
+ - `items: TItem[]` (기본 `[]`) — 행 데이터.
8
+ - `trackByFn: (item, index) => unknown` (기본 `(item) => item`) — 행 식별/추적.
9
+ - `selectMode: "single"|"multi"|undefined` — `"multi"` = 체크박스 피처 컬럼 + 헤더 전체선택 + shift-click 범위 선택; `"single"` = 행별 화살표 셀렉터; `undefined` = 선택 UI 없음.
10
+ - `autoSelect: "click"|"focus"|undefined` — `"click"` = 행/셀 클릭 시 선택; `"focus"` = 셀 포커스 시 선택; `undefined` = 자동 선택 없음.
11
+ - `getItemSelectableFn: (item) => boolean | string | undefined` — 행별 선택 가능 여부. `true`=가능, `false`=불가, `string`=불가+사유 툴팁.
12
+ - `getChildrenFn: (item, index) => TItem[] | undefined` — 설정 시 트리펼침 활성(펼침 피처 컬럼·depth·caret).
13
+ - `useAutoSort: boolean` (기본 false) true 표시 전 클라이언트 정렬(정렬 매니저). 서버 정렬/페이징이면 false.
14
+ - `itemsPerPage: number` (기본 0) — 클라이언트 페이징 크기(`0`=페이징 없음).
15
+ - `totalPageCount: number` (기본 0) 외부 총 페이지 수(서버 페이징). `0` 이면 `items`/`itemsPerPage` 산출.
16
+ - `visiblePageCount: number` (기본 10) — 페이지 번호 표시 개수.
17
+ - `key: string` — 컬럼 설정 영속화 키(`injectSdSystemConfigResource`). 설정 설정(톱니) 버튼 표시. 설정 모달은 key 없으면 throw.
18
+ - `focusMode: "row"|"cell"` (기본 `"cell"`) `"cell"` = 포커스 셀 강조; `"row"` = 셀 인디케이터 숨기고 행만 강조.
19
+ - `inset: boolean` (기본 false) true 테두리·라운드 제거(다른 컨테이너 내장용).
20
+ - `hideConfigBar: boolean` (기본 false) — true 면 상단 도구 바(설정 버튼+페이지네이션) 숨김.
21
+ - `contentStyle: string` — 스크롤 컨테이너 인라인 스타일.
22
+ - `getItemCellClassFn: (item, colKey) => string | undefined` / `getItemCellStyleFn: (item, colKey) => string | undefined` — 셀별 클래스/스타일.
23
+ - `columnControlsInput: readonly SdSheetColumn[]` (기본 `[]`) 투영 컬럼과 합쳐질 프로그래밍 컬럼.
24
+ - model: `selectedKeys: unknown[]` (선택 키) / `expandedItems: TItem[]` (펼친 행) / `sorts: SortingDef[]` (정렬) / `currentPage: number` (0-base).
25
+ - output: `itemKeydown: SdSheetItemKeydownEventParam<TItem>` (`{ item, event }`) / `cellKeydown: SdSheetCellKeydownEventParam<TItem>` (`{ item, key, event }`).
26
+ - 주요 메서드: `onConfigButtonClick()` `SdSheetConfigModal` 열어 컬럼 설정 영속화(key 없으면 throw). `onHeaderClick(event, cell)` — 정렬 토글(shift=다중 정렬).
38
27
 
39
28
  ```html
40
- <sd-sheet [items]="items()" [trackByFn]="trackByFn" [(selectedKeys)]="selectedKeys"
41
- [selectMode]="'multi'" [(sorts)]="sorts" [key]="'order-list'">
29
+ <sd-sheet [items]="items()" [trackByFn]="trackByFn" [(selectedKeys)]="selectedKeys" [(currentPage)]="page" [totalPageCount]="pageLength()">
42
30
  <sd-sheet-column [key]="'name'" [header]="'이름'">
43
- <ng-template [cell]="items()" let-item="item">{{ item.name }}</ng-template>
31
+ <ng-template [cell]="items()" let-item="item"><div class="p-xs-sm">{{ item.name }}</div></ng-template>
44
32
  </sd-sheet-column>
45
33
  </sd-sheet>
46
34
  ```
47
35
 
48
- ## SdSheetColumn — `<sd-sheet-column>`
49
-
50
- ```ts
51
- key = input.required<string>();
52
- header = input<string | string[]>(""); // string[] = 다단 헤더(병합)
53
- headerStyle; tooltip; width = input<string>();
54
- fixed = input(false); hidden = input(false); collapse = input(false);
55
- disableSorting = input(false); disableResizing = input(false); ordering = input(0);
56
- cellTplRef = contentChild.required(SdSheetColumnCellTemplate); // [cell] 템플릿(필수)
57
- headerTplRef = contentChild<TemplateRef<void>>("headerTpl");
58
- summaryTplRef = contentChild<TemplateRef<void>>("summaryTpl");
59
- ```
60
-
61
- - `key` — 컬럼 식별자(정렬/설정/cellKeydown 키). `header` 배열이면 다단 헤더로 병합 표시.
62
- - `width` — CSS 폭(예: `"120px"`). `fixed`=좌측 고정, `hidden`=숨김, `collapse`=설정상 접힘 후보. `disableSorting`/`disableResizing`=정렬/폭조정 비활성, `ordering`=기본 정렬 순서.
63
- - `#headerTpl`/`#summaryTpl` 로 헤더/요약행 커스터마이즈. 셀 본문은 `<ng-template [cell]="items()">`(필수).
36
+ ## `SdSheetColumn<T>` — `<sd-sheet-column>` (디렉티브)
64
37
 
65
- ```ts
66
- // SdSheetCellContext<T> = { $implicit: T; item: T; index: number; depth: number; edit: boolean }
67
- ```
38
+ 컬럼 1개 선언. `sd-sheet`/`sd-crud-list` 직속 자식으로 두면 자동 투영.
68
39
 
69
- - 템플릿 컨텍스트: `item`/`index`/`depth`(트리 깊이)/`edit`(현재 편집모드 여부).
40
+ - `key: string` (required) — 컬럼 키(설정·셀/헤더 템플릿 lookup·정렬 키). select 별칭과 일치시키면 서버 정렬에 컬럼 분기 없이 처리([client-crud.md](../manuals/client-crud.md)).
41
+ - `header: string | string[]` (기본 `""`) — 헤더 텍스트; `string[]` = 다단/그룹 헤더.
42
+ - `headerStyle: string` / `tooltip: string` — 헤더 셀 스타일/툴팁.
43
+ - `width: string` — 컬럼 폭(미지정=자동; px 지정은 명시 지시 시만, 매뉴얼 '폭 약속').
44
+ - `fixed: boolean` — true 면 좌측 고정(sticky).
45
+ - `hidden: boolean` — true 면 미렌더.
46
+ - `collapse: boolean` — 레이아웃 엔진 collapse 플래그.
47
+ - `disableSorting: boolean` — true 면 헤더 정렬 클릭·아이콘 비활성.
48
+ - `disableResizing: boolean` — true 면 리사이저 핸들 제거.
49
+ - `ordering: number` (기본 0) — 컬럼 순서 가중치(작을수록 앞).
50
+ - 콘텐츠: `[cell]` 셀 템플릿(필수, `SdSheetColumnCellTemplate`), `#headerTpl`(커스텀 헤더), `#summaryTpl`(요약 행 셀).
70
51
 
71
- ## SdSheetColumnCellTemplate — `ng-template[cell]`
52
+ ## `SdSheetColumnCellTemplate<T>` — `ng-template[cell]` (디렉티브)
72
53
 
73
- ```ts
74
- cell = input.required<T[]>(); // 타입 추론용 — items() 를 그대로 전달
75
- ```
54
+ `<ng-template [cell]="items()">` 를 셀 본문으로 표시하고 컨텍스트 타입을 좁힘.
76
55
 
77
- - 본문 템플릿 마커. `[cell]="items()"` 항목 배열을 넘겨 `let-item` 타입을 추론. 값 자체는 추론에만 쓰임.
56
+ - `cell: T[]` (required) items 배열(타입 추론용 더미, 실제 데이터는 `sd-sheet` `[items]` 보유).
57
+ - 정적 `ngTemplateContextGuard` — 컨텍스트를 `SdSheetCellContext<T>` 로 좁힘.
78
58
 
79
- ## SdSheetConfigModal `<sd-sheet-config-modal>`
59
+ 컨텍스트 `SdSheetCellContext<T>` 필드: `$implicit: T` / `item: T` / `index: number`(표시 페이지 내 행 인덱스) / `depth: number`(트리 깊이) / `edit: boolean`(인라인 편집 모드 여부). 템플릿에서 `let-item="item"`·`let-index="index"`·`let-depth="depth"`·`let-edit="edit"` 로 받음.
80
60
 
81
- ```ts
82
- sheetKey = input.required<string>();
83
- controls = input.required<readonly SdSheetColumn[]>();
84
- config = input.required<SdSheetConfig | undefined>();
85
- close = output<SdSheetConfig | undefined>(); // SdModalContentDef
86
- ```
61
+ ## `SdSheetConfigModal` — `<sd-sheet-config-modal>`
87
62
 
88
- - 컬럼 고정/순서/폭/숨김을 편집하는 설정 모달. `sd-sheet` 가 설정 버튼 클릭 시 자동으로 띄우며 결과를 `key` 설정으로 저장. 직접 호출할 일은 거의 없음.
63
+ 컬럼 설정(고정·순서·폭·숨김) 편집 모달. `SdModalContentDef<SdSheetConfig | undefined>` 구현. `sd-sheet` 가 설정 버튼 클릭 시 자동으로 띄움 화면에서 직접 다룰 일은 드묾.
89
64
 
90
- ## 관련 타입
65
+ - `sheetKey: string` (required) / `controls: readonly SdSheetColumn[]` (required) / `config: SdSheetConfig | undefined` (required).
66
+ - `close: SdSheetConfig | undefined` — OK 시 빌드된 설정, 취소 시 `undefined`, 초기화 확정 시 `{ columnRecord: {} }`.
91
67
 
92
- ```ts
93
- SdSheetColumnDef { key; header: string|string[]; headerStyle?; tooltip?; width?; fixed; hidden; collapse; disableSorting; disableResizing; ordering }
94
- SdSheetHeaderDef { text; colspan; rowspan; isLastRow; fixed; colDef?; colIndex }
95
- SdSheetConfig { columnRecord: Record<string, { width?; hidden?; fixed?; ordering? }> }
96
- SdSheetItemKeydownEventParam<T> { item: T; event: KeyboardEvent }
97
- SdSheetCellKeydownEventParam<T> { item: T; key: string; event: KeyboardEvent }
98
- ```
68
+ ## 타입
99
69
 
100
- - `SdSheetColumnDef`/`SdSheetHeaderDef` — 레이아웃 엔진이 산출하는 컬럼/헤더셀 메타(내부 타입). `SdSheetConfig` 영속 컬럼 설정. `SdSheet*KeydownEventParam` `itemKeydown`/`cellKeydown` 출력 페이로드(행 단축키·셀 단축키 처리용).
70
+ - `SdSheetColumnDef` — `{ key: string; header: string|string[]; headerStyle?: string; tooltip?: string; width?: string; fixed: boolean; hidden: boolean; collapse: boolean; disableSorting: boolean; disableResizing: boolean; ordering: number }`. 컬럼 디렉티브에서 파생한 정규화 정의.
71
+ - `SdSheetHeaderDef` — `{ text: string; colspan: number; rowspan: number; isLastRow: boolean; fixed: boolean; colDef: SdSheetColumnDef | undefined; colIndex: number }`. 다단 헤더 셀 1개 정의.
72
+ - `SdSheetConfig` — `{ columnRecord: Record<string, { width?: string; hidden?: boolean; fixed?: boolean; ordering?: number }> }`. 사용자별 영속 설정.
73
+ - `SdSheetItemKeydownEventParam<T>` — `{ item: T; event: KeyboardEvent }`. `itemKeydown` 페이로드.
74
+ - `SdSheetCellKeydownEventParam<T>` — `{ item: T; key: string; event: KeyboardEvent }`. `cellKeydown` 페이로드(`key`=컬럼 키).
75
+ - `SdSheetCellContext<T>` — 위 셀 컨텍스트(`sd-sheet-column.ts` 에서 export).
76
+ - `SortingDef` — `{ key: string; desc: boolean }`(`sd-sheet.ts` 재export; 정렬 매니저 원본은 [shared-data.md](./shared-data.md)).