@simplysm/sd-claude 14.0.88 → 14.0.90

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 (135) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -17
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +39 -43
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +174 -80
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -50
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +60 -26
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +109 -37
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +61 -44
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +39 -31
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +73 -85
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -39
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -30
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +71 -67
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +82 -72
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +35 -36
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +71 -43
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +38 -30
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +45 -50
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +42 -55
  19. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +62 -0
  20. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +39 -38
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +95 -103
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +59 -54
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +86 -0
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +57 -66
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +86 -0
  26. package/claude/references/sd-simplysm14/apis/core-common/obj.md +60 -42
  27. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +10 -8
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +29 -32
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +34 -22
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +29 -25
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +40 -53
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +22 -29
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +31 -31
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +26 -26
  36. package/claude/references/sd-simplysm14/apis/excel/cell.md +37 -29
  37. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +29 -15
  38. package/claude/references/sd-simplysm14/apis/excel/style.md +33 -27
  39. package/claude/references/sd-simplysm14/apis/excel/utils.md +29 -19
  40. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +78 -55
  41. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +42 -45
  42. package/claude/references/sd-simplysm14/apis/lint/README.md +27 -21
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +89 -49
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -62
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +149 -67
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +111 -99
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +115 -72
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +134 -92
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +67 -52
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +63 -26
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +51 -40
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +10 -12
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +92 -45
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +226 -108
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +90 -88
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +37 -29
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +45 -20
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +89 -40
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +70 -66
  62. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +47 -47
  63. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +71 -34
  64. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +31 -32
  65. package/claude/references/sd-simplysm14/apis/storage/README.md +34 -28
  66. package/claude/references/sd-simplysm14/manuals/client-app-structure.md +142 -140
  67. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
  68. package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
  69. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
  70. package/claude/references/sd-simplysm14/manuals/client-system-log.md +11 -3
  71. package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
  72. package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
  73. package/claude/rules/sd-design-rules.md +10 -0
  74. package/claude/skills/sd-docs/SKILL.md +58 -46
  75. package/claude/skills/sd-docs/references/{doc-rules.md → subagent-prompt.md} +103 -103
  76. package/claude/skills/sd-impl/SKILL.md +1 -1
  77. package/claude/skills/sd-spec/SKILL.md +858 -858
  78. package/claude/skills/sd-spec/references/example-spec.md +26 -36
  79. package/package.json +1 -1
  80. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -47
  81. package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +0 -29
  82. package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
  83. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
  84. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
  85. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
  86. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
  87. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
  88. package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
  89. package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
  90. package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
  91. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
  92. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
  93. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
  94. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
  95. package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
  96. package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
  97. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
  98. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
  99. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
  100. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
  101. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
  102. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
  103. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
  104. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
  105. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
  106. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
  107. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
  108. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
  109. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
  110. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
  111. package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
  112. package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
  113. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
  114. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
  115. package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
  116. package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
  117. package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
  118. package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
  119. package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
  120. package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
  121. package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
  122. package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
  123. package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
  124. package/claude/skills/sd-review/evals/golden.jsonl +0 -2
  125. package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
  126. package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
  127. package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
  128. package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
  129. package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
  130. package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
  131. package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
  132. package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
  133. package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
  134. package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
  135. package/claude/skills/sd-use/evals/golden.jsonl +0 -6
@@ -1,115 +1,103 @@
1
- # @simplysm/angular — 모달·토스트·Busy·인쇄 (오버레이)
1
+ # @simplysm/angular — 모달·토스트·Busy·인쇄 (오버레이/전역 피드백)
2
2
 
3
- 프로그래밍 방식으로 동적 컴포넌트를 body 부착하는 루트 프로바이더 4종(모달/토스트/Busy/인쇄)과 콘텐츠 컴포넌트가 구현할 인터페이스. 모두 `createComponent` + `ApplicationRef.attachView` 동작.
3
+ 화면에서 프로그래밍 방식으로 모달을 띄우거나, 토스트로 알림·진행률을 표시하거나, busy 인디케이터·인쇄/PDF 출력을 호출할 함께 읽히는 군. provider 는 모두 `providedIn: "root"`, 컴포넌트는 provider 가 동적으로 body 에 attach 하므로 템플릿에 직접 둘 일은 거의 없음.
4
4
 
5
5
  ## SdModalProvider
6
6
 
