ginskill-init 1.0.2 → 2.4.0

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.

Potentially problematic release.


This version of ginskill-init might be problematic. Click here for more details.

@@ -0,0 +1,451 @@
1
+ # Ant Design — Feedback Components (Drawer, Message, Notification, Spin, Skeleton, App)
2
+
3
+ ## Drawer
4
+
5
+ ```typescript
6
+ import { Drawer } from 'antd';
7
+ import type { DrawerProps } from 'antd';
8
+ ```
9
+
10
+ ### DrawerProps
11
+
12
+ | Prop | Type | Default | Description |
13
+ |------|------|---------|-------------|
14
+ | `open` | `boolean` | `false` | Show/hide the drawer |
15
+ | `onClose` | `(e) => void` | — | Called when closing (mask click, ESC, close button) |
16
+ | `title` | `ReactNode` | — | Drawer header title |
17
+ | `placement` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'right'` | Which side slides in from |
18
+ | `width` | `string \| number` | `378` | Width (left/right placement) |
19
+ | `height` | `string \| number` | `378` | Height (top/bottom placement) |
20
+ | `size` | `'default' \| 'large'` | `'default'` | Preset size |
21
+ | `extra` | `ReactNode` | — | Extra content in header (top right) |
22
+ | `footer` | `ReactNode` | — | Footer content |
23
+ | `mask` | `boolean` | `true` | Show backdrop |
24
+ | `maskClosable` | `boolean` | `true` | Close on mask click |
25
+ | `keyboard` | `boolean` | `true` | Close on ESC key |
26
+ | `destroyOnClose` | `boolean` | `false` | Unmount content when closed |
27
+ | `push` | `boolean \| { distance }` | `{ distance: 180 }` | Push sibling content |
28
+ | `zIndex` | `number` | `1000` | Z-index |
29
+ | `afterOpenChange` | `(open) => void` | — | After open/close animation |
30
+ | `getContainer` | `HTMLElement \| (() => HTMLElement) \| false` | `document.body` | Portal container |
31
+ | `styles` | `{ header?, body?, footer?, mask? }` | — | Semantic inline styles |
32
+ | `classNames` | `{ header?, body?, footer? }` | — | Semantic CSS classes |
33
+
34
+ ### Patterns
35
+
36
+ ```tsx
37
+ // Basic controlled drawer
38
+ const [open, setOpen] = useState(false);
39
+ <Button onClick={() => setOpen(true)}>Open</Button>
40
+ <Drawer title="Details" open={open} onClose={() => setOpen(false)} width={480}>
41
+ <p>Content here</p>
42
+ </Drawer>
43
+
44
+ // Form in drawer with footer actions
45
+ <Drawer
46
+ title="Add Record"
47
+ open={open}
48
+ onClose={() => setOpen(false)}
49
+ footer={
50
+ <Space>
51
+ <Button onClick={() => setOpen(false)}>Cancel</Button>
52
+ <Button type="primary" onClick={handleSubmit} loading={saving}>Submit</Button>
53
+ </Space>
54
+ }
55
+ destroyOnClose
56
+ >
57
+ <Form form={form} layout="vertical">
58
+ <Form.Item name="name" label="Name" rules={[{ required: true }]}>
59
+ <Input />
60
+ </Form.Item>
61
+ </Form>
62
+ </Drawer>
63
+
64
+ // Large drawer from left
65
+ <Drawer placement="left" size="large" title="Navigation" open={open} onClose={() => setOpen(false)}>
66
+ <Menu mode="inline" items={menuItems} />
67
+ </Drawer>
68
+
69
+ // Without close button (custom close)
70
+ <Drawer
71
+ title={<span>Title <CloseOutlined onClick={() => setOpen(false)} /></span>}
72
+ closable={false}
73
+ open={open}
74
+ onClose={() => setOpen(false)}
75
+ >
76
+ Content
77
+ </Drawer>
78
+ ```
79
+
80
+ ### Common Mistakes
81
+ - **Always update state in `onClose`** — `onClose={() => setOpen(false)}`; forgetting this means the drawer won't close
82
+ - **Use `destroyOnClose` for forms** — otherwise form state persists from previous open
83
+ - **Long content doesn't scroll by default** — add `styles={{ body: { overflowY: 'auto' } }}` if needed
84
+
85
+ ---
86
+
87
+ ## Message
88
+
89
+ Imperative API — brief top-of-page notifications that auto-dismiss.
90
+
91
+ ```typescript
92
+ import { message } from 'antd';
93
+ ```
94
+
95
+ ### Methods
96
+
97
+ ```typescript
98
+ message.success(content, duration?, onClose?)
99
+ message.error(content, duration?, onClose?)
100
+ message.info(content, duration?, onClose?)
101
+ message.warning(content, duration?, onClose?)
102
+ message.loading(content, duration?, onClose?)
103
+
104
+ // Config object form
105
+ message.success({ content: 'Done!', duration: 2, key: 'myMsg', onClose: () => {} })
106
+
107
+ // Destroy all
108
+ message.destroy()
109
+ message.destroy('specificKey')
110
+
111
+ // Global config
112
+ message.config({ maxCount: 3, duration: 2, top: 8 })
113
+ ```
114
+
115
+ ### Key/Update pattern
116
+
117
+ ```typescript
118
+ // Show loading, then update to success/error
119
+ const key = 'uploading';
120
+ message.loading({ content: 'Uploading...', key, duration: 0 });
121
+
122
+ try {
123
+ await uploadFile();
124
+ message.success({ content: 'Done!', key, duration: 2 });
125
+ } catch {
126
+ message.error({ content: 'Failed!', key, duration: 2 });
127
+ }
128
+ ```
129
+
130
+ ### Patterns
131
+
132
+ ```tsx
133
+ // Inside click handlers
134
+ <Button onClick={() => message.success('Saved!')}>Save</Button>
135
+
136
+ // With duration 0 = never auto-close
137
+ message.loading({ content: 'Processing...', key, duration: 0 });
138
+
139
+ // Limit concurrent messages
140
+ message.config({ maxCount: 3 });
141
+
142
+ // Promise returned (resolves on close)
143
+ await message.success('Saved!');
144
+ ```
145
+
146
+ ### Common Mistakes
147
+ - **`message.xxx` is NOT affected by `ConfigProvider` theme/locale** — use `App.useApp()` for context-aware messages
148
+ - **Without `maxCount`, messages pile up** — always set `message.config({ maxCount: 3 })`
149
+ - **`key` update only works if the original message is still visible** — if it auto-closed, a new one appears
150
+
151
+ ---
152
+
153
+ ## Notification
154
+
155
+ Imperative API — corner notifications with title + description, longer content.
156
+
157
+ ```typescript
158
+ import { notification } from 'antd';
159
+ ```
160
+
161
+ ### Methods
162
+
163
+ ```typescript
164
+ notification.success({ message, description?, placement?, duration?, btn?, key?, onClose?, icon? })
165
+ notification.error({...})
166
+ notification.info({...})
167
+ notification.warning({...})
168
+ notification.open({...})
169
+
170
+ notification.destroy() // close all
171
+ notification.close('key') // close by key
172
+
173
+ // Global config
174
+ notification.config({
175
+ placement: 'topRight', // 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
176
+ duration: 4.5,
177
+ maxCount: 5,
178
+ top: 24, bottom: 24,
179
+ })
180
+ ```
181
+
182
+ ### Key properties
183
+
184
+ | Prop | Type | Default | Description |
185
+ |------|------|---------|-------------|
186
+ | `message` | `ReactNode` | — | Title (required) |
187
+ | `description` | `ReactNode` | — | Body text |
188
+ | `placement` | `'topLeft' \| 'topRight' \| 'bottomLeft' \| 'bottomRight'` | `'topRight'` | Position |
189
+ | `duration` | `number` | `4.5` | Auto-close seconds; `0` = never |
190
+ | `btn` | `ReactNode` | — | Action button |
191
+ | `key` | `string` | — | Unique ID for updates |
192
+ | `icon` | `ReactNode` | — | Custom icon |
193
+ | `onClose` | `() => void` | — | Close callback |
194
+ | `onClick` | `() => void` | — | Click callback |
195
+
196
+ ### Patterns
197
+
198
+ ```tsx
199
+ // Basic
200
+ notification.success({ message: 'Saved', description: 'Your changes were saved.' });
201
+
202
+ // With action button
203
+ notification.warning({
204
+ message: 'Session expiring',
205
+ description: 'Your session will expire in 5 minutes',
206
+ duration: 0,
207
+ key: 'session-warn',
208
+ btn: (
209
+ <Button size="small" type="primary"
210
+ onClick={() => { extendSession(); notification.close('session-warn'); }}>
211
+ Extend
212
+ </Button>
213
+ ),
214
+ });
215
+
216
+ // Update by key
217
+ notification.open({ message: 'Processing...', key: 'progress', duration: 0 });
218
+ // ... later:
219
+ notification.success({ message: 'Done!', key: 'progress' });
220
+
221
+ // Placement options
222
+ notification.info({ message: 'Info', placement: 'bottomRight' });
223
+ ```
224
+
225
+ ### Common Mistakes
226
+ - **`notification.xxx` is NOT affected by `ConfigProvider`** — use `App.useApp()` for context-aware notifications
227
+ - **Without `maxCount`, old notifications accumulate** — set `notification.config({ maxCount: 5 })`
228
+ - **`duration: 4.5` may be too short for long descriptions** — increase or set to `0` for persistent
229
+
230
+ ---
231
+
232
+ ## Spin
233
+
234
+ Loading spinner that can wrap content.
235
+
236
+ ```typescript
237
+ import { Spin } from 'antd';
238
+ import type { SpinProps } from 'antd';
239
+ ```
240
+
241
+ ### SpinProps
242
+
243
+ | Prop | Type | Default | Description |
244
+ |------|------|---------|-------------|
245
+ | `spinning` | `boolean` | `true` | Show spinner |
246
+ | `tip` | `string \| ReactNode` | — | Loading text below spinner |
247
+ | `size` | `'small' \| 'default' \| 'large'` | `'default'` | Spinner size |
248
+ | `delay` | `number` | `0` | Delay (ms) before showing |
249
+ | `indicator` | `ReactNode` | — | Custom spinner element |
250
+ | `fullscreen` | `boolean` | `false` | Full-page overlay |
251
+ | `children` | `ReactNode` | — | Content to overlay when spinning |
252
+ | `wrapperClassName` | `string` | — | Class for wrapper div |
253
+
254
+ ### Patterns
255
+
256
+ ```tsx
257
+ // Standalone spinner
258
+ <Spin />
259
+ <Spin size="large" tip="Loading..." />
260
+
261
+ // Content overlay
262
+ <Spin spinning={isLoading}>
263
+ <Table dataSource={data} columns={columns} />
264
+ </Spin>
265
+
266
+ // Custom indicator
267
+ <Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} />
268
+
269
+ // Delayed (avoid flash for fast operations)
270
+ <Spin spinning={loading} delay={500}>
271
+ <Content />
272
+ </Spin>
273
+
274
+ // Fullscreen
275
+ <Spin fullscreen spinning={processing} tip="Please wait..." />
276
+
277
+ // Conditional (avoid empty wrapper)
278
+ {isLoading ? <Spin /> : <Content />}
279
+ ```
280
+
281
+ ### Common Mistakes
282
+ - **`Spin` wrapping children blocks all interaction** while `spinning={true}` — intentional but verify UX
283
+ - **Custom icon needs `spin` CSS** — use `<LoadingOutlined spin />` from `@ant-design/icons`
284
+ - **`delay` causes flicker** on variable-speed operations — use judiciously (500ms is safe)
285
+
286
+ ---
287
+
288
+ ## Skeleton
289
+
290
+ Placeholder UI that mimics content structure during loading.
291
+
292
+ ```typescript
293
+ import { Skeleton } from 'antd';
294
+ import type { SkeletonProps } from 'antd';
295
+ ```
296
+
297
+ ### SkeletonProps
298
+
299
+ | Prop | Type | Default | Description |
300
+ |------|------|---------|-------------|
301
+ | `loading` | `boolean` | `true` | Show skeleton vs content |
302
+ | `active` | `boolean` | `false` | Shimmer animation |
303
+ | `avatar` | `boolean \| SkeletonAvatarProps` | `false` | Show avatar placeholder |
304
+ | `title` | `boolean \| { width }` | `true` | Show title bar |
305
+ | `paragraph` | `boolean \| { rows, width }` | `true` | Show text lines |
306
+ | `round` | `boolean` | `false` | Rounded corners on all bars |
307
+
308
+ ### Sub-components
309
+
310
+ ```typescript
311
+ // Standalone placeholders
312
+ <Skeleton.Avatar active size="large" shape="circle" />
313
+ <Skeleton.Button active size="large" block />
314
+ <Skeleton.Input active size="default" block />
315
+ <Skeleton.Image active />
316
+
317
+ // Avatar shapes: 'circle' | 'square'
318
+ // Button shapes: 'default' | 'circle' | 'round'
319
+ // Sizes: 'small' | 'default' | 'large' | number
320
+ ```
321
+
322
+ ### Patterns
323
+
324
+ ```tsx
325
+ // Standard loading skeleton
326
+ <Skeleton loading={isLoading} active avatar paragraph={{ rows: 4 }}>
327
+ <ActualContent />
328
+ </Skeleton>
329
+
330
+ // Profile card skeleton
331
+ <Card>
332
+ <Skeleton active avatar={{ size: 'large', shape: 'circle' }} paragraph={{ rows: 2 }} />
333
+ </Card>
334
+
335
+ // List skeleton
336
+ {isLoading
337
+ ? Array.from({ length: 5 }).map((_, i) => (
338
+ <List.Item key={i}>
339
+ <Skeleton active avatar loading />
340
+ </List.Item>
341
+ ))
342
+ : <List dataSource={data} renderItem={...} />
343
+ }
344
+
345
+ // Mixed sub-components (custom layout)
346
+ <Space direction="vertical" style={{ width: '100%' }}>
347
+ <Skeleton.Avatar active size="large" />
348
+ <Skeleton.Button active block size="large" />
349
+ <Skeleton.Input active block />
350
+ </Space>
351
+ ```
352
+
353
+ ### Common Mistakes
354
+ - **`active` is `false` by default** — add `active` for shimmer animation
355
+ - **Match skeleton dimensions to content** — size mismatch causes layout shift when content loads
356
+ - **Paragraph rows default is 3** — set `paragraph={{ rows: 2 }}` to match shorter content
357
+
358
+ ---
359
+
360
+ ## App
361
+
362
+ Context provider that makes `message`, `notification`, and `modal` context-aware (respects `ConfigProvider` theme and locale).
363
+
364
+ ```typescript
365
+ import { App } from 'antd';
366
+ // Must wrap the root of your app (or the subtree that needs context-aware imperative APIs)
367
+ ```
368
+
369
+ ### Why use App?
370
+
371
+ Static methods (`message.xxx`, `notification.xxx`, `Modal.confirm`) do NOT inherit `ConfigProvider` theme/locale. Wrapping with `App` and using `App.useApp()` inside components fixes this.
372
+
373
+ ### Setup
374
+
375
+ ```tsx
376
+ // Root layout — wrap once
377
+ <ConfigProvider theme={myTheme}>
378
+ <App message={{ maxCount: 3 }} notification={{ placement: 'bottomRight' }}>
379
+ <Router>
380
+ <Routes />
381
+ </Router>
382
+ </App>
383
+ </ConfigProvider>
384
+ ```
385
+
386
+ ### App Props
387
+
388
+ | Prop | Type | Description |
389
+ |------|------|-------------|
390
+ | `message` | `MessageConfig` | Message instance config (maxCount, duration, etc.) |
391
+ | `notification` | `NotificationConfig` | Notification instance config |
392
+ | `modal` | `ModalConfig` | Modal instance config |
393
+
394
+ ### useApp() hook
395
+
396
+ ```typescript
397
+ import { App } from 'antd';
398
+
399
+ const MyComponent = () => {
400
+ const { message, notification, modal } = App.useApp();
401
+ // These are context-aware — they respect ConfigProvider theme/locale
402
+
403
+ const handleSave = async () => {
404
+ try {
405
+ await saveData();
406
+ message.success('Saved!');
407
+ } catch {
408
+ message.error('Failed!');
409
+ }
410
+ };
411
+
412
+ const handleDelete = () => {
413
+ modal.confirm({
414
+ title: 'Delete item?',
415
+ content: 'This action cannot be undone.',
416
+ okText: 'Delete',
417
+ okType: 'danger',
418
+ onOk: async () => {
419
+ await deleteItem();
420
+ notification.success({ message: 'Deleted', description: 'Item removed.' });
421
+ },
422
+ });
423
+ };
424
+
425
+ return (
426
+ <>
427
+ <Button onClick={handleSave}>Save</Button>
428
+ <Button danger onClick={handleDelete}>Delete</Button>
429
+ </>
430
+ );
431
+ };
432
+ ```
433
+
434
+ ### Common Mistakes
435
+ - **`App.useApp()` must be called inside an `<App>` wrapper** — calling outside throws an error
436
+ - **Only one `<App>` at root level** — multiple `App` wrappers can cause unexpected behavior
437
+ - **For static method fallback** — if `App` setup is too complex, the static methods still work but won't respect theme
438
+
439
+ ---
440
+
441
+ ## Comparison: When to Use What
442
+
443
+ | Feedback Type | Use | Duration | Position |
444
+ |---------------|-----|----------|----------|
445
+ | Success/error on user action | `message.success/error` | 3s auto | Top center |
446
+ | Important status update | `notification.success/info` | 4.5s auto | Corner |
447
+ | Warning needing action | `notification.warning + btn` | `duration: 0` | Corner |
448
+ | Async loading overlay | `<Spin spinning={loading}>` | — | Over content |
449
+ | Page loading placeholder | `<Skeleton loading={loading}>` | — | In place |
450
+ | Side panel form/content | `<Drawer>` | — | Side slide |
451
+ | Context-aware messages | `App.useApp()` | — | Respects theme |