@simplysm/sd-claude 14.0.93 → 14.0.95

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 (32) hide show
  1. package/claude/references/sd-simplysm14/README.md +10 -6
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +180 -43
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +275 -125
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +54 -59
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +139 -48
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +102 -88
  7. package/claude/references/sd-simplysm14/apis/angular/kanban.md +54 -0
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +60 -36
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +127 -75
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +97 -51
  11. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +74 -58
  12. package/claude/references/sd-simplysm14/apis/angular/sheet.md +81 -60
  13. package/claude/references/sd-simplysm14/apis/excel/README.md +5 -5
  14. package/claude/references/sd-simplysm14/apis/excel/cell.md +3 -3
  15. package/claude/references/sd-simplysm14/apis/excel/style.md +2 -2
  16. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +5 -4
  17. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +2 -2
  18. package/claude/references/sd-simplysm14/manuals/client-app-structure.md +4 -2
  19. package/claude/references/sd-simplysm14/manuals/client-component.md +24 -24
  20. package/claude/references/sd-simplysm14/manuals/client-crud.md +328 -0
  21. package/claude/references/sd-simplysm14/manuals/client-demo.md +6 -19
  22. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +49 -0
  23. package/claude/references/sd-simplysm14/manuals/data-log.md +33 -1
  24. package/claude/references/sd-simplysm14/manuals/orm.md +37 -0
  25. package/claude/sd-system-prompt.md +11 -0
  26. package/claude/skills/sd-debug/SKILL.md +142 -27
  27. package/claude/skills/sd-review/SKILL.md +158 -20
  28. package/claude/skills/sd-spec/SKILL.md +1 -0
  29. package/package.json +1 -1
  30. package/claude/references/sd-simplysm14/apis/angular/infra.md +0 -82
  31. package/claude/skills/sd-debug/workflow.js +0 -390
  32. package/claude/skills/sd-review/workflow.js +0 -324
@@ -1,146 +1,296 @@
1
1
  # @simplysm/angular — 폼·입력 컨트롤
2
2
 
3
- 버튼·앵커, 텍스트/숫자/날짜 입력, 체크박스/스위치, 셀렉트/드롭다운, 폼/접기/탭/리스트/페이지네이션 등 폼·UI 기본 컨트롤 군. 화면 폼·필터·시트 셀에서 함께 쓰임. 공통: 대부분 컨트롤이 `size: "sm"|"lg"`(미지정=기본), `inline`, `inset`(테두리 제거·셀 내장용), `disabled`, `theme` 을 가짐. 매뉴얼(client-component.md "표준 입력 컨트롤"·"버튼 스타일") 역할별 theme/size 규약을 따름.
3
+ 버튼·앵커, 텍스트/숫자/날짜 입력, 체크박스/스위치, 셀렉트/드롭다운, 폼/접기/탭/리스트/페이지네이션 등 폼·UI 기본 컨트롤 군. 화면 폼·필터·시트 셀에서 함께 쓰임. 공통: 대부분 컨트롤이 `size: "sm"|"lg"`(미지정=기본), `inline`(인라인 배치), `inset`(테두리 제거·셀 내장용), `disabled`, `theme` 을 가짐. 입력 컨트롤은 `model()` 양방향, 검증 컨트롤은 `required`/`validatorFn` 보유.
4
4
 
5
- 테마 계열(여러 컨트롤 공통): `"primary"|"secondary"|"info"|"success"|"warning"|"danger"|"gray"|"blue-gray"`. 버튼은 추가로 link 계열(`"link"`·`"link-primary"`…`"link-rev"`)을 가짐.
5
+ ## 버튼
6
6
 
7
- ## 버튼·앵커
7
+ ### SdButton — `<sd-button>`
8
8
 
9
- ### SdButton — `sd-button`
10
- - `type: "button"|"submit"` — 폼 제출 버튼이면 `"submit"`.
11
- - `theme` 위 테마 계열 + link 계열. 데이터 변경 액션은 일반 계열, 유틸/시트 버튼은 link 계열.
12
- - `size: "sm"|"lg"` — 크기. 시트 위 버튼은 `"sm"`.
13
- - `inline`/`inset`/`disabled: boolean` 인라인 배치/테두리 제거/비활성.
14
- - `buttonStyle`/`buttonClass: string` 내부 `<button>` 에 스타일/클래스 주입.
9
+ ```ts
10
+ type = input<"button" | "submit">("button");
11
+ theme = input<"primary"|"secondary"|"info"|"success"|"warning"|"danger"|"gray"|"blue-gray"
12
+ | "link"|"link-primary"|...|"link-blue-gray"|"link-rev">();
13
+ inline; inset; disabled; size = input<"xs"|"sm"|"lg">();
14
+ buttonStyle = input<string>(); buttonClass = input<string>();
15
+ ```
16
+
17
+ - `type` — `"submit"` 이면 폼 제출 트리거(sd-form 안에서). 기본 `"button"`.
18
+ - `theme` — 채움 테마 또는 `link-*`(테두리·배경 없는 링크형), `link-rev`(어두운 배경용 반전). 액션 강조면 채움, 보조면 link.
19
+ - `inset` — 테두리·라운드 제거 후 primary 텍스트색(셀·툴바 내장 버튼). `size="xs"` 는 가장 촘촘한 패딩.
20
+ - `buttonStyle`/`buttonClass` — 내부 `<button>` 에 직접 적용할 style/class 문자열.
21
+
22
+ ```html
23
+ <sd-button [type]="'submit'" [theme]="'primary'">저장</sd-button>
24
+ ```
25
+
26
+ ### SdAnchor — `<sd-anchor>`
15
27
 
