@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.
- package/README.md +234 -225
- package/dist/controls/select/sd-select.js +3 -3
- package/dist/layout/dock/sd-dock-container.js +1 -1
- package/docs/bootstrap/provide-sd-angular.md +37 -0
- package/docs/bootstrap/sd-angular-config-provider.md +16 -0
- package/docs/directives/sd-command-directive.md +30 -0
- package/docs/directives/sd-events.md +25 -0
- package/docs/directives/sd-intersection-directive.md +36 -0
- package/docs/directives/sd-invalid.md +24 -0
- package/docs/directives/sd-resize-directive.md +42 -0
- package/docs/directives/sd-ripple.md +23 -0
- package/docs/directives/sd-router-link.md +38 -0
- package/docs/directives/sd-show-effect.md +18 -0
- package/docs/directives/sd-typed-template.md +69 -0
- package/docs/features/sd-address-search-modal.md +50 -0
- package/docs/features/sd-permission-table.md +20 -0
- package/docs/features/sd-shared-data-components.md +158 -0
- package/docs/features/sd-tiptap-editor.md +26 -0
- package/docs/{pipes.md → pipes/format-pipe.md} +14 -5
- package/docs/plugins/sd-global-error-handler.md +23 -0
- package/docs/{plugins.md → plugins/sd-option-event-plugin.md} +9 -12
- package/docs/provider-types/sd-menu.md +65 -0
- package/docs/provider-types/sd-modal-content-def.md +148 -0
- package/docs/provider-types/sd-toast-content-def.md +73 -0
- package/docs/provider-types/shared-data-base.md +59 -0
- package/docs/providers/sd-activated-modal-provider.md +34 -0
- package/docs/providers/sd-app-structure-provider.md +81 -0
- package/docs/providers/sd-busy-provider.md +18 -0
- package/docs/providers/sd-file-dialog-provider.md +40 -0
- package/docs/providers/sd-local-storage-provider.md +20 -0
- package/docs/providers/sd-modal-provider.md +67 -0
- package/docs/providers/sd-navigate-window-provider.md +18 -0
- package/docs/providers/sd-print-provider.md +25 -0
- package/docs/providers/sd-service-client-factory-provider.md +43 -0
- package/docs/providers/sd-shared-data-provider.md +64 -0
- package/docs/providers/sd-system-config-provider.md +46 -0
- package/docs/providers/sd-system-log-provider.md +18 -0
- package/docs/providers/sd-theme-provider.md +38 -0
- package/docs/providers/sd-toast-provider.md +65 -0
- package/docs/recipes/_common-rules.md +244 -0
- package/docs/recipes/crud-detail/extension-a-edit-save.md +230 -0
- package/docs/recipes/crud-detail/extension-b-delete-restore.md +142 -0
- package/docs/recipes/crud-detail/extension-c-modal-view.md +214 -0
- package/docs/recipes/crud-detail/extension-d-control-view.md +103 -0
- package/docs/recipes/crud-detail/extension-e-auxiliary.md +87 -0
- package/docs/recipes/crud-detail/extension-f-complex-detail.md +234 -0
- package/docs/recipes/crud-detail.md +184 -706
- package/docs/recipes/crud-list/extension-a-inline-edit.md +410 -0
- package/docs/recipes/crud-list/extension-b-selection.md +226 -0
- package/docs/recipes/crud-list/extension-c-inline-delete.md +87 -0
- package/docs/recipes/crud-list/extension-d-select-modal.md +207 -0
- package/docs/recipes/crud-list/extension-e-readonly-modal.md +165 -0
- package/docs/recipes/crud-list/extension-f-modal-edit.md +177 -0
- package/docs/recipes/crud-list/extension-g-excel.md +157 -0
- package/docs/recipes/crud-list.md +196 -787
- package/docs/recipes/data-select-button.md +185 -91
- package/docs/recipes/page-modal-container.md +168 -86
- package/docs/styling/classes.md +149 -0
- package/docs/styling/mixins.md +100 -0
- package/docs/styling/themes.md +35 -0
- package/docs/styling/variables.md +147 -0
- package/docs/{type-utilities.md → type-utilities/directive-input-signals.md} +17 -35
- package/docs/ui-data/sd-list.md +37 -0
- package/docs/ui-data/sd-sheet.md +227 -0
- package/docs/ui-form/sd-additional-button.md +26 -0
- package/docs/ui-form/sd-anchor.md +31 -0
- package/docs/ui-form/sd-button.md +105 -0
- package/docs/ui-form/sd-checkbox-group.md +39 -0
- package/docs/ui-form/sd-checkbox.md +81 -0
- package/docs/ui-form/sd-date-range-picker.md +27 -0
- package/docs/ui-form/sd-form.md +89 -0
- package/docs/ui-form/sd-modal-select-button.md +54 -0
- package/docs/ui-form/sd-numpad.md +26 -0
- package/docs/ui-form/sd-range.md +26 -0
- package/docs/ui-form/sd-select.md +68 -0
- package/docs/ui-form/sd-shared-data-select.md +52 -0
- package/docs/ui-form/sd-state-preset.md +37 -0
- package/docs/ui-form/sd-switch.md +27 -0
- package/docs/ui-form/sd-textarea.md +33 -0
- package/docs/ui-form/sd-textfield.md +145 -0
- package/docs/ui-layout/sd-dock-container.md +64 -0
- package/docs/ui-layout/sd-dock.md +37 -0
- package/docs/ui-layout/sd-gap.md +26 -0
- package/docs/{ui-layout.md → ui-layout/sd-kanban-board.md} +41 -85
- package/docs/ui-layout/sd-kanban-lane.md +34 -0
- package/docs/ui-layout/sd-kanban.md +29 -0
- package/docs/ui-navigation/sd-collapse.md +35 -0
- package/docs/ui-navigation/sd-pagination.md +26 -0
- package/docs/ui-navigation/sd-sidebar-container.md +49 -0
- package/docs/ui-navigation/sd-sidebar-menu.md +22 -0
- package/docs/ui-navigation/sd-sidebar-user.md +43 -0
- package/docs/ui-navigation/sd-tab.md +51 -0
- package/docs/ui-navigation/sd-topbar-container.md +97 -0
- package/docs/ui-navigation/sd-topbar-menu.md +23 -0
- package/docs/ui-navigation/sd-topbar-user.md +38 -0
- package/docs/ui-navigation/sd-topbar.md +30 -0
- package/docs/ui-overlay/sd-busy-container.md +69 -0
- package/docs/ui-overlay/sd-confirm-modal.md +30 -0
- package/docs/ui-overlay/sd-dropdown.md +40 -0
- package/docs/ui-overlay/sd-modal.md +34 -0
- package/docs/ui-overlay/sd-prompt-modal.md +30 -0
- package/docs/ui-overlay/sd-toast.md +35 -0
- package/docs/ui-visual/sd-barcode.md +36 -0
- package/docs/ui-visual/sd-calendar.md +34 -0
- package/docs/ui-visual/sd-echarts.md +32 -0
- package/docs/ui-visual/sd-label.md +24 -0
- package/docs/ui-visual/sd-note.md +23 -0
- package/docs/ui-visual/sd-progress.md +23 -0
- package/docs/utils/inject-routing-signals.md +161 -0
- package/docs/utils/inject-sd-system-config-resource.md +35 -0
- package/docs/utils/mark.md +43 -0
- package/docs/utils/selection-managers.md +96 -0
- package/docs/utils/set-safe-style.md +19 -0
- package/docs/utils/setup-functions.md +93 -0
- package/package.json +5 -5
- package/src/controls/select/sd-select.ts +3 -3
- package/src/core/modal/sd-modal.provider.ts +1 -1
- package/src/core/modal/sd-modal.ts +1 -1
- package/src/core/routing/menu-utils.ts +1 -1
- package/src/core/shared-data/sd-shared-data.provider.ts +7 -7
- package/src/data/shared-data/sd-shared-data-select.ts +2 -2
- package/src/layout/dock/sd-dock-container.ts +1 -1
- package/docs/bootstrap.md +0 -38
- package/docs/directives.md +0 -236
- package/docs/features.md +0 -154
- package/docs/provider-types.md +0 -283
- package/docs/providers.md +0 -379
- package/docs/styling.md +0 -222
- package/docs/ui-data.md +0 -333
- package/docs/ui-form.md +0 -502
- package/docs/ui-navigation.md +0 -303
- package/docs/ui-overlay.md +0 -157
- package/docs/ui-visual.md +0 -127
- 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
|
-
#
|
|
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로 표시 (중복 표시 방지)
|