7
- `class SdModalProvider` 모달을 프로그래밍 방식으로 띄우는 루트 프로바이더.
8
-
9
- - `modalCount: Signal<number>` — 현재 열린 모달 수.
10
- - `showAsync<T>(modal: SdModalInfo<T>, options?: SdModalOptions): Promise<결과 | undefined>` — 모달 표시. 콘텐츠 컴포넌트의 `close` emit 값으로 resolve, 배경/ESC/닫기버튼이면 `undefined`. 닫힘 애니메이션·포커스 복원·z-index 스택 처리.
11
-
12
- `SdModalInfo<T, X>`:
13
- - `title: string` — 모달 헤더 제목.
14
- - `type: Type<T>` — 콘텐츠 컴포넌트 클래스(`SdModalContentDef` 구현).
15
- - `inputs` — 콘텐츠 컴포넌트의 input 값 객체(`initialized`/`close`/`actionTplRef` 제외, `_optionalModalInputs` 로 지정된 키는 optional).
16
-
17
- `SdModalOptions`(모두 optional):
18
- - `key` — 설정 키. 지정 시 크기·위치를 `SdSystemConfigProvider` 에 영속화.
19
- - `hideHeader` — 헤더 전체 숨김.
20
- - `hideCloseButton` — 닫기(X) 버튼만 숨김.
21
- - `headerStyle` — 헤더 인라인 스타일.
22
- - `useCloseByBackdrop` — 배경 클릭으로 닫기 허용. 기본 true.
23
- - `useCloseByEscapeKey` — ESC 로 닫기 허용. 기본 true.
24
- - `float` — 배경 없는 플로팅 모달(다른 조작 가능).
25
- - `fill` — 전체화면 채움.
26
- - `resizable` — 모서리/변 드래그 리사이즈.
27
- - `movable` — 헤더 드래그 이동.
28
- - `position` — `"bottom-right"|"top-right"`. 고정 위치.
29
- - `minHeightPx`/`minWidthPx`/`heightPx`/`widthPx` — 크기 px.
30
- - `noFirstControlFocusing` — 첫 입력요소 자동 포커스 끔(true 면 dialog 자체 포커스).
31
-
32
- `SdModalContentDef<O>`(콘텐츠 컴포넌트 구현 인터페이스):
33
- - `initialized: Signal<boolean>` — 초기화 완료 신호.
34
- - `close: OutputEmitterRef<O | undefined>` — 결과 emit 시 모달 닫힘.
35
- - `actionTplRef?: TemplateRef<any>` — 헤더 우측 액션 영역 템플릿.
36
- - `_optionalModalInputs?: string` — optional 처리할 input 키(타입 마커).
37
-
38
- ```typescript
39
- const result = await sdModal.showAsync(
40
- { title: "사용자 선택", type: UserSelectModal, inputs: { deptId } },
41
- { resizable: true, key: "user-select" },
42
- );
7
+ 컴포넌트를 모달 셸(`SdModal`) 안에 동적 생성해 body 띄움. `close.emit(payload)` 또는 닫기(X/배경/ESC)로 종료.
8
+
9
+ ```ts
10
+ showAsync<T extends SdModalContentDef<any>>(
11
+ modal: SdModalInfo<T>, options?: SdModalOptions,
12
+ ): Promise<Parameters<T["close"]["emit"]>[0] | undefined>
43
13
  ```
44
14
 
45
- ## SdModal
15
+ - `modal.type: Type<T>` — `SdModalContentDef<O>` 를 구현한 컴포넌트 클래스(`SdModal` 자체가 아님). `O` 가 close 페이로드 타입.
16
+ - `modal.title: string` — 모달 헤더 제목.
17
+ - `modal.inputs` — 모달 컴포넌트가 받을 input 값. `initialized`/`close`/`actionTplRef` 와 `_optionalModalInputs` 로 표시된 키는 제외/optional 처리됨. 없으면 `{}`.
18
+ - 반환값 — 컴포넌트가 `close.emit` 한 페이로드. 닫기/취소로 닫히면 `undefined`. 매뉴얼 패턴: `const r = await this._sdModal.showAsync({...}); if (!r) return;`.
19
+ - `modalCount: WritableSignal<number>` — 현재 열린 모달 수.
20
+
21
+ ### SdModalContentDef<O> (모달 컴포넌트가 구현)
22
+
23
+ - `initialized: Signal<boolean>` — 초기화 완료 여부. busy 표시 해제 기준.
24
+ - `close: OutputEmitterRef<O | undefined>` — 결과 emit. `O` 가 `showAsync` 반환 타입.
25
+ - `actionTplRef?: TemplateRef<any>` — 헤더 우측에 끼울 액션 영역 템플릿(있으면 모달 헤더로 브릿지됨).
26
+ - `_optionalModalInputs?: string` — (타입 전용 마커) 이 컴포넌트의 input 중 optional 로 둘 키 이름 리터럴. 런타임 값 아님.
27
+
28
+ ### SdModalOptions (showAsync 2번째 인자)
46
29
 
47
- `<sd-modal>`모달 컴포넌트. 보통 `SdModalProvider` 내부 생성하지만 선언적으로도 사용 가능.
30
+ - `key?: string` 설정 저장 키. 지정 사용자가 조정한 width/height/위치를 `SdSystemConfigProvider` 영속·복원.
31
+ - `hideHeader?: boolean` — true 면 제목/닫기 헤더 숨김. 헤더 없는 풀커스텀 모달용.
32
+ - `hideCloseButton?: boolean` — true 면 헤더 X 버튼만 숨김.
33
+ - `headerStyle?: string` — 헤더 영역 인라인 스타일.
34
+ - `useCloseByBackdrop?: boolean` — 배경 클릭으로 닫기 허용(기본 동작상 컴포넌트 기본 true). false 면 배경 클릭 무시.
35
+ - `useCloseByEscapeKey?: boolean` — ESC 로 닫기 허용. false 면 ESC 무시.
36
+ - `float?: boolean` — true 면 배경(backdrop) 없는 떠있는 패널. 비모달 보조 패널용.
37
+ - `fill?: boolean` — true 면 화면 전체를 채움(풀스크린 모달).
38
+ - `resizable?: boolean` — true 면 8방향 리사이즈 핸들 표시.
39
+ - `movable?: boolean` — true 면 헤더 드래그로 이동.
40
+ - `position?: "bottom-right" | "top-right"` — 고정 위치. 토스트성 알림 모달에 사용.
41
+ - `minHeightPx?/minWidthPx?/heightPx?/widthPx?: number` — 최소/초기 크기.
42
+ - `noFirstControlFocusing?: boolean` — true 면 첫 입력 요소 자동 포커스를 끔(다이얼로그 자체에 포커스).
48
43
 
49
- - `open = model(false)` — 열림 여부 양방향.
50
- - `title`/`hideHeader`/`hideCloseButton`/`headerStyle`/`useCloseByBackdrop`/`useCloseByEscapeKey`/`float`/`fill`/`resizable`/`movable`/`position`/`minHeightPx`/`minWidthPx`/`heightPx`/`widthPx`/`actionTplRef` — `SdModalOptions` 와 동일 의미의 input.
51
- - `key` — 크기·위치 영속화 키.
52
- - `closeRequest = output<void>()` — 배경/ESC/닫기버튼으로 닫기 요청 시 emit.
44
+ ### 내장 모달 컴포넌트
53
45
 
54
- ## SdActivatedModalProvider
46
+ - `SdPromptModal` (`SdModalContentDef<string>`) — 메시지 + 텍스트 입력 후 확인/취소. `message: input.required<string>` (innerHTML). 확인 시 입력값, 취소/닫기 시 `undefined`. 입력은 required 라 빈 값이면 네이티브 검증으로 차단.
47
+ - `SdConfirmModal` (`SdModalContentDef<boolean>`) — 메시지 + 확인/취소. `message: input.required<string>`. 확인 시 `true`, 취소/닫기 시 `undefined`.
48
+ - 사용: `const ok = await this._sdModal.showAsync({ type: SdConfirmModal, title: "확인", inputs: { message: "삭제할까요?" } }); if (!ok) return;`.
55
49
 
56
- `class SdActivatedModalProvider<T>` — 모달 콘텐츠 내부에서 inject 해 모달 컨텍스트에 접근.
50
+ ### SdActivatedModalProvider
57
51
 
58
- - `modalComponent: Signal<SdModal | undefined>` 부모 `SdModal` 인스턴스.
59
- - `contentComponent: Signal<T | undefined>` — 콘텐츠 컴포넌트 인스턴스.
60
- - `canDeactivateFn: () => boolean` — 닫기 차단 함수(true 면 닫힘 허용). `setupCanDeactivate` 가 설정.
52
+ 모달 컴포넌트 내부에서 inject. 자기 모달의 셸/콘텐츠 참조와 이탈 가드를 보유.
61
53
 
62
- ## SdPromptModal / SdConfirmModal
54
+ - `modalComponent: Signal<SdModal | undefined>` / `contentComponent: Signal<T | undefined>` — 셸/콘텐츠 컴포넌트 참조.
55
+ - `canDeactivateFn: () => boolean` — 닫기 시도 시 false 면 닫힘 차단. `setupCanDeactivate`(routing-appstructure.md) 가 모달 컨텍스트에서 이걸 설정.
63
56
 
64
- 내장 범용 모달 콘텐츠. `SdModalProvider.showAsync` 의 `type` 으로 사용.
57
+ ### SdModal (모달 셸)
65
58
 
66
- - `SdPromptModal` `SdModalContentDef<string>`. `message: input.required<string>()` 표시 텍스트 입력. 확인 입력값 emit(필수 검증), 취소 시 `undefined`.
67
- - `SdConfirmModal` — `SdModalContentDef<boolean>`. `message` 표시 후 확인 시 `true`, 취소 시 `undefined` emit.
59
+ `SdModalProvider` 내부적으로 생성하는 컴포넌트. 상속·직접 배치 대상 아님. `<ng-content>` 콘텐츠를 투영하고 위 `SdModalOptions` 와 동일한 input(`title`/`open`/`resizable`/... `closeRequest` output) 보유.
68
60
 
69
61
  ## SdToastProvider
70
62
 
71
- `class SdToastProvider` 토스트 알림 루트 프로바이더.
63
+ 화면 우상단(또는 overlap 모드)에 토스트를 띄움. 알림·비동기 에러 가드·진행률에 사용.
72
64
 
73
- - `alertThemes: WritableSignal<SdToastSeverity[]>` 심각도는 토스트 대신 `window.alert` 표시.
74
- - `overlap: WritableSignal<boolean>` — true 토스트가 기존 토스트를 모두 치우고 겹쳐 표시.
75
- - `beforeShowFn?: (theme) => void` — 토스트 표시 직전 콜백.
76
- - `info/success/warning/danger(message, useProgress?)`심각도별 토스트. `useProgress: true` `WritableSignal<number>`(0~100) 반환(100 도달 1초 후 자동 해제), 아니면 3초 후 자동 해제(호버 시 지연).
77
- - `notify<T>(input: SdToastInput<T>): Promise<결과|undefined>` — 커스텀 컴포넌트 토스트. `close` emit 값으로 resolve, 5초 자동 `undefined`.
78
- - `try<R>(fn, messageFn?): Promise<R|undefined>` — `fn` 실행, `Error` 발생 `danger` 토스트 + 시스템로그 후 `undefined` 반환(비-Error 는 rethrow). `messageFn` 으로 메시지 커스터마이즈.
65
+ - `info/success/warning/danger(message: string): void` 심각도별 토스트 1개 표시(3초 후 자동 해제, hover 중이면 지연). `info`/`success` `aria-live=polite`, `warning`/`danger` 는 `assertive`. 심각도 의미는 `sd-design-rules` 의 분류를 따름(error=문제 발생).
66
+ - `info/...(message: string, useProgress: true): WritableSignal<number>` — progress 모드. 반환된 signal 0~100 set 하면 진행바 갱신, 100 도달 1초 후 자동 해제. 업로드/다운로드 진행률 표시에 사용.
67
+ - `try<R>(fn: () => Promise<R> | R, messageFn?: (err: Error) => string): Promise<R | undefined>` `fn` 실행 중 `Error` 가 throw 되면 잡아서 `danger` 토스트 + 시스템로그 `error` 적재 후 `undefined` 반환(에러를 외부로 전파하지 않음). `Error` 가 아닌 throw 는 그대로 재전파. 매뉴얼의 비동기 작업 표준 가드: `await this._sdToast.try(async () => { ... })`.
68
+ - `notify<T extends SdToastContentDef<any>>(input: SdToastInput<T>): Promise<...>` 커스텀 컴포넌트를 토스트로 띄우고 `close` 페이로드를 반환(5초 후 자동 `undefined`).
69
+ - `alertThemes: WritableSignal<SdToastSeverity[]>` — 여기 심각도는 토스트 대신 `window.alert` 표시(키오스크 강제 확인 필요 화면).
70
+ - `overlap: WritableSignal<boolean>` — true 토스트가 기존 토스트를 제거하고 단독 표시.
71
+ - `beforeShowFn?: (theme: SdToastSeverity) => void` — 토스트 표시 직전 콜백(사운드 등).
79
72
 
80
73
  타입:
81
- - `SdToastSeverity` — `"info"|"success"|"warning"|"danger"`.
82
- - `SdToastTheme` — `"primary"|"secondary"|SdToastSeverity|"gray"|"blue-gray"`.
83
- - `SdToastContentDef<O>` — `{ close: OutputEmitterRef<O|undefined> }`. notify 콘텐츠 구현.
84
- - `SdToastInput<T>` — `{ type: Type<T>; inputs }`(`close` 제외 input).
85
-
86
- ```typescript
87
- sdToast.danger("저장 실패");
88
- const progress = sdToast.info("업로드 중", true);
89
- progress.set(50);
90
- await sdToast.try(() => api.saveAsync());
91
- ```
92
74
 
93
- ## SdToast / SdToastContainer
75
+ - `SdToastSeverity = "info"|"success"|"warning"|"danger"`.
76
+ - `SdToastTheme = "primary"|"secondary"|SdToastSeverity|"gray"|"blue-gray"` — `sd-toast` 컴포넌트 `theme` 입력 범위.
77
+ - `SdToastContentDef<O> = { close: OutputEmitterRef<O | undefined> }` — `notify` 커스텀 컴포넌트 규약.
78
+ - `SdToastInput<T> = { type: Type<T>; inputs: Omit<DirectiveInputSignals<T>, "close"> }`.
94
79
 
95
- - `<sd-toast>` — 개별 토스트. `open`/`progress`/`message` 는 `model`, `useProgress`/`theme` input. theme 따라 `role`/`aria-live`(info·success=polite/status, warning·danger=assertive/alert) 자동.
96
- - `<sd-toast-container>` — 토스트 컨테이너. `overlap = input(false)` — 겹침 모드.
80
+ `SdToast`/`SdToastContainer` 는 provider 동적 생성하는 표시 컴포넌트(직접 배치 불필요).
97
81
 
98
82
  ## SdBusyProvider / SdBusyContainer
99
83
 
100
- 전역·지역 로딩 표시.
84
+ 영역 단위 busy 오버레이.
101
85
 
102
- - `SdBusyProvider` — `type: WritableSignal<SdBusyType>` 기본 스피너 종류, `globalBusyCount: WritableSignal<number>` 0 초과 전역 busy 오버레이 표시. 라우팅·인쇄가 자동 증감.
103
- - `SdBusyType``"spinner"|"bar"|"cube"`. 인디케이터 모양.
104
- - `<sd-busy-container>`지역 busy 래퍼. `busy = input(false)` 표시 여부, `message`/`type`(미지정 시 프로바이더 type 따름)/`progressPercent`(0~100 막대) input. busy 중 키입력 차단.
86
+ - `SdBusyProvider.type: WritableSignal<SdBusyType>` — 전역 기본 인디케이터 종류. `SdBusyType = "spinner"|"bar"|"cube"`. `"bar"` = 상단 가는 진행바, `"spinner"` = 회전 원, `"cube"` = 큐브 애니메이션.
87
+ - `SdBusyProvider.globalBusyCount: WritableSignal<number>` 전역 busy 카운트(>0 이면 화면 전체 busy). 라우팅·인쇄가 ±1. 직접 ±1 해 전역 차단 가능.
88
+ - `SdBusyContainer` (`sd-busy-container`)자식 영역에 busy 오버레이를 씌우는 컨테이너 컴포넌트.
89
+ - `busy: boolean` — true 면 오버레이 표시 + 영역 내 키입력 차단.
90
+ - `message: string` — 인디케이터 옆/아래 표시 메시지.
91
+ - `type: SdBusyType` — 이 영역만의 인디케이터 종류(미지정 시 provider 기본값).
92
+ - `progressPercent: number` — 지정 시 상단 진행바(0~100). 결정형 작업 진행률에 사용.
93
+ - 사용: `<sd-busy-container [busy]="busyCount() > 0">...</sd-busy-container>` (단, `sd-base-container` 가 이미 내장).
105
94
 
106
95
  ## SdPrintProvider
107
96
 
108
- `class SdPrintProvider` 컴포넌트를 인쇄하거나 PDF 버퍼로 만드는 루트 프로바이더. 진행 전역 busy 증가.
109
-
110
- - `printAsync<T>(template: SdPrintInput<T>, options?: { size?: string; margin?: string }): Promise<void>` — 템플릿 컴포넌트를 body 에 부착·`@page` 스타일 주입 후 `window.print()`. 이미지 로드 대기.
111
- - `getPdfBufferAsync<T>(template, options?: { orientation?: "portrait"|"landscape"; pageSize?: string }): Promise<Uint8Array>` — `.page` 요소(없으면 전체)를 캔버스화해 jsPDF 로 PDF 버퍼 생성.
97
+ 인쇄 템플릿 컴포넌트를 동적 생성해 `window.print()` 하거나 PDF 버퍼로 변환. 호출 동안 `globalBusyCount` ±1.
112
98
 
113
- 타입:
114
- - `SdPrint` `{ initialized: Signal<boolean>; _optionalPrintInputs?: string }`. 인쇄 콘텐츠 구현.
115
- - `SdPrintInput<T, X>` — `{ type: Type<T>; inputs }`(`_optionalPrintInputs` 키는 optional).
99
+ - `printAsync<T extends SdPrint>(template: SdPrintInput<T>, options?: { size?: string; margin?: string }): Promise<void>` — 템플릿을 숨겨 붙인 뒤 `@media print` CSS 로 그것만 출력. `size` 기본 `"A4 auto"`, `margin` 기본 `"0"`. 이미지 로드 완료를 기다린 후 인쇄.
100
+ - `getPdfBufferAsync<T extends SdPrint>(template: SdPrintInput<T>, options?: { orientation?: "portrait"|"landscape"; pageSize?: string }): Promise<Uint8Array>` — `.page` 요소들(없으면 루트)을 캔버스로 렌더해 jsPDF 로 PDF 바이트 생성. `orientation` 기본 portrait, `pageSize` 기본 `"a4"`. 첨부/저장용 PDF 가 필요할 때.
101
+ - `SdPrint = { initialized: Signal<boolean>; _optionalPrintInputs?: string }` — 인쇄 템플릿 컴포넌트 규약. `initialized` true 가 되어야 인쇄 진행(데이터 로드 대기).
102
+ - `SdPrintInput<T> = { type: Type<T>; inputs: ... }` — 인쇄 컴포넌트 + input 값(`_optionalPrintInputs` 표시 키는 optional).
103
+ - 사용: `await this._sdPrint.printAsync({ type: OutboundPrintTemplate, inputs: { id } })`. 인쇄 템플릿 파일은 `<domain>.print-template.ts`(client-component.md).
@@ -1,64 +1,79 @@
1
1
  # @simplysm/angular — 라우팅 / 앱 구조(메뉴·권한)
2
2
 
3
- 해시 기반 SPA 내비게이션, 현재 페이지 코드·뷰타입·제목 신호, 앱 구조(AppStructureItem 트리)→메뉴/권한 변환, 권한 테이블 UI. 메뉴 컴포넌트(layout 군)·CRUD 컨테이너(crud 군)가신호·타입을 소비.
3
+ 라우터 링크·현재 페이지 식별·뷰 컨텍스트(page/control/modal)·이탈 가드, 그리고 앱 구조 트리에서 메뉴·권한을 파생하는 군. 화면 컴포넌트의 표준 시그널 `viewType`, 권한 가드 `injectPermsSignal`, 사이드바/탑바 메뉴가 군에 의존.
4
4
 
5
- ## SdNavigateWindowProvider
5
+ ## injectViewTypeSignal / SdViewType
6
6
 
7
- `class SdNavigateWindowProvider` — 해시 라우트를 새 창/탭으로 여는 루트 프로바이더.
7
+ ```ts
8
+ function injectViewTypeSignal(): Signal<SdViewType>
9
+ type SdViewType = "page" | "modal" | "control"
10
+ ```
8
11
 
9
- - `isWindow: boolean` 현재가 별도 모드인지(해시 쿼리 `window=true`).
10
- - `open(navigate, params?, features?)` — 라우트 열기. 창 모드이거나 `features` 있으면 `window.open` 으로 새 창(부모 unload 시 자동 닫힘), 아니면 새 탭(`_blank`).
12
+ 현재 컴포넌트가 어느 컨텍스트에서 동작 중인지 판정하는 signal. `"page"` = 라우팅 진입 화면, `"modal"` = 모달로 열림, `"control"` = 다른 화면 안에 임베드된 자식. 판정 기준: 모달 컨텍스트면 modal, 라우트 컴포넌트의 selector 가 현재 엘리먼트 태그와 일치하고 full/current 페이지 코드가 같으면 page, 그 외 control. 매뉴얼 표준 시그널 `viewType = injectViewTypeSignal()` 로 받아 `sd-base-container [viewType]` 에 전달.
11
13
 
12
- ## SdRouterLink
14
+ ## injectViewTitleSignal
13
15
 
14
- `[sdRouterLink]` 디렉티브클릭 라우팅. Ctrl/Shift 클릭은 창, 모드는 팝업으로 분기.
16
+ - `function injectViewTitleSignal(): Signal<string>` 현재 뷰의 표시 제목. 모달이면 모달 컴포넌트 `title`, 아니면 구조에서 현재 페이지 코드로 찾은 제목(`[상위 > 경로] 현재`). 못 찾으면 `""`. `sd-base-container` 가 page 탑바 제목에 사용.
15
17
 
16
- - `option = input<{ link; params?; window?: { width?; height? }; outletName?; queryParams? } | undefined>()`(alias `sdRouterLink`) — 이동 정보. `link`(경로), `params`(matrix 파라미터), `queryParams`, `outletName`(보조 outlet), `window`(팝업 크기). `undefined` 면 비활성.
18
+ ## injectFullPageCodeSignal / injectCurrentPageCodeSignal
17
19
 
18
- ## 페이지 코드·타이틀·뷰타입 신호
20
+ - `function injectFullPageCodeSignal(): Signal<string>` — 라우터 URL 전체에서 파생한 페이지 코드(`/` → `.` 로 합침, 앞 2세그먼트 제외, `;`/`?` 이후 제거). 메뉴 선택 상태·뷰 타입 판정에 사용.
21
+ - `function injectCurrentPageCodeSignal(): Signal<string> | undefined` — 현재 `ActivatedRoute` 기준 상대 페이지 코드. 라우트 컨텍스트 없으면 `undefined`. 중첩 라우트에서 자기 위치 코드가 필요할 때.
19
22
 
20
- 컴포넌트 생성자/필드에서 호출하는 inject 함수. 라우터·활성모달 컨텍스트에서 값 도출.
23
+ ## setupCanDeactivate
21
24
 
22
- - `injectFullPageCodeSignal(): Signal<string>`전체 라우트 경로를 `.` 연결 페이지 코드로 변환(예: `sale.order`). 메뉴 선택표시·제목 조회 기준.
23
- - `injectCurrentPageCodeSignal(): Signal<string> | undefined` — 현재 활성 라우트 세그먼트 기준 코드(중첩 라우트용). 라우트 없으면 `undefined`.
24
- - `injectViewTitleSignal(): Signal<string>` — 화면 제목. 활성 모달이면 모달 제목, 아니면 앱 구조에서 코드로 제목 조회.
25
- - `injectViewTypeSignal(): Signal<SdViewType>` — `"page"|"modal"|"control"`. 모달 컨텍스트면 modal, 라우트 페이지 컴포넌트면 page, 그 외 control. `SdViewType` 타입 동봉.
26
- - `setupCanDeactivate(fn: () => boolean): void` — 이탈 차단 등록. 모달이면 `canDeactivateFn`, 라우트면 route 의 `canDeactivate` 가드에 추가(파괴 시 해제). `fn()` 이 false 면 닫기/이탈 차단.
25
+ - `function setupCanDeactivate(fn: () => boolean): void`라우터 이탈/모달 닫기 시점에 `fn()` false 이탈/닫기를 차단. 모달 컨텍스트면 `SdActivatedModalProvider.canDeactivateFn` 에, 라우트 컨텍스트면 해당 route 의 `canDeactivate` 에 등록(파괴 시 자동 해제). detail 화면의 변경 가드 표준: `setupCanDeactivate(() => this._checkIgnoreChanges())`(client-component.md).
27
26
 
28
- ## 메뉴 유틸
27
+ ## SdRouterLink (`[sdRouterLink]`)
29
28
 
30
- `SdMenu` 라우터링크/선택판정으로 변환(layout 메뉴 컴포넌트가 사용).
29
+ 라우터 이동을 호스트 클릭에 붙이는 디렉티브. Ctrl/Shift 클릭·window 모드면 새 창/탭으로 분기.
31
30
 
32
- - `getMenuRouterLinkOption(menu: SdMenu): { link; queryParams } | undefined` 리프 메뉴면 `/home/{codeChain}` 링크 + 쿼리파라미터, 그룹/외부url 메뉴면 `undefined`.
33
- - `getIsMenuSelected(menu, fullPageCode, customFn?): boolean` — 선택 여부. `customFn` 있으면 결과, 없으면 `fullPageCode === codeChain.join(".")`.
31
+ - `sdRouterLink: { link: string; params?: Record<string,string>; window?: { width?; height? }; outletName?: string; queryParams?: Record<string,string> } | undefined` — 이동 옵션.
32
+ - `link` — 라우트 경로. `outletName` 있으면 named outlet 이동.
33
+ - `params` — 매트릭스 파라미터, `queryParams` — 쿼리 파라미터.
34
+ - `window` — Ctrl/Shift 클릭 또는 window 컨텍스트일 때 팝업 창 크기(기본 800x800).
35
+ - 미지정(`undefined`) 이면 클릭 무시 + 커서 기본. 메뉴 항목이 leaf 가 아닐 때 등.
36
+ - Alt+클릭은 무시. 사이드바/탑바 메뉴가 `getMenuRouterLinkOption(menu)` 결과를 이 입력에 바인딩.
34
37
 
35
- ## SdAppStructureProvider
38
+ ## SdNavigateWindowProvider
36
39
 
37
- `class SdAppStructureProvider<TModule>` 구조(메뉴·권한) 트리 보관·계산 루트 프로바이더.
40
+ - `isWindow: boolean`현재 문서가 `window=true` 열린 팝업인지(해시 파라미터 검사).
41
+ - `open(navigate: string, params?: Record<string,string>, features?: string): void` — 새 창/탭으로 라우트 열기. window 컨텍스트이거나 `features` 가 있으면 `window=true` 팝업으로(부모 unload 시 자동 close), 아니면 `_blank` 탭으로. `SdRouterLink` 내부 + 화면에서 보조 창을 띄울 때.
38
42
 
39
- - `usableModules: WritableSignal<TModule[] | undefined>` — 활성 모듈 목록(메뉴/권한 필터링 기준).
40
- - `permRecord: WritableSignal<Record<string, boolean> | undefined>` — 권한 레코드(`{코드.use: bool}`).
41
- - `items: WritableSignal<AppStructureItem<TModule>[]>` — 원본 구조. `initialize(items)` 로 설정.
42
- - `usableMenus: Signal<SdMenu[]>` — 모듈·권한 적용된 메뉴 트리.
43
- - `usableFlatMenus: Signal<SdFlatMenu<TModule>[]>` — 평탄화 메뉴 목록.
44
- - `getPermissionsByStructure(items, codeChain?)` / `getTitleByFullCode(fullCode)`(없으면 throw) / `findTitleByFullCode(fullCode)`(없으면 undefined, 결측 보존) / `getItemChainByFullCode(fullCode)` / `getPermsByFullCode(fullCodes, permKeys)`.
43
+ ## SdAppStructureProvider<TModule> / injectPermsSignal
45
44
 
46
- `injectPermsSignal<K>(viewCodes: string[], keys: K[]): Signal<K[]>` 특정 화면코드들의 권한 보유 키만 반환하는 computed.
45
+ 앱 구조 트리(`AppStructureItem[]`, `@simplysm/service-common`)에서 메뉴·권한을 파생하는 root provider. 화면은 보통 `injectPermsSignal` 직접 씀.
47
46
 
48
- ## SdAppStructureUtils
47
+ ```ts
48
+ function injectPermsSignal<K extends string>(viewCodes: string[], keys: K[]): Signal<K[]>
49
+ ```
49
50
 
50
- `abstract class SdAppStructureUtils` — 구조 변환 정적 유틸(프로바이더가 위임). `getMenus`/`getFlatMenus`/`getPermissions`/`getFlatPermissions`/`getTitleByFullCode`/`findTitleByFullCode`/`getItemChainByFullCode`/`getPermsByFullCode` 정적 메서드 제공. 모듈 활성/권한 use 여부로 필터링.
51
+ - `viewCodes`권한 path 목록(도메인 트리 좌표). `keys` 확인할 action 목록. 반환 signal 은 사용자가 보유한 action 들의 배열. 매뉴얼 패턴: `perms = injectPermsSignal(["inventory.outbound"], ["use","edit"])` `this.perms().includes("use")` 인라인 가드.
52
+ - provider 멤버:
53
+ - `usableModules: WritableSignal<TModule[] | undefined>` / `permRecord: WritableSignal<Record<string, boolean> | undefined>` — 인증 후 주입하는 활성 모듈·권한 레코드. 메뉴/권한 계산의 입력.
54
+ - `items: WritableSignal<AppStructureItem<TModule>[]>` / `initialize(items): void` — 앱 구조 트리 세팅.
55
+ - `usableMenus: Signal<SdMenu[]>` — 권한·모듈 필터를 적용한 트리형 메뉴(사이드바용).
56
+ - `usableFlatMenus: Signal<SdFlatMenu<TModule>[]>` — 평탄화된 메뉴 목록(검색/전체메뉴용).
57
+ - `getPermsByFullCode<K>(fullCodes, permKeys): K[]` — 코드 목록에 대해 보유 action 계산(`injectPermsSignal` 내부). 권한 정의 자체가 없는 항목은 모든 action 허용으로 간주.
58
+ - `getPermissionsByStructure(items, codeChain?)` — 권한표(`sd-permission-table`)용 `SdPermission` 트리 생성.
59
+ - `findTitleByFullCode(fullCode): string | undefined` / `getTitleByFullCode(fullCode): string`(못 찾으면 throw) / `getItemChainByFullCode(fullCode)` — 코드로 제목·항목 체인 조회.
51
60
 
52
- ## 타입
61
+ ## SdAppStructureUtils (static 유틸)
53
62
 
54
- - `SdMenu` 메뉴 노드. `title`/`codeChain: string[]`/`url?`/`icon?`/`children?`.
55
- - `SdFlatMenu<TModule>` — 평탄 메뉴. `titleChain`/`codeChain`/`modulesChain`.
56
- - `SdPermission<TModule>` — 권한 노드. `title`/`codeChain`/`modules`/`perms: ("use"|"edit")[] | undefined`/`children`.
63
+ `SdAppStructureProvider` 내부적으로 쓰는 순수 함수 모음(abstract 클래스의 static). 트리 → 메뉴/권한/제목 변환을 provider 밖에서 직접 할 때만 사용.
57
64
 
58
- ## SdPermissionTable
65
+ - `getMenus(items, codeChain, usableModules, permRecord): SdMenu[]` — 모듈·`use` 권한 필터 적용 트리 메뉴. `isNotMenu` 항목·빈 그룹 제외.
66
+ - `getFlatMenus(items, usableModules, permRecord): SdFlatMenu[]` — BFS 평탄화 메뉴.
67
+ - `getPermissions(items, codeChain, usableModules): SdPermission[]` — 권한표용 트리(leaf 의 `perms`/`subPerms` 포함).
68
+ - `getFlatPermissions(items, usableModules)` / `findTitleByFullCode` / `getTitleByFullCode` / `getItemChainByFullCode` / `getPermsByFullCode` — provider 동명 메서드의 구현.
59
69
 
60
- `<sd-permission-table>` — 권한 트리 체크박스 표(use/edit 토글, 상하위 연동). 제네릭 `<TModule>`.
70
+ ## menu-utils
71
+
72
+ - `getMenuRouterLinkOption(menu: SdMenu): { link: string; queryParams: Record<string,string> | undefined } | undefined` — 메뉴를 `sdRouterLink` 옵션으로 변환. children 이 있거나 `url`(외부 링크) 이면 `undefined`(이동 불가 = leaf 아님). 그 외 `codeChain` 으로 `/home/<코드/...>` 링크 + 쿼리 분리.
73
+ - `getIsMenuSelected(menu: SdMenu, fullPageCode: string | undefined, customFn?: (menu) => boolean): boolean` — 메뉴 선택 여부. `customFn` 있으면 위임, 없으면 `fullPageCode === menu.codeChain.join(".")`.
74
+
75
+ ## 타입
61
76
 
62
- - `value = model<Record<string, boolean>>({})` 권한 체크 상태(`{코드.use|edit: bool}`). use 해제 edit 자동 해제, edit use 체크 후에만.
63
- - `items = input<SdPermission<TModule>[]>([])` — 권한 트리(`getPermissionsByStructure` 결과).
64
- - `disabled = input(false)` — 전체 비활성.
77
+ - `SdMenu = { title; codeChain: string[]; url?; icon?; children?: SdMenu[] }` 트리 메뉴 항목. `url` 있으면 외부 링크, `children` 있으면 그룹.
78
+ - `SdFlatMenu<TModule> = { titleChain: string[]; codeChain: string[]; modulesChain: TModule[][] }` — 평탄 메뉴(경로 누적).
79
+ - `SdPermission<TModule> = { title; codeChain; modules; perms: ("use"|"edit")[] | undefined; children }` — 권한표 노드. `perms` 가 undefined 면 그룹(권한 없음). `sd-permission-table` 의 `items` 입력 타입.
@@ -1,43 +1,68 @@
1
- # @simplysm/angular — 선택·정렬·확장 매니저
1
+ # @simplysm/angular — selection/sorting/expanding 매니저 (use* 컴포저블)
2
2
 
3
- 리스트/시트류 컴포넌트 내부에서 signal 바인딩으로 선택·다중정렬·트리확장 상태를 다루는 컴포저블 함수. 컴포넌트 클래스 필드 초기화 호출하며, signal 입력(items/selectedKeys/sorts 등)과 메서드 묶음을 반환. `sd-sheet` 모두 사용.
3
+ 커스텀 목록 컴포넌트에서 선택·정렬·트리펼침 상태 로직을 signal 기반으로 합성하는 함수 컴포저블 군. `sd-sheet` 이들을 조합해 만들어졌고, 직접 그리드/리스트를 만들 같은 로직을 재사용. 모두 함수 호출로 signal·메서드 묶음을 반환(컴포넌트 필드에 보관).
4
4
 
5
- ## useSelectionManager
5
+ ## useSelectionManager<TItem, TKey>
6
6
 
7
- `useSelectionManager<TItem, TKey>(options): { ... }` — 단일/다중 선택 상태 관리.
7
+ 선택(single/multi)·전체선택·선택 가능 여부 로직.
8
8
 
9
- options(모두 Signal/WritableSignal):
10
- - `displayItems: Signal<TItem[]>` 현재 표시 항목.
11
- - `selectedKeys: WritableSignal<TKey[]>` — 선택 키 배열(양방향).
12
- - `selectMode: Signal<"single"|"multi"|undefined>` — 선택 모드. `undefined` 면 선택 비활성.
13
- - `getItemSelectableFn: Signal<((item) => boolean | string) | undefined>` — 선택 가능 판정. `string` 이면 비활성 + 사유.
14
- - `trackByFn: Signal<(item, index) => TKey>` 추출.
9
+ ```ts
10
+ useSelectionManager<TItem, TKey>(options: {
11
+ displayItems: Signal<TItem[]>;
12
+ selectedKeys: WritableSignal<TKey[]>;
13
+ selectMode: Signal<"single" | "multi" | undefined>;
14
+ getItemSelectableFn: Signal<((item: TItem) => boolean | string) | undefined>;
15
+ trackByFn: Signal<(item: TItem, index: number) => TKey>;
16
+ }): { ... }
17
+ ```
15
18
 
16
- 반환: `hasSelectable`/`isAllSelected: Signal<boolean>`, `getSelectable(item): true | string | undefined`, `getCanChangeFn(item): () => boolean`, `select`/`deselect`/`toggle`/`toggleAll`/`isSelected(item)`. single 모드 select 단일 키로 교체, multi 추가/제거.
19
+ - `options.displayItems` 현재 표시 항목. `selectedKeys` 선택 키(WritableSignal, 키는 `trackByFn` 반환값). `selectMode` — 모드(undefined 선택 비활성). `getItemSelectableFn` 행별 선택 가능: `true`=가능, `false`=불가, 문자열=불가+사유. `trackByFn` — 항목→키.
20
+ - 반환:
21
+ - `hasSelectable: Signal<boolean>` — 선택 모드가 켜졌는지.
22
+ - `isAllSelected: Signal<boolean>` — 선택 가능한 항목이 모두 선택됐는지(전체선택 체크 상태).
23
+ - `getSelectable(item): true | string | undefined` — 항목 선택 가능 여부(문자열=사유 툴팁).
24
+ - `getCanChangeFn(item): () => boolean` — 체크박스 `canChangeFn` 에 넘길 가드.
25
+ - `select`/`deselect`/`toggle(item)` — 선택 조작(single 은 단일 키로 대체).
26
+ - `toggleAll()` — 선택 가능 항목 전체 토글.
27
+ - `isSelected(item): boolean`.
28
+ - 키 비교는 `===` 후 `obj.equal`(복합 키 지원).
17
29
 
18
30
  ## useSortingManager
19
31
 
20
- `useSortingManager(options: { sorts: WritableSignal<SortingDef[]> }): { ... }` 다중 정렬 토글·적용.
32
+ 정렬 상태(다중 컬럼) 토글·적용. `sd-sheet` `sorts` `SortingDef` 공유.
21
33
 
22
- - `options.sorts` — 정렬 정의 배열(양방향). `SortingDef = { key: string; desc: boolean }`.
23
- - 반환:
24
- - `defMap: Signal<Map<key, { indexText?; desc }>>` — 컬럼별 정렬 상태(다중 시 순번 indexText).
25
- - `toggle(key, multiple)` — 정렬 토글. `multiple=true`(Shift) 면 누적, 동일 키 재클릭 시 asc→desc→해제 순환.
26
- - `sort<T>(items): T[]` — 현재 정렬 정의로 배열 정렬(null 은 먼저, 문자열은 localeCompare).
34
+ ```ts
35
+ useSortingManager(options: { sorts: WritableSignal<SortingDef[]> }): {
36
+ defMap: Signal<Map<string, { indexText?: string; desc: boolean }>>;
37
+ toggle(key: string, multiple: boolean): void;
38
+ sort<T>(items: T[]): T[];
39
+ }
40
+ ```
41
+
42
+ - `SortingDef = { key: string; desc: boolean }` — 한 정렬 기준. `key`=컬럼 키, `desc`=내림차순 여부.
43
+ - `defMap` — 키별 정렬 상태(헤더 아이콘 표시용; `indexText` 는 다중 정렬 시 순번).
44
+ - `toggle(key, multiple)` — 정렬 토글. 한 키를 누를 때마다 없음→오름차순→내림차순→해제 순환. `multiple`(Shift) true 면 기존 정렬 유지하고 추가, false 면 단일 정렬로 대체.
45
+ - `sort<T>(items)` — 현재 정렬을 적용한 새 배열 반환. null 은 가장 앞, 문자열은 localeCompare, 숫자는 수치 비교. 클라이언트 정렬 시 사용.
46
+
47
+ ## useExpandingManager<T>
27
48
 
28
- ## useExpandingManager
49
+ 트리 항목 펼침/접힘 + 표시 항목 평탄화.
29
50
 
30
- `useExpandingManager<T>(binding): { ... }` — 트리 펼침/접힘 상태 관리.
51
+ ```ts
52
+ useExpandingManager<T>(binding: {
53
+ items: Signal<T[]>;
54
+ expandedItems: WritableSignal<T[]>;
55
+ getChildrenFn: Signal<((item: T, index: number) => T[] | undefined) | undefined>;
56
+ sort: (items: T[]) => T[];
57
+ }): { ... }
58
+ ```
31
59
 
32
- binding:
33
- - `items: Signal<T[]>` — 루트 항목.
34
- - `expandedItems: WritableSignal<T[]>` — 펼친 항목(양방향).
35
- - `getChildrenFn: Signal<((item, index) => T[] | undefined) | undefined>` — 자식 추출.
36
- - `sort: (items) => T[]` — 각 레벨 정렬 함수.
60
+ - `binding.items` — 루트 항목. `expandedItems` — 펼쳐진 항목(WritableSignal). `getChildrenFn` — 자식 조회(undefined 면 자식 없음). `sort` — 각 레벨 정렬 함수(보통 `useSortingManager.sort`).
37
61
  - 반환:
38
- - `displayItems: Signal<T[]>` — 펼침 반영된 평탄 표시 목록.
39
- - `hasExpandable`/`isAllExpanded: Signal<boolean>`.
40
- - `toggle(item)`/`toggleAll()` — 펼침 토글.
41
- - `isVisible(item): boolean` — 모든 조상이 펼쳐졌는지.
42
- - `def(item): ExpandItemDef<T>`항목 메타(없으면 throw).
43
- - `ExpandItemDef<T>` — `{ item; parentDef?; hasChildren; depth }`.
62
+ - `displayItems: Signal<T[]>` — 펼침 상태를 반영해 평탄화·정렬된 표시 항목.
63
+ - `hasExpandable: Signal<boolean>` — 펼칠 수 있는 항목이 있는지(토글 컬럼 표시 기준).
64
+ - `isAllExpanded: Signal<boolean>` 전체 펼침 상태.
65
+ - `toggle(item)` / `toggleAll()`펼침 토글.
66
+ - `isVisible(item): boolean`조상이 모두 펼쳐져 보이는지.
67
+ - `def(item): ExpandItemDef<T>` — 항목 메타(못 찾으면 throw).
68
+ - `ExpandItemDef<T> = { item: T; parentDef: ExpandItemDef<T> | undefined; hasChildren: boolean; depth: number }` — 항목의 부모·자식유무·깊이. 들여쓰기·토글 렌더에 사용.
@@ -1,70 +1,74 @@
1
- # @simplysm/angular — 공유 데이터(shared-data)
2
-
3
- 서버 마스터데이터(코드·분류 등)를 클라이언트에 캐시하고 서버 변경 이벤트로 동기화하며, 선택 UI 로 노출. `SdSharedDataProvider` 를 상속해 데이터셋을 등록하고, 선택 컨트롤(select/select-button/select-list)이 그 핸들의 items 를 소비.
4
-
5
- ## SdSharedDataProvider
6
-
7
- `abstract class SdSharedDataProvider<T extends Record<string, SharedDataBase<...>>>` — 공유 데이터 등록·로드·동기화 추상 프로바이더. 앱별로 상속해 `initialize()` 에서 `register` 호출.
8
-
9
- - `loadingCount: WritableSignal<number>` — 로딩 중 데이터 수.
10
- - `abstract initialize(): void` — 앱이 구현. 보통 여러 `register` 호출.
11
- - `register<K>(name, info: SharedDataInfo<T[K]>)` — 데이터셋 등록(재호출 시 갱신·리스너 재설정).
12
- - `getHandle<K>(name): SharedDataHandle<T[K]>` — 핸들 반환. 최초 접근 시 lazy 로드·이벤트 리스너 등록. 미등록이면 throw.
13
- - `emitAsync<K>(name, changeKeys?)` — 변경 이벤트 발행. `changeKeys` 없으면 전체 리로드 신호, 있으면 해당 키만 부분 갱신.
14
- - `wait(): Promise<void>` — `loadingCount<=0` 까지 대기(CRUD 기반 컨테이너가 초기 로드 동기화에 사용).
15
-
16
- 타입:
17
- - `SharedDataBase<TKey>` — 공유 항목 기본형. `__valueKey`(고유키), `__searchText`(검색대상), `__isHidden`(숨김), `__parentKey?`(트리 부모키).
18
- - `SharedDataInfo<T>` — 등록 정보. `serviceKey`(서비스 클라이언트 키), `getter: (changeKeys?) => Promise<T[]>`(전체/부분 로더), `filter?`(이벤트 필터), `orderBy?`(부분 갱신 후 정렬 키).
19
- - `SharedDataHandle<T>` — `{ items: Signal<T[]>; get(key) => T | undefined }`.
20
- - `SdSharedDataChangeEvent` — 변경 동기화에 쓰는 `defineEvent` 정의(서버↔클라이언트).
21
-
22
- ## SdSharedDataSelect
23
-
24
- `<sd-shared-data-select>` — 공유데이터 드롭다운 선택(검색·트리·모달 연계). 제네릭 `<TItem, TMode, TModal>`.
25
-
26
- - `value = model<...>()` — 선택 키(single=단일, multi=배열). 값 타입은 `TItem["__valueKey"] | undefined`.
27
- - `items = input.required<TItem[]>()` — 공유 항목 배열(핸들의 items).
28
- - `selectMode: TMode("single"|"multi")` — 선택 모드. 기본 `"single"`.
29
- - `disabled`/`required`/`inset`/`inline`/`size` — 공통.
30
- - `useUndefined` — multi 에서도 "미지정" 항목 노출.
31
- - `filterFn?: (item, index, ...params) => boolean` / `filterFnParams?: any[]` — 추가 필터.
32
- - `modal?: SdSelectModalInfo<TModal>` — 검색 아이콘 → 선택 모달.
33
- - `editModal?: SdModalInfo<SdModalContentDef<boolean>>` — 편집 아이콘 → 편집 모달.
34
- - `selectClass`/`multiSelectionDisplayDirection: "vertical"` — 표시 옵션.
35
- - `getIsHiddenFn?: (item, index) => boolean` — 숨김 판정(기본 `__isHidden`, 숨김 항목은 취소선).
36
- - `getSearchTextFn?: (item, index) => string` — 검색 대상 텍스트(기본 `__searchText`).
37
- - `displayOrderByFn?: (item) => 정렬키` — 표시 정렬.
38
- - 항목 템플릿은 `<ng-template [itemOf]>`, 미지정 표시는 `#undefinedTpl`. `__parentKey` 있으면 트리.
39
-
40
- ## SdSharedDataSelectButton
41
-
42
- `<sd-shared-data-select-button>` — 모달 선택 버튼 + 선택값 인라인 표시. 제네릭 `<TItem, TMode, TModal>`.
43
-
44
- - `value = model<...>()` — 선택 키(single/multi).
45
- - `items = input<TItem[]>([])` — 표시 라벨 조회용 전체 항목.
46
- - `modal = input.required<SdSelectModalInfo<TModal>>()` — 선택 모달.
47
- - `selectMode: TMode` — 기본 `"single"`.
48
- - `disabled`/`required`/`inset`/`size` — 공통.
49
- - 선택 항목 라벨은 `<ng-template [itemOf]>`(required)로 렌더.
50
-
51
- ## SdSharedDataSelectList
52
-
53
- `<sd-shared-data-select-list>` — 검색·페이징되는 리스트형 단일 선택(패널 영역용). 제네릭 `<TItem, TModal>`.
54
-
55
- - `selectedItem = model<TItem>()` — 선택 항목(객체). `canChangeFn?: (item) => boolean|Promise<boolean>` 로 변경 차단.
56
- - `items = input.required<TItem[]>()` — 항목 배열. `__isHidden` 항목은 제외.
57
- - `selectedIcon?` — 선택 표시 아이콘.
58
- - `useUndefined` — "미지정" 항목 표시.
59
- - `filterFn?: (item, index) => boolean` — 추가 필터.
60
- - `modal?: SdSelectModalInfo<TModal>` — 외부 검색 모달 버튼.
61
- - `header?` — 패널 헤더 텍스트.
62
- - `pageItemCount?` — 페이지당 항목 수(지정 시 페이지네이션).
63
- - 슬롯: `#headerTpl`/`#filterTpl`(기본 검색 textfield 대체)/`[itemOf]` 항목/`#undefinedTpl`.
1
+ # @simplysm/angular — 공유 마스터 데이터 + 선택 컨트롤
64
2
 
