@simplysm/solid 13.0.96 → 13.0.97
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/package.json +19 -26
- package/README.md +0 -158
- package/docs/display-feedback.md +0 -404
- package/docs/features.md +0 -693
- package/docs/form-controls.md +0 -587
- package/docs/layout-data.md +0 -392
- package/docs/providers-hooks.md +0 -516
package/docs/providers-hooks.md
DELETED
|
@@ -1,516 +0,0 @@
|
|
|
1
|
-
# 프로바이더 & 훅
|
|
2
|
-
|
|
3
|
-
## SystemProvider
|
|
4
|
-
|
|
5
|
-
모든 필수 프로바이더를 한 번에 감싸는 편의 컴포넌트. 대부분의 앱에서는 이것 하나로 충분하다.
|
|
6
|
-
|
|
7
|
-
```tsx
|
|
8
|
-
import { SystemProvider } from "@simplysm/solid";
|
|
9
|
-
|
|
10
|
-
<SystemProvider clientName="my-app" busyVariant="spinner">
|
|
11
|
-
<App />
|
|
12
|
-
</SystemProvider>
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
| Prop | 타입 | 설명 |
|
|
16
|
-
|------|------|------|
|
|
17
|
-
| `clientName` | `string` | 클라이언트 식별 이름 (localStorage 키 접두사 등에 사용) |
|
|
18
|
-
| `busyVariant` | `"spinner" \| "bar"` | 글로벌 BusyProvider 변형 |
|
|
19
|
-
|
|
20
|
-
내부 프로바이더 스택 (위에서 아래 순서):
|
|
21
|
-
`ConfigProvider` > `I18nProvider` > `SyncStorageProvider` > `LoggerProvider` > `NotificationProvider` + `NotificationBanner` > `ErrorLoggerProvider` > `PwaUpdateProvider` > `ClipboardProvider` > `ThemeProvider` > `ServiceClientProvider` > `SharedDataProvider` > `BusyProvider`
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## ThemeProvider
|
|
26
|
-
|
|
27
|
-
라이트/다크/시스템 테마 관리. localStorage에 설정 저장.
|
|
28
|
-
|
|
29
|
-
```tsx
|
|
30
|
-
import { ThemeProvider, useTheme } from "@simplysm/solid";
|
|
31
|
-
|
|
32
|
-
<ThemeProvider>
|
|
33
|
-
<App />
|
|
34
|
-
</ThemeProvider>
|
|
35
|
-
|
|
36
|
-
const theme = useTheme();
|
|
37
|
-
|
|
38
|
-
theme.mode(); // "light" | "dark" | "system"
|
|
39
|
-
theme.resolvedTheme(); // "light" | "dark" (OS 설정 반영)
|
|
40
|
-
theme.setMode("dark");
|
|
41
|
-
theme.cycleMode(); // light -> system -> dark -> light
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### useTheme 반환 타입
|
|
45
|
-
|
|
46
|
-
| 속성 | 타입 | 설명 |
|
|
47
|
-
|------|------|------|
|
|
48
|
-
| `mode()` | `ThemeMode` | 현재 테마 모드 (`"light" \| "dark" \| "system"`) |
|
|
49
|
-
| `setMode(mode)` | `(mode: ThemeMode) => void` | 테마 모드 설정 |
|
|
50
|
-
| `resolvedTheme()` | `ResolvedTheme` | 실제 적용된 테마 (`"light" \| "dark"`) |
|
|
51
|
-
| `cycleMode()` | `() => void` | 다음 모드로 순환 |
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## I18nProvider
|
|
56
|
-
|
|
57
|
-
다국어 지원. 한국어(ko), 영어(en) 내장. 브라우저 언어 자동 감지.
|
|
58
|
-
|
|
59
|
-
```tsx
|
|
60
|
-
import { I18nProvider, useI18n } from "@simplysm/solid";
|
|
61
|
-
|
|
62
|
-
<I18nProvider>
|
|
63
|
-
<App />
|
|
64
|
-
</I18nProvider>
|
|
65
|
-
|
|
66
|
-
const i18n = useI18n();
|
|
67
|
-
|
|
68
|
-
i18n.t("save"); // 번역 조회
|
|
69
|
-
i18n.t("greeting", { name: "Alice" }); // 파라미터 치환
|
|
70
|
-
i18n.locale(); // 현재 로케일
|
|
71
|
-
i18n.setLocale("en");
|
|
72
|
-
|
|
73
|
-
// 사전 확장
|
|
74
|
-
i18n.configure({
|
|
75
|
-
dictionaries: {
|
|
76
|
-
ko: { myKey: "내 값" },
|
|
77
|
-
en: { myKey: "My Value" },
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### useI18n 반환 타입
|
|
83
|
-
|
|
84
|
-
| 속성 | 타입 | 설명 |
|
|
85
|
-
|------|------|------|
|
|
86
|
-
| `t(key, params?)` | `(key: string, params?: Record<string, string>) => string` | 번역 조회 |
|
|
87
|
-
| `locale()` | `Accessor<string>` | 현재 로케일 |
|
|
88
|
-
| `setLocale(locale)` | `(locale: string) => void` | 로케일 변경 |
|
|
89
|
-
| `configure(options)` | `(options: I18nConfigureOptions) => void` | 사전 확장/설정 |
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
## SharedDataProvider
|
|
94
|
-
|
|
95
|
-
서버 데이터를 구독하고 실시간 동기화하는 프로바이더. `ServiceClientProvider`와 `NotificationProvider` 내부에서 사용해야 한다.
|
|
96
|
-
|
|
97
|
-
```tsx
|
|
98
|
-
import { SharedDataProvider, useSharedData } from "@simplysm/solid";
|
|
99
|
-
|
|
100
|
-
// 프로바이더 설정 (SystemProvider 사용 시 자동 포함)
|
|
101
|
-
<SharedDataProvider>
|
|
102
|
-
<App />
|
|
103
|
-
</SharedDataProvider>
|
|
104
|
-
|
|
105
|
-
// 데이터 정의 (자식 컴포넌트에서 한 번만 호출)
|
|
106
|
-
const sharedData = useSharedData<{
|
|
107
|
-
users: User;
|
|
108
|
-
departments: Department;
|
|
109
|
-
}>();
|
|
110
|
-
|
|
111
|
-
sharedData.configure(() => ({
|
|
112
|
-
users: {
|
|
113
|
-
fetch: async (changeKeys) => await api.getUsers(changeKeys),
|
|
114
|
-
getKey: (item) => item.id,
|
|
115
|
-
orderBy: [[(item) => item.name, "asc"]],
|
|
116
|
-
itemSearchText: (item) => item.name,
|
|
117
|
-
isItemHidden: (item) => item.isDeleted,
|
|
118
|
-
},
|
|
119
|
-
departments: {
|
|
120
|
-
fetch: async (changeKeys) => await api.getDepartments(changeKeys),
|
|
121
|
-
getKey: (item) => item.id,
|
|
122
|
-
orderBy: [[(item) => item.sortOrder, "asc"]],
|
|
123
|
-
getParentKey: (item) => item.parentId, // 트리 구조 지원
|
|
124
|
-
},
|
|
125
|
-
}));
|
|
126
|
-
|
|
127
|
-
// 데이터 사용
|
|
128
|
-
const users = sharedData.users.items(); // 반응형 배열
|
|
129
|
-
const user = sharedData.users.get(userId); // 키로 조회
|
|
130
|
-
|
|
131
|
-
// 변경 이벤트 발행 (서버의 모든 구독자에게 전파)
|
|
132
|
-
await sharedData.users.emit([changedUserId]);
|
|
133
|
-
|
|
134
|
-
// 전체 로딩 대기
|
|
135
|
-
await sharedData.wait();
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### SharedDataDefinition
|
|
139
|
-
|
|
140
|
-
| 속성 | 타입 | 설명 |
|
|
141
|
-
|------|------|------|
|
|
142
|
-
| `fetch` | `(changeKeys?) => Promise<TData[]>` | 데이터 조회 함수 |
|
|
143
|
-
| `getKey` | `(item) => string \| number` | 항목 고유 키 추출 |
|
|
144
|
-
| `orderBy` | `[(item) => unknown, "asc" \| "desc"][]` | 정렬 기준 (다중) |
|
|
145
|
-
| `serviceKey` | `string` | 서비스 연결 키 (기본: `"default"`) |
|
|
146
|
-
| `filter` | `unknown` | 서버 이벤트 필터 |
|
|
147
|
-
| `itemSearchText` | `(item) => string` | 검색 텍스트 추출 |
|
|
148
|
-
| `isItemHidden` | `(item) => boolean` | 숨김 여부 |
|
|
149
|
-
| `getParentKey` | `(item) => string \| number \| undefined` | 부모 키 (트리 구조) |
|
|
150
|
-
|
|
151
|
-
### SharedDataAccessor
|
|
152
|
-
|
|
153
|
-
| 메서드/속성 | 타입 | 설명 |
|
|
154
|
-
|------------|------|------|
|
|
155
|
-
| `items()` | `Accessor<TData[]>` | 반응형 데이터 배열 |
|
|
156
|
-
| `get(key)` | `(key) => TData \| undefined` | 키로 단건 조회 |
|
|
157
|
-
| `emit(changeKeys?)` | `(keys?) => Promise<void>` | 변경 이벤트 발행 |
|
|
158
|
-
| `getKey` | `(item) => string \| number` | 키 추출 함수 |
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
## 기타 프로바이더
|
|
163
|
-
|
|
164
|
-
| 프로바이더 | 설명 |
|
|
165
|
-
|-----------|------|
|
|
166
|
-
| `ConfigProvider` | 클라이언트 설정 (localStorage 키 접두사). prop: `clientName: string` |
|
|
167
|
-
| `ServiceClientProvider` | `@simplysm/service-client` WebSocket 연결 통합 |
|
|
168
|
-
| `SyncStorageProvider` | localStorage 동기화 |
|
|
169
|
-
| `LoggerProvider` | 로깅 설정 |
|
|
170
|
-
| `ErrorLoggerProvider` | 글로벌 에러 핸들링 (window.onerror 등) |
|
|
171
|
-
| `ClipboardProvider` | 클립보드 기능 |
|
|
172
|
-
| `PwaUpdateProvider` | PWA Service Worker 업데이트 감지 (5분 간격 폴링, 알림 표시) |
|
|
173
|
-
|
|
174
|
-
---
|
|
175
|
-
|
|
176
|
-
## 훅
|
|
177
|
-
|
|
178
|
-
### createControllableSignal
|
|
179
|
-
|
|
180
|
-
제어/비제어 컴포넌트 패턴 구현. `onChange`가 제공되면 제어 모드, 없으면 비제어 모드.
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
import { createControllableSignal } from "@simplysm/solid";
|
|
184
|
-
|
|
185
|
-
const [value, setValue] = createControllableSignal({
|
|
186
|
-
value: () => props.value ?? "",
|
|
187
|
-
onChange: () => props.onValueChange,
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// 함수형 setter 지원
|
|
191
|
-
setValue((prev) => prev + "!");
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
// 시그니처
|
|
196
|
-
function createControllableSignal<TValue>(options: {
|
|
197
|
-
value: Accessor<TValue>;
|
|
198
|
-
onChange: Accessor<((value: TValue) => void) | undefined>;
|
|
199
|
-
}): [Accessor<TValue>, (newValue: TValue | ((prev: TValue) => TValue)) => TValue];
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### createControllableStore
|
|
203
|
-
|
|
204
|
-
객체 상태용 제어/비제어 패턴. SolidJS store 기반.
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
import { createControllableStore } from "@simplysm/solid";
|
|
208
|
-
|
|
209
|
-
const [items, setItems] = createControllableStore<Item[]>({
|
|
210
|
-
value: () => props.items ?? [],
|
|
211
|
-
onChange: () => props.onItemsChange,
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// SetStoreFunction 오버로드 모두 지원
|
|
215
|
-
setItems(0, "name", "Alice");
|
|
216
|
-
setItems(reconcile(newItems));
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
```typescript
|
|
220
|
-
// 시그니처
|
|
221
|
-
function createControllableStore<TValue extends object>(options: {
|
|
222
|
-
value: () => TValue;
|
|
223
|
-
onChange: () => ((value: TValue) => void) | undefined;
|
|
224
|
-
}): [TValue, SetStoreFunction<TValue>];
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### createIMEHandler
|
|
228
|
-
|
|
229
|
-
IME(한글 등) 입력 처리. 조합 중 DOM 재생성을 방지한다.
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
import { createIMEHandler } from "@simplysm/solid";
|
|
233
|
-
|
|
234
|
-
const ime = createIMEHandler((value) => setValue(value));
|
|
235
|
-
|
|
236
|
-
// 이벤트 핸들러
|
|
237
|
-
onCompositionStart={ime.handleCompositionStart}
|
|
238
|
-
onInput={(e) => ime.handleInput(e.currentTarget.value, e.isComposing)}
|
|
239
|
-
onCompositionEnd={(e) => ime.handleCompositionEnd(e.currentTarget.value)}
|
|
240
|
-
|
|
241
|
-
// 조합 중 값 (display 용)
|
|
242
|
-
ime.composingValue()
|
|
243
|
-
|
|
244
|
-
// 조합 강제 완료
|
|
245
|
-
ime.flushComposition()
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### createMountTransition
|
|
249
|
-
|
|
250
|
-
마운트/언마운트 시 애니메이션 상태를 관리한다.
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
import { createMountTransition } from "@simplysm/solid";
|
|
254
|
-
|
|
255
|
-
const { mounted, animating, unmount } = createMountTransition(() => isVisible());
|
|
256
|
-
|
|
257
|
-
// mounted: DOM에 마운트 여부
|
|
258
|
-
// animating: 진입/퇴장 애니메이션 중 여부
|
|
259
|
-
// unmount: 언마운트 트리거
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
### useLocalStorage
|
|
263
|
-
|
|
264
|
-
반응형 localStorage. `ConfigProvider`의 `clientName`을 키 접두사로 사용한다.
|
|
265
|
-
|
|
266
|
-
```typescript
|
|
267
|
-
import { useLocalStorage } from "@simplysm/solid";
|
|
268
|
-
|
|
269
|
-
const [token, setToken] = useLocalStorage<string>("auth-token");
|
|
270
|
-
|
|
271
|
-
setToken("abc123"); // 저장
|
|
272
|
-
token(); // "abc123"
|
|
273
|
-
setToken(undefined); // 삭제
|
|
274
|
-
|
|
275
|
-
// 함수형 setter
|
|
276
|
-
setToken((prev) => prev ? prev + "-updated" : "new-token");
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
```typescript
|
|
280
|
-
// 시그니처
|
|
281
|
-
function useLocalStorage<TValue>(
|
|
282
|
-
key: string,
|
|
283
|
-
initialValue?: TValue,
|
|
284
|
-
): [Accessor<TValue | undefined>, StorageSetter<TValue>];
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
### useSyncConfig
|
|
288
|
-
|
|
289
|
-
클라이언트명 접두사 붙은 localStorage 동기화.
|
|
290
|
-
|
|
291
|
-
```typescript
|
|
292
|
-
import { useSyncConfig } from "@simplysm/solid";
|
|
293
|
-
|
|
294
|
-
const [config, setConfig] = useSyncConfig("my-setting", defaultValue);
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
### useLogger
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
import { useLogger } from "@simplysm/solid";
|
|
301
|
-
|
|
302
|
-
const logger = useLogger();
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### useRouterLink
|
|
306
|
-
|
|
307
|
-
```typescript
|
|
308
|
-
import { useRouterLink } from "@simplysm/solid";
|
|
309
|
-
|
|
310
|
-
const navigate = useRouterLink();
|
|
311
|
-
navigate("/users");
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
|
|
316
|
-
## createAppStructure
|
|
317
|
-
|
|
318
|
-
앱 메뉴, 라우트, 권한 구조를 선언적으로 정의한다. 모듈별 필터링과 권한 기반 접근 제어를 지원한다.
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
import { createAppStructure } from "@simplysm/solid";
|
|
322
|
-
import type { AppStructureItem } from "@simplysm/solid";
|
|
323
|
-
|
|
324
|
-
type Module = "basic" | "pro" | "enterprise";
|
|
325
|
-
|
|
326
|
-
const items: AppStructureItem<Module>[] = [
|
|
327
|
-
{
|
|
328
|
-
code: "admin",
|
|
329
|
-
title: "관리",
|
|
330
|
-
children: [
|
|
331
|
-
{
|
|
332
|
-
code: "users",
|
|
333
|
-
title: "사용자 관리",
|
|
334
|
-
component: UserPage,
|
|
335
|
-
perms: ["use", "edit"],
|
|
336
|
-
modules: ["basic"],
|
|
337
|
-
},
|
|
338
|
-
{
|
|
339
|
-
code: "roles",
|
|
340
|
-
title: "역할 관리",
|
|
341
|
-
component: RolePage,
|
|
342
|
-
perms: ["use", "edit"],
|
|
343
|
-
modules: ["pro"],
|
|
344
|
-
subPerms: [
|
|
345
|
-
{ code: "advanced", title: "고급 설정", perms: ["use", "edit"], modules: ["enterprise"] },
|
|
346
|
-
],
|
|
347
|
-
},
|
|
348
|
-
],
|
|
349
|
-
},
|
|
350
|
-
];
|
|
351
|
-
|
|
352
|
-
const { AppStructureProvider, useAppStructure } = createAppStructure(() => ({
|
|
353
|
-
items,
|
|
354
|
-
usableModules: () => activeModules(),
|
|
355
|
-
permRecord: () => userPermissions(),
|
|
356
|
-
}));
|
|
357
|
-
|
|
358
|
-
// 프로바이더 설정
|
|
359
|
-
<AppStructureProvider>
|
|
360
|
-
<App />
|
|
361
|
-
</AppStructureProvider>
|
|
362
|
-
|
|
363
|
-
// 사용
|
|
364
|
-
const app = useAppStructure();
|
|
365
|
-
|
|
366
|
-
app.usableRoutes(); // 접근 가능한 라우트 배열
|
|
367
|
-
app.usableMenus(); // 접근 가능한 메뉴 트리
|
|
368
|
-
app.usableFlatMenus(); // 플랫 메뉴 배열 (검색용)
|
|
369
|
-
app.usablePerms(); // 권한 트리 (PermissionTable에 전달)
|
|
370
|
-
app.allFlatPerms; // 모든 권한 목록 (관리용)
|
|
371
|
-
app.perms; // 타입 안전한 권한 객체 (app.perms.admin.users.use)
|
|
372
|
-
|
|
373
|
-
app.getTitleChainByHref("/admin/users"); // ["관리", "사용자 관리"]
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
### AppStructureItem 타입
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
// 그룹 항목 (children 보유)
|
|
380
|
-
interface AppStructureGroupItem<TModule> {
|
|
381
|
-
code: string;
|
|
382
|
-
title: string;
|
|
383
|
-
icon?: Component<IconProps>;
|
|
384
|
-
modules?: TModule[];
|
|
385
|
-
requiredModules?: TModule[];
|
|
386
|
-
children: AppStructureItem<TModule>[];
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// 리프 항목 (페이지 컴포넌트)
|
|
390
|
-
interface AppStructureLeafItem<TModule> {
|
|
391
|
-
code: string;
|
|
392
|
-
title: string;
|
|
393
|
-
icon?: Component<IconProps>;
|
|
394
|
-
modules?: TModule[];
|
|
395
|
-
requiredModules?: TModule[];
|
|
396
|
-
component?: Component;
|
|
397
|
-
perms?: ("use" | "edit")[];
|
|
398
|
-
subPerms?: AppStructureSubPerm<TModule>[];
|
|
399
|
-
isNotMenu?: boolean;
|
|
400
|
-
}
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
### AppStructure 반환 타입
|
|
404
|
-
|
|
405
|
-
| 속성 | 타입 | 설명 |
|
|
406
|
-
|------|------|------|
|
|
407
|
-
| `usableRoutes` | `Accessor<AppRoute[]>` | 모듈/권한 필터링된 라우트 |
|
|
408
|
-
| `usableMenus` | `Accessor<AppMenu[]>` | 필터링된 메뉴 트리 |
|
|
409
|
-
| `usableFlatMenus` | `Accessor<AppFlatMenu[]>` | 플랫 메뉴 배열 |
|
|
410
|
-
| `usablePerms` | `Accessor<AppPerm[]>` | 필터링된 권한 트리 |
|
|
411
|
-
| `allFlatPerms` | `AppFlatPerm[]` | 전체 권한 목록 |
|
|
412
|
-
| `perms` | `InferPerms<TItems>` | 타입 추론된 권한 접근 객체 |
|
|
413
|
-
| `getTitleChainByHref` | `(href: string) => string[]` | href로 타이틀 체인 조회 |
|
|
414
|
-
|
|
415
|
-
---
|
|
416
|
-
|
|
417
|
-
## 스타일 유틸리티
|
|
418
|
-
|
|
419
|
-
### 기본 스타일
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
import { bg, border, text } from "@simplysm/solid";
|
|
423
|
-
|
|
424
|
-
// Tailwind 클래스 프리셋
|
|
425
|
-
bg.surface // bg-white dark:bg-base-900
|
|
426
|
-
bg.muted // bg-base-100 dark:bg-base-800
|
|
427
|
-
bg.subtle // bg-base-200 dark:bg-base-700
|
|
428
|
-
border.default // border-base-200 dark:border-base-700
|
|
429
|
-
text.default // text-base-900 dark:text-base-100
|
|
430
|
-
text.muted // text-base-400 dark:text-base-500
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
### 컨트롤 스타일
|
|
434
|
-
|
|
435
|
-
```typescript
|
|
436
|
-
import { pad, gap } from "@simplysm/solid";
|
|
437
|
-
|
|
438
|
-
pad.xs // px-1 py-0
|
|
439
|
-
pad.sm // px-1.5 py-0.5
|
|
440
|
-
pad.md // px-2 py-1
|
|
441
|
-
pad.lg // px-3 py-2
|
|
442
|
-
pad.xl // px-4 py-3
|
|
443
|
-
|
|
444
|
-
gap.xs // gap-0
|
|
445
|
-
gap.sm // gap-0.5
|
|
446
|
-
gap.md // gap-1
|
|
447
|
-
gap.lg // gap-1.5
|
|
448
|
-
gap.xl // gap-2
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
### 테마 토큰
|
|
452
|
-
|
|
453
|
-
```typescript
|
|
454
|
-
import { themeTokens, type SemanticTheme } from "@simplysm/solid";
|
|
455
|
-
|
|
456
|
-
// 각 semantic theme(base, primary, success, warning, danger, info)별:
|
|
457
|
-
themeTokens.primary.solid // bg-primary-500 text-white
|
|
458
|
-
themeTokens.primary.solidHover // hover:bg-primary-600 dark:hover:bg-primary-400
|
|
459
|
-
themeTokens.primary.light // bg-primary-100 text-primary-900 ...
|
|
460
|
-
themeTokens.primary.text // text-primary-600 dark:text-primary-400
|
|
461
|
-
themeTokens.primary.hoverBg // hover:bg-primary-100 ...
|
|
462
|
-
themeTokens.primary.border // border-primary-300 ...
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
---
|
|
466
|
-
|
|
467
|
-
## 디렉티브
|
|
468
|
-
|
|
469
|
-
### ripple
|
|
470
|
-
|
|
471
|
-
Material Design 스타일 리플 효과. 포인터 다운 시 클릭 위치에서 원형 리플이 확산된다.
|
|
472
|
-
|
|
473
|
-
```tsx
|
|
474
|
-
import { ripple } from "@simplysm/solid";
|
|
475
|
-
void ripple; // TypeScript directive 등록
|
|
476
|
-
|
|
477
|
-
<button use:ripple={!props.disabled}>클릭</button>
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
- `prefers-reduced-motion: reduce` 설정 시 리플 비활성화
|
|
481
|
-
- 내부적으로 `overflow: hidden` 컨테이너를 생성하여 부모 요소에 영향 없음
|
|
482
|
-
- `static` 포지션인 경우 자동으로 `relative`로 변경 (cleanup 시 복원)
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
## 헬퍼
|
|
487
|
-
|
|
488
|
-
### mergeStyles
|
|
489
|
-
|
|
490
|
-
인라인 CSS 문자열 병합.
|
|
491
|
-
|
|
492
|
-
```typescript
|
|
493
|
-
import { mergeStyles } from "@simplysm/solid";
|
|
494
|
-
|
|
495
|
-
mergeStyles("color: red", "font-size: 14px"); // "color: red; font-size: 14px"
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
### createSlot / createSlots
|
|
499
|
-
|
|
500
|
-
컴포넌트 합성을 위한 슬롯 패턴. `createSlot`은 단일 슬롯, `createSlots`는 복수 슬롯을 지원한다.
|
|
501
|
-
|
|
502
|
-
```typescript
|
|
503
|
-
import { createSlot } from "@simplysm/solid";
|
|
504
|
-
|
|
505
|
-
// 1. 슬롯 정의
|
|
506
|
-
const [MySlot, createMySlotAccessor] = createSlot<{ children: JSX.Element }>();
|
|
507
|
-
|
|
508
|
-
// 2. 컴포넌트 내부에서 슬롯 접근
|
|
509
|
-
const [slotValue, SlotProvider] = createMySlotAccessor();
|
|
510
|
-
|
|
511
|
-
// 3. 사용
|
|
512
|
-
<SlotProvider>
|
|
513
|
-
<MySlot><span>슬롯 내용</span></MySlot>
|
|
514
|
-
{/* slotValue()?.children 으로 접근 */}
|
|
515
|
-
</SlotProvider>
|
|
516
|
-
```
|