16
- ### SdAnchor — `sd-anchor`
17
- - `disabled: boolean` — 비활성(클릭/포커스 차단).
18
- - `theme` 테마 계열(기본 `"primary"`). 텍스트 링크형 액션.
28
+ ```ts
29
+ disabled = input(false);
30
+ theme = input<"primary"|"secondary"|"info"|"success"|"warning"|"danger"|"gray"|"blue-gray">("primary");
31
+ ```
32
+
33
+ - 텍스트 링크형 클릭 요소. `disabled` 면 흐려지고 tabindex 제거. 아이콘/짧은 액션에 사용.
34
+
35
+ ### SdAdditionalButton — `<sd-additional-button>`
19
36
 
20
- ### SdAdditionalButton — `sd-additional-button`
21
- 입력 옆에 버튼을 붙이는 컨테이너.
22
- - `size: "sm"|"lg"` — 크기.
23
- - `inset: boolean` — 테두리 제거.
37
+ ```ts
38
+ size = input<"sm" | "lg">();
39
+ inset = input(false);
40
+ ```
41
+
42
+ - 본문(좌) + 우측 버튼 영역 묶음 컨트롤. `<sd-anchor>`/`<sd-button>` 을 콘텐츠로 투영하면 우측 버튼 영역에 배치. 값 표시 + 보조 액션 조합용.
24
43
 
25
- ### SdModalSelectButton<K>`sd-modal-select-button`
26
- 모달로 항목을 골라 값으로 받는 버튼(필수 검증 내장).
27
- - `modal: input.required<SdSelectModalInfo<...>>` — 선택 모달. `selectMode`/현재 선택키 주입.
28
- - `value: model<단일|배열>` — 선택 결과.
29
- - `selectMode: "single"|"multi"` — 선택 모드(기본 single).
30
- - `required`/`disabled`/`inset: boolean`, `size: "sm"|"lg"` — 상태/크기.
31
- - `modalOptions: SdModalOptions` 모달 옵션.
32
- - `searchIcon` — 검색 버튼 아이콘.
44
+ ### SdModalSelectButton — `<sd-modal-select-button>`
45
+
46
+ ```ts
47
+ modal = input.required<SdSelectModalInfo<SdSelectModal<K>>>();
48
+ value = model<SelectModeValue<K>[M]>();
49
+ disabled; required; inset; size = input<"sm"|"lg">();
50
+ selectMode = input<M>("single"); // "single" | "multi"
51
+ modalOptions = input<SdModalOptions>();
52
+ searchIcon = input(tablerSearch);
53
+ // SdSelectModal<TKey> = SdModalContentDef<SelectModalOutputResult<TKey>> + selectMode/selectedKeys inputs
54
+ // SdSelectModalInfo<T> = SdModalInfo<T, "selectMode" | "selectedKeys">
55
+ ```
56
+
57
+ - 모달로 선택해 값을 채우는 버튼. 검색 버튼 클릭 시 `modal` 을 띄워(현재 값·selectMode 자동 주입) 결과 `{ selectedKeys }` 로 `value` 갱신.
58
+ - `selectMode` — `"single"`=`value` 가 단일 키, `"multi"`=키 배열. 지우기(eraser) 버튼은 `required=false` 이고 값이 있을 때만 노출.
59
+ - `required` 면 빈 선택 시 native invalid(`setupInvalid`). `modal` 은 `SdSelectModal` 계약(selectMode input + selectedKeys model)을 구현한 모달.
33
60
 
34
61
  ## 텍스트·숫자·날짜 입력
35
62
 
36
- ### SdTextfield<K>`sd-textfield`
37
- 타입별(`type` 으로 결정) 단일 입력. `SdTextfieldTypes` 키로 값 타입 결정.
38
- - `type: input.required<K>` — `SdTextfieldTypes` 의 키. 값 타입을 결정.
39
- - `value: model<SdTextfieldTypes[K]>` — 값(타입에 따라 string/number/`DateOnly`/`DateTime`/`Time`).
40
- - `placeholder`/`title: string` 안내/타이틀.
41
- - `disabled`/`readonly`/`required: boolean` — 상태.
42
- - `min`/`max: SdTextfieldTypes[K]`, `minlength`/`maxlength`/`pattern`/`step` — 검증 제약.
43
- - `validatorFn: (value) => string | undefined` — 커스텀 검증(반환 문자열이 오류 메시지).
44
- - `format: string` `format` 타입의 마스킹 패턴.
45
- - `useNumberComma: boolean` 숫자 천단위 콤마(기본 true).
46
- - `minDigits: number` 숫자 표시 최소 자릿수.
47
- - `inline`/`inset: boolean`, `size: "sm"|"lg"`, `theme` 배치/크기/테마.
48
- - `inputStyle`/`inputClass`/`autocomplete` — 내부 input 속성.
49
-
50
- `SdTextfieldTypes` 키: `number`(number), `text`/`password`/`color`/`email`/`format`(string), `date`/`month`/`year`(`DateOnly`), `datetime`/`datetime-sec`(`DateTime`), `time`/`time-sec`(`Time`). 런타임 키 배열 `sdTextfieldTypes`.
63
+ ### SdTextfield — `<sd-textfield>`
64
+
65
+ ```ts
66
+ value = model<SdTextfieldTypes[K]>();
67
+ type = input.required<K>(); // K extends keyof SdTextfieldTypes
68
+ placeholder; title; inputStyle; inputClass;
69
+ disabled; readonly; required;
70
+ min/max = input<SdTextfieldTypes[K]>(); minlength; maxlength; pattern = input<string>();
71
+ validatorFn = input<(value) => string | undefined>(); format = input<string>();
72
+ step; autocomplete; useNumberComma = input(true); minDigits = input<number>();
73
+ inline; inset; size = input<"sm"|"lg">(); theme;
74
+ // SdTextfieldTypes: number, text, password, color, email, format,
75
+ // date/month/year(DateOnly), datetime/datetime-sec(DateTime), time/time-sec(Time)
76
+ ```
77
+
78
+ - `type` — 값 타입을 결정(제네릭으로 `value` 타입 추론). 날짜·시간 타입은 `DateOnly`/`DateTime`/`Time` 객체를 값으로 주고받음(문자열 아님).
79
+ - `format` — `type="format"` 에서 `X` 자리 마스크(예: `"XXX-XXXX"`, `|` 로 길이 분기). `type="number"` 의 `useNumberComma`=천단위 콤마 표시, `minDigits`=최소 소수자릿수.
80
+ - `required`/`min`/`max`/`minlength`/`maxlength`/`pattern`/`validatorFn` — 타입별 핸들러 + 사용자 함수로 검증, 실패 시 native invalid 표시. `readonly`/`disabled` 면 input 대신 표시용 텍스트만 렌더.
81
+ - `sdTextfieldTypes` — `(keyof SdTextfieldTypes)[]` 상수. 타입 목록을 순회/검증할 때 사용.
51
82
 
