@simplysm/angular 14.0.49 → 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 (134) hide show
  1. package/README.md +234 -225
  2. package/dist/controls/select/sd-select.js +3 -3
  3. package/dist/layout/dock/sd-dock-container.js +1 -1
  4. package/docs/bootstrap/provide-sd-angular.md +37 -0
  5. package/docs/bootstrap/sd-angular-config-provider.md +16 -0
  6. package/docs/directives/sd-command-directive.md +30 -0
  7. package/docs/directives/sd-events.md +25 -0
  8. package/docs/directives/sd-intersection-directive.md +36 -0
  9. package/docs/directives/sd-invalid.md +24 -0
  10. package/docs/directives/sd-resize-directive.md +42 -0
  11. package/docs/directives/sd-ripple.md +23 -0
  12. package/docs/directives/sd-router-link.md +38 -0
  13. package/docs/directives/sd-show-effect.md +18 -0
  14. package/docs/directives/sd-typed-template.md +69 -0
  15. package/docs/features/sd-address-search-modal.md +50 -0
  16. package/docs/features/sd-permission-table.md +20 -0
  17. package/docs/features/sd-shared-data-components.md +158 -0
  18. package/docs/features/sd-tiptap-editor.md +26 -0
  19. package/docs/{pipes.md → pipes/format-pipe.md} +14 -5
  20. package/docs/plugins/sd-global-error-handler.md +23 -0
  21. package/docs/{plugins.md → plugins/sd-option-event-plugin.md} +9 -12
  22. package/docs/provider-types/sd-menu.md +65 -0
  23. package/docs/provider-types/sd-modal-content-def.md +148 -0
  24. package/docs/provider-types/sd-toast-content-def.md +73 -0
  25. package/docs/provider-types/shared-data-base.md +59 -0
  26. package/docs/providers/sd-activated-modal-provider.md +34 -0
  27. package/docs/providers/sd-app-structure-provider.md +81 -0
  28. package/docs/providers/sd-busy-provider.md +18 -0
  29. package/docs/providers/sd-file-dialog-provider.md +40 -0
  30. package/docs/providers/sd-local-storage-provider.md +20 -0
  31. package/docs/providers/sd-modal-provider.md +67 -0
  32. package/docs/providers/sd-navigate-window-provider.md +18 -0
  33. package/docs/providers/sd-print-provider.md +25 -0
  34. package/docs/providers/sd-service-client-factory-provider.md +43 -0
  35. package/docs/providers/sd-shared-data-provider.md +64 -0
  36. package/docs/providers/sd-system-config-provider.md +46 -0
  37. package/docs/providers/sd-system-log-provider.md +18 -0
  38. package/docs/providers/sd-theme-provider.md +38 -0
  39. package/docs/providers/sd-toast-provider.md +65 -0
  40. package/docs/recipes/_common-rules.md +244 -0
  41. package/docs/recipes/crud-detail/extension-a-edit-save.md +230 -0
  42. package/docs/recipes/crud-detail/extension-b-delete-restore.md +142 -0
  43. package/docs/recipes/crud-detail/extension-c-modal-view.md +214 -0
  44. package/docs/recipes/crud-detail/extension-d-control-view.md +103 -0
  45. package/docs/recipes/crud-detail/extension-e-auxiliary.md +87 -0
  46. package/docs/recipes/crud-detail/extension-f-complex-detail.md +234 -0
  47. package/docs/recipes/crud-detail.md +184 -706
  48. package/docs/recipes/crud-list/extension-a-inline-edit.md +410 -0
  49. package/docs/recipes/crud-list/extension-b-selection.md +226 -0
  50. package/docs/recipes/crud-list/extension-c-inline-delete.md +87 -0
  51. package/docs/recipes/crud-list/extension-d-select-modal.md +207 -0
  52. package/docs/recipes/crud-list/extension-e-readonly-modal.md +165 -0
  53. package/docs/recipes/crud-list/extension-f-modal-edit.md +177 -0
  54. package/docs/recipes/crud-list/extension-g-excel.md +157 -0
  55. package/docs/recipes/crud-list.md +196 -787
  56. package/docs/recipes/data-select-button.md +185 -91
  57. package/docs/recipes/page-modal-container.md +168 -86
  58. package/docs/styling/classes.md +149 -0
  59. package/docs/styling/mixins.md +100 -0
  60. package/docs/styling/themes.md +35 -0
  61. package/docs/styling/variables.md +147 -0
  62. package/docs/{type-utilities.md → type-utilities/directive-input-signals.md} +17 -35
  63. package/docs/ui-data/sd-list.md +37 -0
  64. package/docs/ui-data/sd-sheet.md +227 -0
  65. package/docs/ui-form/sd-additional-button.md +26 -0
  66. package/docs/ui-form/sd-anchor.md +31 -0
  67. package/docs/ui-form/sd-button.md +105 -0
  68. package/docs/ui-form/sd-checkbox-group.md +39 -0
  69. package/docs/ui-form/sd-checkbox.md +81 -0
  70. package/docs/ui-form/sd-date-range-picker.md +27 -0
  71. package/docs/ui-form/sd-form.md +89 -0
  72. package/docs/ui-form/sd-modal-select-button.md +54 -0
  73. package/docs/ui-form/sd-numpad.md +26 -0
  74. package/docs/ui-form/sd-range.md +26 -0
  75. package/docs/ui-form/sd-select.md +68 -0
  76. package/docs/ui-form/sd-shared-data-select.md +52 -0
  77. package/docs/ui-form/sd-state-preset.md +37 -0
  78. package/docs/ui-form/sd-switch.md +27 -0
  79. package/docs/ui-form/sd-textarea.md +33 -0
  80. package/docs/ui-form/sd-textfield.md +145 -0
  81. package/docs/ui-layout/sd-dock-container.md +64 -0
  82. package/docs/ui-layout/sd-dock.md +37 -0
  83. package/docs/ui-layout/sd-gap.md +26 -0
  84. package/docs/{ui-layout.md → ui-layout/sd-kanban-board.md} +41 -85
  85. package/docs/ui-layout/sd-kanban-lane.md +34 -0
  86. package/docs/ui-layout/sd-kanban.md +29 -0
  87. package/docs/ui-navigation/sd-collapse.md +35 -0
  88. package/docs/ui-navigation/sd-pagination.md +26 -0
  89. package/docs/ui-navigation/sd-sidebar-container.md +49 -0
  90. package/docs/ui-navigation/sd-sidebar-menu.md +22 -0
  91. package/docs/ui-navigation/sd-sidebar-user.md +43 -0
  92. package/docs/ui-navigation/sd-tab.md +51 -0
  93. package/docs/ui-navigation/sd-topbar-container.md +97 -0
  94. package/docs/ui-navigation/sd-topbar-menu.md +23 -0
  95. package/docs/ui-navigation/sd-topbar-user.md +38 -0
  96. package/docs/ui-navigation/sd-topbar.md +30 -0
  97. package/docs/ui-overlay/sd-busy-container.md +69 -0
  98. package/docs/ui-overlay/sd-confirm-modal.md +30 -0
  99. package/docs/ui-overlay/sd-dropdown.md +40 -0
  100. package/docs/ui-overlay/sd-modal.md +34 -0
  101. package/docs/ui-overlay/sd-prompt-modal.md +30 -0
  102. package/docs/ui-overlay/sd-toast.md +35 -0
  103. package/docs/ui-visual/sd-barcode.md +36 -0
  104. package/docs/ui-visual/sd-calendar.md +34 -0
  105. package/docs/ui-visual/sd-echarts.md +32 -0
  106. package/docs/ui-visual/sd-label.md +24 -0
  107. package/docs/ui-visual/sd-note.md +23 -0
  108. package/docs/ui-visual/sd-progress.md +23 -0
  109. package/docs/utils/inject-routing-signals.md +161 -0
  110. package/docs/utils/inject-sd-system-config-resource.md +35 -0
  111. package/docs/utils/mark.md +43 -0
  112. package/docs/utils/selection-managers.md +96 -0
  113. package/docs/utils/set-safe-style.md +19 -0
  114. package/docs/utils/setup-functions.md +93 -0
  115. package/package.json +5 -5
  116. package/src/controls/select/sd-select.ts +3 -3
  117. package/src/core/modal/sd-modal.provider.ts +1 -1
  118. package/src/core/modal/sd-modal.ts +1 -1
  119. package/src/core/routing/menu-utils.ts +1 -1
  120. package/src/core/shared-data/sd-shared-data.provider.ts +7 -7
  121. package/src/data/shared-data/sd-shared-data-select.ts +2 -2
  122. package/src/layout/dock/sd-dock-container.ts +1 -1
  123. package/docs/bootstrap.md +0 -38
  124. package/docs/directives.md +0 -236
  125. package/docs/features.md +0 -154
  126. package/docs/provider-types.md +0 -283
  127. package/docs/providers.md +0 -379
  128. package/docs/styling.md +0 -222
  129. package/docs/ui-data.md +0 -333
  130. package/docs/ui-form.md +0 -502
  131. package/docs/ui-navigation.md +0 -303
  132. package/docs/ui-overlay.md +0 -157
  133. package/docs/ui-visual.md +0 -127
  134. package/docs/utils.md +0 -244
