@simplysm/angular 14.0.48 → 14.0.51

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 (144) hide show
  1. package/README.md +234 -226
  2. package/dist/controls/select/sd-select.js +3 -3
  3. package/dist/index.d.ts +0 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +0 -2
  6. package/dist/layout/dock/sd-dock-container.js +1 -1
  7. package/dist/styles.css +9 -0
  8. package/docs/bootstrap/provide-sd-angular.md +37 -0
  9. package/docs/bootstrap/sd-angular-config-provider.md +16 -0
  10. package/docs/directives/sd-command-directive.md +30 -0
  11. package/docs/directives/sd-events.md +25 -0
  12. package/docs/directives/sd-intersection-directive.md +36 -0
  13. package/docs/directives/sd-invalid.md +24 -0
  14. package/docs/directives/sd-resize-directive.md +42 -0
  15. package/docs/directives/sd-ripple.md +23 -0
  16. package/docs/directives/sd-router-link.md +38 -0
  17. package/docs/directives/sd-show-effect.md +18 -0
  18. package/docs/directives/sd-typed-template.md +69 -0
  19. package/docs/features/sd-address-search-modal.md +50 -0
  20. package/docs/features/sd-permission-table.md +20 -0
  21. package/docs/features/sd-shared-data-components.md +158 -0
  22. package/docs/features/sd-tiptap-editor.md +26 -0
  23. package/docs/{pipes.md → pipes/format-pipe.md} +14 -5
  24. package/docs/plugins/sd-global-error-handler.md +23 -0
  25. package/docs/{plugins.md → plugins/sd-option-event-plugin.md} +9 -12
  26. package/docs/provider-types/sd-menu.md +65 -0
  27. package/docs/provider-types/sd-modal-content-def.md +148 -0
  28. package/docs/provider-types/sd-toast-content-def.md +73 -0
  29. package/docs/provider-types/shared-data-base.md +59 -0
  30. package/docs/providers/sd-activated-modal-provider.md +34 -0
  31. package/docs/providers/sd-app-structure-provider.md +81 -0
  32. package/docs/providers/sd-busy-provider.md +18 -0
  33. package/docs/providers/sd-file-dialog-provider.md +40 -0
  34. package/docs/providers/sd-local-storage-provider.md +20 -0
  35. package/docs/providers/sd-modal-provider.md +67 -0
  36. package/docs/providers/sd-navigate-window-provider.md +18 -0
  37. package/docs/providers/sd-print-provider.md +25 -0
  38. package/docs/providers/sd-service-client-factory-provider.md +43 -0
  39. package/docs/providers/sd-shared-data-provider.md +64 -0
  40. package/docs/providers/sd-system-config-provider.md +46 -0
  41. package/docs/providers/sd-system-log-provider.md +18 -0
  42. package/docs/providers/sd-theme-provider.md +38 -0
  43. package/docs/providers/sd-toast-provider.md +65 -0
  44. package/docs/recipes/_common-rules.md +244 -0
  45. package/docs/recipes/crud-detail/extension-a-edit-save.md +230 -0
  46. package/docs/recipes/crud-detail/extension-b-delete-restore.md +142 -0
  47. package/docs/recipes/crud-detail/extension-c-modal-view.md +214 -0
  48. package/docs/recipes/crud-detail/extension-d-control-view.md +103 -0
  49. package/docs/recipes/crud-detail/extension-e-auxiliary.md +87 -0
  50. package/docs/recipes/crud-detail/extension-f-complex-detail.md +234 -0
  51. package/docs/recipes/crud-detail.md +215 -722
  52. package/docs/recipes/crud-list/extension-a-inline-edit.md +410 -0
  53. package/docs/recipes/crud-list/extension-b-selection.md +226 -0
  54. package/docs/recipes/crud-list/extension-c-inline-delete.md +87 -0
  55. package/docs/recipes/crud-list/extension-d-select-modal.md +207 -0
  56. package/docs/recipes/crud-list/extension-e-readonly-modal.md +165 -0
  57. package/docs/recipes/crud-list/extension-f-modal-edit.md +177 -0
  58. package/docs/recipes/crud-list/extension-g-excel.md +157 -0
  59. package/docs/recipes/crud-list.md +293 -626
  60. package/docs/recipes/data-select-button.md +194 -101
  61. package/docs/recipes/page-modal-container.md +168 -86
  62. package/docs/styling/classes.md +149 -0
  63. package/docs/styling/mixins.md +100 -0
  64. package/docs/styling/themes.md +35 -0
  65. package/docs/styling/variables.md +147 -0
  66. package/docs/{type-utilities.md → type-utilities/directive-input-signals.md} +17 -35
  67. package/docs/ui-data/sd-list.md +37 -0
  68. package/docs/ui-data/sd-sheet.md +227 -0
  69. package/docs/ui-form/sd-additional-button.md +26 -0
  70. package/docs/ui-form/sd-anchor.md +31 -0
  71. package/docs/ui-form/sd-button.md +105 -0
  72. package/docs/ui-form/sd-checkbox-group.md +39 -0
  73. package/docs/ui-form/sd-checkbox.md +81 -0
  74. package/docs/ui-form/sd-date-range-picker.md +27 -0
  75. package/docs/ui-form/sd-form.md +89 -0
  76. package/docs/ui-form/sd-modal-select-button.md +54 -0
  77. package/docs/ui-form/sd-numpad.md +26 -0
  78. package/docs/ui-form/sd-range.md +26 -0
  79. package/docs/ui-form/sd-select.md +68 -0
  80. package/docs/ui-form/sd-shared-data-select.md +52 -0
  81. package/docs/ui-form/sd-state-preset.md +37 -0
  82. package/docs/ui-form/sd-switch.md +27 -0
  83. package/docs/ui-form/sd-textarea.md +33 -0
  84. package/docs/ui-form/sd-textfield.md +145 -0
  85. package/docs/ui-layout/sd-dock-container.md +64 -0
  86. package/docs/ui-layout/sd-dock.md +37 -0
  87. package/docs/ui-layout/sd-gap.md +26 -0
  88. package/docs/{ui-layout.md → ui-layout/sd-kanban-board.md} +41 -85
  89. package/docs/ui-layout/sd-kanban-lane.md +34 -0
  90. package/docs/ui-layout/sd-kanban.md +29 -0
  91. package/docs/ui-navigation/sd-collapse.md +35 -0
  92. package/docs/ui-navigation/sd-pagination.md +26 -0
  93. package/docs/ui-navigation/sd-sidebar-container.md +49 -0
  94. package/docs/ui-navigation/sd-sidebar-menu.md +22 -0
  95. package/docs/ui-navigation/sd-sidebar-user.md +43 -0
  96. package/docs/ui-navigation/sd-tab.md +51 -0
  97. package/docs/ui-navigation/sd-topbar-container.md +97 -0
  98. package/docs/ui-navigation/sd-topbar-menu.md +23 -0
  99. package/docs/ui-navigation/sd-topbar-user.md +38 -0
  100. package/docs/ui-navigation/sd-topbar.md +30 -0
  101. package/docs/ui-overlay/sd-busy-container.md +69 -0
  102. package/docs/ui-overlay/sd-confirm-modal.md +30 -0
  103. package/docs/ui-overlay/sd-dropdown.md +40 -0
  104. package/docs/ui-overlay/sd-modal.md +34 -0
  105. package/docs/ui-overlay/sd-prompt-modal.md +30 -0
  106. package/docs/ui-overlay/sd-toast.md +35 -0
  107. package/docs/ui-visual/sd-barcode.md +36 -0
  108. package/docs/ui-visual/sd-calendar.md +34 -0
  109. package/docs/ui-visual/sd-echarts.md +32 -0
  110. package/docs/ui-visual/sd-label.md +24 -0
  111. package/docs/ui-visual/sd-note.md +23 -0
  112. package/docs/ui-visual/sd-progress.md +23 -0
  113. package/docs/utils/inject-routing-signals.md +161 -0
  114. package/docs/utils/inject-sd-system-config-resource.md +35 -0
  115. package/docs/utils/mark.md +43 -0
  116. package/docs/utils/selection-managers.md +96 -0
  117. package/docs/utils/set-safe-style.md +19 -0
  118. package/docs/utils/setup-functions.md +93 -0
  119. package/package.json +7 -7
  120. package/scss/commons/_styles.scss +12 -0
  121. package/src/controls/select/sd-select.ts +3 -3
  122. package/src/core/modal/sd-modal.provider.ts +1 -1
  123. package/src/core/modal/sd-modal.ts +1 -1
  124. package/src/core/routing/menu-utils.ts +1 -1
  125. package/src/core/shared-data/sd-shared-data.provider.ts +7 -7
  126. package/src/data/shared-data/sd-shared-data-select.ts +2 -2
  127. package/src/index.ts +0 -3
  128. package/src/layout/dock/sd-dock-container.ts +1 -1
  129. package/dist/data/getOrmDataEditToastErrorMessage.d.ts +0 -2
  130. package/dist/data/getOrmDataEditToastErrorMessage.d.ts.map +0 -1
  131. package/dist/data/getOrmDataEditToastErrorMessage.js +0 -8
  132. package/docs/bootstrap.md +0 -38
  133. package/docs/directives.md +0 -236
  134. package/docs/features.md +0 -169
  135. package/docs/provider-types.md +0 -283
  136. package/docs/providers.md +0 -379
  137. package/docs/styling.md +0 -222
  138. package/docs/ui-data.md +0 -333
  139. package/docs/ui-form.md +0 -502
  140. package/docs/ui-navigation.md +0 -273
  141. package/docs/ui-overlay.md +0 -157
  142. package/docs/ui-visual.md +0 -127
  143. package/docs/utils.md +0 -244
  144. package/src/data/getOrmDataEditToastErrorMessage.ts +0 -10