52
83
  ```html
53
- <sd-textfield [type]="'number'" [(value)]="data().qty" (valueChange)="mark(data)" [required]="true" />
84
+ <sd-textfield [type]="'number'" [(value)]="qty" [required]="true" [min]="1" />
85
+ <sd-textfield [type]="'date'" [(value)]="orderDate" />
54
86
  ```
55
87
 
56
- ### SdTextarea — `sd-textarea`
57
- 여러 줄 텍스트. `value: model<string>`, `minRows: number`(기본 1), 외에 textfield 와 유사한 `placeholder`/`disabled`/`readonly`/`required`/`validatorFn`/`size`/`theme`/`inline`/`inset`/`inputStyle`/`inputClass`.
88
+ ### SdTextarea — `<sd-textarea>`
89
+
90
+ ```ts
91
+ value = model<string>();
92
+ placeholder; title; minRows = input(1);
93
+ disabled; readonly; required; inline; inset; size = input<"sm"|"lg">();
94
+ validatorFn = input<(value: string | undefined) => string | undefined>();
95
+ theme; inputStyle; inputClass;
96
+ ```
58
97
 
59
- ### SdNumpad `sd-numpad`
60
- 화면 숫자 키패드 입력.
61
- - `value: model<number>` — 값.
62
- - `placeholder: string`, `required`/`inputDisabled: boolean` — 안내/필수/직접입력 비활성.
63
- - `useEnterButton`/`useMinusButton: boolean` — 엔터/마이너스 버튼 노출.
64
- - `enterButtonClick: output` — 엔터 버튼 클릭.
98
+ - 여러 텍스트. 행 수는 `minRows` 와 줄바꿈 개수 중 큰 값으로 자동 확장. 빈 입력은 undefined(결측 보존).
65
99
 
66
- ### SdRange<K>`sd-range`
67
- 같은 타입 두 값(from/to) 범위 입력.
68
- - `type: input.required<K>` — `SdTextfieldTypes` 키.
69
- - `from`/`to: model<SdTextfieldTypes[K]>` — 범위 양끝.
70
- - `required`/`disabled: boolean`, `inputStyle: string`.
100
+ ### SdNumpad`<sd-numpad>`
71
101
 
72
- ### SdDateRangePicker — `sd-date-range-picker`
73
- 기간 유형 + 날짜 범위 입력.
74
- - `periodType: model<"일"|"월"|"범위">` 기간 단위(기본 `"범위"`). `"일"`/`"월"` 단일 날짜를 from/to 로 환산.
75
- - `from`/`to: model<DateOnly>` — 시작/끝 날짜.
76
- - `required: boolean`.
102
+ ```ts
103
+ value = model<number>(); placeholder; required;
104
+ inputDisabled = input(false); useEnterButton = input(false); useMinusButton = input(false);
105
+ enterButtonClick = output();
106
+ ```
107
+
108
+ - 터치 숫자 키패드. 상단 표시 textfield + 0~9·소수점·BS·C 버튼. `useEnterButton`=ENT 버튼 노출(눌리면 `enterButtonClick`), `useMinusButton`=부호 토글 버튼, `inputDisabled`=상단 직접 입력 차단(버튼만).
109
+
110
+ ### SdRange — `<sd-range>`
111
+
112
+ ```ts
113
+ type = input.required<K>(); // keyof SdTextfieldTypes
114
+ from = model<SdTextfieldTypes[K]>(); to = model<SdTextfieldTypes[K]>();
115
+ inputStyle; required; disabled;
116
+ ```
117
+
118
+ - `from ~ to` 두 textfield 묶음. `to` 의 min 이 `from` 으로 자동 설정. 날짜/숫자 범위 입력에 사용.
119
+
120
+ ### SdDateRangePicker — `<sd-date-range-picker>`
121
+
122
+ ```ts
123
+ periodType = model<"일" | "월" | "범위">("범위");
124
+ from = model<DateOnly>(); to = model<DateOnly>(); required;
125
+ ```
126
+
127
+ - 기간 선택. `periodType` `"일"`=단일 날짜(from=to), `"월"`=해당 월 1일~말일 자동 세팅, `"범위"`=from/to 직접. 검색 필터의 기간 조건에 사용.
77
128
 
