igniteui-cli 14.10.0-alpha.3 → 14.10.0-alpha.4
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 +4 -4
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-components/SKILL.md +161 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-components/reference/CHARTS-GRIDS.md +127 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-components/reference/COMPONENT-CATALOGUE.md +301 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-components/reference/EVENT-HANDLING.md +70 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-components/reference/INSTALLATION.md +139 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-components/reference/JSX-PATTERNS.md +187 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-components/reference/REFS-FORMS.md +229 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-components/reference/REVEAL-SDK.md +198 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-components/reference/TROUBLESHOOTING.md +147 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-customize-theme/SKILL.md +182 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-customize-theme/reference/CSS-THEMING.md +265 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-customize-theme/reference/MCP-SERVER.md +75 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-customize-theme/reference/REVEAL-THEME.md +86 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-customize-theme/reference/SASS-THEMING.md +125 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-customize-theme/reference/TROUBLESHOOTING.md +35 -0
- package/templates/react/igr-ts/projects/_base/files/__dot__claude/skills/igniteui-react-optimize-bundle-size/SKILL.md +439 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Event Handling
|
|
2
|
+
|
|
3
|
+
## How Events Work
|
|
4
|
+
|
|
5
|
+
Ignite UI React wrappers map web component custom events to React-style `onXxx` callback props. The event name is converted from the web component event name to a camelCase `on`-prefixed prop.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { IgrButton, IgrInput, IgrCheckbox } from 'igniteui-react';
|
|
9
|
+
|
|
10
|
+
function MyForm() {
|
|
11
|
+
const handleClick = (event: CustomEvent) => {
|
|
12
|
+
console.log('Button clicked');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const handleInput = (event: CustomEvent) => {
|
|
16
|
+
// event.target is the underlying web component element (e.g., igc-input)
|
|
17
|
+
console.log('Input value:', (event.target as any).value);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const handleChange = (event: CustomEvent) => {
|
|
21
|
+
console.log('Checkbox changed:', event.detail);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
<IgrButton onClick={handleClick}>
|
|
27
|
+
<span>Submit</span>
|
|
28
|
+
</IgrButton>
|
|
29
|
+
<IgrInput label="Name" onInput={handleInput} />
|
|
30
|
+
<IgrCheckbox onChange={handleChange}>Accept terms</IgrCheckbox>
|
|
31
|
+
</>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Common Event Props
|
|
37
|
+
|
|
38
|
+
| Component | Event Prop | Fires When |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `IgrButton` | `onClick` | Button is clicked |
|
|
41
|
+
| `IgrInput` | `onInput` | Value changes (each keystroke) |
|
|
42
|
+
| `IgrInput` | `onChange` | Value committed (blur / Enter) |
|
|
43
|
+
| `IgrCheckbox` | `onChange` | Checked state changes |
|
|
44
|
+
| `IgrSwitch` | `onChange` | Toggle state changes |
|
|
45
|
+
| `IgrSelect` | `onChange` | Selection changes |
|
|
46
|
+
| `IgrCombo` | `onChange` | Selection changes |
|
|
47
|
+
| `IgrSlider` | `onInput` | Slider value changes (live) |
|
|
48
|
+
| `IgrSlider` | `onChange` | Slider value committed |
|
|
49
|
+
| `IgrDialog` | `onClosing` | Dialog is about to close |
|
|
50
|
+
| `IgrDialog` | `onClosed` | Dialog has closed |
|
|
51
|
+
| `IgrTabs` | `onChange` | Active tab changes |
|
|
52
|
+
| `IgrCalendar` | `onChange` | Selected date changes |
|
|
53
|
+
| `IgrDatepicker` | `onChange` | Selected date changes |
|
|
54
|
+
|
|
55
|
+
## TypeScript Event Types
|
|
56
|
+
|
|
57
|
+
When using TypeScript, event handlers receive the underlying `CustomEvent`:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { IgrInput } from 'igniteui-react';
|
|
61
|
+
|
|
62
|
+
function SearchBar() {
|
|
63
|
+
const handleInput = (e: CustomEvent) => {
|
|
64
|
+
const value = (e.target as HTMLInputElement).value;
|
|
65
|
+
console.log('Search:', value);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return <IgrInput label="Search" onInput={handleInput} />;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Installation & Setup
|
|
2
|
+
|
|
3
|
+
## Install the Package
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Core UI components (MIT)
|
|
7
|
+
npm install igniteui-react
|
|
8
|
+
|
|
9
|
+
# If you need the lightweight grid (MIT)
|
|
10
|
+
# Requires BOTH packages - igniteui-react and igniteui-grid-lite
|
|
11
|
+
npm install igniteui-react igniteui-grid-lite
|
|
12
|
+
|
|
13
|
+
# If you need advanced grids (commercial)
|
|
14
|
+
npm install igniteui-react-grids
|
|
15
|
+
|
|
16
|
+
# If you need charts (commercial)
|
|
17
|
+
npm install igniteui-react-charts
|
|
18
|
+
|
|
19
|
+
# If you need gauges (commercial)
|
|
20
|
+
npm install igniteui-react-gauges
|
|
21
|
+
|
|
22
|
+
# If you need maps (commercial)
|
|
23
|
+
npm install igniteui-react-maps
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Peer Dependencies
|
|
27
|
+
|
|
28
|
+
`igniteui-react` requires `react` and `react-dom` (v18+ or v19+). These are typically already in your project:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install react react-dom
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Import a Theme (REQUIRED)
|
|
35
|
+
|
|
36
|
+
> **CRITICAL:** Components will render without styles, with broken icons and missing visuals if the theme CSS is not imported. **Always import the theme CSS before using any Ignite UI component.**
|
|
37
|
+
|
|
38
|
+
Import one theme CSS file in your entry point (`main.tsx`, `index.tsx`, or `App.tsx`). The theme CSS must be imported **in every file that uses Ignite UI components** if your framework does not have a single global entry point (e.g., Next.js — see below).
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
// main.tsx or index.tsx
|
|
42
|
+
import 'igniteui-webcomponents/themes/light/bootstrap.css';
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Available themes:
|
|
46
|
+
|
|
47
|
+
| Import | Theme |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `igniteui-webcomponents/themes/light/bootstrap.css` | Bootstrap Light |
|
|
50
|
+
| `igniteui-webcomponents/themes/dark/bootstrap.css` | Bootstrap Dark |
|
|
51
|
+
| `igniteui-webcomponents/themes/light/material.css` | Material Light |
|
|
52
|
+
| `igniteui-webcomponents/themes/dark/material.css` | Material Dark |
|
|
53
|
+
| `igniteui-webcomponents/themes/light/fluent.css` | Fluent Light |
|
|
54
|
+
| `igniteui-webcomponents/themes/dark/fluent.css` | Fluent Dark |
|
|
55
|
+
| `igniteui-webcomponents/themes/light/indigo.css` | Indigo Light |
|
|
56
|
+
| `igniteui-webcomponents/themes/dark/indigo.css` | Indigo Dark |
|
|
57
|
+
|
|
58
|
+
Grid theme CSS files follow the same pattern under `igniteui-react-grids/grids/themes/`.
|
|
59
|
+
|
|
60
|
+
**For grids**, you **must also** import the grid theme CSS. Without it, the grid will be missing styles and icons will show as placeholders:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import 'igniteui-webcomponents/themes/light/bootstrap.css';
|
|
64
|
+
import 'igniteui-react-grids/grids/themes/light/bootstrap.css';
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Next.js Setup
|
|
68
|
+
|
|
69
|
+
In Next.js, there is no single `main.tsx` entry point. Import the theme CSS **in each client component file** that uses Ignite UI components, or in a shared layout component:
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
// app/components/DataTable.tsx
|
|
73
|
+
'use client';
|
|
74
|
+
|
|
75
|
+
import 'igniteui-webcomponents/themes/light/bootstrap.css';
|
|
76
|
+
import 'igniteui-react-grids/grids/themes/light/bootstrap.css';
|
|
77
|
+
|
|
78
|
+
import { IgrGrid, IgrColumn, IgrPaginator } from 'igniteui-react-grids';
|
|
79
|
+
|
|
80
|
+
export default function DataTable({ data }: { data: any[] }) {
|
|
81
|
+
return (
|
|
82
|
+
<IgrGrid data={data} autoGenerate={false}>
|
|
83
|
+
<IgrColumn field="name" header="Name" />
|
|
84
|
+
<IgrColumn field="email" header="Email" />
|
|
85
|
+
<IgrPaginator perPage={10} />
|
|
86
|
+
</IgrGrid>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Alternatively, import themes once in a root layout:
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
// app/layout.tsx
|
|
95
|
+
import 'igniteui-webcomponents/themes/light/bootstrap.css';
|
|
96
|
+
import 'igniteui-react-grids/grids/themes/light/bootstrap.css';
|
|
97
|
+
|
|
98
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
99
|
+
return (
|
|
100
|
+
<html lang="en">
|
|
101
|
+
<body>{children}</body>
|
|
102
|
+
</html>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Minimal App Example (Vite / CRA)
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
// main.tsx
|
|
111
|
+
import React from 'react';
|
|
112
|
+
import ReactDOM from 'react-dom/client';
|
|
113
|
+
import 'igniteui-webcomponents/themes/light/bootstrap.css';
|
|
114
|
+
import App from './App';
|
|
115
|
+
|
|
116
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
117
|
+
<React.StrictMode>
|
|
118
|
+
<App />
|
|
119
|
+
</React.StrictMode>
|
|
120
|
+
);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
// App.tsx
|
|
125
|
+
import { IgrButton, IgrInput } from 'igniteui-react';
|
|
126
|
+
|
|
127
|
+
function App() {
|
|
128
|
+
return (
|
|
129
|
+
<div>
|
|
130
|
+
<IgrInput label="Name" />
|
|
131
|
+
<IgrButton><span>Submit</span></IgrButton>
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export default App;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
> **No `defineComponents()` needed.** React wrappers auto-register. See [CHARTS-GRIDS.md](./reference/CHARTS-GRIDS.md) for exceptions (charts, gauges, maps).
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# JSX Usage Patterns
|
|
2
|
+
|
|
3
|
+
## Props vs HTML Attributes
|
|
4
|
+
|
|
5
|
+
Ignite UI React components accept props just like any React component. Use JSX expression syntax for dynamic values:
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
// ✅ Correct — JSX expression
|
|
9
|
+
<IgrInput label="Email" placeholder="you@example.com" type="email" />
|
|
10
|
+
<IgrSlider value={50} min={0} max={100} />
|
|
11
|
+
<IgrButton disabled={isLoading}>Submit</IgrButton>
|
|
12
|
+
|
|
13
|
+
// ❌ Wrong — string for numeric/boolean values
|
|
14
|
+
<IgrSlider value="50" />
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Children vs Slots
|
|
18
|
+
|
|
19
|
+
Ignite UI components use the web component **slot** mechanism under the hood. In JSX, pass children to the default slot and use the `slot` attribute to target named slots:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<IgrButton>
|
|
23
|
+
{/* Default slot — button label */}
|
|
24
|
+
<span>Click Me</span>
|
|
25
|
+
</IgrButton>
|
|
26
|
+
|
|
27
|
+
<IgrCard>
|
|
28
|
+
<IgrCardHeader>
|
|
29
|
+
<h3 slot="title">Card Title</h3>
|
|
30
|
+
<p slot="subtitle">Card subtitle</p>
|
|
31
|
+
</IgrCardHeader>
|
|
32
|
+
<IgrCardContent>
|
|
33
|
+
<p>Default slot content inside the card body.</p>
|
|
34
|
+
</IgrCardContent>
|
|
35
|
+
<IgrCardActions>
|
|
36
|
+
<IgrButton slot="start">Cancel</IgrButton>
|
|
37
|
+
<IgrButton slot="end">Confirm</IgrButton>
|
|
38
|
+
</IgrCardActions>
|
|
39
|
+
</IgrCard>
|
|
40
|
+
|
|
41
|
+
<IgrNavDrawerItem>
|
|
42
|
+
<span slot="icon">📊</span>
|
|
43
|
+
<span slot="content">Dashboard</span>
|
|
44
|
+
</IgrNavDrawerItem>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
> **Tip:** Check the component documentation for available slot names. Common slots include `start`, `end`, `prefix`, `suffix`, `header`, `content`, `icon`, `title`, `subtitle`.
|
|
48
|
+
|
|
49
|
+
## Render Props (Grids & Complex Components)
|
|
50
|
+
|
|
51
|
+
Some components like the Data Grid support **render props** for custom cell rendering:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { IgrGrid, IgrColumn } from 'igniteui-react-grids';
|
|
55
|
+
|
|
56
|
+
function UserGrid({ users }: { users: User[] }) {
|
|
57
|
+
return (
|
|
58
|
+
<IgrGrid data={users} autoGenerate={false}>
|
|
59
|
+
<IgrColumn field="name" header="Name" />
|
|
60
|
+
<IgrColumn field="email" header="Email" />
|
|
61
|
+
<IgrColumn
|
|
62
|
+
field="status"
|
|
63
|
+
header="Status"
|
|
64
|
+
bodyTemplate={(ctx) => (
|
|
65
|
+
<IgrBadge>{ctx.cell.value}</IgrBadge>
|
|
66
|
+
)}
|
|
67
|
+
/>
|
|
68
|
+
</IgrGrid>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## IgrTabs — Content Panels vs Navigation
|
|
74
|
+
|
|
75
|
+
`IgrTabs` supports two distinct usage patterns. Choosing the wrong one is a common mistake.
|
|
76
|
+
|
|
77
|
+
### Pattern 1: Tabs with Content Panels (inline content)
|
|
78
|
+
|
|
79
|
+
Use `IgrTab` with inline content when the tabbed content is rendered inline — no routing involved. The tab label can be set via a `label` prop or via a `slot="label"` element:
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { IgrTabs, IgrTab } from 'igniteui-react';
|
|
83
|
+
|
|
84
|
+
// Simple text labels using the label prop
|
|
85
|
+
function SettingsPage() {
|
|
86
|
+
return (
|
|
87
|
+
<IgrTabs>
|
|
88
|
+
<IgrTab label="Profile" selected>
|
|
89
|
+
<p>Profile settings content here</p>
|
|
90
|
+
</IgrTab>
|
|
91
|
+
<IgrTab label="Security">
|
|
92
|
+
<p>Security settings content here</p>
|
|
93
|
+
</IgrTab>
|
|
94
|
+
<IgrTab label="Notifications">
|
|
95
|
+
<p>Notification preferences content here</p>
|
|
96
|
+
</IgrTab>
|
|
97
|
+
</IgrTabs>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Alternative: Using slot="label" for complex headers (e.g., with icons):**
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import { IgrTabs, IgrTab, IgrIcon } from 'igniteui-react';
|
|
106
|
+
|
|
107
|
+
function SettingsPage() {
|
|
108
|
+
return (
|
|
109
|
+
<IgrTabs>
|
|
110
|
+
<IgrTab selected>
|
|
111
|
+
<span slot="label">
|
|
112
|
+
<IgrIcon name="home" collection="material" />
|
|
113
|
+
Profile
|
|
114
|
+
</span>
|
|
115
|
+
<p>Profile settings content here</p>
|
|
116
|
+
</IgrTab>
|
|
117
|
+
<IgrTab>
|
|
118
|
+
<span slot="label">
|
|
119
|
+
<IgrIcon name="security" collection="material" />
|
|
120
|
+
Security
|
|
121
|
+
</span>
|
|
122
|
+
<p>Security settings content here</p>
|
|
123
|
+
</IgrTab>
|
|
124
|
+
<IgrTab>
|
|
125
|
+
<span slot="label">
|
|
126
|
+
<IgrIcon name="notifications" collection="material" />
|
|
127
|
+
Notifications
|
|
128
|
+
</span>
|
|
129
|
+
<p>Notification preferences content here</p>
|
|
130
|
+
</IgrTab>
|
|
131
|
+
</IgrTabs>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Pattern 2: Tabs as Navigation (with React Router — NO inline content)
|
|
137
|
+
|
|
138
|
+
> **⚠️ CRITICAL:** When using `IgrTabs` for navigation with React Router (or any router), **do NOT include inline content in `IgrTab`**. Only render tab labels (via `label` prop or `slot="label"`), and let the router's `<Outlet />` handle the content below the tabs.
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import { IgrTabs, IgrTab } from 'igniteui-react';
|
|
142
|
+
import { useNavigate, useLocation, Outlet } from 'react-router-dom';
|
|
143
|
+
|
|
144
|
+
const tabs = [
|
|
145
|
+
{ path: '/dashboard', label: 'Dashboard' },
|
|
146
|
+
{ path: '/orders', label: 'Orders' },
|
|
147
|
+
{ path: '/customers', label: 'Customers' },
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
function MainLayout() {
|
|
151
|
+
const navigate = useNavigate();
|
|
152
|
+
const location = useLocation();
|
|
153
|
+
|
|
154
|
+
const handleTabChange = (e: CustomEvent) => {
|
|
155
|
+
const selectedIndex = (e.target as any).selectedIndex;
|
|
156
|
+
if (selectedIndex !== undefined && tabs[selectedIndex]) {
|
|
157
|
+
navigate(tabs[selectedIndex].path);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<div>
|
|
163
|
+
{/* Tabs for navigation — label prop only, no inline content */}
|
|
164
|
+
<IgrTabs onChange={handleTabChange}>
|
|
165
|
+
{tabs.map((tab) => (
|
|
166
|
+
<IgrTab
|
|
167
|
+
key={tab.path}
|
|
168
|
+
label={tab.label}
|
|
169
|
+
selected={location.pathname === tab.path}
|
|
170
|
+
/>
|
|
171
|
+
))}
|
|
172
|
+
</IgrTabs>
|
|
173
|
+
|
|
174
|
+
{/* Router outlet renders the routed view */}
|
|
175
|
+
<main>
|
|
176
|
+
<Outlet />
|
|
177
|
+
</main>
|
|
178
|
+
</div>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Key rules for tabs-as-navigation:**
|
|
184
|
+
- ✅ Use only `IgrTab` with label prop or `slot="label"` — no inline content
|
|
185
|
+
- ✅ Sync the active tab to the current route using the `selected` prop
|
|
186
|
+
- ✅ Handle `onChange` to call `navigate()` for route changes
|
|
187
|
+
- ✅ Use `<Outlet />` (or the equivalent in your router) for content rendering
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Refs & Forms
|
|
2
|
+
|
|
3
|
+
## Refs & Imperative API
|
|
4
|
+
|
|
5
|
+
Use `useRef` to access the underlying web component element and call imperative methods (e.g., showing/hiding a dialog, focusing an input).
|
|
6
|
+
Components from `igniteui-react`, `igniteui-react-grids` and `igniteui-react-dockmanager` are React Function Components forward the native element to `useRef` and expose alias with the same name for accessing the custom element API:
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { useRef } from 'react';
|
|
10
|
+
import { IgrDialog, IgrButton, IgrInput } from 'igniteui-react';
|
|
11
|
+
|
|
12
|
+
function MyPage() {
|
|
13
|
+
const dialogRef = useRef<IgrDialog>(null);
|
|
14
|
+
const inputRef = useRef<IgrInput>(null);
|
|
15
|
+
|
|
16
|
+
const openDialog = () => {
|
|
17
|
+
// Access the underlying web component and call its methods
|
|
18
|
+
dialogRef.current?.show();
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const focusInput = () => {
|
|
22
|
+
inputRef.current?.focus();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
<IgrButton onClick={openDialog}>
|
|
28
|
+
<span>Open Dialog</span>
|
|
29
|
+
</IgrButton>
|
|
30
|
+
|
|
31
|
+
<IgrDialog ref={dialogRef} title="Confirmation">
|
|
32
|
+
<p>Are you sure?</p>
|
|
33
|
+
<IgrButton slot="footer" onClick={() => dialogRef.current?.hide()}>
|
|
34
|
+
<span>Close</span>
|
|
35
|
+
</IgrButton>
|
|
36
|
+
</IgrDialog>
|
|
37
|
+
|
|
38
|
+
<IgrButton onClick={focusInput}>
|
|
39
|
+
<span>Focus Input</span>
|
|
40
|
+
</IgrButton>
|
|
41
|
+
<IgrInput ref={inputRef} label="Name" />
|
|
42
|
+
</>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
> **Tip:** The ref gives you direct access to the web component's DOM element. You can call any method documented in the web component API.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Uncontrolled Components
|
|
51
|
+
|
|
52
|
+
`igniteui-react` Inputs integrate with the native form handling through Element internals, allowing to take advantage of the native state management adn validation to create intuitive, straight-forward forms:
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { useRef } from 'react';
|
|
56
|
+
import { IgrInput, IgrButton } from 'igniteui-react';
|
|
57
|
+
|
|
58
|
+
function SimpleForm() {
|
|
59
|
+
const nameRef = useRef<IgrInput>(null);
|
|
60
|
+
|
|
61
|
+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
62
|
+
// e.preventDefault(); // optionally prevent default submit form custom handling
|
|
63
|
+
const formData = new FormData(e.target);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<form onSubmit={handleSubmit}>
|
|
68
|
+
<IgrInput name="name" label="Name" required={true} />
|
|
69
|
+
<IgrInput name="description" label="description" minLength={0}>
|
|
70
|
+
<IgrButton type="submit">
|
|
71
|
+
<span>Submit</span>
|
|
72
|
+
</IgrButton>
|
|
73
|
+
</form>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Controlled Components with `useState`
|
|
79
|
+
|
|
80
|
+
Wire up Ignite UI form components with React state for controlled behavior:
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { useState } from 'react';
|
|
84
|
+
import { IgrInput, IgrCheckbox, IgrSelect, IgrSelectItem } from 'igniteui-react';
|
|
85
|
+
|
|
86
|
+
function ProfileForm() {
|
|
87
|
+
const [name, setName] = useState('');
|
|
88
|
+
const [newsletter, setNewsletter] = useState(false);
|
|
89
|
+
const [role, setRole] = useState('user');
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<form>
|
|
93
|
+
<IgrInput
|
|
94
|
+
label="Name"
|
|
95
|
+
value={name}
|
|
96
|
+
onInput={(e: CustomEvent<string>) => setName(e.detail) }
|
|
97
|
+
/>
|
|
98
|
+
|
|
99
|
+
<IgrCheckbox
|
|
100
|
+
checked={newsletter}
|
|
101
|
+
onChange={(e: CustomEvent<IgcCheckboxChangeEventArgs>) =>
|
|
102
|
+
setNewsletter(e.detail.checked)
|
|
103
|
+
}
|
|
104
|
+
>
|
|
105
|
+
Subscribe to newsletter
|
|
106
|
+
</IgrCheckbox>
|
|
107
|
+
|
|
108
|
+
<IgrSelect
|
|
109
|
+
label="Role"
|
|
110
|
+
value={role}
|
|
111
|
+
onChange={(e: CustomEvent<IgcSelectItemComponent>) =>
|
|
112
|
+
setRole(e.detail.value)
|
|
113
|
+
}
|
|
114
|
+
>
|
|
115
|
+
<IgrSelectItem value="user">User</IgrSelectItem>
|
|
116
|
+
<IgrSelectItem value="admin">Admin</IgrSelectItem>
|
|
117
|
+
<IgrSelectItem value="editor">Editor</IgrSelectItem>
|
|
118
|
+
</IgrSelect>
|
|
119
|
+
</form>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## React Hook Form Integration
|
|
125
|
+
|
|
126
|
+
Ignite UI components are form-associated web components. You can integrate them with React Hook Form using `Controller`:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { useForm, Controller } from 'react-hook-form';
|
|
130
|
+
import { IgrInput, IgrCheckbox, IgrButton } from 'igniteui-react';
|
|
131
|
+
|
|
132
|
+
interface FormData {
|
|
133
|
+
email: string;
|
|
134
|
+
acceptTerms: boolean;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function SignUpForm() {
|
|
138
|
+
const { control, handleSubmit, formState: { errors } } = useForm<FormData>();
|
|
139
|
+
|
|
140
|
+
const onSubmit = (data: FormData) => {
|
|
141
|
+
console.log(data);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
146
|
+
<Controller
|
|
147
|
+
name="email"
|
|
148
|
+
control={control}
|
|
149
|
+
rules={{ required: 'Email is required' }}
|
|
150
|
+
render={({ field }) => (
|
|
151
|
+
<IgrInput
|
|
152
|
+
label="Email"
|
|
153
|
+
type="email"
|
|
154
|
+
value={field.value || ''}
|
|
155
|
+
onInput={(e: CustomEvent<string>) =>
|
|
156
|
+
field.onChange(e.detail)
|
|
157
|
+
}
|
|
158
|
+
onBlur={() => field.onBlur()}
|
|
159
|
+
invalid={!!errors.email}
|
|
160
|
+
/>
|
|
161
|
+
)}
|
|
162
|
+
/>
|
|
163
|
+
{errors.email && <span>{errors.email.message}</span>}
|
|
164
|
+
|
|
165
|
+
<Controller
|
|
166
|
+
name="acceptTerms"
|
|
167
|
+
control={control}
|
|
168
|
+
rules={{ required: 'You must accept the terms' }}
|
|
169
|
+
render={({ field }) => (
|
|
170
|
+
<IgrCheckbox
|
|
171
|
+
checked={field.value || false}
|
|
172
|
+
onChange={(e: CustomEvent<IgcCheckboxChangeEventArgs>) =>
|
|
173
|
+
field.onChange(e.detail.checked)
|
|
174
|
+
}
|
|
175
|
+
>
|
|
176
|
+
I accept the terms and conditions
|
|
177
|
+
</IgrCheckbox>
|
|
178
|
+
)}
|
|
179
|
+
/>
|
|
180
|
+
|
|
181
|
+
<IgrButton type="submit">
|
|
182
|
+
<span>Sign Up</span>
|
|
183
|
+
</IgrButton>
|
|
184
|
+
</form>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## TypeScript
|
|
190
|
+
|
|
191
|
+
### Importing Component Types
|
|
192
|
+
|
|
193
|
+
Each component exports its props interface. Import and use them for type-safe code:
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import { IgrButton, IgrInput } from 'igniteui-react';
|
|
197
|
+
import type { ComponentProps } from 'react';
|
|
198
|
+
|
|
199
|
+
// Get the props type for a component
|
|
200
|
+
type ButtonProps = ComponentProps<typeof IgrButton>;
|
|
201
|
+
type InputProps = ComponentProps<typeof IgrInput>;
|
|
202
|
+
|
|
203
|
+
// Use in your own components
|
|
204
|
+
interface FormFieldProps {
|
|
205
|
+
label: string;
|
|
206
|
+
inputProps?: Partial<InputProps>;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function FormField({ label, inputProps }: FormFieldProps) {
|
|
210
|
+
return (
|
|
211
|
+
<div>
|
|
212
|
+
<IgrInput label={label} {...inputProps} />
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Auto-complete
|
|
219
|
+
|
|
220
|
+
IDEs with TypeScript support will provide auto-complete for all `Igr*` component props. Make sure your `tsconfig.json` includes:
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"compilerOptions": {
|
|
225
|
+
"jsx": "react-jsx",
|
|
226
|
+
"moduleResolution": "bundler"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|