65
- ## matchesSearchText
3
+ 고객사·품목 등 자주 참조하는 마스터 데이터를 한 번 등록해 어느 화면에서든 공유 signal 로 쓰고, 그 데이터를 선택하는 드롭다운/버튼/리스트 컨트롤을 제공하는 군. 등록·항목 추가 절차는 `client-shared-data.md` 참조.
4
+
5
+ ## SdSharedDataProvider<T> (abstract)
6
+
7
+ 마스터 데이터를 이름별로 등록·로드·이벤트 동기화하는 root provider. 앱은 이걸 상속한 `AppSharedDataProvider` 를 만들고 `useSharedSignal` 헬퍼를 함께 export(client-shared-data.md).
8
+
9
+ - `abstract initialize(): void` — 여기서 `register(name, info)` 로 항목 등록(앱이 구현).
10
+ - `register<K>(name: K, info: SharedDataInfo<T[K]>): void` — 항목 등록. 재호출 시 기존 리스너 정리 + generation 증가로 이전 결과 무시 후 재로드.
11
+ - `getHandle<K>(name: K): SharedDataHandle<T[K]>` — 항목 핸들 반환(첫 접근 시 lazy 로드 + 변경 이벤트 리스너 등록). 미등록 이름이면 throw. `useSharedSignal` 이 이걸 감쌈.
12
+ - `emitAsync<K>(name: K, changeKeys?: (string|number)[]): Promise<void>` — 변경 브로드캐스트. `changeKeys` 주면 해당 키만 부분 갱신, 없으면 전체 리로드(다른 클라이언트 포함).
13
+ - `wait(): Promise<void>` — 진행 중 로드가 끝날 때까지 대기. `sd-base-container` 가 ready 전에 호출.
14
+ - `loadingCount: WritableSignal<number>` — 진행 중 로드 수.
15
+
16
+ ### 타입
17
+
18
+ - `SharedDataBase<TKey extends string|number>` — 모든 공유 항목이 상속할 베이스. 매직 필드: `__valueKey: TKey`(항목 키), `__searchText: string`(검색용 텍스트), `__isHidden: boolean`(숨김), `__parentKey?: TKey`(트리 부모). getter 의 select 결과에 빠짐없이 포함.
19
+ - `SharedDataInfo<T>` — 등록 정보. `serviceKey: string`(이벤트 채널), `getter: (changeKeys?) => Promise<T[]>`(조회; changeKeys 주면 부분), `filter?: unknown`(이벤트 필터 매칭), `orderBy?: (item) => string|number|DateOnly|DateTime|Time|undefined`(정렬 키).
20
+ - `SharedDataHandle<T>` — `{ items: Signal<T[]>; get(key): T | undefined }`. 화면이 `useSharedSignal(name)` 으로 받아 `.items()`·`.get(id)` 사용.
21
+ - `SdSharedDataChangeEvent` — 변경 동기화에 쓰이는 `defineEvent`. payload `{ name; filter }`, data `(string|number)[] | undefined`.
22
+
23
+ 사용(화면): `sharedCustomers = useSharedSignal("고객사"); sharedCustomers.items(); sharedCustomers.get(id)`.
24
+
25
+ ## 선택 컨트롤
26
+
27
+ 공유 데이터(또는 `SharedDataBase` 호환 배열)를 항목으로 받아 선택. 매직 필드(`__searchText`/`__isHidden`/`__parentKey`)를 자동 활용(검색·숨김·트리).
28
+
29
+ ### SdSharedDataSelect (`sd-shared-data-select`)
66
30
 