78
129
  ## 체크박스·스위치
79
130
 
80
- ### SdCheckbox — `sd-checkbox`
81
- - `value: model<boolean>` — 체크 여부.
82
- - `radio: boolean` — 라디오 모양(그룹 내 단일 선택).
83
- - `canChangeFn: (item) => boolean | Promise<boolean>` — 변경 허용 가드(`setupModelHook` 경유).
84
- - `icon` 체크 아이콘(기본 `tablerCheck`).
85
- - `disabled`/`inline`/`inset: boolean`, `size: "sm"|"lg"` 상태/크기.
86
- - `theme` — 테마 계열 + `"white"`. `contentStyle: string`.
87
-
88
- ### SdSwitch — `sd-switch`
89
- - `value: model<boolean>` — on/off.
90
- - `canChangeFn` — 변경 가드.
91
- - `disabled`/`inline`/`inset: boolean`, `size`, `theme` 상태/크기/테마.
92
-
93
- ### SdCheckboxGroup<T> / SdCheckboxGroupItem<T>
94
- 여러 항목 다중 선택 그룹.
95
- - `SdCheckboxGroup`: `value: model<T[]>`(선택된 값 배열), `disabled: boolean`.
96
- - `SdCheckboxGroupItem`: `value: input.required<T>`(이 항목 값), `inline: boolean`.
97
-
98
- ## 셀렉트·드롭다운
99
-
100
- ### SdSelect<T, M> — `sd-select`
101
- 드롭다운 선택. 직속 `<sd-select-item>` 또는 `[items]`+`[itemOf]` 템플릿으로 옵션 구성.
102
- - `selectMode: M`("single"|"multi", 기본 single) — 선택 모드.
103
- - `value: model<단일|배열>`선택 값(들).
104
- - `items: input<T[]>` + `trackByFn`/`getChildrenFn` — 데이터 기반 옵션(트리 지원).
105
- - `placeholder: string`, `required`/`disabled`/`inline`/`inset: boolean`, `size`.
106
- - `hideSelectAll: boolean` — multi 모드 전체선택 숨김.
107
- - `multiSelectionDisplayDirection: "vertical"` 다중 선택 표시를 세로로.
108
- - `dropdownOpen: model<boolean>` — 드롭다운 열림 상태.
109
- - `contentClass`/`contentStyle: string`.
110
-
111
- `SelectModeValue<T>` — `{ multi: T[]; single: T }`(선택 모드별 값 타입).
112
-
113
- ### SdSelectItem<T> — `sd-select-item`
114
- - `value: input<T>` — 항목 값.
115
- - `disabled`/`hidden: boolean` — 비활성/숨김.
116
-
117
- ### SdSelectButton — `sd-select-button`
118
- 셀렉트 드롭다운 안에 끼우는 액션 버튼(리플 내장). 입력 없음.
119
-
120
- ### SdDropdown / SdDropdownPopup `sd-dropdown` / `sd-dropdown-popup`
121
- 범용 드롭다운. `SdDropdown`: `open: model<boolean>`, `disabled: boolean`. 자식으로 트리거 컨텐츠 + `<sd-dropdown-popup>`(팝업 본문) 배치.
122
-
123
- ## 폼·레이아웃 컨트롤
124
-
125
- ### SdForm `sd-form`
126
- Enter submit 처리. `formSubmit: output<SubmitEvent>`, `formInvalid: output`. (`sd-crud-*` 는 내부에 폼 보유, 별도 래핑 불필요.)
127
-
128
- ### SdCollapse / SdCollapseIcon
129
- - `SdCollapse`(`sd-collapse`): `open: boolean` — 펼침. 자식을 높이 애니메이션으로 접기/펼치기.
130
- - `SdCollapseIcon`(`sd-collapse-icon`): `icon`(기본 chevron), `open: boolean`, `openRotate: number`(열림 회전각, 기본 90).
131
-
132
- ### SdTab / SdTabItem
133
- - `SdTab`(`sd-tab`): `value: model<any>` — 선택된 탭 값.
134
- - `SdTabItem`(`sd-tab-item`): `value: input<any>` — 이 탭 값. 클릭 시 부모 `value` 갱신.
135
-
136
- ### SdList / SdListItem
137
- - `SdList`(`sd-list`): `inset: boolean` — 테두리 제거(중첩 리스트).
138
- - `SdListItem`(`sd-list-item`): `layout: "accordion"|"flat"`(기본 accordion, 자식 펼침 방식), `open: model<boolean>`, `selected: boolean`, `selectedIcon: string`, `readonly: boolean`, `contentStyle`/`contentClass: string`.
139
-
140
- ### SdGap — `sd-gap`
141
- 간격. `height`/`width: "xxs"|"xs"|"sm"|"default"|"lg"|"xl"|"xxl"`(토큰 간격), `heightPx`/`widthPx`/`widthEm: number`(절대 간격).
142
-
143
- ### SdPagination — `sd-pagination`
144
- - `currentPage: model<number>` 현재 페이지(0 기반).
145
- - `totalPageCount: number` — 총 페이지 수.
146
- - `visiblePageCount: number` — 한 번에 보이는 페이지 번호 수(기본 10).
131
+ ### SdCheckbox — `<sd-checkbox>`
132
+
133
+ ```ts
134
+ value = model(false);
135
+ canChangeFn = input<(item: boolean) => boolean | Promise<boolean>>(() => true);
136
+ icon = input(tablerCheck); radio = input(false); disabled;
137
+ size = input<"sm"|"lg">(); inline; inset;
138
+ theme = input<...|"white">(); contentStyle = input<string>();
139
+ ```
140
+
141
+ - `value` — 체크 상태(model 양방향). `canChangeFn` 이 false 반환 시 변경 거부(`setupModelHook`), Promise 면 비동기 확인 후 적용.
142
+ - `radio` true 면 라디오 외형(클릭 시 항상 true 로만 set, 해제 불가). `icon` 으로 체크 아이콘 교체, `theme="white"` 어두운 배경용.
143
+
144
+ ### SdSwitch `<sd-switch>`
145
+
146
+ ```ts
147
+ value = model(false);
148
+ canChangeFn = input<(item: boolean) => boolean | Promise<boolean>>(() => true);
149
+ disabled; inline; inset; size = input<"sm"|"lg">(); theme;
150
+ ```
151
+
152
+ - 토글 스위치. on 이면 success(또는 `theme`) 색. `canChangeFn` 동작은 체크박스와 동일.
153
+
154
+ ### SdCheckboxGroup / SdCheckboxGroupItem `<sd-checkbox-group>` / `<sd-checkbox-group-item>`
155
+
156
+ ```ts
157
+ // group
158
+ value = model<T[]>([]); disabled = input(false);
159
+ // item
160
+ value = input.required<T>(); inline = input(false);
161
+ ```
162
+
163
+ - 그룹은 선택된 값 배열(`T[]`)을 보유. 각 item 의 `value` 가 그룹 배열에 포함되면 체크, 토글 시 배열에서 추가/제거. 다중 선택 묶음에 사용.
164
+
165
+ ## 선택(select)·드롭다운
166
+
167
+ ### SdSelect — `<sd-select>`
168
+
169
+ ```ts
170
+ selectMode = input("single"); // "single" | "multi"
171
+ value = model<SelectModeValue<any>[M]>(); // single=T, multi=T[]
172
+ placeholder; disabled; inline; inset; size = input<"sm"|"lg">(); required;
173
+ hideSelectAll = input(false); multiSelectionDisplayDirection = input<"vertical">();
174
+ items = input<T[]>(); trackByFn = input<(item, index) => unknown>((item)=>item);
175
+ getChildrenFn = input<(item: T) => T[] | undefined>();
176
+ contentClass; contentStyle; dropdownOpen = model(false);
177
+ // SelectModeValue<T> = { multi: T[]; single: T }
178
+ selectItem(v); toggleItem(v); onSelectAll(); onDeselectAll(); openDropdown(); closeDropdown();
179
+ ```
180
+
181
+ - `selectMode` `"single"`=단일(선택 시 닫힘), `"multi"`=다중(체크박스·전체선택바). `value` 타입이 모드에 따라 분기.
182
+ - 항목은 `<sd-select-item>` 을 콘텐츠로 두거나 `items`+`itemOf` 템플릿으로 렌더. `getChildrenFn` 지정 시 트리 평탄화. `hideSelectAll`=multi 전체선택바 숨김, `multiSelectionDisplayDirection="vertical"`=선택 표시 세로 나열.
183
+ - `#headerTpl`/`#beforeTpl`/`itemOf` 템플릿 슬롯으로 검색바·상단 항목·반복 항목 커스터마이즈. `<sd-select-button>` 콘텐츠는 우측 액션 버튼으로 투영.
184
+
185
+ ```html
186
+ <sd-select [(value)]="status" [required]="true">
187
+ <sd-select-item [value]="'active'">사용</sd-select-item>
188
+ <sd-select-item [value]="'inactive'">미사용</sd-select-item>
189
+ </sd-select>
190
+ ```
191
+
192
+ ### SdSelectItem `<sd-select-item>`
193
+
194
+ ```ts
195
+ value = input<T | undefined>(undefined); disabled = input(false); hidden = input(false);
196
+ ```
197
+
198
+ - 한 선택 항목. multi 모드면 좌측 체크박스 자동 표시. `value` 가 부모 select 값과 매칭되면 선택 강조. `hidden`=DOM 유지하되 숨김(검색 필터 등).
199
+
200
+ ### SdSelectButton — `<sd-select-button>`
201
+
202
+ ```ts
203
+ // 입력 없음. 콘텐츠 투영용.
204
+ ```
205
+
206
+ - select 우측에 붙는 액션 버튼 슬롯(ripple 내장). 관리/검색 모달 트리거 등에 사용.
207
+
208
+ ### SdDropdown / SdDropdownPopup — `<sd-dropdown>` / `<sd-dropdown-popup>`
209
+
210
+ ```ts
211
+ // dropdown
212
+ open = model(false); disabled = input(false);
213
+ // popup: 입력 없음
214
+ ```
215
+
216
+ - 임의 콘텐츠를 여는 드롭다운. 트리거 콘텐츠 + `<sd-dropdown-popup>` 한 쌍. 열리면 popup 을 body 로 이동해 위치 계산(모바일은 하단 시트 + backdrop). 키보드 ↑/↓/Esc/Space 로 열고닫기·이동. select·테마셀렉터·탑바메뉴가 내부 사용.
217
+
218
+ ## 폼·접기·탭·리스트·기타
219
+
220
+ ### SdForm — `<sd-form>`
221
+
222
+ ```ts
223
+ formElRef = viewChild.required<ElementRef<HTMLFormElement>>("formEl");
224
+ get formEl: HTMLFormElement;
225
+ formSubmit = output<SubmitEvent>(); formInvalid = output();
226
+ requestSubmit(): void;
227
+ ```
228
+
229
+ - native form 래퍼. 제출 시 `checkValidity()` 통과하면 `formSubmit`, 실패하면 `reportValidity()` 후 `formInvalid`. `requestSubmit()` 으로 외부에서 제출 트리거(CTRL+S 등).
230
+
231
+ ```html
232
+ <sd-form (formSubmit)="onSubmit()"> ... <sd-button [type]="'submit'">저장</sd-button> </sd-form>
233
+ ```
234
+
235
+ ### SdCollapse / SdCollapseIcon — `<sd-collapse>` / `<sd-collapse-icon>`
236
+
237
+ ```ts
238
+ // collapse
239
+ open = input(false);
240
+ // collapse-icon
241
+ icon = input(tablerChevronDown); open = input(false); openRotate = input(90);
242
+ ```
243
+
244
+ - `sd-collapse` 는 `open` 에 따라 콘텐츠 높이를 애니메이션 접기/펼치기. `sd-collapse-icon` 은 `open` 시 `openRotate`(deg) 회전하는 펼침 표시 아이콘(보통 collapse 헤더에 동반).
245
+
246
+ ### SdTab / SdTabItem — `<sd-tab>` / `<sd-tab-item>`
247
+
248
+ ```ts
249
+ // tab
250
+ value = model<any>();
251
+ // tab-item
252
+ value = input<any>();
253
+ ```
254
+
255
+ - `sd-tab` 은 현재 탭 값을 고르는 선택 컨트롤(콘텐츠 컨테이너 아님). `sd-tab-item` 의 `value` 가 부모 값과 같으면 선택. 콘텐츠 분기는 바깥에서 `@if`/`@switch` 로. 값 시그널은 literal union 으로.
256
+
257
+ ```html
258
+ <sd-tab [(value)]="activeTab">
259
+ <sd-tab-item [value]="'info'">기본정보</sd-tab-item>
260
+ <sd-tab-item [value]="'history'">이력</sd-tab-item>
261
+ </sd-tab>
262
+ ```
263
+
264
+ ### SdList / SdListItem — `<sd-list>` / `<sd-list-item>`
265
+
266
+ ```ts
267
+ // list
268
+ inset = input(false);
269
+ // list-item
270
+ layout = input<"accordion" | "flat">("accordion");
271
+ open = model(false); selected = input(false); selectedIcon = input<string>();
272
+ readonly = input(false); contentStyle; contentClass;
273
+ // #toolTpl 슬롯, 중첩 <sd-list> 로 트리
274
+ ```
275
+
276
+ - `inset` — 카드 외형(테두리·배경) 제거(임베드용). list-item 의 `layout` `"accordion"`=클릭 시 자식 펼침(트리), `"flat"`=섹션 헤더(항상 펼침).
277
+ - `selected`=선택 강조, `selectedIcon`=리프 항목 좌측 선택 아이콘, `readonly`=클릭 펼침 비활성. 자식 `<sd-list>` 중첩으로 다단 트리. 사이드바 메뉴가 이를 사용.
278
+
279
+ ### SdGap — `<sd-gap>`
280
+
281
+ ```ts
282
+ height/width = input<"xxs"|"xs"|"sm"|"default"|"lg"|"xl"|"xxl">();
283
+ heightPx; widthPx; widthEm = input<number>();
284
+ ```
285
+
286
+ - 간격 스페이서. 토큰(`height`/`width`) 또는 px/em 으로 지정. width 류면 inline-block, height 면 block, 값 0 이면 미표시.
287
+
288
+ ### SdPagination — `<sd-pagination>`
289
+
290
+ ```ts
291
+ currentPage = model(0); // 0-based
292
+ totalPageCount = input(0); visiblePageCount = input(10);
293
+ goToPage(p); goToFirst(); goToLast(); goToNextGroup(); goToPrevGroup();
294
+ ```
295
+
296
+ - 페이지 네비게이션. `currentPage` 는 0-base 양방향. `visiblePageCount` 묶음 단위로 이전/다음 그룹·처음/끝 이동. 시트·리스트 페이징에 사용.
@@ -1,80 +1,75 @@
1
1
  # @simplysm/angular — CRUD 화면 골격
