@simplysm/angular 14.0.47 → 14.0.49

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 +467 -0
  2. package/dist/controls/dropdown/sd-dropdown-popup.d.ts +1 -0
  3. package/dist/controls/dropdown/sd-dropdown-popup.d.ts.map +1 -1
  4. package/dist/controls/dropdown/sd-dropdown-popup.js +6 -1
  5. package/dist/controls/dropdown/sd-dropdown.d.ts.map +1 -1
  6. package/dist/controls/dropdown/sd-dropdown.js +6 -2
  7. package/dist/controls/select/sd-select.d.ts.map +1 -1
  8. package/dist/controls/select/sd-select.js +12 -34
  9. package/dist/core/routing/injectViewTypeSignal.d.ts +1 -1
  10. package/dist/core/routing/injectViewTypeSignal.d.ts.map +1 -1
  11. package/dist/core/routing/injectViewTypeSignal.js +15 -7
  12. package/dist/core/selection/useSelectionManager.d.ts +1 -0
  13. package/dist/core/selection/useSelectionManager.d.ts.map +1 -1
  14. package/dist/core/selection/useSelectionManager.js +54 -14
  15. package/dist/data/shared-data/sd-shared-data-select-button.d.ts +11 -5
  16. package/dist/data/shared-data/sd-shared-data-select-button.d.ts.map +1 -1
  17. package/dist/data/shared-data/sd-shared-data-select-button.js +68 -29
  18. package/dist/data/sheet/sd-sheet.d.ts +4 -1
  19. package/dist/data/sheet/sd-sheet.d.ts.map +1 -1
  20. package/dist/data/sheet/sd-sheet.js +23 -4
  21. package/dist/index.d.ts +0 -14
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +0 -19
  24. package/dist/styles.css +9 -0
  25. package/docs/bootstrap.md +38 -0
  26. package/docs/directives.md +236 -0
  27. package/docs/features.md +154 -0
  28. package/docs/pipes.md +32 -0
  29. package/docs/plugins.md +37 -0
  30. package/docs/provider-types.md +283 -0
  31. package/docs/providers.md +379 -0
  32. package/docs/recipes/crud-detail.md +875 -0
  33. package/docs/recipes/crud-list.md +1037 -0
  34. package/docs/recipes/data-select-button.md +318 -0
  35. package/docs/recipes/page-modal-container.md +178 -0
  36. package/docs/styling.md +222 -0
  37. package/docs/type-utilities.md +250 -0
  38. package/docs/ui-data.md +333 -0
  39. package/docs/ui-form.md +502 -0
  40. package/docs/ui-layout.md +140 -0
  41. package/docs/ui-navigation.md +303 -0
  42. package/docs/ui-overlay.md +157 -0
  43. package/docs/ui-visual.md +127 -0
  44. package/docs/utils.md +244 -0
  45. package/package.json +21 -20
  46. package/scss/commons/_styles.scss +12 -0
  47. package/src/controls/dropdown/sd-dropdown-popup.ts +6 -2
  48. package/src/controls/dropdown/sd-dropdown.ts +3 -4
  49. package/src/controls/select/sd-select.ts +6 -30
  50. package/src/core/routing/injectViewTypeSignal.ts +17 -10
  51. package/src/core/selection/useSelectionManager.ts +62 -12
  52. package/src/data/shared-data/sd-shared-data-select-button.ts +49 -14
  53. package/src/data/sheet/sd-sheet.ts +21 -0
  54. package/src/index.ts +0 -32
  55. package/dist/core/commons.d.ts +0 -2
  56. package/dist/core/commons.d.ts.map +0 -1
  57. package/dist/core/commons.js +0 -4
  58. package/dist/core/injectParent.d.ts +0 -7
  59. package/dist/core/injectParent.d.ts.map +0 -1
  60. package/dist/core/injectParent.js +0 -30
  61. package/dist/core/selection/setupCumulateSelectedKeys.d.ts +0 -9
  62. package/dist/core/selection/setupCumulateSelectedKeys.d.ts.map +0 -1
  63. package/dist/core/selection/setupCumulateSelectedKeys.js +0 -27
  64. package/dist/core/withBusy.d.ts +0 -3
  65. package/dist/core/withBusy.d.ts.map +0 -1
  66. package/dist/core/withBusy.js +0 -9
  67. package/dist/data/data-detail/sd-data-detail.base.d.ts +0 -51
  68. package/dist/data/data-detail/sd-data-detail.base.d.ts.map +0 -1
  69. package/dist/data/data-detail/sd-data-detail.base.js +0 -127
  70. package/dist/data/data-detail/sd-data-detail.d.ts +0 -30
  71. package/dist/data/data-detail/sd-data-detail.d.ts.map +0 -1
  72. package/dist/data/data-detail/sd-data-detail.js +0 -495
  73. package/dist/data/data-select-button/sd-data-select-button.base.d.ts +0 -24
  74. package/dist/data/data-select-button/sd-data-select-button.base.d.ts.map +0 -1
  75. package/dist/data/data-select-button/sd-data-select-button.base.js +0 -68
  76. package/dist/data/data-select-button/sd-data-select-button.d.ts +0 -15
  77. package/dist/data/data-select-button/sd-data-select-button.d.ts.map +0 -1
  78. package/dist/data/data-select-button/sd-data-select-button.js +0 -152
  79. package/dist/data/data-sheet/injectDataSheetExcelManager.d.ts +0 -14
  80. package/dist/data/data-sheet/injectDataSheetExcelManager.d.ts.map +0 -1
  81. package/dist/data/data-sheet/injectDataSheetExcelManager.js +0 -31
  82. package/dist/data/data-sheet/injectDataSheetInlineEditManager.d.ts +0 -26
  83. package/dist/data/data-sheet/injectDataSheetInlineEditManager.d.ts.map +0 -1
  84. package/dist/data/data-sheet/injectDataSheetInlineEditManager.js +0 -54
  85. package/dist/data/data-sheet/injectDataSheetModalEditManager.d.ts +0 -19
  86. package/dist/data/data-sheet/injectDataSheetModalEditManager.d.ts.map +0 -1
  87. package/dist/data/data-sheet/injectDataSheetModalEditManager.js +0 -44
  88. package/dist/data/data-sheet/injectDataSheetRefreshManager.d.ts +0 -25
  89. package/dist/data/data-sheet/injectDataSheetRefreshManager.d.ts.map +0 -1
  90. package/dist/data/data-sheet/injectDataSheetRefreshManager.js +0 -50
  91. package/dist/data/data-sheet/sd-data-sheet-column.d.ts +0 -8
  92. package/dist/data/data-sheet/sd-data-sheet-column.d.ts.map +0 -1
  93. package/dist/data/data-sheet/sd-data-sheet-column.js +0 -15
  94. package/dist/data/data-sheet/sd-data-sheet.base.d.ts +0 -75
  95. package/dist/data/data-sheet/sd-data-sheet.base.d.ts.map +0 -1
  96. package/dist/data/data-sheet/sd-data-sheet.base.js +0 -188
  97. package/dist/data/data-sheet/sd-data-sheet.d.ts +0 -49
  98. package/dist/data/data-sheet/sd-data-sheet.d.ts.map +0 -1
  99. package/dist/data/data-sheet/sd-data-sheet.js +0 -968
  100. package/dist/data/data-sheet/sd-data-sheet.types.d.ts +0 -17
  101. package/dist/data/data-sheet/sd-data-sheet.types.d.ts.map +0 -1
  102. package/dist/data/data-sheet/sd-data-sheet.types.js +0 -1
  103. package/dist/data/data-sheet/setupCloserWhenSingleSelectionChange.d.ts +0 -9
  104. package/dist/data/data-sheet/setupCloserWhenSingleSelectionChange.d.ts.map +0 -1
  105. package/dist/data/data-sheet/setupCloserWhenSingleSelectionChange.js +0 -20
  106. package/dist/data/data-sheet/useDataSheetFilterManager.d.ts +0 -13
  107. package/dist/data/data-sheet/useDataSheetFilterManager.d.ts.map +0 -1
  108. package/dist/data/data-sheet/useDataSheetFilterManager.js +0 -19
  109. package/dist/data/getOrmDataEditToastErrorMessage.d.ts +0 -2
  110. package/dist/data/getOrmDataEditToastErrorMessage.d.ts.map +0 -1
  111. package/dist/data/getOrmDataEditToastErrorMessage.js +0 -8
  112. package/dist/layout/base-container/sd-base-container.d.ts +0 -27
  113. package/dist/layout/base-container/sd-base-container.d.ts.map +0 -1
  114. package/dist/layout/base-container/sd-base-container.js +0 -193
  115. package/src/core/commons.ts +0 -4
  116. package/src/core/injectParent.ts +0 -45
  117. package/src/core/selection/setupCumulateSelectedKeys.ts +0 -38
  118. package/src/core/withBusy.ts +0 -13
  119. package/src/data/data-detail/sd-data-detail.base.ts +0 -187
  120. package/src/data/data-detail/sd-data-detail.ts +0 -237
  121. package/src/data/data-select-button/sd-data-select-button.base.ts +0 -104
  122. package/src/data/data-select-button/sd-data-select-button.ts +0 -105
  123. package/src/data/data-sheet/injectDataSheetExcelManager.ts +0 -57
  124. package/src/data/data-sheet/injectDataSheetInlineEditManager.ts +0 -89
  125. package/src/data/data-sheet/injectDataSheetModalEditManager.ts +0 -76
  126. package/src/data/data-sheet/injectDataSheetRefreshManager.ts +0 -90
  127. package/src/data/data-sheet/sd-data-sheet-column.ts +0 -10
  128. package/src/data/data-sheet/sd-data-sheet.base.ts +0 -294
  129. package/src/data/data-sheet/sd-data-sheet.ts +0 -479
  130. package/src/data/data-sheet/sd-data-sheet.types.ts +0 -18
  131. package/src/data/data-sheet/setupCloserWhenSingleSelectionChange.ts +0 -29
  132. package/src/data/data-sheet/useDataSheetFilterManager.ts +0 -30
  133. package/src/data/getOrmDataEditToastErrorMessage.ts +0 -10
  134. package/src/layout/base-container/sd-base-container.ts +0 -121
