@simplysm/solid 13.0.93 → 13.0.96
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 +22 -9
- package/dist/components/features/crud-sheet/CrudSheet.d.ts.map +1 -1
- package/dist/components/features/crud-sheet/CrudSheet.js +14 -9
- package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
- package/dist/components/features/crud-sheet/CrudSheet.types.d.ts +2 -0
- package/dist/components/features/crud-sheet/CrudSheet.types.d.ts.map +1 -1
- package/dist/components/layout/sidebar/Sidebar.d.ts +5 -0
- package/dist/components/layout/sidebar/Sidebar.d.ts.map +1 -1
- package/dist/components/layout/sidebar/Sidebar.js +7 -4
- package/dist/components/layout/sidebar/Sidebar.js.map +2 -2
- package/docs/display-feedback.md +142 -17
- package/docs/features.md +270 -3
- package/docs/form-controls.md +226 -17
- package/docs/layout-data.md +6 -0
- package/docs/providers-hooks.md +145 -40
- package/package.json +5 -5
- package/src/components/features/crud-sheet/CrudSheet.tsx +15 -7
- package/src/components/features/crud-sheet/CrudSheet.types.ts +2 -0
- package/src/components/layout/sidebar/Sidebar.tsx +9 -3
- package/tests/components/features/crud-sheet/CrudSheet.spec.tsx +116 -1
package/docs/display-feedback.md
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Card
|
|
4
4
|
|
|
5
|
-
그림자와 호버 효과가 있는 카드 컨테이너.
|
|
5
|
+
그림자와 호버 효과가 있는 카드 컨테이너. 페이드 인 애니메이션 포함.
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
8
|
import { Card } from "@simplysm/solid";
|
|
9
9
|
|
|
10
10
|
<Card>카드 내용</Card>
|
|
11
|
+
<Card class="p-4">커스텀 패딩</Card>
|
|
11
12
|
```
|
|
12
13
|
|
|
14
|
+
`<div>` HTML 속성을 모두 상속한다.
|
|
15
|
+
|
|
13
16
|
---
|
|
14
17
|
|
|
15
18
|
## Alert
|
|
@@ -21,8 +24,16 @@ import { Alert } from "@simplysm/solid";
|
|
|
21
24
|
|
|
22
25
|
<Alert theme="success">저장되었습니다.</Alert>
|
|
23
26
|
<Alert theme="danger">오류가 발생했습니다.</Alert>
|
|
27
|
+
<Alert theme="warning">주의가 필요합니다.</Alert>
|
|
28
|
+
<Alert theme="info">참고 사항입니다.</Alert>
|
|
24
29
|
```
|
|
25
30
|
|
|
31
|
+
| Prop | 타입 | 기본값 | 설명 |
|
|
32
|
+
|------|------|--------|------|
|
|
33
|
+
| `theme` | `SemanticTheme` | `"base"` | 색상 테마 |
|
|
34
|
+
|
|
35
|
+
`<div>` HTML 속성을 모두 상속한다.
|
|
36
|
+
|
|
26
37
|
---
|
|
27
38
|
|
|
28
39
|
## Icon
|
|
@@ -31,34 +42,82 @@ Tabler Icons 래퍼.
|
|
|
31
42
|
|
|
32
43
|
```tsx
|
|
33
44
|
import { Icon } from "@simplysm/solid";
|
|
34
|
-
import { IconUser } from "@tabler/icons-solidjs";
|
|
45
|
+
import { IconUser, IconSettings } from "@tabler/icons-solidjs";
|
|
35
46
|
|
|
36
47
|
<Icon icon={IconUser} size="1.5em" />
|
|
48
|
+
<Icon icon={IconSettings} size={24} />
|
|
37
49
|
```
|
|
38
50
|
|
|
51
|
+
| Prop | 타입 | 기본값 | 설명 |
|
|
52
|
+
|------|------|--------|------|
|
|
53
|
+
| `icon` | `Component<TablerIconProps>` | (필수) | Tabler 아이콘 컴포넌트 |
|
|
54
|
+
| `size` | `string \| number` | `"1.25em"` | 아이콘 크기 |
|
|
55
|
+
|
|
56
|
+
Tabler `IconProps`의 나머지 속성(`class`, `color` 등)을 모두 상속한다.
|
|
57
|
+
|
|
39
58
|
---
|
|
40
59
|
|
|
41
|
-
## Link
|
|
60
|
+
## Link
|
|
61
|
+
|
|
62
|
+
테마 색상의 링크 컴포넌트.
|
|
42
63
|
|
|
43
64
|
```tsx
|
|
44
|
-
import { Link
|
|
65
|
+
import { Link } from "@simplysm/solid";
|
|
45
66
|
|
|
46
67
|
<Link href="/users">사용자 목록</Link>
|
|
47
|
-
<
|
|
68
|
+
<Link theme="danger" onClick={handleDelete}>삭제</Link>
|
|
69
|
+
<Link disabled>비활성 링크</Link>
|
|
48
70
|
```
|
|
49
71
|
|
|
72
|
+
| Prop | 타입 | 기본값 | 설명 |
|
|
73
|
+
|------|------|--------|------|
|
|
74
|
+
| `theme` | `SemanticTheme` | `"primary"` | 색상 테마 |
|
|
75
|
+
| `disabled` | `boolean` | `false` | 비활성화 |
|
|
76
|
+
|
|
77
|
+
`<a>` HTML 속성을 모두 상속한다.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Tag
|
|
82
|
+
|
|
83
|
+
테마 색상의 태그/배지.
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { Tag } from "@simplysm/solid";
|
|
87
|
+
|
|
88
|
+
<Tag>기본</Tag>
|
|
89
|
+
<Tag theme="primary">Primary</Tag>
|
|
90
|
+
<Tag theme="success">완료</Tag>
|
|
91
|
+
<Tag theme="danger">긴급</Tag>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
| Prop | 타입 | 기본값 | 설명 |
|
|
95
|
+
|------|------|--------|------|
|
|
96
|
+
| `theme` | `SemanticTheme` | `"base"` | 색상 테마 |
|
|
97
|
+
|
|
98
|
+
`<span>` HTML 속성을 모두 상속한다.
|
|
99
|
+
|
|
50
100
|
---
|
|
51
101
|
|
|
52
102
|
## Barcode
|
|
53
103
|
|
|
54
|
-
바코드/QR 코드
|
|
104
|
+
바코드/QR 코드 생성 (bwip-js 기반).
|
|
55
105
|
|
|
56
106
|
```tsx
|
|
57
107
|
import { Barcode } from "@simplysm/solid";
|
|
58
108
|
|
|
59
|
-
<Barcode value="
|
|
109
|
+
<Barcode type="qrcode" value="https://example.com" />
|
|
110
|
+
<Barcode type="code128" value="1234567890" />
|
|
111
|
+
<Barcode type="ean13" value="4006381333931" />
|
|
60
112
|
```
|
|
61
113
|
|
|
114
|
+
| Prop | 타입 | 설명 |
|
|
115
|
+
|------|------|------|
|
|
116
|
+
| `type` | `BarcodeType` | 바코드 타입 (필수). `"qrcode"`, `"code128"`, `"ean13"` 등 100+ 타입 |
|
|
117
|
+
| `value` | `string` | 바코드 값 |
|
|
118
|
+
|
|
119
|
+
`<div>` HTML 속성을 모두 상속한다.
|
|
120
|
+
|
|
62
121
|
---
|
|
63
122
|
|
|
64
123
|
## Echarts
|
|
@@ -78,6 +137,13 @@ import { Echarts } from "@simplysm/solid";
|
|
|
78
137
|
/>
|
|
79
138
|
```
|
|
80
139
|
|
|
140
|
+
| Prop | 타입 | 설명 |
|
|
141
|
+
|------|------|------|
|
|
142
|
+
| `option` | `EChartsOption` | ECharts 옵션 (필수) |
|
|
143
|
+
| `busy` | `boolean` | 로딩 상태 (`showLoading`/`hideLoading`) |
|
|
144
|
+
|
|
145
|
+
`<div>` HTML 속성을 모두 상속한다. 내부적으로 SVG 렌더러를 사용하며, 컨테이너 크기 변경 시 자동 리사이즈한다.
|
|
146
|
+
|
|
81
147
|
---
|
|
82
148
|
|
|
83
149
|
## Dialog
|
|
@@ -96,16 +162,24 @@ import { Dialog } from "@simplysm/solid";
|
|
|
96
162
|
</Dialog>
|
|
97
163
|
```
|
|
98
164
|
|
|
99
|
-
| Prop | 타입 | 설명 |
|
|
100
|
-
|
|
101
|
-
| `open` | `boolean` | 열림 상태 |
|
|
102
|
-
| `onOpenChange` | `(v: boolean) => void` | 상태 콜백 |
|
|
103
|
-
| `mode` | `"float" \| "fill"` | 모드 |
|
|
104
|
-
| `resizable` | `boolean` | 리사이즈 가능 |
|
|
105
|
-
| `draggable` | `boolean` | 드래그 가능 |
|
|
106
|
-
| `width
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
165
|
+
| Prop | 타입 | 기본값 | 설명 |
|
|
166
|
+
|------|------|--------|------|
|
|
167
|
+
| `open` | `boolean` | | 열림 상태 |
|
|
168
|
+
| `onOpenChange` | `(v: boolean) => void` | | 상태 콜백 |
|
|
169
|
+
| `mode` | `"float" \| "fill"` | `"float"` | 모드 |
|
|
170
|
+
| `resizable` | `boolean` | `false` | 리사이즈 가능 |
|
|
171
|
+
| `draggable` | `boolean` | `false` | 드래그 가능 |
|
|
172
|
+
| `width` | `string` | | 너비 |
|
|
173
|
+
| `height` | `string` | | 높이 |
|
|
174
|
+
| `closeOnEscape` | `boolean` | `true` | ESC 닫기 |
|
|
175
|
+
| `beforeClose` | `() => boolean` | | 닫기 전 확인 (false 반환 시 취소) |
|
|
176
|
+
|
|
177
|
+
### 서브 컴포넌트
|
|
178
|
+
|
|
179
|
+
| 컴포넌트 | 설명 |
|
|
180
|
+
|----------|------|
|
|
181
|
+
| `Dialog.Header` | 다이얼로그 헤더 (닫기 버튼 포함) |
|
|
182
|
+
| `Dialog.Action` | 하단 액션 영역 |
|
|
109
183
|
|
|
110
184
|
### 프로그래밍 방식 다이얼로그
|
|
111
185
|
|
|
@@ -139,6 +213,22 @@ import { Dropdown } from "@simplysm/solid";
|
|
|
139
213
|
</Dropdown>
|
|
140
214
|
```
|
|
141
215
|
|
|
216
|
+
| Prop | 타입 | 기본값 | 설명 |
|
|
217
|
+
|------|------|--------|------|
|
|
218
|
+
| `open` | `boolean` | | 열림 상태 |
|
|
219
|
+
| `onOpenChange` | `(v: boolean) => void` | | 상태 콜백 |
|
|
220
|
+
| `position` | `{ x: number; y: number }` | | 절대 위치 (컨텍스트 메뉴용) |
|
|
221
|
+
| `maxHeight` | `number` | `300` | 최대 높이 (px) |
|
|
222
|
+
| `disabled` | `boolean` | `false` | 비활성화 |
|
|
223
|
+
| `keyboardNav` | `boolean` | `false` | 키보드 네비게이션 (Select 등에서 사용) |
|
|
224
|
+
|
|
225
|
+
### 서브 컴포넌트
|
|
226
|
+
|
|
227
|
+
| 컴포넌트 | 설명 |
|
|
228
|
+
|----------|------|
|
|
229
|
+
| `Dropdown.Trigger` | 드롭다운 트리거 |
|
|
230
|
+
| `Dropdown.Content` | 드롭다운 콘텐츠 |
|
|
231
|
+
|
|
142
232
|
---
|
|
143
233
|
|
|
144
234
|
## Collapse
|
|
@@ -148,11 +238,20 @@ import { Dropdown } from "@simplysm/solid";
|
|
|
148
238
|
```tsx
|
|
149
239
|
import { Collapse } from "@simplysm/solid";
|
|
150
240
|
|
|
241
|
+
<Button aria-expanded={expanded()} onClick={() => setExpanded(!expanded())}>
|
|
242
|
+
토글
|
|
243
|
+
</Button>
|
|
151
244
|
<Collapse open={expanded()}>
|
|
152
245
|
<div>접힌 내용</div>
|
|
153
246
|
</Collapse>
|
|
154
247
|
```
|
|
155
248
|
|
|
249
|
+
| Prop | 타입 | 기본값 | 설명 |
|
|
250
|
+
|------|------|--------|------|
|
|
251
|
+
| `open` | `boolean` | `false` | 열림 상태 |
|
|
252
|
+
|
|
253
|
+
`<div>` HTML 속성을 모두 상속한다. 높이 애니메이션이 자동 적용된다.
|
|
254
|
+
|
|
156
255
|
---
|
|
157
256
|
|
|
158
257
|
## Tabs
|
|
@@ -168,6 +267,17 @@ import { Tabs } from "@simplysm/solid";
|
|
|
168
267
|
</Tabs>
|
|
169
268
|
```
|
|
170
269
|
|
|
270
|
+
| Prop (Tabs) | 타입 | 설명 |
|
|
271
|
+
|------------|------|------|
|
|
272
|
+
| `value` | `string` | 선택된 탭 값 |
|
|
273
|
+
| `onValueChange` | `(v: string) => void` | 변경 콜백 |
|
|
274
|
+
| `size` | `ComponentSize` | 크기 |
|
|
275
|
+
|
|
276
|
+
| Prop (Tabs.Tab) | 타입 | 설명 |
|
|
277
|
+
|-----------------|------|------|
|
|
278
|
+
| `value` | `string` | 탭 식별 값 |
|
|
279
|
+
| `disabled` | `boolean` | 비활성화 |
|
|
280
|
+
|
|
171
281
|
---
|
|
172
282
|
|
|
173
283
|
## Notification
|
|
@@ -259,8 +369,23 @@ busy.hide();
|
|
|
259
369
|
import { Progress } from "@simplysm/solid";
|
|
260
370
|
|
|
261
371
|
<Progress value={progress()} />
|
|
372
|
+
<Progress value={75} theme="success" />
|
|
373
|
+
|
|
374
|
+
// 커스텀 콘텐츠
|
|
375
|
+
<Progress value={50} theme="primary" size="lg">
|
|
376
|
+
<span>50% 완료</span>
|
|
377
|
+
</Progress>
|
|
262
378
|
```
|
|
263
379
|
|
|
380
|
+
| Prop | 타입 | 기본값 | 설명 |
|
|
381
|
+
|------|------|--------|------|
|
|
382
|
+
| `value` | `number` | (필수) | 진행률 (0-100) |
|
|
383
|
+
| `theme` | `SemanticTheme` | `"primary"` | 색상 테마 |
|
|
384
|
+
| `size` | `ComponentSize` | `"md"` | 크기 |
|
|
385
|
+
| `inset` | `boolean` | `false` | 테두리 없음 |
|
|
386
|
+
|
|
387
|
+
`<div>` HTML 속성을 모두 상속한다. children이 있으면 퍼센트 텍스트 대신 커스텀 콘텐츠를 표시한다.
|
|
388
|
+
|
|
264
389
|
---
|
|
265
390
|
|
|
266
391
|
## Print
|
package/docs/features.md
CHANGED
|
@@ -39,7 +39,7 @@ const result = await dialog.open(UserList);
|
|
|
39
39
|
|
|
40
40
|
## CrudSheet
|
|
41
41
|
|
|
42
|
-
DataSheet 기반 CRUD 기능 통합 컴포넌트. 인라인/다이얼로그 편집, 검색, Excel 가져오기/내보내기, 배치 작업을 지원한다.
|
|
42
|
+
DataSheet 기반 CRUD 기능 통합 컴포넌트. 인라인/다이얼로그 편집, 검색, Excel 가져오기/내보내기, 선택 모드, 배치 작업을 지원한다.
|
|
43
43
|
|
|
44
44
|
편집 방식은 `inlineEdit`과 `dialogEdit` 중 하나를 선택한다 (동시 사용 불가).
|
|
45
45
|
|
|
@@ -80,6 +80,7 @@ function ProductSheet(props: { close?: (result?: boolean) => void }) {
|
|
|
80
80
|
dialogEdit={{
|
|
81
81
|
editItem: (item) => dialog.open(ProductDetail, { item }),
|
|
82
82
|
deleteItems: (items) => deleteProducts(items),
|
|
83
|
+
restoreItems: (items) => restoreProducts(items),
|
|
83
84
|
}}
|
|
84
85
|
>
|
|
85
86
|
<CrudSheet.Column header="상품명" key="name" editTrigger>
|
|
@@ -90,15 +91,241 @@ function ProductSheet(props: { close?: (result?: boolean) => void }) {
|
|
|
90
91
|
}
|
|
91
92
|
```
|
|
92
93
|
|
|
94
|
+
### CrudSheet Props
|
|
95
|
+
|
|
96
|
+
| Prop | 타입 | 설명 |
|
|
97
|
+
|------|------|------|
|
|
98
|
+
| `search` | `(filter, page, sorts) => Promise<SearchResult<TItem>>` | 데이터 조회 함수. `page`가 `undefined`면 전체 조회 (Excel 등) |
|
|
99
|
+
| `getItemKey` | `(item: TItem) => string \| number \| undefined` | 아이템 고유 키 반환 |
|
|
100
|
+
| `close` | `() => void` | Dialog 모드 활성화. 전달 시 Dialog 헤더에 새로고침 버튼 표시 |
|
|
101
|
+
| `inlineEdit` | `InlineEditConfig<TItem>` | 인라인 편집 설정 (`dialogEdit`과 동시 사용 불가) |
|
|
102
|
+
| `dialogEdit` | `DialogEditConfig<TItem>` | 다이얼로그 편집 설정 (`inlineEdit`과 동시 사용 불가) |
|
|
103
|
+
| `editable` | `boolean` | 편집 버튼 표시 여부 (기본값: `true`) |
|
|
104
|
+
| `isItemEditable` | `(item: TItem) => boolean` | 아이템별 편집 가능 여부 (dialogEdit의 editTrigger 링크에 적용) |
|
|
105
|
+
| `isItemDeletable` | `(item: TItem) => boolean` | 아이템별 삭제 가능 여부 (삭제 버튼/링크 비활성화) |
|
|
106
|
+
| `isItemDeleted` | `(item: TItem) => boolean` | 아이템 삭제 상태 확인 (취소선 표시) |
|
|
107
|
+
| `isItemSelectable` | `(item: TItem) => boolean \| string` | 아이템별 선택 가능 여부 |
|
|
108
|
+
| `filterInitial` | `TFilter` | 필터 초기값 |
|
|
109
|
+
| `items` | `TItem[]` | 제어 모드: 외부에서 아이템 배열 전달 |
|
|
110
|
+
| `onItemsChange` | `(items: TItem[]) => void` | 제어 모드: 아이템 변경 콜백 |
|
|
111
|
+
| `storageKey` | `string` | DataSheet 컬럼 너비 등 상태 저장 키 |
|
|
112
|
+
| `lastModifiedAtProp` | `string` | 자동 "최종수정일시" 컬럼 추가 (DateTime 타입, hidden) |
|
|
113
|
+
| `lastModifiedByProp` | `string` | 자동 "수정자" 컬럼 추가 (string 타입, hidden) |
|
|
114
|
+
| `excel` | `ExcelConfig<TItem>` | Excel 가져오기/내보내기 설정 |
|
|
115
|
+
| `selectionMode` | `"single" \| "multiple"` | 선택 모드 활성화 |
|
|
116
|
+
| `selectedKeys` | `(string \| number)[]` | 제어 모드: 선택된 키 배열 |
|
|
117
|
+
| `onSelectedKeysChange` | `(keys) => void` | 제어 모드: 선택 변경 콜백 |
|
|
118
|
+
| `onSelect` | `(result: SelectResult<TItem>) => void` | 선택 확인 콜백 (single 모드는 클릭 시 자동 호출) |
|
|
119
|
+
| `onSubmitComplete` | `() => void` | 저장 완료 후 콜백 |
|
|
120
|
+
| `hideAutoTools` | `boolean` | 자동 생성 툴바 버튼 숨기기 |
|
|
121
|
+
| `class` | `string` | CSS 클래스 |
|
|
122
|
+
|
|
123
|
+
### 설정 타입
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// 인라인 편집 설정
|
|
127
|
+
interface InlineEditConfig<TItem> {
|
|
128
|
+
submit: (diffs: ArrayOneWayDiffResult<TItem>[]) => Promise<void>;
|
|
129
|
+
newItem: () => TItem;
|
|
130
|
+
deleteProp?: keyof TItem & string; // 삭제 플래그 프로퍼티 (예: "isDeleted")
|
|
131
|
+
diffsExcludes?: string[]; // diff 비교 제외 프로퍼티
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 다이얼로그 편집 설정
|
|
135
|
+
interface DialogEditConfig<TItem> {
|
|
136
|
+
editItem: (item?: TItem) => Promise<boolean | undefined>; // 등록(item 없음)/수정 다이얼로그
|
|
137
|
+
deleteItems?: (items: TItem[]) => Promise<boolean>; // 선택 항목 삭제
|
|
138
|
+
restoreItems?: (items: TItem[]) => Promise<boolean>; // 선택 항목 복원
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Excel 설정
|
|
142
|
+
interface ExcelConfig<TItem> {
|
|
143
|
+
download: (items: TItem[]) => Promise<void>; // 전체 데이터 다운로드
|
|
144
|
+
upload?: (file: File) => Promise<void>; // .xlsx 파일 업로드
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 조회 결과
|
|
148
|
+
interface SearchResult<TItem> {
|
|
149
|
+
items: TItem[];
|
|
150
|
+
pageCount?: number; // 페이지 수 (미설정 시 페이징 없음)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 선택 결과
|
|
154
|
+
interface SelectResult<TItem> {
|
|
155
|
+
items: TItem[];
|
|
156
|
+
keys: (string | number)[];
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 선택 모드
|
|
161
|
+
|
|
162
|
+
`selectionMode`를 설정하면 체크박스 컬럼이 자동 추가된다. Dialog 모드에서 사용하면 선택 전용 UI로 전환된다.
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
// 다중 선택 (제어 모드)
|
|
166
|
+
const [selectedKeys, setSelectedKeys] = createSignal<number[]>([]);
|
|
167
|
+
|
|
168
|
+
<CrudSheet
|
|
169
|
+
search={searchFn}
|
|
170
|
+
getItemKey={(item) => item.id}
|
|
171
|
+
selectionMode="multiple"
|
|
172
|
+
selectedKeys={selectedKeys()}
|
|
173
|
+
onSelectedKeysChange={setSelectedKeys}
|
|
174
|
+
>
|
|
175
|
+
<CrudSheet.Column key="name" header="이름">
|
|
176
|
+
{(ctx) => <div>{ctx.item.name}</div>}
|
|
177
|
+
</CrudSheet.Column>
|
|
178
|
+
</CrudSheet>
|
|
179
|
+
|
|
180
|
+
// Dialog에서 단일 선택 (클릭 시 자동 확정)
|
|
181
|
+
<CrudSheet
|
|
182
|
+
close={props.close}
|
|
183
|
+
search={searchFn}
|
|
184
|
+
getItemKey={(item) => item.id}
|
|
185
|
+
selectionMode="single"
|
|
186
|
+
onSelect={(result) => props.close?.({ selectedKeys: result.keys })}
|
|
187
|
+
>
|
|
188
|
+
...
|
|
189
|
+
</CrudSheet>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Excel 가져오기/내보내기
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
<CrudSheet
|
|
196
|
+
search={searchFn}
|
|
197
|
+
getItemKey={(item) => item.id}
|
|
198
|
+
excel={{
|
|
199
|
+
download: async (items) => {
|
|
200
|
+
// items는 전체 데이터 (페이징 무시하고 재조회)
|
|
201
|
+
await exportToExcel(items);
|
|
202
|
+
},
|
|
203
|
+
upload: async (file) => {
|
|
204
|
+
// .xlsx 파일 업로드 후 자동 새로고침
|
|
205
|
+
await importFromExcel(file);
|
|
206
|
+
},
|
|
207
|
+
}}
|
|
208
|
+
>
|
|
209
|
+
...
|
|
210
|
+
</CrudSheet>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### 키보드 단축키
|
|
214
|
+
|
|
215
|
+
| 단축키 | 동작 |
|
|
216
|
+
|--------|------|
|
|
217
|
+
| `Ctrl+S` | 저장 (인라인 편집 모드) |
|
|
218
|
+
| `Ctrl+Alt+L` | 새로고침 |
|
|
219
|
+
|
|
220
|
+
활성화 조건: 해당 CrudSheet 영역에 포커스/클릭이 있어야 한다 (다중 CrudSheet 환경에서 충돌 방지).
|
|
221
|
+
|
|
93
222
|
### CrudSheet 서브 컴포넌트
|
|
94
223
|
|
|
95
224
|
| 컴포넌트 | 설명 |
|
|
96
225
|
|----------|------|
|
|
97
226
|
| `CrudSheet.Column` | 컬럼 정의. `editTrigger`로 dialogEdit 시 클릭 편집 링크 표시 |
|
|
98
|
-
| `CrudSheet.Filter` | 검색 필터
|
|
99
|
-
| `CrudSheet.Tools` | 커스텀 툴바
|
|
227
|
+
| `CrudSheet.Filter` | 검색 필터 영역. render prop으로 `(filter, setFilter)` 전달 |
|
|
228
|
+
| `CrudSheet.Tools` | 커스텀 툴바 버튼. render prop으로 `CrudSheetContext` 전달 |
|
|
100
229
|
| `CrudSheet.Header` | 시트 상단 커스텀 헤더 영역 |
|
|
101
230
|
|
|
231
|
+
### CrudSheet.Column
|
|
232
|
+
|
|
233
|
+
`DataSheetColumn`의 모든 props를 상속하며, 추가로 `editTrigger`와 CRUD 전용 셀 컨텍스트를 제공한다.
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
<CrudSheet.Column<TItem>
|
|
237
|
+
key="name" // 컬럼 식별 키 (정렬, 저장에 사용)
|
|
238
|
+
header="이름" // 헤더 텍스트 또는 JSX
|
|
239
|
+
editTrigger // dialogEdit 시 클릭 편집 링크 표시
|
|
240
|
+
fixed // 고정 컬럼
|
|
241
|
+
hidden // 기본 숨김 (사용자가 토글 가능)
|
|
242
|
+
collapse // 접기 가능
|
|
243
|
+
width="150px" // 초기 너비
|
|
244
|
+
sortable={true} // 정렬 가능 (기본: true)
|
|
245
|
+
resizable={true} // 리사이즈 가능 (기본: true)
|
|
246
|
+
summary={(items) => items.reduce((sum, item) => sum + item.amount, 0)}
|
|
247
|
+
>
|
|
248
|
+
{(ctx) => (
|
|
249
|
+
// ctx: CrudSheetCellContext<TItem>
|
|
250
|
+
// ctx.item: 현재 아이템
|
|
251
|
+
// ctx.index: 배열 인덱스
|
|
252
|
+
// ctx.row: 행 번호
|
|
253
|
+
// ctx.depth: 트리 깊이
|
|
254
|
+
// ctx.setItem(key, value): 인라인 편집 시 값 변경
|
|
255
|
+
<TextInput value={ctx.item.name} onValueChange={(v) => ctx.setItem("name", v)} />
|
|
256
|
+
)}
|
|
257
|
+
</CrudSheet.Column>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### CrudSheet.Filter
|
|
261
|
+
|
|
262
|
+
검색 필터 영역. render prop으로 현재 `filter` 상태와 `setFilter` (SolidJS store setter)를 전달한다. 검색 버튼은 자동 생성된다.
|
|
263
|
+
|
|
264
|
+
```tsx
|
|
265
|
+
<CrudSheet.Filter<{ searchText?: string; status?: string }>>
|
|
266
|
+
{(filter, setFilter) => (
|
|
267
|
+
<>
|
|
268
|
+
<FormGroup.Item label="검색어">
|
|
269
|
+
<TextInput value={filter.searchText ?? ""} onValueChange={(v) => setFilter("searchText", v)} />
|
|
270
|
+
</FormGroup.Item>
|
|
271
|
+
<FormGroup.Item label="상태">
|
|
272
|
+
<Select
|
|
273
|
+
value={filter.status}
|
|
274
|
+
onValueChange={(v) => setFilter("status", v)}
|
|
275
|
+
items={["active", "inactive"]}
|
|
276
|
+
/>
|
|
277
|
+
</FormGroup.Item>
|
|
278
|
+
</>
|
|
279
|
+
)}
|
|
280
|
+
</CrudSheet.Filter>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### CrudSheet.Tools
|
|
284
|
+
|
|
285
|
+
커스텀 툴바 버튼. render prop으로 `CrudSheetContext`를 전달하여 내부 상태 접근 및 액션 호출이 가능하다.
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
<CrudSheet.Tools<TItem>>
|
|
289
|
+
{(ctx) => (
|
|
290
|
+
<>
|
|
291
|
+
<Button size="sm" onClick={() => void handleCustomAction(ctx.selection())}>
|
|
292
|
+
커스텀 액션 ({ctx.selection().length}건)
|
|
293
|
+
</Button>
|
|
294
|
+
<Show when={ctx.hasChanges()}>
|
|
295
|
+
<span class="text-warning-500">변경사항 있음</span>
|
|
296
|
+
</Show>
|
|
297
|
+
</>
|
|
298
|
+
)}
|
|
299
|
+
</CrudSheet.Tools>
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
#### CrudSheetContext API
|
|
303
|
+
|
|
304
|
+
| 멤버 | 타입 | 설명 |
|
|
305
|
+
|------|------|------|
|
|
306
|
+
| `items()` | `TItem[]` | 현재 페이지 아이템 목록 |
|
|
307
|
+
| `selection()` | `TItem[]` | 현재 선택된 아이템 목록 |
|
|
308
|
+
| `page()` | `number` | 현재 페이지 번호 |
|
|
309
|
+
| `sorts()` | `SortingDef[]` | 현재 정렬 정의 |
|
|
310
|
+
| `busy()` | `boolean` | 로딩 중 여부 |
|
|
311
|
+
| `hasChanges()` | `boolean` | 변경사항 존재 여부 (인라인 편집) |
|
|
312
|
+
| `save()` | `Promise<void>` | 저장 실행 |
|
|
313
|
+
| `refresh()` | `Promise<void>` | 데이터 새로고침 |
|
|
314
|
+
| `addItem()` | `void` | 행 추가 (인라인 편집) |
|
|
315
|
+
| `clearSelection()` | `void` | 선택 초기화 |
|
|
316
|
+
| `setPage(page)` | `void` | 페이지 변경 |
|
|
317
|
+
| `setSorts(sorts)` | `void` | 정렬 변경 |
|
|
318
|
+
|
|
319
|
+
### CrudSheet.Header
|
|
320
|
+
|
|
321
|
+
시트 상단에 커스텀 헤더 영역을 추가한다.
|
|
322
|
+
|
|
323
|
+
```tsx
|
|
324
|
+
<CrudSheet.Header>
|
|
325
|
+
<div class="p-2 text-lg font-bold">사용자 관리</div>
|
|
326
|
+
</CrudSheet.Header>
|
|
327
|
+
```
|
|
328
|
+
|
|
102
329
|
---
|
|
103
330
|
|
|
104
331
|
## CrudDetail
|
|
@@ -131,6 +358,46 @@ function UserDetail(props: { close?: (result?: boolean) => void; userId: number
|
|
|
131
358
|
const result = await dialog.open(UserDetail, { userId: 123 });
|
|
132
359
|
```
|
|
133
360
|
|
|
361
|
+
### CrudDetail Props
|
|
362
|
+
|
|
363
|
+
| Prop | 타입 | 설명 |
|
|
364
|
+
|------|------|------|
|
|
365
|
+
| `load` | `() => Promise<{ data: TData; info: CrudDetailInfo }>` | 데이터 로드 함수 (필수) |
|
|
366
|
+
| `children` | `(ctx: CrudDetailContext<TData>) => JSX.Element` | 폼 렌더링 (필수) |
|
|
367
|
+
| `submit` | `(data: TData) => Promise<boolean \| undefined>` | 저장 함수 |
|
|
368
|
+
| `toggleDelete` | `(del: boolean) => Promise<boolean \| undefined>` | 삭제/복원 토글 |
|
|
369
|
+
| `editable` | `boolean` | 편집 가능 여부 |
|
|
370
|
+
| `deletable` | `boolean` | 삭제 가능 여부 |
|
|
371
|
+
| `data` | `TData` | 제어 모드: 외부 데이터 |
|
|
372
|
+
| `onDataChange` | `(data: TData) => void` | 제어 모드: 데이터 변경 콜백 |
|
|
373
|
+
| `close` | `(result?: boolean) => void` | Dialog 모드 활성화 |
|
|
374
|
+
| `class` | `string` | CSS 클래스 |
|
|
375
|
+
|
|
376
|
+
### CrudDetailInfo
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
interface CrudDetailInfo {
|
|
380
|
+
isNew: boolean; // 신규 레코드 여부
|
|
381
|
+
isDeleted: boolean; // 삭제 상태
|
|
382
|
+
lastModifiedAt?: DateTime; // 최종 수정 일시
|
|
383
|
+
lastModifiedBy?: string; // 최종 수정자
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### CrudDetailContext
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
interface CrudDetailContext<TData> {
|
|
391
|
+
data: TData; // 현재 데이터 (store)
|
|
392
|
+
setData: SetStoreFunction<TData>; // 데이터 수정 (SolidJS store setter)
|
|
393
|
+
info: () => CrudDetailInfo; // 상세 정보
|
|
394
|
+
busy: () => boolean; // 로딩 중 여부
|
|
395
|
+
hasChanges: () => boolean; // 변경사항 존재 여부
|
|
396
|
+
save: () => Promise<void>; // 저장 실행
|
|
397
|
+
refresh: () => Promise<void>; // 데이터 새로고침
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
134
401
|
### CrudDetail 서브 컴포넌트
|
|
135
402
|
|
|
136
403
|
| 컴포넌트 | 설명 |
|