2
2
 
3
- 목록/단건 화면의 표준 컨테이너 골격. `sd-base-container`(공통 셸) 위에 `sd-crud-list`(목록), `sd-crud-detail`(단건)이 얹힘. 표준 시그널(ready/initialized/busyCount/viewType)·page/modal/control 컨텍스트별 탑바·하단바 자동 구성·CTRL+S 저장을 내장. 화면 데이터 흐름·시그널 전파 규약은 client-component.md / client-crud.md 를 따름.
3
+ 목록/단건 화면의 표준 컨테이너 골격. `sd-base-container`(공통 셸) 위에 `sd-crud-list`(목록), `sd-crud-detail`(단건)이 얹힘. 표준 시그널(ready/initialized/busyCount/viewType)·page/modal/control 컨텍스트별 탑바·하단바 자동 구성·CTRL+S 저장을 내장. 사용 절차는 [client-crud.md](../manuals/client-crud.md) 참조.
4
4
 
5
- ## 표준 시그널 (3 컴포넌트 공통)
5
+ ## SdBaseContainer `<sd-base-container>`
6
6
 
7
- 세 컴포넌트 모두 화면 표준 시그널을 입력/모델로 받음:
8
- - `ready: model<boolean>` 자식이 데이터 로드를 시작해도 되는 시점. 컨테이너가 공유데이터 로드 완료 후 true 로 set.
9
- - `initialized: input<boolean>` 데이터 로드 완료 여부(자식이 set 한 값을 받음). true 가 되어야 본문 렌더.
10
- - `busyCount: model<number>` 진행 중 비동기 작업 수. 0 보다 크면 busy 표시.
11
- - `restricted: input<boolean>` 권한 없음. true 면 "사용권한이 없습니다" 안내만 표시.
12
- - `viewType: input.required<SdViewType>` — page/modal/control 컨텍스트(`injectViewTypeSignal()` 전달). page 면 탑바를, modal 이면 하단 확인/취소를 구성.
13
-
14
- ## SdBaseContainer
15
-
16
- selector `sd-base-container`. 모든 화면의 공통 셸(busy·권한 안내·page 탑바·콘텐츠/명령 슬롯).
17
-
18
- - 위 표준 시그널 + 슬롯: `#topbarTpl`(page 탑바 추가 영역), `#commandTpl`(상단 명령 줄), `#contentTpl`(본문), `#bottomCommandTpl`(하단 명령 줄).
19
- - 생성자에서 `SdSharedDataProvider` 가 있으면 그 로드 완료(`wait()`)를 기다린 뒤 `ready` 를 true 로 set.
20
-
21
- ```html
22
- <sd-base-container [(ready)]="ready" [initialized]="initialized()" [(busyCount)]="busyCount"
23
- [restricted]="!perms().includes('use')" [viewType]="viewType()">
24
- <ng-template #contentTpl>...</ng-template>
25
- </sd-base-container>
7
+ ```ts
8
+ ready = model(false); initialized = input(false); busyCount = model(0);
9
+ restricted = input(false); viewType = input.required<SdViewType>(); // "page"|"modal"|"control"
10
+ viewTitle = injectViewTitleSignal(); // 자동
11
+ // 슬롯: #topbarTpl #commandTpl #contentTpl #bottomCommandTpl
26
12
  ```
27
13
 
28
- ## SdCrudList<TItem, TKey>
29
-
30
- selector `sd-crud-list`. 검색폼·등록/삭제/복원 버튼·시트·페이지네이션을 갖춘 목록 골격. 직속 자식 `<sd-sheet-column>` 들을 내부 시트로 투영. `Ctrl+S` 로 저장 명령.
31
-
32
- 추가 입력:
33
- - `readonly: input<boolean>` — 읽기 전용. true 면 등록/삭제/저장 버튼 숨김.
34
- - `inlineEdit: input<boolean>` — 시트 셀 인라인 편집 + 저장 버튼 노출(기본 true). `readonly` 면 무시.
35
- - `selectMode: "single"|"multi"` 선택 모드. 모달 선택 시 하단 확인/해제 버튼 구성.
36
- - `key: input.required<string>` 시트 컬럼 구성 영속화 키.
37
- - `items: input<TItem[]>` 데이터.
38
- - `selectedKeys: model<NonNullable<TKey>[]>` — 선택 키.
39
- - `currDeletedItems: input<TItem[]>` 삭제 표시 행(복원 대상 판정).
40
- - `currentPage: model<number>`, `totalPageCount`/`itemsPerPage`/`visiblePageCount: input<number>` — 페이징(서버 페이징은 `totalPageCount`, 클라 페이징은 `itemsPerPage`).
41
- - `sorts: model<SortingDef[]>` 정렬 상태.
42
- - `trackByFn: input.required<(item) => TKey>` — 행 키 함수.
43
- - `getItemSelectableFn: input<(item) => boolean | string>` — 행 선택 가능 여부(문자열 반환 시 불가 사유). 내부 시트로 전달.
44
-
45
- 출력:
46
- - `filterSubmit: output` 검색폼 제출(조회).
47
- - `submit: output` 인라인 편집 저장(Ctrl+S/저장 버튼).
48
- - `create: output` 등록 버튼.
49
- - `delete: output<TItem[]>` — 선택 행 삭제(선택 항목 전달).
50
- - `restore: output<TItem[]>` — 선택된 삭제 행 복원.
14
+ - `initialized` — false 동안 busy 표시. `busyCount`>0 이면 busy(model 양방향, 화면이 +1/-1 누적). `restricted`=권한 없음 안내 화면 표시(콘텐츠 미렌더).
15
+ - `viewType` — `"page"`=탑바(제목+#topbarTpl) 포함, `"modal"`/`"control"`=콘텐츠만. `ready` 는 공유데이터 로드 완료 후 true(model, 화면이 이를 보고 초기화 진행).
16
+ - 직접 쓰기보다 `sd-crud-list`/`sd-crud-detail` 또는 커스텀 화면 셸로 사용. 슬롯은 `#topbarTpl`(탑바 추가), `#commandTpl`(명령영역), `#contentTpl`(본문), `#bottomCommandTpl`(하단).
17
+
18
+ ## SdCrudList — `<sd-crud-list>`
19
+
20
+ ```ts
21
+ ready = model(false); initialized = input(false); busyCount = model(0);
22
+ restricted = input(false); readonly = input(false); inlineEdit = input(true);
23
+ viewType = input.required<SdViewType>(); selectMode = input<"single" | "multi">();
24
+ key = input.required<string>();
25
+ items = input<TItem[]>([]); selectedKeys = model<NonNullable<TKey>[]>([]);
26
+ currDeletedItems = input<TItem[]>([]);
27
+ currentPage = model(0); totalPageCount = input(0); itemsPerPage = input(0); visiblePageCount = input(10);
28
+ sorts = model<SortingDef[]>([]);
29
+ trackByFn = input.required<(item: TItem) => TKey>();
30
+ getItemSelectableFn = input<(item: TItem) => boolean | string>();
31
+
32
+ filterSubmit = output(); submit = output(); create = output();
33
+ delete = output<TItem[]>(); restore = output<TItem[]>();
34
+ // 슬롯: #filterTpl #toolTpl #commandTpl #bottomCommandTpl, 직속 <sd-sheet-column>
35
+ ```
51
36
 
