ginskill-init 1.0.1 → 1.0.3

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,414 @@
1
+ # Ant Design — Form, Table, Modal, Button Deep Reference
2
+
3
+ ---
4
+
5
+ ## FORM
6
+
7
+ ### Form Props
8
+
9
+ | Prop | Type | Default | Notes |
10
+ |------|------|---------|-------|
11
+ | `form` | `FormInstance` | — | Created by `Form.useForm()` |
12
+ | `layout` | `horizontal \| vertical \| inline` | `horizontal` | |
13
+ | `initialValues` | `object` | — | **Not reactive after mount** — use `setFieldsValue()` for async data |
14
+ | `onFinish` | `(values) => void` | — | Called after successful validation |
15
+ | `onFinishFailed` | `({ values, errorFields }) => void` | — | Called when validation fails |
16
+ | `onValuesChange` | `(changed, all) => void` | — | Fires on any change |
17
+ | `disabled` | `boolean` | `false` | Disables all antd controls |
18
+ | `size` | `small \| middle \| large` | — | |
19
+ | `variant` | `outlined \| borderless \| filled \| underlined` | `outlined` | |
20
+ | `requiredMark` | `boolean \| 'optional'` | `true` | |
21
+ | `labelCol` | `ColProps` | — | Grid layout for labels |
22
+ | `wrapperCol` | `ColProps` | — | Grid layout for inputs |
23
+ | `scrollToFirstError` | `boolean \| ScrollOptions` | `false` | |
24
+ | `validateMessages` | `ValidateMessages` | — | Override validation message templates |
25
+ | `preserve` | `boolean` | `true` | Keep unmounted field values |
26
+
27
+ ### Form.Item Props
28
+
29
+ | Prop | Type | Notes |
30
+ |------|------|-------|
31
+ | `name` | `string \| number \| (string\|number)[]` | Field path; supports nested `['user', 'address']` |
32
+ | `label` | `ReactNode` | |
33
+ | `rules` | `Rule[]` | |
34
+ | `required` | `boolean` | Shows asterisk only; add rule for actual validation |
35
+ | `valuePropName` | `string` | Use `'checked'` for Checkbox/Switch (default: `'value'`) |
36
+ | `validateTrigger` | `string \| string[]` | When to validate (default: `'onChange'`) |
37
+ | `dependencies` | `NamePath[]` | Re-validates when listed fields change |
38
+ | `noStyle` | `boolean` | Removes visual styling; field still registers |
39
+ | `hasFeedback` | `boolean` | Shows validation status icon |
40
+ | `initialValue` | any | Field-level default; overrides Form `initialValues` for this field |
41
+ | `tooltip` | `ReactNode \| TooltipProps` | Tooltip on label |
42
+ | `normalize` | `(value, prev, all) => any` | Transform value before storing |
43
+ | `getValueFromEvent` | `(event) => any` | Extract value from change event |
44
+
45
+ ### FormInstance Methods
46
+
47
+ ```tsx
48
+ const [form] = Form.useForm<MyValues>();
49
+
50
+ // Reading
51
+ form.getFieldValue('email');
52
+ form.getFieldsValue(['email', 'name']);
53
+ form.getFieldsValue(true); // all including unstored
54
+ form.getFieldError('email'); // string[]
55
+ form.isFieldTouched('email'); // boolean
56
+
57
+ // Writing
58
+ form.setFieldValue('email', 'x@x.com');
59
+ form.setFieldsValue({ email: 'x', name: 'y' });
60
+
61
+ // Validation
62
+ form.validateFields(); // Promise<Values>
63
+ form.validateFields(['email', 'name']);
64
+
65
+ // Reset & Scroll
66
+ form.resetFields();
67
+ form.resetFields(['email']);
68
+ form.scrollToField('email');
69
+
70
+ // Programmatic submit
71
+ form.submit();
72
+ ```
73
+
74
+ ### Hooks
75
+
76
+ ```tsx
77
+ // Reactive field watch — preferred over getFieldValue in render
78
+ const email = Form.useWatch('email', form);
79
+ const address = Form.useWatch(['user', 'address'], form); // nested
80
+
81
+ // Access form status inside custom component inside Form.Item
82
+ const { status, errors } = Form.Item.useStatus();
83
+
84
+ // Get nearest ancestor form without prop drilling
85
+ const form = Form.useFormInstance();
86
+ ```
87
+
88
+ ### Validation Rules
89
+
90
+ ```tsx
91
+ rules={[
92
+ { required: true, message: 'Required' },
93
+ { type: 'email', message: 'Invalid email' },
94
+ { type: 'url' },
95
+ { min: 6, max: 50 },
96
+ { len: 11, message: 'Must be exactly 11 chars' },
97
+ { pattern: /^[a-z]+$/i, message: 'Letters only' },
98
+ { whitespace: true, message: 'Cannot be only whitespace' },
99
+ { warningOnly: true, message: 'This is just a warning' },
100
+
101
+ // Async validator
102
+ {
103
+ validator: async (_, value) => {
104
+ if (!value || value.length < 3) throw new Error('Too short');
105
+ },
106
+ },
107
+
108
+ // Cross-field (compare with another field)
109
+ ({ getFieldValue }) => ({
110
+ validator(_, value) {
111
+ if (!value || getFieldValue('password') === value) return Promise.resolve();
112
+ return Promise.reject(new Error('Passwords must match'));
113
+ },
114
+ }),
115
+ ]}
116
+ ```
117
+
118
+ ### Form.List — Dynamic Fields
119
+
120
+ ```tsx
121
+ <Form.List name="users">
122
+ {(fields, { add, remove }) => (
123
+ <>
124
+ {fields.map((field) => (
125
+ <Form.Item key={field.key}>
126
+ <Form.Item {...field} name={[field.name, 'email']} noStyle
127
+ rules={[{ type: 'email', required: true }]}>
128
+ <Input placeholder="Email" />
129
+ </Form.Item>
130
+ <Button onClick={() => remove(field.name)}>Remove</Button>
131
+ </Form.Item>
132
+ ))}
133
+ <Button onClick={() => add()}>Add</Button>
134
+ <Button onClick={() => add({ email: 'default@example.com' })}>Add with Default</Button>
135
+ </>
136
+ )}
137
+ </Form.List>
138
+ ```
139
+
140
+ ### Common Form Mistakes
141
+
142
+ 1. **`valuePropName="checked"` required for Checkbox/Switch** — otherwise stores event object
143
+ 2. **`initialValues` is not reactive** — use `form.setFieldsValue()` for async data loads
144
+ 3. **`htmlType="submit"` required on submit Button** — `onClick={() => form.submit()}` bypasses form events
145
+ 4. **Do NOT call `form.getFieldsValue()` during render** — use `Form.useWatch()` instead
146
+ 5. **Do NOT wrap multiple controls in one `Form.Item` with `name`** — use `noStyle` on inner items
147
+ 6. **`Form.useForm()` only works in functional components** — use `ref` for class components
148
+
149
+ ---
150
+
151
+ ## TABLE
152
+
153
+ ### Table Props
154
+
155
+ | Prop | Type | Default | Notes |
156
+ |------|------|---------|-------|
157
+ | `dataSource` | `T[]` | — | Array of data |
158
+ | `columns` | `ColumnsType<T>` | — | Column config |
159
+ | `rowKey` | `string \| (record) => string` | `'key'` | **Always set this** |
160
+ | `pagination` | `TablePaginationConfig \| false` | — | `false` disables pagination |
161
+ | `loading` | `boolean \| SpinProps` | `false` | Loading overlay |
162
+ | `rowSelection` | `TableRowSelection<T>` | — | Checkbox/radio selection |
163
+ | `expandable` | `ExpandableConfig<T>` | — | Expandable rows |
164
+ | `onChange` | `(pagination, filters, sorter, extra) => void` | — | Server-side handler |
165
+ | `scroll` | `{ x?, y? }` | — | Scrollable area |
166
+ | `sticky` | `boolean \| { offsetHeader }` | `false` | Sticky header |
167
+ | `virtual` | `boolean` | `false` | Virtual scrolling (requires `scroll.y`) |
168
+ | `size` | `large \| middle \| small` | `large` | Density |
169
+ | `bordered` | `boolean` | `false` | Cell borders |
170
+ | `summary` | `(data) => ReactNode` | — | Summary row |
171
+ | `onRow` | `(record, index) => HTMLAttributes` | — | Row event handlers |
172
+
173
+ ### Column Props
174
+
175
+ | Prop | Type | Notes |
176
+ |------|------|-------|
177
+ | `title` | `ReactNode` | Header content |
178
+ | `dataIndex` | `string \| string[]` | Use array for nested: `['user', 'name']` |
179
+ | `key` | `string` | Required if no `dataIndex` |
180
+ | `render` | `(value, record, index) => ReactNode` | Custom cell renderer |
181
+ | `width` | `string \| number` | **Required with `fixed`** |
182
+ | `fixed` | `'left' \| 'right'` | Requires `scroll.x` on table |
183
+ | `sorter` | `boolean \| CompareFn \| { compare, multiple }` | |
184
+ | `filters` | `{ text, value }[]` | Filter dropdown options |
185
+ | `onFilter` | `(value, record) => boolean` | Client-side filter |
186
+ | `filterSearch` | `boolean` | Search in filter dropdown |
187
+ | `filteredValue` | `any[]` | Controlled filter |
188
+ | `sortOrder` | `'ascend' \| 'descend' \| null` | Controlled sort |
189
+ | `ellipsis` | `boolean` | Truncate with tooltip |
190
+ | `align` | `left \| right \| center` | |
191
+ | `responsive` | `Breakpoint[]` | Hide at certain breakpoints |
192
+ | `hidden` | `boolean` | v5.13+ — hide column |
193
+ | `children` | `ColumnsType<T>` | Grouped header columns |
194
+ | `onCell` | `(record, index) => HTMLAttributes` | Cell events/props |
195
+ | `shouldCellUpdate` | `(record, prev) => boolean` | Optimize re-renders |
196
+
197
+ ### TypeScript Column Pattern
198
+
199
+ ```tsx
200
+ import type { TableColumnsType, TableProps } from 'antd';
201
+
202
+ const columns: TableColumnsType<User> = [
203
+ {
204
+ title: 'Name', dataIndex: 'name', key: 'name', fixed: 'left', width: 150,
205
+ sorter: (a, b) => a.name.localeCompare(b.name),
206
+ },
207
+ {
208
+ title: 'Status', dataIndex: 'status',
209
+ filters: [{ text: 'Active', value: 'active' }, { text: 'Inactive', value: 'inactive' }],
210
+ onFilter: (value, record) => record.status === value,
211
+ render: (status) => <Tag color={status === 'active' ? 'green' : 'red'}>{status}</Tag>,
212
+ },
213
+ {
214
+ title: 'Actions', key: 'actions', fixed: 'right', width: 120,
215
+ render: (_, record) => <Button onClick={() => edit(record)}>Edit</Button>,
216
+ },
217
+ ];
218
+ ```
219
+
220
+ ### Row Selection
221
+
222
+ ```tsx
223
+ const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
224
+
225
+ const rowSelection: TableProps<User>['rowSelection'] = {
226
+ type: 'checkbox',
227
+ selectedRowKeys: selectedKeys,
228
+ onChange: (keys) => setSelectedKeys(keys),
229
+ getCheckboxProps: (record) => ({ disabled: record.status === 'inactive' }),
230
+ selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT, Table.SELECTION_NONE],
231
+ preserveSelectedRowKeys: true, // keep selection across pages
232
+ };
233
+ ```
234
+
235
+ ### Server-Side onChange
236
+
237
+ ```tsx
238
+ const handleChange: TableProps<User>['onChange'] = (pagination, filters, sorter, extra) => {
239
+ // extra.action: 'paginate' | 'sort' | 'filter'
240
+ fetchData({
241
+ page: pagination.current,
242
+ pageSize: pagination.pageSize,
243
+ sortField: Array.isArray(sorter) ? undefined : sorter.field,
244
+ sortOrder: Array.isArray(sorter) ? undefined : sorter.order,
245
+ filters,
246
+ });
247
+ };
248
+ ```
249
+
250
+ ### Common Table Mistakes
251
+
252
+ 1. **Always set `rowKey`** — never use array index; breaks selection/expansion when filtered
253
+ 2. **Fixed columns require explicit `width` + `scroll.x`** on the table
254
+ 3. **Nested `dataIndex` needs array** — `['user', 'name']` not `'user.name'`
255
+ 4. **Memoize `columns`** — `useMemo` prevents table re-renders on every parent render
256
+ 5. **`virtual` requires `scroll.y`** — needs fixed container height
257
+ 6. **`sorter` must return `0` for equal items** — not a non-zero value
258
+
259
+ ---
260
+
261
+ ## MODAL
262
+
263
+ ### Modal Props
264
+
265
+ | Prop | Type | Default | Notes |
266
+ |------|------|---------|-------|
267
+ | `open` | `boolean` | `false` | Visibility |
268
+ | `title` | `ReactNode` | — | Header |
269
+ | `footer` | `ReactNode \| null \| function` | OK + Cancel | `null` removes footer |
270
+ | `width` | `string \| number \| BreakpointMap` | `520` | Responsive: `{ xs: '95%', md: 600 }` |
271
+ | `centered` | `boolean` | `false` | Vertically center |
272
+ | `onOk` | `(e) => void` | — | OK button handler |
273
+ | `onCancel` | `(e) => void` | — | Cancel/close handler |
274
+ | `confirmLoading` | `boolean` | `false` | Loading on OK button |
275
+ | `okText` | `ReactNode` | `'OK'` | |
276
+ | `cancelText` | `ReactNode` | `'Cancel'` | |
277
+ | `okButtonProps` | `ButtonProps` | — | |
278
+ | `destroyOnHidden` | `boolean` | `false` | Unmount children when closed (v5.25+) |
279
+ | `forceRender` | `boolean` | `false` | Pre-render hidden content |
280
+ | `keyboard` | `boolean` | `true` | ESC closes modal |
281
+ | `mask` | `boolean` | `true` | Show backdrop |
282
+ | `afterClose` | `() => void` | — | After close animation |
283
+ | `zIndex` | `number` | `1000` | |
284
+ | `classNames` | `{ header, body, footer, mask }` | — | Semantic class overrides |
285
+ | `styles` | `{ header, body, footer, mask }` | — | Semantic style overrides |
286
+
287
+ ### Static Methods
288
+
289
+ ```tsx
290
+ Modal.confirm({
291
+ title: 'Delete?',
292
+ content: 'This cannot be undone.',
293
+ okText: 'Yes, delete',
294
+ okType: 'danger',
295
+ onOk: async () => { await deleteItem(); }, // Promise keeps OK loading until resolved
296
+ });
297
+ Modal.info({ ... });
298
+ Modal.success({ ... });
299
+ Modal.warning({ ... });
300
+ Modal.error({ ... });
301
+ Modal.destroyAll();
302
+
303
+ // Update / close returned instance
304
+ const instance = Modal.confirm({ ... });
305
+ instance.update({ title: 'New title' });
306
+ instance.destroy();
307
+ ```
308
+
309
+ ### useModal — Preferred for Context (theme/locale)
310
+
311
+ ```tsx
312
+ const App = () => {
313
+ const [modal, contextHolder] = Modal.useModal();
314
+
315
+ return (
316
+ <>
317
+ {contextHolder} {/* Must be inside context providers */}
318
+ <Button onClick={() => modal.confirm({ title: 'Sure?', onOk: del })}>Delete</Button>
319
+ </>
320
+ );
321
+ };
322
+ ```
323
+
324
+ ### Modal + Form Pattern
325
+
326
+ ```tsx
327
+ const EditModal = ({ record, onClose, onSave }) => {
328
+ const [form] = Form.useForm();
329
+ const [loading, setLoading] = useState(false);
330
+
331
+ const handleOk = async () => {
332
+ try {
333
+ const values = await form.validateFields();
334
+ setLoading(true);
335
+ await onSave(values);
336
+ onClose();
337
+ } finally {
338
+ setLoading(false);
339
+ }
340
+ };
341
+
342
+ return (
343
+ <Modal
344
+ open={!!record}
345
+ title="Edit"
346
+ onOk={handleOk}
347
+ onCancel={onClose}
348
+ confirmLoading={loading}
349
+ destroyOnHidden // resets form state when closed
350
+ width={600}
351
+ >
352
+ <Form form={form} layout="vertical" initialValues={record}>
353
+ <Form.Item name="name" label="Name" rules={[{ required: true }]}>
354
+ <Input />
355
+ </Form.Item>
356
+ </Form>
357
+ </Modal>
358
+ );
359
+ };
360
+ ```
361
+
362
+ ### Common Modal Mistakes
363
+
364
+ 1. **Use `Modal.useModal()` not `Modal.confirm()`** in app code — static methods lose context
365
+ 2. **Use `destroyOnHidden`** (v5.25+) not deprecated `destroyOnClose` — ensures form resets
366
+ 3. **`footer={null}`** removes footer entirely — empty array `[]` shows footer with no buttons
367
+ 4. **`contextHolder` must be inside relevant providers** to inherit theme/locale context
368
+ 5. **`onOk` Promise enables auto confirmLoading** in static methods — manual management needed in component form
369
+
370
+ ---
371
+
372
+ ## BUTTON
373
+
374
+ ### Button Props
375
+
376
+ | Prop | Type | Default | Notes |
377
+ |------|------|---------|-------|
378
+ | `type` | `primary \| default \| dashed \| text \| link` | `default` | Legacy shorthand |
379
+ | `color` | `default \| primary \| danger \| blue \| green \| ...13 presets` | — | v5.21+ |
380
+ | `variant` | `outlined \| dashed \| solid \| filled \| text \| link` | — | v5.21+ |
381
+ | `size` | `large \| middle \| small` | `middle` | |
382
+ | `shape` | `default \| circle \| round` | `default` | |
383
+ | `block` | `boolean` | `false` | Full width |
384
+ | `loading` | `boolean \| { delay?, icon? }` | `false` | `delay` prevents spinner flash |
385
+ | `disabled` | `boolean` | `false` | |
386
+ | `danger` | `boolean` | `false` | Red color intent |
387
+ | `ghost` | `boolean` | `false` | Transparent bg — for colored backgrounds |
388
+ | `icon` | `ReactNode` | — | |
389
+ | `iconPosition` | `start \| end` | `start` | |
390
+ | `href` | `string` | — | Renders as `<a>` |
391
+ | `target` | `string` | — | Used with `href` |
392
+ | `htmlType` | `submit \| reset \| button` | `button` | **Must be `submit` in forms** |
393
+ | `onClick` | `(event) => void` | — | |
394
+
395
+ ### type → color + variant mapping (v5.21+)
396
+
397
+ | `type` | Equivalent |
398
+ |--------|-----------|
399
+ | `primary` | `color="primary" variant="solid"` |
400
+ | `default` | `color="default" variant="outlined"` |
401
+ | `dashed` | `color="default" variant="dashed"` |
402
+ | `text` | `color="default" variant="text"` |
403
+ | `link` | `color="default" variant="link"` |
404
+ | `primary` + `danger` | `color="danger" variant="solid"` |
405
+ | `primary` + `ghost` | `color="primary" variant="outlined"` |
406
+
407
+ ### Common Button Mistakes
408
+
409
+ 1. **`htmlType="submit"` is required** inside `<Form>` — default `"button"` won't trigger `onFinish`
410
+ 2. **`danger` is a modifier, not a `type`** — there is no `type="danger"`
411
+ 3. **`ghost` only works on colored backgrounds** — invisible on white
412
+ 4. **`loading={{ delay: 300 }}`** prevents spinner flash for fast operations
413
+ 5. **Icon-only buttons need `aria-label`** — `<Button shape="circle" icon={<X />} aria-label="Close" />`
414
+ 6. **Don't mix `type` and `color+variant` simultaneously** — `color`+`variant` wins