@@ -0,0 +1,37 @@
1
+ # `provideSdAngular`
2
+
3
+ 모든 기반 설정을 제공하는 환경 프로바이더 팩토리. `bootstrapApplication`의 `providers`에 추가한다.
4
+
5
+ ```typescript
6
+ function provideSdAngular(opt: { clientName: string }): EnvironmentProviders
7
+ ```
8
+
9
+ ## Parameters
10
+
11
+ | Param | Type | Description |
12
+ |-------|------|-------------|
13
+ | `opt.clientName` | `string` | 클라이언트 이름 (localStorage 키 접두사 등에 사용) |
14
+
15
+ ## 제공하는 기능
16
+
17
+ - `provideZonelessChangeDetection()` — Zone 없는 변경 감지
18
+ - `IMAGE_CONFIG` — 이미지 크기/지연 로딩 경고 비활성화
19
+ - `provideNgIconsConfig({ strokeWidth: 1.5, size: "1.33em" })`
20
+ - `SdOptionEventPlugin` 등록 (`EVENT_MANAGER_PLUGINS` multi-provider) — `.capture`/`.passive`/`.once` 이벤트 수식어
21
+ - `SdGlobalErrorHandlerPlugin` — 글로벌 에러 핸들러
22
+ - 테마 초기화 (localStorage 동기화, body 클래스 토글)
23
+ - Service Worker 업데이트 폴링 (5분 간격, 실패 시 exponential backoff, 최대 1시간)
24
+ - 라우터 네비게이션 busy 상태 추적 (`SdBusyProvider.globalBusyCount` signal 증감)
25
+
26
+ ## Usage
27
+
28
+ ```typescript
29
+ import { provideSdAngular } from "@simplysm/angular";
30
+
31
+ bootstrapApplication(AppComponent, {
32
+ providers: [
33
+ provideSdAngular({ clientName: "my-app" }),
34
+ provideRouter(routes),
35
+ ],
36
+ });
37
+ ```
@@ -0,0 +1,16 @@
1
+ # `SdAngularConfigProvider`
2
+
3
+ `clientName` 설정을 보유하는 프로바이더. [`provideSdAngular`](./provide-sd-angular.md)에서 자동 설정된다.
4
+
5
+ ```typescript
6
+ @Injectable({ providedIn: "root" })
7
+ class SdAngularConfigProvider {
8
+ clientName!: string;
9
+ }
10
+ ```
11
+
12
+ ## Members
13
+
14
+ | Member | Kind | Type | Description |
15
+ |--------|------|------|-------------|
16
+ | `clientName` | property | `string` | 클라이언트 이름 |
@@ -0,0 +1,30 @@
1
+ # `SdCommandDirective`
2
+
3
+ 키보드 단축키를 output 이벤트로 제공하는 디렉티브. `document` keydown을 감지하며, `shouldProcessCommandEvent()`로 최상위 모달만 이벤트 처리한다.
4
+
5
+ ```typescript
6
+ @Directive({ selector: "[sdRefreshCommand],[sdSaveCommand],[sdInsertCommand]" })
7
+ class SdCommandDirective {
8
+ sdRefreshCommand = output<KeyboardEvent>(); // Ctrl+Alt+L
9
+ sdSaveCommand = output<KeyboardEvent>(); // Ctrl+S
10
+ sdInsertCommand = output<KeyboardEvent>(); // Ctrl+Insert
11
+ }
12
+ ```
13
+
14
+ ## Members
15
+
16
+ | Member | Kind | Type | Description |
17
+ |--------|------|------|-------------|
18
+ | `sdRefreshCommand` | output | `KeyboardEvent` | Ctrl+Alt+L 단축키 이벤트 |
19
+ | `sdSaveCommand` | output | `KeyboardEvent` | Ctrl+S 단축키 이벤트 |
20
+ | `sdInsertCommand` | output | `KeyboardEvent` | Ctrl+Insert 단축키 이벤트 |
21
+
22
+ ## Usage
23
+
24
+ ```html
25
+ <div
26
+ (sdSaveCommand)="onSave($event)"
27
+ (sdRefreshCommand)="onRefresh($event)"
28
+ (sdInsertCommand)="onInsert($event)"
29
+ ></div>
30
+ ```
@@ -0,0 +1,25 @@
1
+ # `SdEvents`
2
+
3
+ `.capture`, `.passive`, `.once` 수식어 및 커스텀 이벤트 바인딩을 지원하는 디렉티브. Angular 템플릿에서 해당 이벤트를 사용할 때 자동 매칭된다.
4
+
5
+ ```typescript
6
+ @Directive({
7
+ selector: `[click.capture], [scroll.passive], ...`,
8
+ })
9
+ class SdEvents {}
10
+ ```
11
+
12
+ ## 지원 이벤트
13
+
14
+ - 클릭: `click.capture`, `click.once`, `click.capture.once`
15
+ - 마우스: `mousedown.capture`, `mouseup.capture`, `mouseover.capture`, `mouseout.capture`
16
+ - 키보드: `keydown.capture`, `keyup.capture`
17
+ - 포커스: `focus.capture`, `blur.capture`
18
+ - 폼: `invalid.capture`
19
+ - 스크롤: `scroll.capture`, `scroll.passive`, `scroll.capture.passive`
20
+ - 휠: `wheel.passive`, `wheel.capture.passive`
21
+ - 터치: `touchstart.passive`, `touchstart.capture.passive`, `touchmove.passive`, `touchmove.capture.passive`, `touchend.passive`
22
+ - 드래그: `dragover.capture`, `dragenter.capture`, `dragleave.capture`, `drop.capture`
23
+ - 애니메이션: `transitionend.once`, `animationend.once`
24
+
25
+ > **NOTE:** `sdResize`, `sdSaveCommand`, `sdRefreshCommand`, `sdInsertCommand`는 `SdEvents`에서 분리되어 각각 [`SdResizeDirective`](./sd-resize-directive.md), [`SdIntersectionDirective`](./sd-intersection-directive.md), [`SdCommandDirective`](./sd-command-directive.md)로 독립 디렉티브로 제공된다.
@@ -0,0 +1,36 @@
1
+ # `SdIntersectionDirective`
2
+
3
+ IntersectionObserver 기반 intersection output 이벤트 디렉티브.
4
+
5
+ ```typescript
6
+ @Directive({ selector: "[sdIntersection]" })
7
+ class SdIntersectionDirective {
8
+ sdIntersection = output<SdIntersectionEvent>();
9
+ }
10
+ ```
11
+
12
+ ## Members
13
+
14
+ | Member | Kind | Type | Description |
15
+ |--------|------|------|-------------|
16
+ | `sdIntersection` | output | `SdIntersectionEvent` | 뷰포트와의 교차 변경 시 발생 |
17
+
18
+ ## Related Types
19
+
20
+ ### `SdIntersectionEvent`
21
+
22
+ ```typescript
23
+ interface SdIntersectionEvent {
24
+ entry: IntersectionObserverEntry;
25
+ }
26
+ ```
27
+
28
+ | Field | Type | Description |
29
+ |-------|------|-------------|
30
+ | `entry` | `IntersectionObserverEntry` | 마지막 IntersectionObserver 엔트리 |
31
+
32
+ ## Usage
33
+
34
+ ```html
35
+ <div (sdIntersection)="onIntersect($event)"></div>
36
+ ```
@@ -0,0 +1,24 @@
1
+ # `SdInvalid`
2
+
3
+ 호스트 요소에 유효성 검증 표시기를 추가하는 디렉티브. 빨간 점 표시기와 숨겨진 input으로 구성.
4
+
5
+ ```typescript
6
+ @Directive({ selector: "[sdInvalid]" })
7
+ class SdInvalid {
8
+ invalidMessage = input.required<string>({ alias: "sdInvalid" });
9
+ }
10
+ ```
11
+
12
+ ## Members
13
+
14
+ | Member | Kind | Type | Description |
15
+ |--------|------|------|-------------|
16
+ | `invalidMessage` (`sdInvalid`) | input (required) | `string` | 유효성 오류 메시지. 빈 문자열이면 유효, 비어있지 않으면 무효 |
17
+
18
+ ## Usage
19
+
20
+ ```html
21
+ <div [sdInvalid]="name이 비어있습니다">
22
+ <!-- 메시지가 있으면 빨간 점 표시기 노출 -->
23
+ </div>
24
+ ```
@@ -0,0 +1,42 @@
1
+ # `SdResizeDirective`
2
+
3
+ ResizeObserver 기반 resize output 이벤트 디렉티브. `requestAnimationFrame`으로 디바운스.
4
+
5
+ ```typescript
6
+ @Directive({ selector: "[sdResize]" })
7
+ class SdResizeDirective {
8
+ sdResize = output<SdResizeEvent>();
9
+ }
10
+ ```
11
+
12
+ ## Members
13
+
14
+ | Member | Kind | Type | Description |
15
+ |--------|------|------|-------------|
16
+ | `sdResize` | output | `SdResizeEvent` | 요소 크기 변경 시 발생 |
17
+
18
+ ## Related Types
19
+
20
+ ### `SdResizeEvent`
21
+
22
+ ```typescript
23
+ interface SdResizeEvent {
24
+ heightChanged: boolean;
25
+ widthChanged: boolean;
26
+ target: HTMLElement;
27
+ contentRect: DOMRectReadOnly;
28
+ }
29
+ ```
30
+
31
+ | Field | Type | Description |
32
+ |-------|------|-------------|
33
+ | `heightChanged` | `boolean` | 높이 변경 여부 |
34
+ | `widthChanged` | `boolean` | 너비 변경 여부 |
35
+ | `target` | `HTMLElement` | 대상 요소 |
36
+ | `contentRect` | `DOMRectReadOnly` | 컨텐츠 영역 크기 |
37
+
38
+ ## Usage
39
+
40
+ ```html
41
+ <div (sdResize)="onResize($event)"></div>
42
+ ```
@@ -0,0 +1,23 @@
1
+ # `SdRipple`
2
+
3
+ 호스트 요소에 리플 효과를 추가하는 디렉티브.
4
+
5
+ ```typescript
6
+ @Directive({ selector: "[sdRipple]" })
7
+ class SdRipple {
8
+ enabled = input.required({ alias: "sdRipple", transform: booleanAttribute });
9
+ }
10
+ ```
11
+
12
+ ## Members
13
+
14
+ | Member | Kind | Type | Description |
15
+ |--------|------|------|-------------|
16
+ | `enabled` (`sdRipple`) | input (required) | `boolean` | 리플 효과 활성화 여부 |
17
+
18
+ ## Usage
19
+
20
+ ```html
21
+ <div [sdRipple]="true">리플 효과 있음</div>
22
+ <div sdRipple>리플 효과 있음 (boolean attribute)</div>
23
+ ```
@@ -0,0 +1,38 @@
1
+ # `SdRouterLink`
2
+
3
+ 라우터 네비게이션 디렉티브. 일반 클릭은 라우터 네비게이션, Ctrl/Shift+클릭은 새 창, 팝업 윈도우에서는 팝업 형태로 열린다.
4
+
5
+ ```typescript
6
+ @Directive({
7
+ selector: "[sdRouterLink]",
8
+ host: {
9
+ "[style.cursor]": "option() ? 'pointer' : ''",
10
+ "(click)": "onClick($event)",
11
+ },
12
+ })
13
+ class SdRouterLink {
14
+ option = input<{
15
+ link: string;
16
+ params?: Record<string, string>;
17
+ window?: { width?: number; height?: number };
18
+ outletName?: string;
19
+ queryParams?: Record<string, string>;
20
+ } | undefined>(undefined, { alias: "sdRouterLink" });
21
+ }
22
+ ```
23
+
24
+ ## Members
25
+
26
+ | Member | Kind | Type | Description |
27
+ |--------|------|------|-------------|
28
+ | `option` (`sdRouterLink`) | input | `{ link, params?, window?, outletName?, queryParams? } \| undefined` | 라우터 링크 옵션 |
29
+
30
+ ## Option Fields
31
+
32
+ | Field | Type | Description |
33
+ |-------|------|-------------|
34
+ | `link` | `string` | 네비게이션 경로 |
35
+ | `params` | `Record<string, string> \| undefined` | 라우터 파라미터 |
36
+ | `window` | `{ width?, height? } \| undefined` | 팝업 윈도우 크기 (설정 시 팝업으로 열림) |
37
+ | `outletName` | `string \| undefined` | named outlet |
38
+ | `queryParams` | `Record<string, string> \| undefined` | 쿼리 파라미터 |
@@ -0,0 +1,18 @@
1
+ # `SdShowEffect`
2
+
3
+ 뷰포트에 진입할 때 reveal 애니메이션을 적용하는 디렉티브. IntersectionObserver 사용.
4
+
5
+ ```typescript
6
+ @Directive({ selector: "[sdShowEffect]" })
7
+ class SdShowEffect {
8
+ enabled = input.required({ alias: "sdShowEffect", transform: booleanAttribute });
9
+ sdShowEffectType = input<"l2r" | "t2b">("t2b");
10
+ }
11
+ ```
12
+
13
+ ## Members
14
+
15
+ | Member | Kind | Type | Default | Description |
16
+ |--------|------|------|---------|-------------|
17
+ | `enabled` (`sdShowEffect`) | input (required) | `boolean` | - | 효과 활성화 여부 |
18
+ | `sdShowEffectType` | input | `"l2r" \| "t2b"` | `"t2b"` | 애니메이션 방향 (좌→우 / 위→아래) |
@@ -0,0 +1,69 @@
1
+ # `SdTypedTemplate`
2
+
3
+ `ng-template`의 컨텍스트 타입을 지정하는 디렉티브. 타입 가드를 통해 템플릿 내부에서 정확한 타입을 사용할 수 있다.
4
+
5
+ ```typescript
6
+ @Directive({ selector: "ng-template[typed]" })
7
+ class SdTypedTemplate<T> {
8
+ typed = input.required<T>();
9
+
10
+ static ngTemplateContextGuard<TypeToken>(
11
+ _dir: SdTypedTemplate<TypeToken>,
12
+ _ctx: unknown,
13
+ ): _ctx is TypeToken;
14
+ }
15
+ ```
16
+
17
+ ## Members
18
+
19
+ | Member | Kind | Type | Description |
20
+ |--------|------|------|-------------|
21
+ | `typed` | input (required) | `T` | 컨텍스트 타입 토큰 |
22
+
23
+ ## Related Types
24
+
25
+ ### `SdItemOfTemplate`
26
+
27
+ 항목 반복 템플릿의 컨텍스트 타입을 지정하는 디렉티브.
28
+
29
+ ```typescript
30
+ @Directive({ selector: "ng-template[itemOf]" })
31
+ class SdItemOfTemplate<TItem> {
32
+ itemOf = input.required<TItem[]>();
33
+
34
+ static ngTemplateContextGuard<TContextItem>(
35
+ _dir: SdItemOfTemplate<TContextItem>,
36
+ _ctx: unknown,
37
+ ): _ctx is SdItemOfTemplateContext<TContextItem>;
38
+ }
39
+ ```
40
+
41
+ ### `SdItemOfTemplateContext`
42
+
43
+ ```typescript
44
+ interface SdItemOfTemplateContext<TItem> {
45
+ $implicit: TItem;
46
+ item: TItem;
47
+ index: number;
48
+ depth: number;
49
+ }
50
+ ```
51
+
52
+ | Field | Type | Description |
53
+ |-------|------|-------------|
54
+ | `$implicit` | `TItem` | 현재 항목 (let-item으로 접근) |
55
+ | `item` | `TItem` | 현재 항목 (명시적 접근) |
56
+ | `index` | `number` | 인덱스 |
57
+ | `depth` | `number` | 깊이 (트리 구조에서 사용) |
58
+
59
+ ## Usage
60
+
61
+ ```html
62
+ <ng-template [typed]="typedVar" let-item>
63
+ <!-- item의 타입이 typeof typedVar -->
64
+ </ng-template>
65
+
66
+ <ng-template [itemOf]="items()" let-item let-index="index">
67
+ {{ item.name }}
68
+ </ng-template>
69
+ ```
@@ -0,0 +1,50 @@
1
+ # `SdAddressSearchModal`
2
+
3
+ Daum Postcode API를 사용한 주소 검색 모달. `SdModalContentDef<Address>`를 구현한다.
4
+
5
+ ```typescript
6
+ @Component({ selector: "sd-address-search-modal" })
7
+ class SdAddressSearchModal implements SdModalContentDef<Address>, OnInit {
8
+ close = output<Address>();
9
+ initialized = signal(false);
10
+ }
11
+ ```
12
+
13
+ ## Members
14
+
15
+ | Member | Kind | Type | Description |
16
+ |--------|------|------|-------------|
17
+ | `close` | output | `Address` | 주소 선택 완료 시 발생 |
18
+ | `initialized` | signal | `boolean` | 초기화 완료 여부 |
19
+
20
+ ## Related Types
21
+
22
+ ### `Address`
23
+
24
+ ```typescript
25
+ interface Address {
26
+ postNumber: string | undefined;
27
+ address: string | undefined;
28
+ buildingName: string | undefined;
29
+ }
30
+ ```
31
+
32
+ | Field | Type | Description |
33
+ |-------|------|-------------|
34
+ | `postNumber` | `string \| undefined` | 우편번호 |
35
+ | `address` | `string \| undefined` | 주소 |
36
+ | `buildingName` | `string \| undefined` | 건물명 |
37
+
38
+ ## Usage
39
+
40
+ ```typescript
41
+ const result = await this._sdModal.showAsync({
42
+ title: "주소 검색",
43
+ type: SdAddressSearchModal,
44
+ inputs: {},
45
+ });
46
+ if (result != null) {
47
+ this.data().address = result.address;
48
+ mark(this.data);
49
+ }
50
+ ```
@@ -0,0 +1,20 @@
1
+ # `SdPermissionTable`
2
+
3
+ 권한 매트릭스 테이블. `SdPermission` 트리를 테이블로 렌더링하여 use/edit 체크박스를 표시한다.
4
+
5
+ ```typescript
6
+ @Component({ selector: "sd-permission-table" })
7
+ class SdPermissionTable<TModule = unknown> {
8
+ value = model<Record<string, boolean>>({});
9
+ items = input<SdPermission<TModule>[]>([]);
10
+ disabled = input(false, { transform: booleanAttribute });
11
+ }
12
+ ```
13
+
14
+ ## Members
15
+
16
+ | Member | Kind | Type | Default | Description |
17
+ |--------|------|------|---------|-------------|
18
+ | `value` | model | `Record<string, boolean>` | `{}` | 권한 레코드 (two-way). 키는 `codeChain.join(".") + ".use"` 또는 `".edit"` 형태 |
19
+ | `items` | input | `SdPermission<TModule>[]` | `[]` | 권한 트리 |
20
+ | `disabled` | input | `boolean` | `false` | 비활성화 |
@@ -0,0 +1,158 @@
1
+ # Shared Data Components
2
+
3
+ 공유 데이터(`SdSharedDataProvider`)와 연동하는 UI 컴포넌트 모음.
4
+
5
+ ## `SdSharedDataSelect`
6
+
7
+ 공유 데이터 드롭다운 선택 컴포넌트. 검색 기능 포함.
8
+
9
+ ```typescript
10
+ @Component({ selector: "sd-shared-data-select" })
11
+ class SdSharedDataSelect<
12
+ TItem extends SharedDataBase<string | number>,
13
+ TMode extends keyof SelectModeValue<...>,
14
+ TModal extends SdSelectModal<any>
15
+ > {
16
+ value = model<SelectModeValue<TItem["__valueKey"] | undefined>[TMode]>();
17
+ items = input.required<TItem[]>();
18
+ disabled = input(false, { transform: booleanAttribute });
19
+ required = input(false, { transform: booleanAttribute });
20
+ useUndefined = input(false, { transform: booleanAttribute });
21
+ inset = input(false, { transform: booleanAttribute });
22
+ inline = input(false, { transform: booleanAttribute });
23
+ size = input<"sm" | "lg">();
24
+ selectMode = input("single" as TMode);
25
+ filterFn = input<(item: TItem, index: number, ...params: any[]) => boolean>();
26
+ filterFnParams = input<any[]>();
27
+ modal = input<SdSelectModalInfo<TModal>>();
28
+ editModal = input<SdModalInfo<SdModalContentDef<boolean>>>();
29
+ selectClass = input<string>();
30
+ multiSelectionDisplayDirection = input<"vertical">();
31
+ getIsHiddenFn = input<(item: TItem, index: number) => boolean>();
32
+ getSearchTextFn = input<(item: TItem, index: number) => string>();
33
+ displayOrderKeyProp = input<string>();
34
+ }
35
+ ```
36
+
37
+ ### Members
38
+
39
+ | Member | Kind | Type | Default | Description |
40
+ |--------|------|------|---------|-------------|
41
+ | `value` | model | `SelectModeValue<TItem["__valueKey"] \| undefined>[TMode]` | - | 선택된 값 (two-way) |
42
+ | `items` | input (required) | `TItem[]` | - | 공유 데이터 항목 |
43
+ | `disabled` | input | `boolean` | `false` | 비활성화 |
44
+ | `required` | input | `boolean` | `false` | 필수 |
45
+ | `useUndefined` | input | `boolean` | `false` | undefined 항목 포함 |
46
+ | `inset` | input | `boolean` | `false` | 삽입 스타일 |
47
+ | `inline` | input | `boolean` | `false` | 인라인 표시 |
48
+ | `size` | input | `"sm" \| "lg" \| undefined` | `undefined` | 크기 |
49
+ | `selectMode` | input | `TMode` | `"single"` | 선택 모드 |
50
+ | `filterFn` | input | `((item, index, ...params) => boolean) \| undefined` | `undefined` | 필터 함수 |
51
+ | `modal` | input | `SdSelectModalInfo<TModal> \| undefined` | `undefined` | 모달 선택 정보 |
52
+
53
+ ### 시트 셀 내 사용 패턴
54
+
55
+ ```html
56
+ <sd-sheet-column [header]="'거래처'" [key]="'vendorId'">
57
+ <ng-template [cell]="items()" let-item let-edit="edit">
58
+ <sd-shared-data-select
59
+ [items]="sharedVendors()"
60
+ [inset]="true"
61
+ [size]="'sm'"
62
+ [disabled]="!canEdit()"
63
+ [readonly]="!edit"
64
+ [(value)]="item.vendorId"
65
+ (valueChange)="mark(items)"
66
+ >
67
+ <ng-template [itemOf]="sharedVendors()">
68
+ <div class="flex-row gap-sm">
69
+ <div>{{ item.__searchText }}</div>
70
+ </div>
71
+ </ng-template>
72
+ </sd-shared-data-select>
73
+ </ng-template>
74
+ </sd-sheet-column>
75
+ ```
76
+
77
+ 시트 셀 내 사용 시 `[inset]="true" [size]="'sm'"`을 반드시 지정한다.
78
+
79
+ ## `SdSharedDataSelectButton`
80
+
81
+ 공유 데이터 모달 선택 버튼. `<sd-modal-select-button>`을 컴포지션하여 공유 데이터 항목을 표시한다.
82
+
83
+ ```typescript
84
+ @Component({ selector: "sd-shared-data-select-button" })
85
+ class SdSharedDataSelectButton<
86
+ TItem extends SharedDataBase<string | number>,
87
+ TMode extends keyof SelectModeValue<string | number>,
88
+ TModal extends SdSelectModal<any>
89
+ > {
90
+ value = model<SelectModeValue<string | number>[TMode]>();
91
+ items = input<TItem[]>([]);
92
+ modal = input.required<SdSelectModalInfo<TModal>>();
93
+ selectMode = input<TMode>("single" as TMode);
94
+ disabled = input(false, { transform: booleanAttribute });
95
+ required = input(false, { transform: booleanAttribute });
96
+ inset = input(false, { transform: booleanAttribute });
97
+ size = input<"sm" | "lg">();
98
+ }
99
+ ```
100
+
101
+ ### Members
102
+
103
+ | Member | Kind | Type | Default | Description |
104
+ |--------|------|------|---------|-------------|
105
+ | `value` | model | `SelectModeValue<string \| number>[TMode]` | - | 선택된 값 (two-way) |
106
+ | `items` | input | `TItem[]` | `[]` | 공유 데이터 항목 |
107
+ | `modal` | input (required) | `SdSelectModalInfo<TModal>` | - | 모달 정보 |
108
+ | `selectMode` | input | `TMode` | `"single"` | 선택 모드 |
109
+ | `disabled` | input | `boolean` | `false` | 비활성화 |
110
+ | `required` | input | `boolean` | `false` | 필수 |
111
+
112
+ ## `SdSharedDataSelectList`
113
+
114
+ 공유 데이터 목록형 선택 컴포넌트. 검색, 페이지네이션, 외부 링크 기능을 포함한다.
115
+
116
+ ```typescript
117
+ @Component({ selector: "sd-shared-data-select-list" })
118
+ class SdSharedDataSelectList<
119
+ TItem extends SharedDataBase<string | number>,
120
+ TModal extends SdSelectModal<any>
121
+ > {
122
+ selectedItem = model<TItem>();
123
+ canChangeFn = input<(item: TItem | undefined) => boolean | Promise<boolean>>(() => true);
124
+ items = input.required<TItem[]>();
125
+ selectedIcon = input<string>();
126
+ useUndefined = input(false, { transform: booleanAttribute });
127
+ filterFn = input<(item: TItem, index: number) => boolean>();
128
+ modal = input<SdSelectModalInfo<TModal>>();
129
+ header = input<string>();
130
+ pageItemCount = input<number>();
131
+ }
132
+ ```
133
+
134
+ ### Members
135
+
136
+ | Member | Kind | Type | Default | Description |
137
+ |--------|------|------|---------|-------------|
138
+ | `selectedItem` | model | `TItem \| undefined` | - | 선택된 항목 (two-way) |
139
+ | `canChangeFn` | input | `(item) => boolean \| Promise<boolean>` | `() => true` | 선택 변경 가능 여부 함수 |
140
+ | `items` | input (required) | `TItem[]` | - | 공유 데이터 항목 |
141
+ | `selectedIcon` | input | `string \| undefined` | `undefined` | 선택됨 아이콘 |
142
+ | `useUndefined` | input | `boolean` | `false` | undefined 항목 포함 |
143
+ | `filterFn` | input | `((item, index) => boolean) \| undefined` | `undefined` | 필터 함수 |
144
+ | `modal` | input | `SdSelectModalInfo<TModal> \| undefined` | `undefined` | 모달 선택 정보 |
145
+ | `header` | input | `string \| undefined` | `undefined` | 헤더 텍스트 |
146
+ | `pageItemCount` | input | `number \| undefined` | `undefined` | 페이지당 항목 수 |
147
+
148
+ Content children: `#headerTpl`, `#filterTpl`, `SdItemOfTemplate`, `#undefinedTpl`
149
+
150
+ ## `matchesSearchText`
151
+
152
+ 공백 구분 AND 조건 텍스트 검색 매칭 함수.
153
+
154
+ ```typescript
155
+ function matchesSearchText(itemText: string, searchQuery: string | undefined): boolean
156
+ ```
157
+
158
+ 모든 검색어(공백으로 분할)가 `itemText`에 포함되면 `true`. `searchQuery`가 undefined이거나 빈 문자열이면 항상 `true`.
@@ -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로 표시 (중복 표시 방지)