@@ -0,0 +1,26 @@
1
+ # `SdTiptapEditor`
2
+
3
+ TipTap 기반 리치 텍스트 에디터 컴포넌트. 툴바를 통해 텍스트 서식(제목, 굵게, 기울임, 색상, 정렬 등)을 제어한다.
4
+
5
+ ```typescript
6
+ @Component({ selector: "sd-tiptap-editor", ... })
7
+ export class SdTiptapEditor
8
+ ```
9
+
10
+ ## Members
11
+
12
+ | Member | Kind | Type | Description |
13
+ |--------|------|------|-------------|
14
+ | `value` | model | `string \| undefined` | HTML 콘텐츠 |
15
+ | `disabled` | input | `boolean` | 비활성화 — 툴바 숨김, 편집 불가 (기본값: `false`) |
16
+ | `readonly` | input | `boolean` | 읽기 전용 — 편집 불가, 툴바 표시 (기본값: `false`) |
17
+ | `required` | input | `boolean` | 필수 여부 (기본값: `false`) |
18
+ | `placeholder` | input | `string \| undefined` | 플레이스홀더 텍스트 |
19
+ | `validatorFn` | input | `(value: string \| undefined) => string \| undefined` | 커스텀 유효성 검사 함수 |
20
+ | `extensions` | input | `AnyExtension[] \| undefined` | 추가 TipTap 확장 (기본 확장: StarterKit, TextStyle, Color, Highlight, TextAlign, Image, Underline, Placeholder) |
21
+
22
+ ## Usage
23
+
24
+ ```html
25
+ <sd-tiptap-editor [(value)]="content" [placeholder]="'내용을 입력하세요'" />
26
+ ```
@@ -1,6 +1,4 @@
1
- # Pipes
2
-
3
- ## `FormatPipe`
1
+ # `FormatPipe`
4
2
 