52
- 슬롯: `#filterTpl`(검색 항목), `#commandTpl`/`#bottomCommandTpl`(명령 줄), `#toolTpl`(도구 ).
37
+ - 목록 표준 골격(시트 + 검색폼 + 등록/삭제/복구 버튼 + CTRL+S 저장 + 모달 선택모드).
38
+ - `readonly` — 편집 전체 불가(시트 선택만). `inlineEdit`(기본 true) — true 면 시트를 `<sd-form>` 으로 감싸 셀 인라인 편집 + per-row 삭제컬럼, false 면 조회·선택 전용(편집은 외부 상세/모달). 둘은 직교.
39
+ - `selectMode` — `"single"`=행 클릭 즉시(modal 이면 close), `"multi"`=하단 확인 버튼. modal+selectMode 면 close 시 `{ selectedKeys }` 자동 전달.
40
+ - `currDeletedItems` — 현재 삭제(soft delete)된 항목들. 해당 행은 취소선 + 복구 버튼으로 표시. `key` 는 시트 설정 저장 키(`<key>-sheet`).
41
+ - 출력: `filterSubmit`(검색), `submit`(인라인 편집 저장, inlineEdit=true 일 때만), `create`(등록), `delete`/`restore`(선택 항목 배열). `<sd-sheet-column>` 을 직속에 두면 내부 시트로 투영.
53
42
 
