@wangs-ui/skills 1.0.36
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/dist/bin.js +84 -0
- package/dist/index.js +2 -0
- package/dist/skills/create-form/SKILL.md +166 -0
- package/dist/skills/data-table/SKILL.md +178 -0
- package/dist/skills/dialog-modal/SKILL.md +131 -0
- package/dist/skills/i18n-usage/SKILL.md +87 -0
- package/dist/skills/layout-navigation/SKILL.md +63 -0
- package/dist/skills/wangs-ui-components/SKILL.md +89 -0
- package/dist/src-C22mKmnQ.js +210 -0
- package/package.json +58 -0
- package/skills/create-form/SKILL.md +166 -0
- package/skills/data-table/SKILL.md +178 -0
- package/skills/dialog-modal/SKILL.md +131 -0
- package/skills/i18n-usage/SKILL.md +87 -0
- package/skills/layout-navigation/SKILL.md +63 -0
- package/skills/wangs-ui-components/SKILL.md +89 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: data-table
|
|
3
|
+
description: Real-world patterns for building full CRUD DataTables with server pagination, search filters, batch actions, and confirmation modals.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Real-World DataTable & Filter Workflows
|
|
7
|
+
|
|
8
|
+
Use this skill when building administrative grids, filtered listing pages, or management dashboards with `@wangs-ui/react-core`.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. MCP Inspection Step (Before Building Table)
|
|
13
|
+
|
|
14
|
+
Query the MCP server to inspect supported features and slots:
|
|
15
|
+
|
|
16
|
+
- `get-documentation({ id: "datatable" })` — Check `paginator`, `lazy`, `onPage`, `onSort`, `selectionMode`, and `dataKey`.
|
|
17
|
+
- `get-documentation({ id: "column" })` — Check `body` template, `sortable`, `frozen`, and `headerStyle`.
|
|
18
|
+
- `get-documentation({ id: "tag" })` — Check severity colors for status pill badges (`success`, `warning`, `danger`, `info`).
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 2. Recipe: Full CRUD Data Grid with Filter Toolbar & Batch Actions
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import React, { useState } from 'react';
|
|
26
|
+
import DataTable, { Column } from '@wangs-ui/react-core/primitive/datatable';
|
|
27
|
+
import Button from '@wangs-ui/react-core/primitive/button';
|
|
28
|
+
import InputText from '@wangs-ui/react-core/primitive/inputtext';
|
|
29
|
+
import Select from '@wangs-ui/react-core/primitive/select';
|
|
30
|
+
import Tag from '@wangs-ui/react-core/primitive/tag';
|
|
31
|
+
import Dialog from '@wangs-ui/react-core/primitive/dialog';
|
|
32
|
+
import Card from '@wangs-ui/react-core/primitive/card';
|
|
33
|
+
import { useI18n } from '@wangs-ui/react-i18n';
|
|
34
|
+
import { SearchLine, DeleteBin6Line, EditLine, AddLine } from '@wangs-ui/react-icons';
|
|
35
|
+
|
|
36
|
+
interface CustomerRecord {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
email: string;
|
|
40
|
+
status: 'active' | 'pending' | 'suspended';
|
|
41
|
+
createdAt: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default function CustomerManagementView() {
|
|
45
|
+
const { t } = useI18n();
|
|
46
|
+
const [records, setRecords] = useState<CustomerRecord[]>([]);
|
|
47
|
+
const [loading, setLoading] = useState(false);
|
|
48
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
49
|
+
const [selectedStatus, setSelectedStatus] = useState<string | null>(null);
|
|
50
|
+
const [selectedRows, setSelectedRows] = useState<CustomerRecord[]>([]);
|
|
51
|
+
const [deleteTarget, setDeleteTarget] = useState<CustomerRecord | null>(null);
|
|
52
|
+
|
|
53
|
+
// Status Badge Template
|
|
54
|
+
const statusTemplate = (row: CustomerRecord) => {
|
|
55
|
+
const severityMap: Record<string, 'success' | 'warning' | 'danger'> = {
|
|
56
|
+
active: 'success',
|
|
57
|
+
pending: 'warning',
|
|
58
|
+
suspended: 'danger',
|
|
59
|
+
};
|
|
60
|
+
return <Tag value={t(row.status)} severity={severityMap[row.status] || 'info'} />;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Row Actions Template
|
|
64
|
+
const actionsTemplate = (row: CustomerRecord) => (
|
|
65
|
+
<div className="flex items-center gap-xs">
|
|
66
|
+
<Button
|
|
67
|
+
variant="text"
|
|
68
|
+
icon={<EditLine />}
|
|
69
|
+
aria-label={t('Edit')}
|
|
70
|
+
onClick={() => console.log('Edit', row.id)}
|
|
71
|
+
/>
|
|
72
|
+
<Button
|
|
73
|
+
variant="text"
|
|
74
|
+
severity="danger"
|
|
75
|
+
icon={<DeleteBin6Line />}
|
|
76
|
+
aria-label={t('Delete')}
|
|
77
|
+
onClick={() => setDeleteTarget(row)}
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Card className="flex flex-col gap-m p-6">
|
|
84
|
+
{/* 1. Filter & Search Toolbar */}
|
|
85
|
+
<div className="flex flex-wrap items-center justify-between gap-s">
|
|
86
|
+
<div className="flex flex-wrap items-center gap-s">
|
|
87
|
+
<InputText
|
|
88
|
+
value={searchQuery}
|
|
89
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
90
|
+
placeholder={t('Search customer...')}
|
|
91
|
+
className="w-64"
|
|
92
|
+
/>
|
|
93
|
+
<Select
|
|
94
|
+
value={selectedStatus}
|
|
95
|
+
options={[
|
|
96
|
+
{ label: t('All Statuses'), value: null },
|
|
97
|
+
{ label: t('Active'), value: 'active' },
|
|
98
|
+
{ label: t('Pending'), value: 'pending' },
|
|
99
|
+
{ label: t('Suspended'), value: 'suspended' },
|
|
100
|
+
]}
|
|
101
|
+
onChange={(e) => setSelectedStatus(e.value)}
|
|
102
|
+
placeholder={t('Filter status')}
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<Button label={t('Add Customer')} icon={<AddLine />} severity="primary" />
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
{/* 2. Batch Selection Action Bar */}
|
|
110
|
+
{selectedRows.length > 0 && (
|
|
111
|
+
<div className="flex items-center justify-between rounded bg-primary-50 px-4 py-2 text-primary-900">
|
|
112
|
+
<span className="p font-medium">
|
|
113
|
+
{t('{{count}} items selected', { count: selectedRows.length })}
|
|
114
|
+
</span>
|
|
115
|
+
<Button
|
|
116
|
+
size="small"
|
|
117
|
+
severity="danger"
|
|
118
|
+
label={t('Delete Selected')}
|
|
119
|
+
icon={<DeleteBin6Line />}
|
|
120
|
+
onClick={() => console.log('Batch delete', selectedRows)}
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
)}
|
|
124
|
+
|
|
125
|
+
{/* 3. Paginated DataTable */}
|
|
126
|
+
<DataTable
|
|
127
|
+
value={records}
|
|
128
|
+
loading={loading}
|
|
129
|
+
selection={selectedRows}
|
|
130
|
+
onSelectionChange={(e) => setSelectedRows(e.value)}
|
|
131
|
+
dataKey="id"
|
|
132
|
+
paginator
|
|
133
|
+
rows={10}
|
|
134
|
+
rowsPerPageOptions={[10, 25, 50]}
|
|
135
|
+
emptyMessage={t('No customers found')}
|
|
136
|
+
>
|
|
137
|
+
<Column selectionMode="multiple" headerStyle={{ width: '3rem' }} />
|
|
138
|
+
<Column field="name" header={t('Customer Name')} sortable />
|
|
139
|
+
<Column field="email" header={t('Email')} sortable />
|
|
140
|
+
<Column field="status" header={t('Status')} body={statusTemplate} sortable />
|
|
141
|
+
<Column header={t('Actions')} body={actionsTemplate} headerStyle={{ width: '6rem' }} />
|
|
142
|
+
</DataTable>
|
|
143
|
+
|
|
144
|
+
{/* 4. Delete Confirmation Dialog */}
|
|
145
|
+
<Dialog
|
|
146
|
+
visible={!!deleteTarget}
|
|
147
|
+
onHide={() => setDeleteTarget(null)}
|
|
148
|
+
header={t('Delete Customer')}
|
|
149
|
+
footer={
|
|
150
|
+
<div className="flex justify-end gap-xs">
|
|
151
|
+
<Button variant="text" label={t('Cancel')} onClick={() => setDeleteTarget(null)} />
|
|
152
|
+
<Button
|
|
153
|
+
severity="danger"
|
|
154
|
+
label={t('Delete')}
|
|
155
|
+
onClick={() => {
|
|
156
|
+
// Execute delete API
|
|
157
|
+
setDeleteTarget(null);
|
|
158
|
+
}}
|
|
159
|
+
/>
|
|
160
|
+
</div>
|
|
161
|
+
}
|
|
162
|
+
>
|
|
163
|
+
<p className="p">
|
|
164
|
+
{t('Are you sure you want to delete {{name}}?', { name: deleteTarget?.name })}
|
|
165
|
+
</p>
|
|
166
|
+
</Dialog>
|
|
167
|
+
</Card>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 3. Mandatory Best Practices
|
|
175
|
+
|
|
176
|
+
1. **Always Supply `dataKey`**: Never enable row selection without `dataKey="id"`.
|
|
177
|
+
2. **Translate All Headers & Messages**: Pass table headers and empty state messages into `t()`.
|
|
178
|
+
3. **Control Batch Action Appearance**: Show batch action banner only when `selectedRows.length > 0`.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dialog-modal
|
|
3
|
+
description: Real-world patterns for modal forms, async confirmation workflows, and multi-step dialogs with Wangs UI Dialog and Modal components.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Real-World Dialog & Modal Workflows
|
|
7
|
+
|
|
8
|
+
Use this skill when building interactive modals, create/edit modal forms, destructive action confirmations, or slide-in overlay panels.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. MCP Inspection Step (Before Implementing Overlays)
|
|
13
|
+
|
|
14
|
+
Query the MCP server to check overlay configuration and animation options:
|
|
15
|
+
|
|
16
|
+
- `get-documentation({ id: "dialog" })` — Check `header`, `footer`, `visible`, `onHide`, `modal`, and `dismissableMask`.
|
|
17
|
+
- `get-documentation({ id: "modal" })` — Check fullscreen modes, size variants, and slide-in drawer options.
|
|
18
|
+
- `get-documentation({ id: "toast" })` — Check severity toasts (`success`, `error`, `info`, `warn`) to trigger after modal actions.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 2. Recipe: Create/Edit Form inside a Modal Dialog
|
|
23
|
+
|
|
24
|
+
This real-world recipe coordinates a modal wrapper with an embedded `@wangs-ui/form`, handles saving state, prevents accidental dismiss while saving, and resets state upon close:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import React, { useState, useEffect } from 'react';
|
|
28
|
+
import Dialog from '@wangs-ui/react-core/primitive/dialog';
|
|
29
|
+
import Button from '@wangs-ui/react-core/primitive/button';
|
|
30
|
+
import InputText from '@wangs-ui/react-core/primitive/inputtext';
|
|
31
|
+
import { Form, Field } from '@wangs-ui/react-core';
|
|
32
|
+
import { useFormControl } from '@wangs-ui/form';
|
|
33
|
+
import { useI18n } from '@wangs-ui/react-i18n';
|
|
34
|
+
|
|
35
|
+
interface EditItemModel {
|
|
36
|
+
title: string;
|
|
37
|
+
code: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface ItemModalProps {
|
|
41
|
+
visible: boolean;
|
|
42
|
+
item?: EditItemModel | null;
|
|
43
|
+
onHide: () => void;
|
|
44
|
+
onSaved: (item: EditItemModel) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default function ItemFormModal({ visible, item, onHide, onSaved }: ItemModalProps) {
|
|
48
|
+
const { t } = useI18n();
|
|
49
|
+
const formControl = useFormControl<EditItemModel>({ type: 'json' });
|
|
50
|
+
const [saving, setSaving] = useState(false);
|
|
51
|
+
|
|
52
|
+
// Sync form values when modal opens or item changes
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (visible) {
|
|
55
|
+
formControl.reset(item || { title: '', code: '' });
|
|
56
|
+
}
|
|
57
|
+
}, [visible, item]);
|
|
58
|
+
|
|
59
|
+
const handleFormSubmit = async (values: EditItemModel) => {
|
|
60
|
+
setSaving(true);
|
|
61
|
+
try {
|
|
62
|
+
// Execute API call: await api.save(values);
|
|
63
|
+
onSaved(values);
|
|
64
|
+
onHide();
|
|
65
|
+
} finally {
|
|
66
|
+
setSaving(false);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const footerActions = (
|
|
71
|
+
<div className="flex justify-end gap-xs">
|
|
72
|
+
<Button type="button" variant="text" label={t('Cancel')} onClick={onHide} disabled={saving} />
|
|
73
|
+
<Button
|
|
74
|
+
type="submit"
|
|
75
|
+
form="modal-item-form"
|
|
76
|
+
label={item ? t('Save Changes') : t('Create Item')}
|
|
77
|
+
severity="primary"
|
|
78
|
+
loading={saving}
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<Dialog
|
|
85
|
+
visible={visible}
|
|
86
|
+
onHide={() => !saving && onHide()}
|
|
87
|
+
header={item ? t('Edit Item') : t('New Item')}
|
|
88
|
+
footer={footerActions}
|
|
89
|
+
style={{ width: '450px' }}
|
|
90
|
+
modal
|
|
91
|
+
>
|
|
92
|
+
<Form
|
|
93
|
+
id="modal-item-form"
|
|
94
|
+
control={formControl}
|
|
95
|
+
onSubmit={handleFormSubmit}
|
|
96
|
+
className="flex flex-col gap-m pt-xs"
|
|
97
|
+
>
|
|
98
|
+
<Field<string>
|
|
99
|
+
name="title"
|
|
100
|
+
label={t('Item Title')}
|
|
101
|
+
required
|
|
102
|
+
rules={{ required: t('Title is required') }}
|
|
103
|
+
>
|
|
104
|
+
{(field) => (
|
|
105
|
+
<InputText {...field} placeholder={t('Enter title')} value={field.value || ''} />
|
|
106
|
+
)}
|
|
107
|
+
</Field>
|
|
108
|
+
|
|
109
|
+
<Field<string>
|
|
110
|
+
name="code"
|
|
111
|
+
label={t('Item Code')}
|
|
112
|
+
required
|
|
113
|
+
rules={{ required: t('Code is required') }}
|
|
114
|
+
>
|
|
115
|
+
{(field) => (
|
|
116
|
+
<InputText {...field} placeholder={t('e.g. SKU-100')} value={field.value || ''} />
|
|
117
|
+
)}
|
|
118
|
+
</Field>
|
|
119
|
+
</Form>
|
|
120
|
+
</Dialog>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 3. Mandatory Best Practices
|
|
128
|
+
|
|
129
|
+
1. **Decouple Submit Button from Form Body**: Use `form="modal-item-form"` on the submit button inside `footer` so actions stay neatly aligned in the footer bar.
|
|
130
|
+
2. **Prevent Close During Mutation**: Guard `onHide={() => !saving && onHide()}` to prevent accidental dismissal during in-flight network requests.
|
|
131
|
+
3. **Always Reset on Open**: Sync initial values in an effect keyed on `visible`.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: i18n-usage
|
|
3
|
+
description: Real-world internationalization, currency formatting, localized date pickers, and plural interpolation with @wangs-ui/react-i18n.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Application Internationalization & Formatting
|
|
7
|
+
|
|
8
|
+
Use this skill when handling multi-language UI, currency inputs, localized dates, or dynamic sentence translations.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. MCP Inspection Step (Before Localizing Complex Components)
|
|
13
|
+
|
|
14
|
+
Query the MCP server to inspect component-specific localization props:
|
|
15
|
+
|
|
16
|
+
- `get-documentation({ id: "currencyinput" })` — Check currency prefix, locale formatting, and min/max constraints.
|
|
17
|
+
- `get-documentation({ id: "datepicker" })` — Check month/day names, firstDayOfWeek, and dateFormat options.
|
|
18
|
+
- `get-documentation({ id: "languageswitcher" })` — Check language picker dropdown variants.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 2. Recipe: Localized Currency, Date, and Pluralization Flow
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import React, { useState } from 'react';
|
|
26
|
+
import Card from '@wangs-ui/react-core/primitive/card';
|
|
27
|
+
import CurrencyInput from '@wangs-ui/react-core/primitive/currencyinput';
|
|
28
|
+
import DatePicker from '@wangs-ui/react-core/primitive/datepicker';
|
|
29
|
+
import { useI18n } from '@wangs-ui/react-i18n';
|
|
30
|
+
|
|
31
|
+
export default function InvoiceSummary() {
|
|
32
|
+
const { t, currentLocale, setLocale } = useI18n();
|
|
33
|
+
const [amount, setAmount] = useState<number | null>(1500000);
|
|
34
|
+
const [dueDate, setDueDate] = useState<Date | null>(new Date());
|
|
35
|
+
const itemCount = 5;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Card className="flex flex-col gap-m p-6">
|
|
39
|
+
<div className="flex items-center justify-between">
|
|
40
|
+
<h2 className="heading-2">{t('Invoice Summary')}</h2>
|
|
41
|
+
{/* Language selector toggle */}
|
|
42
|
+
<button
|
|
43
|
+
className="text-primary-600 underline text-sm"
|
|
44
|
+
onClick={() => setLocale(currentLocale === 'en' ? 'id' : 'en')}
|
|
45
|
+
>
|
|
46
|
+
{currentLocale === 'en' ? 'Bahasa Indonesia' : 'English'}
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
{/* 1. Currency Formatting Input */}
|
|
51
|
+
<div className="flex flex-col gap-xs">
|
|
52
|
+
<label className="heading-4">{t('Total Amount')}</label>
|
|
53
|
+
<CurrencyInput
|
|
54
|
+
value={amount}
|
|
55
|
+
onValueChange={(e) => setAmount(e.value ?? null)}
|
|
56
|
+
currency={currentLocale === 'id' ? 'IDR' : 'USD'}
|
|
57
|
+
locale={currentLocale === 'id' ? 'id-ID' : 'en-US'}
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{/* 2. Localized Date Picker */}
|
|
62
|
+
<div className="flex flex-col gap-xs">
|
|
63
|
+
<label className="heading-4">{t('Payment Due Date')}</label>
|
|
64
|
+
<DatePicker
|
|
65
|
+
value={dueDate}
|
|
66
|
+
onChange={(e) => setDueDate(e.value as Date)}
|
|
67
|
+
dateFormat={currentLocale === 'id' ? 'dd/mm/yy' : 'mm/dd/yy'}
|
|
68
|
+
showIcon
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
{/* 3. Parameterized Translation */}
|
|
73
|
+
<p className="p text-secondary-600">
|
|
74
|
+
{t('Invoice includes {{count}} billed line items.', { count: itemCount })}
|
|
75
|
+
</p>
|
|
76
|
+
</Card>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 3. Mandatory Translation Rules
|
|
84
|
+
|
|
85
|
+
1. **Sentence Keys in Natural English**: Always write `t('Invoice Summary')` instead of artificial dotted paths like `t('invoice.summary.title')`.
|
|
86
|
+
2. **Dynamic Variables in Double Braces**: Always use `t('Hello, {{name}}', { name })`.
|
|
87
|
+
3. **No Concatenation**: Never write `t('Total:') + ' ' + total`. Use `t('Total: {{total}}', { total })`.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: layout-navigation
|
|
3
|
+
description: Guidelines and patterns for page layout, sidebar navigation, breadcrumbs, and tabs using Wangs UI layout blocks.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Layout & Navigation Structure
|
|
7
|
+
|
|
8
|
+
Use this skill when constructing application shells, multi-level sidebars, page headers, breadcrumbs, or tabbed views with `@wangs-ui/react-core`.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. App Shell Pattern
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import React from 'react';
|
|
16
|
+
import AppLayout from '@wangs-ui/react-core/blocks/applayout';
|
|
17
|
+
import Sidebar from '@wangs-ui/react-core/blocks/sidebar';
|
|
18
|
+
import Breadcrumb from '@wangs-ui/react-core/primitive/breadcrumb';
|
|
19
|
+
import { HomeLine, UserLine, SettingsLine } from '@wangs-ui/react-icons';
|
|
20
|
+
import { useI18n } from '@wangs-ui/react-i18n';
|
|
21
|
+
|
|
22
|
+
export default function MainAppShell({ children }: { children: React.ReactNode }) {
|
|
23
|
+
const { t } = useI18n();
|
|
24
|
+
|
|
25
|
+
const navigationItems = [
|
|
26
|
+
{ label: t('Dashboard'), icon: <HomeLine />, href: '/dashboard' },
|
|
27
|
+
{ label: t('Users'), icon: <UserLine />, href: '/users' },
|
|
28
|
+
{ label: t('Settings'), icon: <SettingsLine />, href: '/settings' },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<AppLayout
|
|
33
|
+
sidebar={<Sidebar items={navigationItems} />}
|
|
34
|
+
header={
|
|
35
|
+
<header className="flex h-14 items-center justify-between border-b border-secondary-200 px-6">
|
|
36
|
+
<Breadcrumb model={[{ label: t('Home') }, { label: t('Dashboard') }]} />
|
|
37
|
+
</header>
|
|
38
|
+
}
|
|
39
|
+
>
|
|
40
|
+
<main className="p-6">{children}</main>
|
|
41
|
+
</AppLayout>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 2. Best Practices
|
|
49
|
+
|
|
50
|
+
1. **Page Title & Breadcrumb Alignment**:
|
|
51
|
+
Every page view inside the layout should provide clear `.heading-1` hierarchy and synchronized breadcrumbs.
|
|
52
|
+
2. **Spacing Grid Consistency**:
|
|
53
|
+
Use consistent outer container padding (`p-6` / `p-xxl`) across views.
|
|
54
|
+
3. **Tabbed Subviews**:
|
|
55
|
+
When separating complex forms or detail views, use `<Tabs>` component with controlled tab index.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 3. MCP Navigation & Block Inspection
|
|
60
|
+
|
|
61
|
+
To inspect complete navigation options, badge counters, collapsible sidebars, or responsive header controls:
|
|
62
|
+
|
|
63
|
+
- Call MCP tool `get-documentation({ id: "sidebar" })` or `get-documentation({ id: "breadcrumb" })` to view full configuration options and live stories.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wangs-ui-components
|
|
3
|
+
description: Foundational rules, subpath imports, design tokens, and the MCP Discovery Protocol for building React apps with Wangs UI.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Wangs UI Component Fundamentals & MCP Protocol
|
|
7
|
+
|
|
8
|
+
Use this skill whenever you write or modify UI components using Wangs UI (`@wangs-ui/react-core`, `@wangs-ui/react-icons`, `@wangs-ui/react-presets`).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. The MCP Discovery Protocol (Mandatory Before Writing Code)
|
|
13
|
+
|
|
14
|
+
Do **NOT** guess component props, Pass-Through (`pt`) slots, or event names. Follow this discovery protocol:
|
|
15
|
+
|
|
16
|
+
```mermaid
|
|
17
|
+
graph TD
|
|
18
|
+
A[Identify Component Needed] --> B[Call get-documentation id]
|
|
19
|
+
B --> C{Need live story / variant?}
|
|
20
|
+
C -->|Yes| D[Call get-documentation-for-story]
|
|
21
|
+
C -->|No| E[Check Graphify: query_graph]
|
|
22
|
+
D --> E
|
|
23
|
+
E --> F[Implement Component with Subpath Imports]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
1. **Step 1: Inspect Props & Types**:
|
|
27
|
+
Call `get-documentation({ id: "<component-name>" })` (e.g. `button`, `inputtext`, `datatable`) to get the exact prop interfaces, severity variants, and sizes.
|
|
28
|
+
2. **Step 2: Inspect Live Usage & Slots**:
|
|
29
|
+
Call `get-documentation-for-story({ id: "<component-name>", storyName: "<variant>" })` to view how props, icons, and pass-through (`pt`) classes are composed in real code.
|
|
30
|
+
3. **Step 3: Inspect Codebase Relationships**:
|
|
31
|
+
Call `query_graph({ query: "<ComponentName>" })` to see how other parts of the monorepo compose this component.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 2. Subpath Modular Imports (Mandatory)
|
|
36
|
+
|
|
37
|
+
Always import via specific subpaths to guarantee tree-shaking and avoid bundling entire packages:
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
// Primitives
|
|
41
|
+
import Button from '@wangs-ui/react-core/primitive/button';
|
|
42
|
+
import Card from '@wangs-ui/react-core/primitive/card';
|
|
43
|
+
import InputText from '@wangs-ui/react-core/primitive/inputtext';
|
|
44
|
+
import Select from '@wangs-ui/react-core/primitive/select';
|
|
45
|
+
import Tag from '@wangs-ui/react-core/primitive/tag';
|
|
46
|
+
|
|
47
|
+
// Providers & Hooks
|
|
48
|
+
import { WangsUiProvider } from '@wangs-ui/react-core/api';
|
|
49
|
+
import { useI18n } from '@wangs-ui/react-i18n';
|
|
50
|
+
|
|
51
|
+
// Icons
|
|
52
|
+
import { SearchLine, AddLine, DeleteBin6Line, CheckLine } from '@wangs-ui/react-icons';
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 3. Strict Primitive Substitution Rule
|
|
58
|
+
|
|
59
|
+
Never write raw HTML when a Wangs UI primitive exists:
|
|
60
|
+
|
|
61
|
+
| Forbidden Raw HTML | Mandatory Wangs UI Component | Subpath Import |
|
|
62
|
+
| :------------------------ | :--------------------------- | :------------------------------------------- |
|
|
63
|
+
| `<button>` | `Button` | `@wangs-ui/react-core/primitive/button` |
|
|
64
|
+
| `<input type="text">` | `InputText` | `@wangs-ui/react-core/primitive/inputtext` |
|
|
65
|
+
| `<input type="number">` | `InputNumber` | `@wangs-ui/react-core/primitive/inputnumber` |
|
|
66
|
+
| `<input type="checkbox">` | `Checkbox` | `@wangs-ui/react-core/primitive/checkbox` |
|
|
67
|
+
| `<select>` | `Select` | `@wangs-ui/react-core/primitive/select` |
|
|
68
|
+
| `<dialog>` / modal | `Dialog` / `Modal` | `@wangs-ui/react-core/primitive/dialog` |
|
|
69
|
+
| `<table>` | `DataTable` | `@wangs-ui/react-core/primitive/datatable` |
|
|
70
|
+
| Container box | `Card` | `@wangs-ui/react-core/primitive/card` |
|
|
71
|
+
| Pill badge | `Tag` / `Badge` | `@wangs-ui/react-core/primitive/tag` |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 4. Typography Scale & 4px Spacing Tokens
|
|
76
|
+
|
|
77
|
+
### Typography Helper Classes
|
|
78
|
+
|
|
79
|
+
- `.heading-1` — Page title (22px, 600)
|
|
80
|
+
- `.heading-2` — Section / Card title (18px, 600)
|
|
81
|
+
- `.heading-3` — Sub-header (16px, 500)
|
|
82
|
+
- `.heading-4` — Field label (14px, 500)
|
|
83
|
+
- `.heading-5` — Small group header (12px, 600)
|
|
84
|
+
- `.p` — Body copy (12px, 500)
|
|
85
|
+
|
|
86
|
+
### 4px Spacing Tokens
|
|
87
|
+
|
|
88
|
+
- Gap: `gap-xs` (4px), `gap-s` (6px), `gap-md` (8px), `gap-m` (12px), `gap-l` (16px), `gap-xl` (20px), `gap-xxl` (24px)
|
|
89
|
+
- Padding: `p-xs`, `p-s`, `p-md`, `p-m`, `p-l`, `p-xl`, `p-xxl`
|