5
3
  DateTime, DateOnly, string 값을 포맷팅하는 파이프. 파이프명: `format`
6
4
 
@@ -11,6 +9,19 @@ class FormatPipe implements PipeTransform {
11
9
  }
12
10
  ```
13
11
 
12
+ ## Parameters
13
+
14
+ | Param | Type | Description |
15
+ |-------|------|-------------|
16
+ | `value` | `string \| DateTime \| DateOnly \| undefined` | 포맷팅할 값 |
17
+ | `format` | `string` | 포맷 문자열 |
18
+
19
+ ## Returns
20
+
21
+ `string` — 포맷팅된 문자열. `undefined` 또는 `null` 입력 시 빈 문자열 반환.
22
+
23
+ ## Usage
24
+
14
25
  ### DateTime / DateOnly
15
26
 
16
27
  `value.toFormatString(format)`을 호출한다.
@@ -28,5 +39,3 @@ format 문자열에서 `X` 문자를 값의 문자로 치환한다. `|`로 구
28
39
  <!-- "01012345678" → "010-1234-5678" -->
29
40
  {{ phone | format: "XXX-XXXX-XXXX|XX-XXX-XXXX|XX-XXXX-XXXX" }}
30
41
  ```
31
-
32
- `undefined` 또는 `null` 입력 시 빈 문자열 반환.
@@ -0,0 +1,23 @@
1
+ # `SdGlobalErrorHandlerPlugin`
2
+
3
+ 글로벌 에러 핸들러. Angular `ErrorHandler`를 구현하여 처리되지 않은 에러를 포착하고 화면에 표시한다.
4
+
5
+ ```typescript
6
+ @Injectable({ providedIn: null })
7
+ export class SdGlobalErrorHandlerPlugin implements ErrorHandler
8
+ ```
9
+
10
+ `provideSdAngular()`에 의해 자동 등록된다. 직접 등록할 필요 없다.
11
+
12
+ ## Members
13
+
14
+ | Member | Kind | Type | Description |
15
+ |--------|------|------|-------------|
16
+ | `handleError(event)` | method | `(any) => void` | 에러 처리. `PromiseRejectionEvent`, `ErrorEvent`, `Error` 등을 구분하여 처리 |
17
+
18
+ ## 동작
19
+
20
+ - `PromiseRejectionEvent` → reason이 `Error`면 메시지+스택, 객체면 JSON 표시
21
+ - `ErrorEvent` → error가 `Error`면 메시지+스택, string이면 그대로 표시
22
+ - `Error` → 메시지+스택 표시
23
+ - devMode에서는 toast로, prodMode에서는 alert로 표시 (중복 표시 방지)
@@ -1,14 +1,6 @@
1
- # Plugins
1
+ # `SdOptionEventPlugin`
2
2
 
3
- Angular의 `EventManagerPlugin`을 확장하여 커스텀 이벤트 옵션을 지원한다. `provideSdAngular()`에서 `SdOptionEventPlugin`만 자동 등록된다.
4
-
5
- > **NOTE:** 이전 버전의 커맨드 플러그인(`sdSaveCommand`, `sdRefreshCommand`, `sdInsertCommand`)과 옵저버 플러그인(`sdResize`, `sdIntersection`)은 디렉티브로 대체되었다. 자세한 내용은 [directives.md](./directives.md)의 `SdCommandDirective`, `SdResizeDirective`, `SdIntersectionDirective` 항목을 참고한다.
6
-
7
- ## Option Plugin
8
-
9
- ### `SdOptionEventPlugin`
10
-
11
- `.capture`, `.passive`, `.once` 이벤트 수식어 플러그인. 네이티브 DOM 이벤트에 addEventListener options를 적용한다.
3
+ `.capture`, `.passive`, `.once` 이벤트 수식어 플러그인. 네이티브 DOM 이벤트에 addEventListener options를 적용한다. [`provideSdAngular`](../bootstrap/provide-sd-angular.md)에서 자동 등록된다.
12
4
 