@@ -0,0 +1,502 @@
1
+ # UI - Form
2
+
3
+ ## Buttons
4
+
5
+ ### `SdButton`
6
+
7
+ 버튼 컴포넌트.
8
+
9
+ ```typescript
10
+ @Component({ selector: "sd-button" })
11
+ class SdButton {
12
+ type = input<"button" | "submit">("button");
13
+ theme = input<"primary" | "secondary" | "info" | ... | "link" | "link-primary" | ... | "link-rev">();
14
+ inline = input(false, { transform: booleanAttribute });
15
+ inset = input(false, { transform: booleanAttribute });
16
+ size = input<"sm" | "lg">();
17
+ disabled = input(false, { transform: booleanAttribute });
18
+ buttonStyle = input<string>();
19
+ buttonClass = input<string>();
20
+ }
21
+ ```
22
+
23
+ | Input | Type | Default | Description |
24
+ |-------|------|---------|-------------|
25
+ | `type` | `"button" \| "submit"` | `"button"` | 버튼 HTML 타입 |
26
+ | `theme` | `string \| undefined` | `undefined` | 테마 (primary, secondary, info, success, warning, danger, gray, blue-gray, link, link-primary, link-secondary, link-info, link-success, link-warning, link-danger, link-gray, link-blue-gray, link-rev) |
27
+ | `inline` | `boolean` | `false` | 인라인 표시 |
28
+ | `inset` | `boolean` | `false` | 테두리 없는 삽입 스타일 |
29
+ | `size` | `"sm" \| "lg" \| undefined` | `undefined` | 크기 |
30
+ | `disabled` | `boolean` | `false` | 비활성화 |
31
+ | `buttonStyle` | `string \| undefined` | `undefined` | 버튼 인라인 스타일 |
32
+ | `buttonClass` | `string \| undefined` | `undefined` | 버튼 CSS 클래스 |
33
+
34
+ 호스트 속성: `data-sd-theme`, `data-sd-inline`, `data-sd-size`, `data-sd-inset`, `data-sd-disabled`
35
+
36
+ **스타일 적용**: 배경/색상/테두리/패딩 등 시각적 스타일은 내부 `<button>`에 적용되므로 `buttonClass`/`buttonStyle`을 사용한다. 호스트(`<sd-button>`)에 직접 `class`/`style`을 줘도 버튼 외형은 변경되지 않는다.
37
+
38
+ ### `SdAnchor`
39
+
40
+ 앵커(인라인 버튼) 컴포넌트. 텍스트 내 클릭 가능 요소.
41
+
42
+ ```typescript
43
+ @Component({ selector: "sd-anchor" })
44
+ class SdAnchor {
45
+ disabled = input(false, { transform: booleanAttribute });
46
+ theme = input<"primary" | "secondary" | "info" | "success" | "warning" | "danger" | "gray" | "blue-gray">("primary");
47
+ }
48
+ ```
49
+
50
+ | Input | Type | Default | Description |
51
+ |-------|------|---------|-------------|
52
+ | `disabled` | `boolean` | `false` | 비활성화 |
53
+ | `theme` | `string` | `"primary"` | 테마 색상 |
54
+
55
+ ### `SdAdditionalButton`
56
+
57
+ 추가 동작 버튼. 드롭다운 포함.
58
+
59
+ ```typescript
60
+ @Component({ selector: "sd-additional-button" })
61
+ class SdAdditionalButton {
62
+ size = input<"sm" | "lg">();
63
+ inset = input(false, { transform: booleanAttribute });
64
+ }
65
+ ```
66
+
67
+ ### `SdModalSelectButton`
68
+
69
+ 모달을 열어 선택하는 버튼 컴포넌트. 선택/지우기 버튼과 값 표시 영역으로 구성.
70
+
71
+ ```typescript
72
+ @Component({ selector: "sd-modal-select-button" })
73
+ class SdModalSelectButton<T extends object, K, M extends keyof SelectModeValue<K>> {
74
+ modal = input.required<SdSelectModalInfo<SdSelectModal<T>>>();
75
+ value = model<SelectModeValue<K>[M]>();
76
+ selectedItems = model<T[]>([]);
77
+ disabled = input(false, { transform: booleanAttribute });
78
+ required = input(false, { transform: booleanAttribute });
79
+ inset = input(false, { transform: booleanAttribute });
80
+ size = input<"sm" | "lg">();
81
+ selectMode = input<M>("single" as M);
82
+ modalOptions = input<SdModalOptions>();
83
+ searchIcon = input(tablerSearch);
84
+ }
85
+ ```
86
+
87
+ | Input | Type | Default | Description |
88
+ |-------|------|---------|-------------|
89
+ | `modal` | `SdSelectModalInfo<...>` | required | 모달 정보 |
90
+ | `value` | `SelectModeValue<K>[M]` | - | 선택된 값 (two-way) |
91
+ | `selectedItems` | `T[]` | `[]` | 선택된 항목 객체 배열 (two-way) |
92
+ | `disabled` | `boolean` | `false` | 비활성화 |
93
+ | `required` | `boolean` | `false` | 필수 (지우기 버튼 숨김) |
94
+ | `inset` | `boolean` | `false` | 삽입 스타일 |
95
+ | `size` | `"sm" \| "lg" \| undefined` | `undefined` | 크기 |
96
+ | `selectMode` | `M` | `"single"` | 선택 모드 |
97
+ | `modalOptions` | `SdModalOptions \| undefined` | `undefined` | 모달 옵션 |
98
+ | `searchIcon` | `string` | `tablerSearch` | 검색 아이콘 |
99
+
100
+ ## Inputs
101
+
102
+ ### `SdTextfield`
103
+
104
+ 텍스트 입력 컴포넌트. 13가지 타입을 지원한다.
105
+
106
+ ```typescript
107
+ @Component({ selector: "sd-textfield" })
108
+ class SdTextfield<K extends keyof SdTextfieldTypes> {
109
+ value = model<SdTextfieldTypes[K]>();
110
+ type = input.required<K>();
111
+ placeholder = input<string>();
112
+ title = input<string>();
113
+ inputStyle = input<string>();
114
+ inputClass = input<string>();
115
+ disabled = input(false, { transform: booleanAttribute });
116
+ readonly = input(false, { transform: booleanAttribute });
117
+ required = input(false, { transform: booleanAttribute });
118
+ min = input<SdTextfieldTypes[K]>();
119
+ max = input<SdTextfieldTypes[K]>();
120
+ minlength = input<number>();
121
+ maxlength = input<number>();
122
+ pattern = input<string>();
123
+ validatorFn = input<(value: SdTextfieldTypes[K] | undefined) => string | undefined>();
124
+ format = input<string>();
125
+ step = input<number>();
126
+ autocomplete = input<string>();
127
+ useNumberComma = input(true, { transform: booleanAttribute });
128
+ minDigits = input<number>();
129
+ inline = input(false, { transform: booleanAttribute });
130
+ inset = input(false, { transform: booleanAttribute });
131
+ size = input<"sm" | "lg">();
132
+ theme = input<string>();
133
+ }
134
+ ```
135
+
136
+ | Input | Type | Default | Description |
137
+ |-------|------|---------|-------------|
138
+ | `type` | `keyof SdTextfieldTypes` | required | 입력 타입 (number, text, password, color, email, format, date, month, year, datetime, datetime-sec, time, time-sec) |
139
+ | `value` | `SdTextfieldTypes[K] \| undefined` | - | 값 (two-way) |
140
+ | `placeholder` | `string \| undefined` | `undefined` | 플레이스홀더 |
141
+ | `title` | `string \| undefined` | `undefined` | title 속성 (없으면 placeholder 사용) |
142
+ | `inputStyle` | `string \| undefined` | `undefined` | input 인라인 스타일 |
143
+ | `inputClass` | `string \| undefined` | `undefined` | input CSS 클래스 |
144
+ | `disabled` | `boolean` | `false` | 비활성화 |
145
+ | `readonly` | `boolean` | `false` | 읽기 전용 |
146
+ | `required` | `boolean` | `false` | 필수 |
147
+ | `min` | `SdTextfieldTypes[K] \| undefined` | `undefined` | 최소값 |
148
+ | `max` | `SdTextfieldTypes[K] \| undefined` | `undefined` | 최대값 |
149
+ | `minlength` | `number \| undefined` | `undefined` | 최소 길이 |
150
+ | `maxlength` | `number \| undefined` | `undefined` | 최대 길이 |
151
+ | `pattern` | `string \| undefined` | `undefined` | 입력 패턴 (정규식) |
152
+ | `validatorFn` | `((value) => string \| undefined) \| undefined` | `undefined` | 커스텀 유효성 검증 함수 (에러 메시지 반환) |
153
+ | `format` | `string \| undefined` | `undefined` | format 타입에서 사용할 포맷 문자열 |
154
+ | `step` | `number \| undefined` | `undefined` | 증감 단위 |
155
+ | `autocomplete` | `string \| undefined` | `undefined` | autocomplete 속성 |
156
+ | `useNumberComma` | `boolean` | `true` | number 타입에서 천 단위 쉼표 사용 |
157
+ | `minDigits` | `number \| undefined` | `undefined` | number 타입 최소 자릿수 |
158
+ | `inline` | `boolean` | `false` | 인라인 표시 |
159
+ | `inset` | `boolean` | `false` | 삽입 스타일 |
160
+ | `size` | `"sm" \| "lg" \| undefined` | `undefined` | 크기 |
161
+ | `theme` | `string \| undefined` | `undefined` | 테마 |
162
+
163
+ **스타일 적용**: 배경/테두리/패딩/텍스트 정렬 등 입력 영역 스타일은 내부 `<input>`(및 readonly/disabled 시 표시되는 `._contents`)에 적용되므로 `inputClass`/`inputStyle`을 사용한다. 호스트(`<sd-textfield>`)에 직접 `class`/`style`을 줘도 입력 영역 외형은 변경되지 않는다. 미리 정의된 테마 색상은 `theme` input으로 지정한다.
164
+
165
+ ### `SdTextarea`
166
+
167
+ 멀티라인 텍스트 입력 컴포넌트.
168
+
169
+ ```typescript
170
+ @Component({ selector: "sd-textarea" })
171
+ class SdTextarea {
172
+ value = model<string>();
173
+ placeholder = input<string>();
174
+ title = input<string>();
175
+ minRows = input<number>(1);
176
+ disabled = input(false, { transform: booleanAttribute });
177
+ readonly = input(false, { transform: booleanAttribute });
178
+ required = input(false, { transform: booleanAttribute });
179
+ inline = input(false, { transform: booleanAttribute });
180
+ inset = input(false, { transform: booleanAttribute });
181
+ size = input<"sm" | "lg">();
182
+ validatorFn = input<(value: string | undefined) => string | undefined>();
183
+ theme = input<string>();
184
+ inputStyle = input<string>();
185
+ inputClass = input<string>();
186
+ }
187
+ ```
188
+
189
+ **스타일 적용**: 배경/테두리/패딩 등 입력 영역 스타일은 내부 `<textarea>`(및 readonly/disabled 시 표시되는 `._contents`)에 적용되므로 `inputClass`/`inputStyle`을 사용한다. 호스트(`<sd-textarea>`)에 직접 `class`/`style`을 줘도 입력 영역 외형은 변경되지 않는다. 미리 정의된 테마 색상은 `theme` input으로 지정한다.
190
+
191
+ ### `SdNumpad`
192
+
193
+ 숫자 패드 컴포넌트.
194
+
195
+ ```typescript
196
+ @Component({ selector: "sd-numpad" })
197
+ class SdNumpad {
198
+ placeholder = input<string>();
199
+ value = model<number>();
200
+ required = input(false, { transform: booleanAttribute });
201
+ inputDisabled = input(false, { transform: booleanAttribute });
202
+ useEnterButton = input(false, { transform: booleanAttribute });
203
+ useMinusButton = input(false, { transform: booleanAttribute });
204
+
205
+ enterButtonClick = output();
206
+ }
207
+ ```
208
+
209
+ | Input | Type | Default | Description |
210
+ |-------|------|---------|-------------|
211
+ | `placeholder` | `string \| undefined` | `undefined` | 플레이스홀더 |
212
+ | `value` | `number \| undefined` | - | 값 (two-way) |
213
+ | `required` | `boolean` | `false` | 필수 |
214
+ | `inputDisabled` | `boolean` | `false` | 입력 필드 비활성화 |
215
+ | `useEnterButton` | `boolean` | `false` | 엔터 버튼 표시 |
216
+ | `useMinusButton` | `boolean` | `false` | 마이너스 버튼 표시 |
217
+
218
+ | Output | Type | Description |
219
+ |--------|------|-------------|
220
+ | `enterButtonClick` | `void` | 엔터 버튼 클릭 시 발생 |
221
+
222
+ ### `SdRange`
223
+
224
+ 범위 슬라이더 컴포넌트.
225
+
226
+ ```typescript
227
+ @Component({ selector: "sd-range" })
228
+ class SdRange<K extends keyof SdTextfieldTypes> {
229
+ type = input.required<K>();
230
+ from = model<SdTextfieldTypes[K]>();
231
+ to = model<SdTextfieldTypes[K]>();
232
+ inputStyle = input<string>();
233
+ required = input(false, { transform: booleanAttribute });
234
+ disabled = input(false, { transform: booleanAttribute });
235
+ }
236
+ ```
237
+
238
+ **스타일 적용**: 입력 요소 스타일은 내부 `<input>`에 적용되므로 `inputStyle`을 사용한다(클래스 input 없음). 호스트(`<sd-range>`)에 직접 `class`/`style`을 줘도 입력 영역 외형은 변경되지 않는다.
239
+
240
+ ### `SdDateRangePicker`
241
+
242
+ 날짜 범위 선택기.
243
+
244
+ ```typescript
245
+ @Component({ selector: "sd-date-range-picker" })
246
+ class SdDateRangePicker {
247
+ periodType = model<"일" | "월" | "범위">("범위");
248
+ from = model<DateOnly>();
249
+ to = model<DateOnly>();
250
+ required = input(false, { transform: booleanAttribute });
251
+ }
252
+ ```
253
+
254
+ | Input | Type | Default | Description |
255
+ |-------|------|---------|-------------|
256
+ | `periodType` | `"일" \| "월" \| "범위"` | `"범위"` | 기간 선택 타입 (two-way) |
257
+ | `from` | `DateOnly \| undefined` | - | 시작일 (two-way) |
258
+ | `to` | `DateOnly \| undefined` | - | 종료일 (two-way) |
259
+ | `required` | `boolean` | `false` | 필수 |
260
+
261
+ ## Choice
262
+
263
+ ### `SdStatePreset`
264
+
265
+ 상태 프리셋 저장/불러오기 컴포넌트. 사용자 설정을 저장하고 복원한다.
266
+
267
+ ```typescript
268
+ @Component({ selector: "sd-state-preset" })
269
+ class SdStatePreset {
270
+ key = input.required<string>();
271
+ state = model<any>();
272
+ size = input<"sm" | "lg">();
273
+ }
274
+ ```
275
+
276
+ | Input | Type | Default | Description |
277
+ |-------|------|---------|-------------|
278
+ | `key` | `string` | required | 프리셋 저장 키 |
279
+ | `state` | `any` | - | 상태 데이터 (two-way) |
280
+ | `size` | `"sm" \| "lg" \| undefined` | `undefined` | 크기 |
281
+
282
+ ### `SdStatePresetDef`
283
+
284
+ ```typescript
285
+ interface SdStatePresetDef {
286
+ name: string;
287
+ state: any;
288
+ }
289
+ ```
290
+
291
+ | Field | Type | Description |
292
+ |-------|------|-------------|
293
+ | `name` | `string` | 프리셋 이름 |
294
+ | `state` | `any` | 저장된 상태 데이터 |
295
+
296
+ ## Checkbox
297
+
298
+ ### `SdCheckbox`
299
+
300
+ 체크박스 컴포넌트.
301
+
302
+ ```typescript
303
+ @Component({ selector: "sd-checkbox" })
304
+ class SdCheckbox {
305
+ value = model(false);
306
+ canChangeFn = input<(item: boolean) => boolean | Promise<boolean>>(() => true);
307
+ icon = input(tablerCheck);
308
+ radio = input(false, { transform: booleanAttribute });
309
+ disabled = input(false, { transform: booleanAttribute });
310
+ size = input<"sm" | "lg">();
311
+ inline = input(false, { transform: booleanAttribute });
312
+ inset = input(false, { transform: booleanAttribute });
313
+ theme = input<"primary" | "secondary" | ...>();
314
+ contentStyle = input<string>();
315
+ }
316
+ ```
317
+
318
+ | Input | Type | Default | Description |
319
+ |-------|------|---------|-------------|
320
+ | `value` | `boolean` | `false` | 체크 여부 (two-way) |
321
+ | `canChangeFn` | `(item: boolean) => boolean \| Promise<boolean>` | `() => true` | 값 변경 가능 여부 함수 |
322
+ | `icon` | `string` | `tablerCheck` | 체크 아이콘 |
323
+ | `radio` | `boolean` | `false` | 라디오 버튼 스타일 |
324
+ | `disabled` | `boolean` | `false` | 비활성화 |
325
+ | `size` | `"sm" \| "lg" \| undefined` | `undefined` | 크기 |
326
+ | `inline` | `boolean` | `false` | 인라인 표시 |
327
+ | `inset` | `boolean` | `false` | 삽입 스타일 |
328
+ | `theme` | `string \| undefined` | `undefined` | 테마 색상 |
329
+ | `contentStyle` | `string \| undefined` | `undefined` | 컨텐츠 인라인 스타일 |
330
+
331
+ **스타일 적용**: `contentStyle`은 체크박스 옆 라벨 텍스트(`._contents`) 영역에만 적용된다. 인디케이터 박스의 배경/색상은 `theme` input으로 변경한다(별도 class/style input 없음). 호스트(`<sd-checkbox>`)에 직접 `class`/`style`을 주면 전체 영역 layout/여백 정도만 영향을 주며 인디케이터 외형은 바뀌지 않는다.
332
+
333
+ ### `SdSwitch`
334
+
335
+ 스위치 토글 컴포넌트.
336
+
337
+ ```typescript
338
+ @Component({ selector: "sd-switch" })
339
+ class SdSwitch {
340
+ value = model(false);
341
+ canChangeFn = input<(item: boolean) => boolean | Promise<boolean>>(() => true);
342
+ disabled = input(false, { transform: booleanAttribute });
343
+ inline = input(false, { transform: booleanAttribute });
344
+ inset = input(false, { transform: booleanAttribute });
345
+ size = input<"sm" | "lg">();
346
+ theme = input<string>();
347
+ }
348
+ ```
349
+
350
+ ### `SdCheckboxGroup`
351
+
352
+ 체크박스 그룹 컴포넌트.
353
+
354
+ ```typescript
355
+ @Component({ selector: "sd-checkbox-group" })
356
+ class SdCheckboxGroup<T> {
357
+ value = model<T[]>([]);
358
+ disabled = input(false, { transform: booleanAttribute });
359
+ }
360
+ ```
361
+
362
+ ### `SdCheckboxGroupItem`
363
+
364
+ 체크박스 그룹 항목.
365
+
366
+ ```typescript
367
+ @Component({ selector: "sd-checkbox-group-item" })
368
+ class SdCheckboxGroupItem<T> {
369
+ value = input.required<T>();
370
+ inline = input(false, { transform: booleanAttribute });
371
+ }
372
+ ```
373
+
374
+ ## Editor
375
+
376
+ ### `SdTiptapEditor`
377
+
378
+ TipTap 기반 리치 텍스트 에디터.
379
+
380
+ ```typescript
381
+ @Component({ selector: "sd-tiptap-editor" })
382
+ class SdTiptapEditor {
383
+ value = model<string>();
384
+ disabled = input(false, { transform: booleanAttribute });
385
+ readonly = input(false, { transform: booleanAttribute });
386
+ required = input(false, { transform: booleanAttribute });
387
+ placeholder = input<string>();
388
+ validatorFn = input<(value: string | undefined) => string | undefined>();
389
+ extensions = input<AnyExtension[]>();
390
+ }
391
+ ```
392
+
393
+ | Input | Type | Default | Description |
394
+ |-------|------|---------|-------------|
395
+ | `value` | `string \| undefined` | - | HTML 콘텐츠 (two-way) |
396
+ | `disabled` | `boolean` | `false` | 비활성화 |
397
+ | `readonly` | `boolean` | `false` | 읽기 전용 |
398
+ | `required` | `boolean` | `false` | 필수 |
399
+ | `placeholder` | `string \| undefined` | `undefined` | 플레이스홀더 |
400
+ | `validatorFn` | `((value) => string \| undefined) \| undefined` | `undefined` | 커스텀 유효성 검증 함수 |
401
+ | `extensions` | `AnyExtension[] \| undefined` | `undefined` | 추가 TipTap 확장 |
402
+
403
+ ## Select
404
+
405
+ ### `SdSelect`
406
+
407
+ 드롭다운 선택 컴포넌트. single/multi 모드를 지원한다.
408
+
409
+ ```typescript
410
+ @Component({ selector: "sd-select" })
411
+ class SdSelect<T, M extends keyof SelectModeValue<T>> {
412
+ selectMode = input("single" as M);
413
+ value = model<SelectModeValue<any>[M]>();
414
+ placeholder = input<string>();
415
+ disabled = input(false, { transform: booleanAttribute });
416
+ inline = input(false, { transform: booleanAttribute });
417
+ inset = input(false, { transform: booleanAttribute });
418
+ size = input<"sm" | "lg">();
419
+ required = input(false, { transform: booleanAttribute });
420
+ hideSelectAll = input(false, { transform: booleanAttribute });
421
+ multiSelectionDisplayDirection = input<"vertical">();
422
+ items = input<T[]>();
423
+ getChildrenFn = input<(item: T) => T[] | undefined>();
424
+ contentClass = input<string>();
425
+ contentStyle = input<string>();
426
+ }
427
+ ```
428
+
429
+ | Input | Type | Default | Description |
430
+ |-------|------|---------|-------------|
431
+ | `selectMode` | `M` | `"single"` | 선택 모드 |
432
+ | `value` | `SelectModeValue<any>[M]` | - | 선택된 값 (two-way) |
433
+ | `placeholder` | `string \| undefined` | `undefined` | 플레이스홀더 |
434
+ | `disabled` | `boolean` | `false` | 비활성화 |
435
+ | `inline` | `boolean` | `false` | 인라인 표시 |
436
+ | `inset` | `boolean` | `false` | 삽입 스타일 |
437
+ | `size` | `"sm" \| "lg" \| undefined` | `undefined` | 크기 |
438
+ | `required` | `boolean` | `false` | 필수 |
439
+ | `hideSelectAll` | `boolean` | `false` | multi 모드에서 전체 선택 숨김 |
440
+ | `multiSelectionDisplayDirection` | `"vertical" \| undefined` | `undefined` | multi 모드 표시 방향 |
441
+ | `items` | `T[] \| undefined` | `undefined` | 항목 배열 (selectMode가 "multi"일 때 내부 목록 렌더링용) |
442
+ | `getChildrenFn` | `((item) => T[] \| undefined) \| undefined` | `undefined` | 트리 구조 자식 함수 |
443
+ | `contentClass` | `string \| undefined` | `undefined` | 선택값이 표시되는 영역에 추가할 CSS 클래스 |
444
+ | `contentStyle` | `string \| undefined` | `undefined` | 선택값이 표시되는 영역에 적용할 인라인 스타일 |
445
+
446
+ **스타일 적용**: `contentClass`/`contentStyle`은 트리거 영역(선택값 텍스트와 드롭다운 화살표가 함께 놓인 박스)에만 적용된다. 드롭다운으로 펼쳐지는 팝업 영역은 이 input으로 스타일링할 수 없다. 호스트(`<sd-select>`)에 직접 `class`/`style`을 주면 전체 영역 layout/여백/너비 정도에만 영향을 준다.
447
+
448
+ ### `SelectModeValue`
449
+
450
+ ```typescript
451
+ type SelectModeValue<T> = {
452
+ multi: T[];
453
+ single: T;
454
+ }
455
+ ```
456
+
457
+ ### `SdSelectItem`
458
+
459
+ 드롭다운 선택 항목.
460
+
461
+ ```typescript
462
+ @Component({ selector: "sd-select-item" })
463
+ class SdSelectItem<T> {
464
+ value = input.required<T>();
465
+ disabled = input(false, { transform: booleanAttribute });
466
+ hidden = input(false, { transform: booleanAttribute });
467
+ }
468
+ ```
469
+
470
+ ### `SdSelectButton`
471
+
472
+ 버튼 스타일 선택 컴포넌트.
473
+
474
+ ```typescript
475
+ @Component({ selector: "sd-select-button" })
476
+ class SdSelectButton<T> { }
477
+ ```
478
+
479
+ ## Form
480
+
481
+ ### `SdForm`
482
+
483
+ 폼 래퍼 컴포넌트. `<form>` 태그를 렌더링하며 submit 이벤트 처리 및 유효성 검증을 수행한다.
484
+
485
+ ```typescript
486
+ @Component({ selector: "sd-form" })
487
+ class SdForm {
488
+ formSubmit = output<SubmitEvent>();
489
+ formInvalid = output();
490
+
491
+ requestSubmit(): void;
492
+ }
493
+ ```
494
+
495
+ | Output | Type | Description |
496
+ |--------|------|-------------|
497
+ | `formSubmit` | `SubmitEvent` | 유효성 검증 통과 시 발생 |
498
+ | `formInvalid` | `void` | 유효성 검증 실패 시 발생 (`reportValidity()` 호출 후) |
499
+
500
+ | Method | Description |
501
+ |--------|-------------|
502
+ | `requestSubmit()` | 프로그래밍 방식으로 submit 트리거 |
@@ -0,0 +1,140 @@
1
+ # UI - Layout
2
+
3
+ ## `SdDockContainer`
4
+
5
+ 도킹 레이아웃 컨테이너. `SdDock`과 함께 사용하여 상/하/좌/우 고정 영역을 만든다.
6
+
7
+ ```typescript
8
+ @Component({ selector: "sd-dock-container" })
9
+ class SdDockContainer { }
10
+ ```
11
+
12
+ ## `SdDock`
13
+
14
+ 도킹 영역 컴포넌트. `SdDockContainer` 내부에서 사용.
15
+
16
+ ```typescript
17
+ @Component({ selector: "sd-dock" })
18
+ class SdDock {
19
+ key = input<string>();
20
+ position = input<"top" | "bottom" | "right" | "left">("top");
21
+ resizable = input(false, { transform: booleanAttribute });
22
+ }
23
+ ```
24
+
25
+ | Input | Type | Default | Description |
26
+ |-------|------|---------|-------------|
27
+ | `key` | `string \| undefined` | `undefined` | 리사이즈 설정 저장 키 |
28
+ | `position` | `"top" \| "bottom" \| "right" \| "left"` | `"top"` | 도킹 위치 |
29
+ | `resizable` | `boolean` | `false` | 크기 조절 가능 여부 |
30
+
31
+ ## `SdGap`
32
+
33
+ 간격(gap) 컴포넌트. 요소 사이에 공간을 추가한다.
34
+
35
+ ```typescript
36
+ @Component({ selector: "sd-gap" })
37
+ class SdGap { }
38
+ ```
39
+
40
+ ## `SdKanbanBoard`
41
+
42
+ 칸반 보드 컨테이너. 드래그앤드롭을 지원한다.
43
+
44
+ ```typescript
45
+ @Component({ selector: "sd-kanban-board" })
46
+ class SdKanbanBoard<L, T> {
47
+ selectedValues = model<T[]>([]);
48
+ drop = output<SdKanbanBoardDropInfo<L, T>>();
49
+ }
50
+ ```
51
+
52
+ | Input | Type | Default | Description |
53
+ |-------|------|---------|-------------|
54
+ | `selectedValues` | `T[]` | `[]` | 선택된 칸반 아이템 값 배열 (two-way) |
55
+
56
+ ### `SdKanbanBoardDropInfo`
57
+
58
+ ```typescript
59
+ interface SdKanbanBoardDropInfo<L, T> {
60
+ sourceKanbanValue?: T;
61
+ targetLaneValue?: L;
62
+ targetKanbanValue?: T;
63
+ }
64
+ ```
65
+
66
+ | Field | Type | Description |
67
+ |-------|------|-------------|
68
+ | `sourceKanbanValue` | `T \| undefined` | 드래그한 칸반 아이템의 값 |
69
+ | `targetLaneValue` | `L \| undefined` | 드롭 대상 레인의 값 |
70
+ | `targetKanbanValue` | `T \| undefined` | 드롭 대상 칸반 아이템의 값 (칸반 위에 드롭 시) |
71
+
72
+ ### `SdKanbanDragRef`
73
+
74
+ ```typescript
75
+ interface SdKanbanDragRef<_L, T> {
76
+ value(): T | undefined;
77
+ heightOnDrag(): number;
78
+ }
79
+ ```
80
+
81
+ | Field | Type | Description |
82
+ |-------|------|-------------|
83
+ | `value()` | `T \| undefined` | 드래그 중인 칸반 아이템의 값 |
84
+ | `heightOnDrag()` | `number` | 드래그 시작 시점의 요소 높이 |
85
+
86
+ ### `SdKanbanDropTarget`
87
+
88
+ ```typescript
89
+ interface SdKanbanDropTarget<L, T> {
90
+ targetLaneValue(): L | undefined;
91
+ targetKanbanValue?(): T | undefined;
92
+ }
93
+ ```
94
+
95
+ | Field | Type | Description |
96
+ |-------|------|-------------|
97
+ | `targetLaneValue()` | `L \| undefined` | 드롭 대상 레인의 값 |
98
+ | `targetKanbanValue?()` | `T \| undefined` | 드롭 대상 칸반 아이템의 값 (optional) |
99
+
100
+ ## `SdKanban`
101
+
102
+ 칸반 아이템 컴포넌트.
103
+
104
+ ```typescript
105
+ @Component({ selector: "sd-kanban" })
106
+ class SdKanban<L, T> {
107
+ value = input<T>();
108
+ selectable = input(false, { transform: booleanAttribute });
109
+ draggable = input(false, { transform: booleanAttribute });
110
+ contentClass = input<string>();
111
+ }
112
+ ```
113
+
114
+ | Input | Type | Default | Description |
115
+ |-------|------|---------|-------------|
116
+ | `value` | `T \| undefined` | `undefined` | 칸반 아이템의 값 |
117
+ | `selectable` | `boolean` | `false` | 선택 가능 여부 |
118
+ | `draggable` | `boolean` | `false` | 드래그 가능 여부 |
119
+ | `contentClass` | `string \| undefined` | `undefined` | 컨텐츠 CSS 클래스 |
120
+
121
+ ## `SdKanbanLane`
122
+
123
+ 칸반 레인 컴포넌트.
124
+
125
+ ```typescript
126
+ @Component({ selector: "sd-kanban-lane" })
127
+ class SdKanbanLane<L, T> {
128
+ busy = input(false, { transform: booleanAttribute });
129
+ useCollapse = input(false, { transform: booleanAttribute });
130
+ collapse = model(false);
131
+ value = input<L>();
132
+ }
133
+ ```
134
+
135
+ | Input | Type | Default | Description |
136
+ |-------|------|---------|-------------|
137
+ | `busy` | `boolean` | `false` | busy 상태 |
138
+ | `useCollapse` | `boolean` | `false` | 접기 기능 사용 |
139
+ | `collapse` | `boolean` | `false` | 접힘 상태 (two-way) |
140
+ | `value` | `L \| undefined` | `undefined` | 레인의 값 |