54
43
  ```html
55
- <sd-crud-list [viewType]="viewType()" [key]="'goods-list'" [(selectedKeys)]="selectedKeys"
56
- [items]="items()" [trackByFn]="trackByFn" [totalPageCount]="pageLength()" [(currentPage)]="page"
57
- (filterSubmit)="onFilterSubmit()" (create)="onCreate()" (delete)="onDelete($event)">
44
+ <sd-crud-list [(ready)]="ready" [initialized]="initialized()" [(busyCount)]="busyCount"
45
+ [restricted]="!perms().includes('use')" [readonly]="!canEdit()" [viewType]="viewType()"
46
+ [key]="'role'" [items]="items()" [trackByFn]="trackByFn" [(selectedKeys)]="selectedKeys"
47
+ (create)="onCreate()" (delete)="onDelete($event)" (restore)="onRestore($event)">
58
48
  <ng-template #filterTpl>...</ng-template>
59
49
  <sd-sheet-column [key]="'name'" [header]="'이름'">
60
- <ng-template [cell]="items()" let-item="item"><div class="p-xs-sm">{{ item.name }}</div></ng-template>
50
+ <ng-template [cell]="items()" let-item="item">{{ item.name }}</ng-template>
61
51
  </sd-sheet-column>
62
52
  </sd-crud-list>
63
53
  ```