67
- `matchesSearchText(itemText: string, searchQuery: string | undefined): boolean` — 공백 분리 AND 부분일치 검색 매처(소문자 비교). 위 선택 컨트롤이 검색에 사용.
31
+ 드롭다운 셀렉트(검색창·트리·미지정 항목·모달 연동 내장).
32
+
33
+ - `value: model<...>` — 선택 키(single) 또는 키 배열(multi). 미지정은 `undefined`.
34
+ - `items: input.required<TItem[]>` — 공유 항목 배열(`SharedDataBase` 상속).
35
+ - `selectMode: "single"|"multi"` — 선택 모드(기본 single).
36
+ - `required: boolean` — 빈 값이면 invalid.
37
+ - `useUndefined: boolean` — multi 에서도 "미지정" 항목 노출(single 은 required 아니면 자동 노출).
38
+ - `filterFn: (item, index, ...params) => boolean` + `filterFnParams: any[]` — 표시 항목 필터.
39
+ - `getIsHiddenFn: (item, index) => boolean` — 숨김 판정(기본 `__isHidden`; 숨김 항목은 취소선 + 검색 시에만 표시).
40
+ - `getSearchTextFn: (item, index) => string` — 검색 대상 텍스트(기본 `__searchText`).
41
+ - `displayOrderByFn: (item) => ...` — 표시 정렬 키.
42
+ - `modal: SdSelectModalInfo<TModal>` — 검색 버튼으로 띄울 선택 모달. `editModal: SdModalInfo<...>` — 편집 버튼 모달.
43
+ - `multiSelectionDisplayDirection: "vertical"` — multi 표시 세로 나열.
44
+ - `disabled`/`inset`/`inline`/`size`/`selectClass` — 공통/스타일.
45
+ - 항목 템플릿: `<ng-template [itemOf]="items()" let-item="item">`, 미지정 표시 `#undefinedTpl`.
46
+ - 사용: `<sd-shared-data-select [items]="sharedCustomers.items()" [(value)]="data().customerId"><ng-template [itemOf]="sharedCustomers.items()" let-item="item">{{ item.name }}</ng-template></sd-shared-data-select>`.
47
+
48
+ ### SdSharedDataSelectButton (`sd-shared-data-select-button`)
49
+
50
+ 값 표시 + 모달 검색 버튼(드롭다운 없이 모달 전용). 항목 수가 많아 드롭다운이 부적합할 때.
51
+
52
+ - `value: model<...>` — 선택 키/키배열.
53
+ - `items: TItem[]` — 표시명 매핑용 항목 배열.
54
+ - `modal: input.required<SdSelectModalInfo<TModal>>` — 띄울 선택 모달.
55
+ - `selectMode: "single"|"multi"` / `disabled` / `required` / `inset` / `size` — 공통.
56
+ - 선택 항목 표시 템플릿: `<ng-template [itemOf]>`(필수).
57
+
58
+ ### SdSharedDataSelectList (`sd-shared-data-select-list`)
59
+
60
+ 검색창 + 리스트로 단건 선택(좌측 마스터 리스트 패널 등). `flex-column fill`.
61
+
62
+ - `selectedItem: model<TItem>` — 선택된 항목(키 아닌 항목 객체). `canChangeFn: (item|undefined) => boolean|Promise<boolean>` — 변경 가드.
63
+ - `items: input.required<TItem[]>` — 항목 배열(`__isHidden` 항목 자동 제외).
64
+ - `useUndefined: boolean` — "미지정" 항목 노출.
65
+ - `filterFn: (item, index) => boolean` — 추가 필터.
66
+ - `selectedIcon: string` — 선택 표시 아이콘.
67
+ - `pageItemCount: number` — 페이지당 항목 수(지정 시 페이지네이션).
68
+ - `modal: SdSelectModalInfo<TModal>` — 우상단 외부 링크로 띄울 모달.
69
+ - `header: string` — 상단 헤더 텍스트.
70
+ - 템플릿: `#headerTpl`(헤더 우측), `#filterTpl`(검색창 대체), `<ng-template [itemOf]>`(항목), `#undefinedTpl`(미지정).
71
+
72
+ ## matchesSearchText
68
73
 
69
- - `itemText` — 항목의 검색 대상 텍스트.
70
- - `searchQuery` — 검색어. 빈/`undefined` 면 모두 매치(true). 각 단어가 모두 포함돼야 매치.
74
+ - `function matchesSearchText(itemText: string, searchQuery: string | undefined): boolean` — 공백 구분 다중 검색어 AND 매칭(대소문자 무시). 빈 쿼리면 true. 위 선택 컨트롤들이 내부 검색에 사용. 커스텀 목록에서 동일 검색 동작이 필요할 때 직접 호출.