13
5
  ```typescript
14
6
  class SdOptionEventPlugin extends EventManagerPlugin {
@@ -16,9 +8,14 @@ class SdOptionEventPlugin extends EventManagerPlugin {
16
8
  }
17
9
  ```
18
10
 
19
- 사용법: `(scroll.passive)="onScroll($event)"`, `(click.capture.once)="onClick($event)"`
11
+ ## Usage
12
+
13
+ ```html
14
+ <div (scroll.passive)="onScroll($event)">...</div>
15
+ <div (click.capture.once)="onClick($event)">...</div>
16
+ ```
20
17
 
21
- ## Error Handler
18
+ ## Related Types
22
19
 
23
20
  ### `SdGlobalErrorHandlerPlugin`
24
21
 
@@ -0,0 +1,65 @@
1
+ # `SdMenu`
2
+
3
+ 메뉴 트리 노드.
4
+
5
+ ```typescript
6
+ interface SdMenu {
7
+ title: string;
8
+ codeChain: string[];
9
+ url?: string;
10
+ icon?: string;
11
+ children?: SdMenu[];
12
+ }
13
+ ```
14
+
15
+ ## Fields
16
+
17
+ | Field | Type | Description |
18
+ |-------|------|-------------|
19
+ | `title` | `string` | 메뉴 제목 |
20
+ | `codeChain` | `string[]` | 코드 체인 (루트부터 현재까지) |
21
+ | `url` | `string \| undefined` | 외부 URL |
22
+ | `icon` | `string \| undefined` | 아이콘 |
23
+ | `children` | `SdMenu[] \| undefined` | 하위 메뉴 |
24
+
25
+ ## Related Types
26
+
27
+ ### `SdFlatMenu`
28
+
29
+ 플랫 메뉴 항목 (리프만).
30
+
31
+ ```typescript
32
+ interface SdFlatMenu<TModule = unknown> {
33
+ titleChain: string[];
34
+ codeChain: string[];
35
+ modulesChain: TModule[][];
36
+ }
37
+ ```
38
+
39
+ | Field | Type | Description |
40
+ |-------|------|-------------|
41
+ | `titleChain` | `string[]` | 타이틀 체인 |
42
+ | `codeChain` | `string[]` | 코드 체인 |
43
+ | `modulesChain` | `TModule[][]` | 모듈 체인 |
44
+
45
+ ### `SdPermission`
46
+
47
+ 권한 트리 노드.
48
+
49
+ ```typescript
50
+ interface SdPermission<TModule = unknown> {
51
+ title: string;
52
+ codeChain: string[];
53
+ modules: TModule[] | undefined;
54
+ perms: ("use" | "edit")[] | undefined;
55
+ children: SdPermission<TModule>[] | undefined;
56
+ }
57
+ ```
58
+
59
+ | Field | Type | Description |
60
+ |-------|------|-------------|
61
+ | `title` | `string` | 권한 제목 |
62
+ | `codeChain` | `string[]` | 코드 체인 |
63
+ | `modules` | `TModule[] \| undefined` | 모듈 제한 |
64
+ | `perms` | `("use" \| "edit")[] \| undefined` | 권한 목록 |
65
+ | `children` | `SdPermission<TModule>[] \| undefined` | 하위 권한 |
@@ -0,0 +1,148 @@
1
+ # `SdModalContentDef`
2
+
3
+ 모달 컴포넌트가 구현해야 하는 인터페이스.
4
+
5
+ ```typescript
6
+ interface SdModalContentDef<O> {
7
+ initialized: Signal<boolean>;
8
+ close: OutputEmitterRef<O | undefined>;
9
+ actionTplRef?: TemplateRef<any>;
10
+ readonly _optionalModalInputs?: string;
11
+ }
12
+ ```
13
+
14
+ ## Fields
15
+
16
+ | Field | Type | Description |
17
+ |-------|------|-------------|
18
+ | `initialized` | `Signal<boolean>` | 초기화 완료 여부 |
19
+ | `close` | `OutputEmitterRef<O \| undefined>` | 닫기 output (결과 전달) |
20
+ | `actionTplRef` | `TemplateRef<any> \| undefined` | 모달 헤더 액션 영역 템플릿 |
21
+ | `_optionalModalInputs` | `string \| undefined` | optional로 취급할 input 키 목록 (리터럴 타입) |
22
+
23
+ ## 구현 패턴
24
+
25
+ ```typescript
26
+ export class CustomerDetail implements SdModalContentDef<boolean | undefined> {
27
+ initialized = signal(false);
28
+ close = output<boolean | undefined>();
29
+ actionTplRef?: TemplateRef<any>;
30
+
31
+ private readonly _modalActionTpl = viewChild("modalActionTpl", { read: TemplateRef });
32
+
33
+ constructor() {
34
+ effect(() => {
35
+ this.actionTplRef = this._modalActionTpl();
36
+ });
37
+ }
38
+
39
+ protected async onSubmit(): Promise<void> {
40
+ // ... (ORM upsert)
41
+ this.close.emit(true);
42
+ }
43
+ }
44
+ ```
45
+
46
+ ## Related Types
47
+
48
+ ### `SdModalInfo`
49
+
50
+ 모달 생성 시 전달하는 정보.
51
+
52
+ ```typescript
53
+ interface SdModalInfo<T extends SdModalContentDef<any>, X extends keyof any = ""> {
54
+ title: string;
55
+ type: Type<T>;
56
+ inputs: WithOptional<Omit<DirectiveInputSignals<T>, SdModalExcludeKeys | X>, ...>;
57
+ }
58
+ ```
59
+
60
+ | Field | Type | Description |
61
+ |-------|------|-------------|
62
+ | `title` | `string` | 모달 제목 |
63
+ | `type` | `Type<T>` | 모달 컴포넌트 타입 |
64
+ | `inputs` | `object` | 컴포넌트 inputs (initialized, close, actionTplRef 제외) |
65
+
66
+ ### `SdModalOptions`
67
+
68
+ 모달 옵션.
69
+
70
+ ```typescript
71
+ interface SdModalOptions {
72
+ key?: string;
73
+ hideHeader?: boolean;
74
+ hideCloseButton?: boolean;
75
+ useCloseByBackdrop?: boolean;
76
+ useCloseByEscapeKey?: boolean;
77
+ float?: boolean;
78
+ fill?: boolean;
79
+ resizable?: boolean;
80
+ movable?: boolean;
81
+ position?: "bottom-right" | "top-right";
82
+ minHeightPx?: number;
83
+ minWidthPx?: number;
84
+ heightPx?: number;
85
+ widthPx?: number;
86
+ headerStyle?: string;
87
+ noFirstControlFocusing?: boolean;
88
+ }
89
+ ```
90
+
91
+ | Field | Type | Description |
92
+ |-------|------|-------------|
93
+ | `key` | `string \| undefined` | 동일 키의 모달이 이미 열려 있으면 재사용 |
94
+ | `hideHeader` | `boolean \| undefined` | 헤더 숨김 |
95
+ | `hideCloseButton` | `boolean \| undefined` | 닫기 버튼 숨김 |
96
+ | `useCloseByBackdrop` | `boolean \| undefined` | 배경 클릭으로 닫기 |
97
+ | `useCloseByEscapeKey` | `boolean \| undefined` | ESC 키로 닫기 |
98
+ | `float` | `boolean \| undefined` | 플로팅 모달 |
99
+ | `fill` | `boolean \| undefined` | 전체 화면 채우기 |
100
+ | `resizable` | `boolean \| undefined` | 크기 조절 가능 |
101
+ | `movable` | `boolean \| undefined` | 이동 가능 |
102
+ | `position` | `"bottom-right" \| "top-right" \| undefined` | 위치 프리셋 |
103
+ | `minHeightPx` | `number \| undefined` | 최소 높이 (px) |
104
+ | `minWidthPx` | `number \| undefined` | 최소 너비 (px) |
105
+ | `heightPx` | `number \| undefined` | 높이 (px) |
106
+ | `widthPx` | `number \| undefined` | 너비 (px) |
107
+ | `headerStyle` | `string \| undefined` | 헤더 인라인 스타일 |
108
+ | `noFirstControlFocusing` | `boolean \| undefined` | 첫 번째 컨트롤 자동 포커스 비활성화 |
109
+
110
+ ### `SelectModalOutputResult`
111
+
112
+ 모달 선택 결과.
113
+
114
+ ```typescript
115
+ interface SelectModalOutputResult<T> {
116
+ selectedItemKeys: any[];
117
+ selectedItems: T[];
118
+ }
119
+ ```
120
+
121
+ | Field | Type | Description |
122
+ |-------|------|-------------|
123
+ | `selectedItemKeys` | `any[]` | 선택된 항목 키 배열 |
124
+ | `selectedItems` | `T[]` | 선택된 항목 배열 |
125
+
126
+ ### `SdSelectModal`
127
+
128
+ 선택 모달 컴포넌트가 구현해야 하는 인터페이스. `SdModalContentDef<SelectModalOutputResult<T>>`를 확장한다.
129
+
130
+ ```typescript
131
+ interface SdSelectModal<T> extends SdModalContentDef<SelectModalOutputResult<T>> {
132
+ selectMode: InputSignal<"single" | "multi" | undefined>;
133
+ selectedItemKeys: InputSignal<any[]>;
134
+ }
135
+ ```
136
+
137
+ | Field | Type | Description |
138
+ |-------|------|-------------|
139
+ | `selectMode` | `InputSignal<"single" \| "multi" \| undefined>` | 선택 모드 |
140
+ | `selectedItemKeys` | `InputSignal<any[]>` | 이미 선택된 항목 키 배열 (복원용) |
141
+
142
+ ### `SdSelectModalInfo`
143
+
144
+ 모달 선택 정보.
145
+
146
+ ```typescript
147
+ type SdSelectModalInfo<T extends SdSelectModal<any>> = SdModalInfo<T, "selectMode" | "selectedItemKeys">
148
+ ```
@@ -0,0 +1,73 @@
1
+ # `SdToastContentDef`
2
+
3
+ 커스텀 토스트 컴포넌트가 구현해야 하는 인터페이스.
4
+
5
+ ```typescript
6
+ interface SdToastContentDef<O> {
7
+ close: OutputEmitterRef<O | undefined>;
8
+ }
9
+ ```
10
+
11
+ ## Fields
12
+
13
+ | Field | Type | Description |
14
+ |-------|------|-------------|
15
+ | `close` | `OutputEmitterRef<O \| undefined>` | 닫기 output |
16
+
17
+ ## Related Types
18
+
19
+ ### `SdToastInput`
20
+
21
+ 커스텀 토스트 생성 입력.
22
+
23
+ ```typescript
24
+ interface SdToastInput<T extends SdToastContentDef<any>> {
25
+ type: Type<T>;
26
+ inputs: Omit<DirectiveInputSignals<T>, "close">;
27
+ }
28
+ ```
29
+
30
+ ### `SdToastSeverity`
31
+
32
+ ```typescript
33
+ type SdToastSeverity = "info" | "success" | "warning" | "danger";
34
+ ```
35
+
36
+ ### `SdToastTheme`
37
+
38
+ ```typescript
39
+ type SdToastTheme = "primary" | "secondary" | SdToastSeverity | "gray" | "blue-gray";
40
+ ```
41
+
42
+ ### `SdBusyType`
43
+
44
+ ```typescript
45
+ type SdBusyType = "spinner" | "bar" | "cube";
46
+ ```
47
+
48
+ ## Related Types (Print)
49
+
50
+ ### `SdPrint`
51
+
52
+ 인쇄 컴포넌트가 구현해야 하는 인터페이스.
53
+
54
+ ```typescript
55
+ interface SdPrint {
56
+ initialized: Signal<boolean>;
57
+ readonly _optionalPrintInputs?: string;
58
+ }
59
+ ```
60
+
61
+ | Field | Type | Description |
62
+ |-------|------|-------------|
63
+ | `initialized` | `Signal<boolean>` | 초기화 완료 여부 (true 될 때까지 대기 후 인쇄) |
64
+ | `_optionalPrintInputs` | `string \| undefined` | optional로 취급할 input 키 목록 |
65
+
66
+ ### `SdPrintInput`
67
+
68
+ ```typescript
69
+ interface SdPrintInput<T, X extends keyof any = ""> {
70
+ type: Type<T>;
71
+ inputs: WithOptional<Omit<DirectiveInputSignals<T>, "_optionalPrintInputs" | X>, ...>;
72
+ }
73
+ ```
@@ -0,0 +1,59 @@
1
+ # `SharedDataBase`
2
+
3
+ 공유 데이터 기본 인터페이스. 모든 공유 데이터 항목이 구현해야 한다.
4
+
5
+ ```typescript
6
+ interface SharedDataBase<TKey extends string | number> {
7
+ __valueKey: TKey;
8
+ __searchText: string;
9
+ __isHidden: boolean;
10
+ __parentKey?: TKey;
11
+ }
12
+ ```
13
+
14
+ ## Fields
15
+
16
+ | Field | Type | Description |
17
+ |-------|------|-------------|
18
+ | `__valueKey` | `TKey` | 고유 키 |
19
+ | `__searchText` | `string` | 검색 대상 텍스트 |
20
+ | `__isHidden` | `boolean` | 숨김 여부 |
21
+ | `__parentKey` | `TKey \| undefined` | 부모 키 (트리 구조용) |
22
+
23
+ ## Related Types
24
+
25
+ ### `SharedDataInfo`
26
+
27
+ 공유 데이터 등록 정보.
28
+
29
+ ```typescript
30
+ interface SharedDataInfo<T extends SharedDataBase<string | number>> {
31
+ serviceKey: string;
32
+ getter: (changeKeys?: (string | number)[]) => Promise<T[]>;
33
+ filter?: unknown;
34
+ orderBy?: (a: T, b: T) => number;
35
+ }
36
+ ```
37
+
38
+ | Field | Type | Description |
39
+ |-------|------|-------------|
40
+ | `serviceKey` | `string` | ServiceClient 연결 키 |
41
+ | `getter` | `(changeKeys?) => Promise<T[]>` | 데이터 조회 함수. changeKeys 전달 시 부분 조회 |
42
+ | `filter` | `unknown` | 이벤트 필터 (같은 이름의 다른 필터 구분용) |
43
+ | `orderBy` | `((a, b) => number) \| undefined` | 정렬 함수 |
44
+
45
+ ### `SharedDataHandle`
46
+
47
+ 공유 데이터 핸들. `SdSharedDataProvider.getHandle()`이 반환하는 객체.
48
+
49
+ ```typescript
50
+ interface SharedDataHandle<T extends SharedDataBase<string | number>> {
51
+ items: Signal<T[]>;
52
+ get(key: T["__valueKey"] | undefined): T | undefined;
53
+ }
54
+ ```
55
+
56
+ | Field | Type | Description |
57
+ |-------|------|-------------|
58
+ | `items` | `Signal<T[]>` | 데이터 항목 signal |
59
+ | `get(key)` | `(key) => T \| undefined` | 키로 항목 조회 |
@@ -0,0 +1,34 @@
1
+ # `SdActivatedModalProvider`
2
+
3
+ 모달 내부에서 inject하여 모달/컨텐츠 컴포넌트 참조를 얻는 프로바이더. `@Injectable()`로 모달별 인스턴스 생성.
4
+
5
+ ```typescript
6
+ @Injectable()
7
+ class SdActivatedModalProvider<T extends SdModalContentDef<any> = SdModalContentDef<any>> {
8
+ modalComponent = signal<any>(undefined);
9
+ contentComponent = signal<T | undefined>(undefined);
10
+ canDeactivateFn: () => boolean;
11
+ }
12
+ ```
13
+
14
+ ## Members
15
+
16
+ | Member | Kind | Type | Description |
17
+ |--------|------|------|-------------|
18
+ | `modalComponent` | property | `WritableSignal<any>` | SdModal 인스턴스 |
19
+ | `contentComponent` | property | `WritableSignal<T \| undefined>` | 컨텐츠 컴포넌트 인스턴스 |
20
+ | `canDeactivateFn` | property | `() => boolean` | 모달 닫기 가능 여부 판별 함수 (기본: `() => true`) |
21
+
22
+ ## Usage
23
+
24
+ 모달 내부 컴포넌트에서 `optional: true`로 inject하여 모달 컨텍스트를 확인한다. 타이틀 계산은 `injectViewTitleSignal()`이 내부에서 `SdActivatedModalProvider`를 자동으로 처리하므로 직접 inject할 필요가 없다:
25
+
26
+ ```typescript
27
+ // 타이틀 — injectViewTitleSignal()이 modal/page 분기를 자동 처리
28
+ protected readonly viewTitle = injectViewTitleSignal();
29
+
30
+ // canDeactivateFn 등 모달 컨텍스트가 직접 필요한 경우에만 inject
31
+ private readonly _sdActivatedModal = inject(SdActivatedModalProvider, { optional: true });
32
+ ```
33
+
34
+ `optional: true`를 사용하는 이유: 동일 컴포넌트가 page 뷰로도 사용될 수 있으며, page 뷰에서는 `SdActivatedModalProvider`가 제공되지 않는다.
@@ -0,0 +1,81 @@
1
+ # `SdAppStructureProvider`
2
+
3
+ 앱 구조(메뉴/권한) 관리 프로바이더. 서브클래스 없이 직접 사용한다.
4
+
5
+ ```typescript
6
+ @Injectable({ providedIn: "root" })
7
+ class SdAppStructureProvider<TModule = unknown> {
8
+ readonly usableModules = signal<TModule[] | undefined>(undefined);
9
+ readonly permRecord = signal<Record<string, boolean> | undefined>(undefined);
10
+ readonly items = signal<AppStructureItem<TModule>[]>([]);
11
+
12
+ usableMenus: Signal<SdMenu[]>;
13
+ usableFlatMenus: Signal<SdFlatMenu<TModule>[]>;
14
+
15
+ async initialize(serviceKey: string): Promise<void>;
16
+ getPermissionsByStructure(items, codeChain?): SdPermission<TModule>[];
17
+ getTitleByFullCode(fullCode: string): string;
18
+ getItemChainByFullCode(fullCode: string): AppStructureItem<TModule>[];
19
+ getPermsByFullCode<K extends string>(fullCodes: string[], permKeys: K[]): K[];
20
+ }
21
+ ```
22
+
23
+ ## Members
24
+
25
+ | Member | Kind | Type | Description |
26
+ |--------|------|------|-------------|
27
+ | `usableModules` | property | `WritableSignal<TModule[] \| undefined>` | 사용 가능한 모듈 목록. 외부에서 `set()`/`update()`로 설정 |
28
+ | `permRecord` | property | `WritableSignal<Record<string, boolean> \| undefined>` | 권한 레코드. 외부에서 `set()`/`update()`로 설정 |
29
+ | `items` | property | `WritableSignal<AppStructureItem<TModule>[]>` | 앱 구조 항목 배열 (`initialize()`로 초기화) |
30
+ | `usableMenus` | computed | `Signal<SdMenu[]>` | 사용 가능한 메뉴 트리 |
31
+ | `usableFlatMenus` | computed | `Signal<SdFlatMenu<TModule>[]>` | 사용 가능한 플랫 메뉴 |
32
+ | `initialize(serviceKey)` | method | `(string) => Promise<void>` | 서비스에서 앱 구조 로드 |
33
+ | `getTitleByFullCode(fullCode)` | method | `(string) => string` | fullCode로 타이틀 조회 |
34
+ | `getItemChainByFullCode(fullCode)` | method | `(string) => AppStructureItem<TModule>[]` | fullCode로 항목 체인 조회 |
35
+ | `getPermsByFullCode(fullCodes, permKeys)` | method | `(string[], K[]) => K[]` | fullCode 배열에서 보유 권한 키 반환 |
36
+
37
+ ## Related Types
38
+
39
+ ### `injectPermsSignal`
40
+
41
+ 앱 구조(`SdAppStructureProvider`)에서 지정된 뷰 코드와 키 배열에 대한 권한을 조회하는 signal. 생성자/필드 이니셜라이저에서 호출한다.
42
+
43
+ ```typescript
44
+ function injectPermsSignal<K extends string>(viewCodes: string[], keys: K[]): Signal<K[]>
45
+ ```
46
+
47
+ | Parameter | Type | Description |
48
+ |-----------|------|-------------|
49
+ | `viewCodes` | `string[]` | 조회할 뷰 코드 배열 (예: `["sales.customer"]`) |
50
+ | `keys` | `K[]` | 권한 키 배열 (예: `["use", "edit"]`) |
51
+
52
+ 반환: 현재 사용자가 보유한 권한 키 배열의 signal
53
+
54
+ #### 사용 패턴
55
+
56
+ ```typescript
57
+ protected readonly perms = injectPermsSignal(["sales.customer"], ["use", "edit"]);
58
+ protected readonly canEdit = computed(() => this.perms().includes("edit"));
59
+ ```
60
+
61
+ ```html
62
+ @if (!perms().includes("use")) {
63
+ <sd-note theme="warning">이 화면의 사용 권한이 없습니다.</sd-note>
64
+ }
65
+ ```
66
+
67
+ ### `SdAppStructureUtils`
68
+
69
+ 앱 구조 유틸리티. 정적 메서드로 메뉴/권한 조회 로직을 제공한다.
70
+
71
+ ```typescript
72
+ abstract class SdAppStructureUtils {
73
+ static getTitleByFullCode<TModule>(items, fullCode): string;
74
+ static getPermsByFullCode<TModule, K extends string>(items, fullCodes, permKeys, permRecord): K[];
75
+ static getItemChainByFullCode<TModule>(items, fullCode): AppStructureItem<TModule>[];
76
+ static getMenus<TModule>(items, codeChain, usableModules, permRecord): SdMenu[];
77
+ static getFlatMenus<TModule>(items, usableModules, permRecord): SdFlatMenu<TModule>[];
78
+ static getPermissions<TModule>(items, codeChain, usableModules): SdPermission<TModule>[];
79
+ static getFlatPermissions<TModule>(items, usableModules): FlatPermission<TModule>[];
80
+ }
81
+ ```
@@ -0,0 +1,18 @@
1
+ # `SdBusyProvider`
2
+
3
+ 글로벌 busy 상태 관리 프로바이더.
4
+
5
+ ```typescript
6
+ @Injectable({ providedIn: "root" })
7
+ class SdBusyProvider {
8
+ type = signal<SdBusyType>("bar");
9
+ globalBusyCount = signal(0);
10
+ }
11
+ ```
12
+
13
+ ## Members
14
+
15
+ | Member | Kind | Type | Description |
16
+ |--------|------|------|-------------|
17
+ | `type` | property | `WritableSignal<SdBusyType>` | busy 표시 유형 (`"spinner" \| "bar" \| "cube"`) |
18
+ | `globalBusyCount` | property | `WritableSignal<number>` | 글로벌 busy 카운트 (0보다 크면 busy 표시) |
@@ -0,0 +1,40 @@
1
+ # `SdFileDialogProvider`
2
+
3
+ 네이티브 파일 선택 대화상자 프로바이더.
4
+
5
+ ```typescript
6
+ @Injectable({ providedIn: "root" })
7
+ class SdFileDialogProvider {
8
+ async showAsync(multiple?: false, accept?: string): Promise<File | undefined>;
9
+ async showAsync(multiple: true, accept?: string): Promise<File[] | undefined>;
10
+ }
11
+ ```
12
+
13
+ ## Members
14
+
15
+ | Member | Kind | Type | Description |
16
+ |--------|------|------|-------------|
17
+ | `showAsync(multiple?, accept?)` | method | overloaded | 파일 선택 대화상자 열기. `multiple=true`이면 `File[]`, 아니면 `File \| undefined` 반환 |
18
+
19
+ ## Parameters
20
+
21
+ | Param | Type | Description |
22
+ |-------|------|-------------|
23
+ | `multiple` | `boolean \| undefined` | 다중 선택 여부 |
24
+ | `accept` | `string \| undefined` | 허용 파일 타입 (예: `"image/*"`, `".xlsx"`) |
25
+
26
+ ## Usage
27
+
28
+ 엑셀 파일 업로드 시 단일 파일을 선택받는 패턴:
29
+
30
+ ```typescript
31
+ private readonly _sdFileDialog = inject(SdFileDialogProvider);
32
+
33
+ async onUploadExcelButtonClick(): Promise<void> {
34
+ const file = await this._sdFileDialog.showAsync(false, ".xlsx");
35
+ if (file == null) return;
36
+ if (Array.isArray(file)) return;
37
+
38
+ // file: File 객체
39
+ }
40
+ ```
@@ -0,0 +1,20 @@
1
+ # `SdLocalStorageProvider`
2
+
3
+ `clientName` 스코프 localStorage 래퍼. 키가 `{clientName}.{key}` 형태로 저장된다.
4
+
5
+ ```typescript
6
+ @Injectable({ providedIn: "root" })
7
+ class SdLocalStorageProvider<T> {
8
+ set<K extends keyof T & string>(key: K, value: T[K]): void;
9
+ get<K extends keyof T & string>(key: K): T[K] | undefined;
10
+ remove(key: keyof T & string): void;
11
+ }
12
+ ```
13
+
14
+ ## Members
15
+
16
+ | Member | Kind | Type | Description |
17
+ |--------|------|------|-------------|
18
+ | `set(key, value)` | method | `(K, T[K]) => void` | 값 저장 |
19
+ | `get(key)` | method | `(K) => T[K] \| undefined` | 값 조회 |
20
+ | `remove(key)` | method | `(keyof T & string) => void` | 값 삭제 |