64
54
 
65
- ## SdCrudDetail
55
+ ## SdCrudDetail — `<sd-crud-detail>`
66
56
 
67
- selector `sd-crud-detail`. 단건 편집 골격(폼 + 저장 명령).
57
+ ```ts
58
+ ready = model(false); initialized = input(false); busyCount = model(0);
59
+ restricted = input(false); readonly = input(false);
60
+ viewType = input.required<SdViewType>();
61
+ submit = output();
62
+ // 슬롯: #contentTpl(필수) #commandTpl #bottomCommandTpl
63
+ ```
68
64
 
69
- 추가 입력/출력:
70
- - `readonly: input<boolean>` 읽기 전용(저장 버튼 숨김).
71
- - `submit: output` 저장(폼 제출/저장 버튼).
72
- - 슬롯: `#commandTpl`(상단 명령), `#contentTpl`(폼 본문), `#bottomCommandTpl`(하단 명령).
73
- - `onSaveButtonClick()` — 내부 폼의 `requestSubmit()` 호출(저장 트리거).
65
+ - 단건 편집 표준 골격(폼 래핑 + CTRL+S 저장 + 저장 버튼 + modal "확인" 자동).
66
+ - `readonly` — true 면 `#contentTpl` 을 `<sd-form>` 없이 그대로(읽기), false 폼으로 감싸 `submit` 출력. `viewType` `"page"`=상단 저장버튼, `"control"`=명령영역 저장버튼, `"modal"`=하단 우측 "확인" 자동.
67
+ - `#contentTpl`(필수) 본문, `#commandTpl`/`#bottomCommandTpl` 추가 액션.
74
68
 
75
69
  ```html
76
- <sd-crud-detail [viewType]="viewType()" [(ready)]="ready" [initialized]="initialized()"
77
- [(busyCount)]="busyCount" (submit)="onSubmit()">
78
- <ng-template #contentTpl><div class="form-table">...</div></ng-template>
70
+ <sd-crud-detail [(ready)]="ready" [initialized]="initialized()" [(busyCount)]="busyCount"
71
+ [restricted]="!perms().includes('use')" [readonly]="!canEdit()" [viewType]="viewType()"
72
+ (submit)="onSubmit()">
73
+ <ng-template #contentTpl><!-- 폼 본문 --></ng-template>
79
74
  </sd-crud-detail>
80
75
  ```