@festo-ui/react 10.1.0-dev.875 → 10.1.1-dev.918
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/components/popovers/popover/Popover.js +5 -1
- package/llm-doc/README.md +20 -0
- package/llm-doc/components.md +661 -0
- package/llm-doc/forms.md +333 -0
- package/llm-doc/installation.md +75 -0
- package/llm-doc/patterns.md +179 -0
- package/package.json +5 -2
|
@@ -54,7 +54,11 @@ const Popover = /*#__PURE__*/ forwardRef(({ children, className, style, containe
|
|
|
54
54
|
children: [
|
|
55
55
|
/*#__PURE__*/ jsx("div", {
|
|
56
56
|
className: className,
|
|
57
|
-
style:
|
|
57
|
+
style: {
|
|
58
|
+
width: 'fit-content',
|
|
59
|
+
height: 'fit-content',
|
|
60
|
+
...style
|
|
61
|
+
},
|
|
58
62
|
ref: (node)=>{
|
|
59
63
|
refs.setReference(node);
|
|
60
64
|
if ('function' == typeof handleRef) handleRef(node);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @festo-ui/react — AI Agent Documentation
|
|
2
|
+
|
|
3
|
+
This documentation describes the React component library `@festo-ui/react` of the Festo Design System. It is optimized for AI agents that should use this library in projects.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
| File | Content |
|
|
8
|
+
|------|--------|
|
|
9
|
+
| [installation.md](./installation.md) | Installation, setup, imports |
|
|
10
|
+
| [components.md](./components.md) | All UI components (Accordion, Button, Card, Modal, etc.) |
|
|
11
|
+
| [forms.md](./forms.md) | All form components (TextInput, Select, Checkbox, etc.) |
|
|
12
|
+
| [patterns.md](./patterns.md) | Common patterns: Controlled/Uncontrolled, FormData, Styling |
|
|
13
|
+
|
|
14
|
+
## Quick Reference
|
|
15
|
+
|
|
16
|
+
**Package:** `@festo-ui/react`
|
|
17
|
+
**CSS:** `@festo-ui/react/index.css`
|
|
18
|
+
**License:** Apache-2.0
|
|
19
|
+
**Peer Dependencies:** React 18+
|
|
20
|
+
**CSS Prefix:** `fwe-` (Festo Web Essentials)
|
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
# UI Components
|
|
2
|
+
|
|
3
|
+
## Accordion
|
|
4
|
+
|
|
5
|
+
Collapsible content sections.
|
|
6
|
+
|
|
7
|
+
### Components
|
|
8
|
+
- `Accordion` — Container
|
|
9
|
+
- `AccordionHeader` — Header of the entire accordion
|
|
10
|
+
- `AccordionItem` — Single collapsible element
|
|
11
|
+
- `AccordionItemHeader` — Header of an item
|
|
12
|
+
- `AccordionItemBody` — Content of an item
|
|
13
|
+
|
|
14
|
+
### Accordion Props
|
|
15
|
+
| Prop | Type | Default | Description |
|
|
16
|
+
|------|------|---------|-------------|
|
|
17
|
+
| `showMore` | `string` | `"Show more"` | Text for "Show more" |
|
|
18
|
+
| `showLess` | `string` | `"Show less"` | Text for "Show less" |
|
|
19
|
+
| `keepItemsOpen` | `boolean` | `true` | Multiple items open simultaneously. `false` = auto-collapse |
|
|
20
|
+
| `transparent` | `boolean` | `false` | Transparent background |
|
|
21
|
+
| `highlighted` | `boolean` | `true` | Highlighted style |
|
|
22
|
+
|
|
23
|
+
### AccordionItem Props
|
|
24
|
+
| Prop | Type | Description |
|
|
25
|
+
|------|------|-------------|
|
|
26
|
+
| `expanded` | `boolean` | Controlled: whether open |
|
|
27
|
+
| `defaultExpanded` | `boolean` | Initial state (default: `false`) |
|
|
28
|
+
| `onChange` | `(expanded: boolean, event) => void` | Callback on state change |
|
|
29
|
+
|
|
30
|
+
### Example — Basic
|
|
31
|
+
```tsx
|
|
32
|
+
<Accordion>
|
|
33
|
+
<AccordionHeader>Title</AccordionHeader>
|
|
34
|
+
<AccordionItem>
|
|
35
|
+
<AccordionItemHeader>Item 1</AccordionItemHeader>
|
|
36
|
+
<AccordionItemBody>Content 1</AccordionItemBody>
|
|
37
|
+
</AccordionItem>
|
|
38
|
+
<AccordionItem>
|
|
39
|
+
<AccordionItemHeader>Item 2</AccordionItemHeader>
|
|
40
|
+
<AccordionItemBody>Content 2</AccordionItemBody>
|
|
41
|
+
</AccordionItem>
|
|
42
|
+
</Accordion>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Example — Auto-Collapse
|
|
46
|
+
```tsx
|
|
47
|
+
<Accordion keepItemsOpen={false}>
|
|
48
|
+
<AccordionHeader>Title</AccordionHeader>
|
|
49
|
+
<AccordionItem>
|
|
50
|
+
<AccordionItemHeader>Item 1</AccordionItemHeader>
|
|
51
|
+
<AccordionItemBody>Content 1</AccordionItemBody>
|
|
52
|
+
</AccordionItem>
|
|
53
|
+
</Accordion>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Example — Controlled
|
|
57
|
+
```tsx
|
|
58
|
+
const [expanded, setExpanded] = useState('item1');
|
|
59
|
+
const handleChange = (panel) => (isExpanded) => {
|
|
60
|
+
setExpanded(isExpanded ? panel : false);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
<Accordion>
|
|
64
|
+
<AccordionItem expanded={expanded === 'item1'} onChange={handleChange('item1')}>
|
|
65
|
+
<AccordionItemHeader>Item 1</AccordionItemHeader>
|
|
66
|
+
<AccordionItemBody>Content 1</AccordionItemBody>
|
|
67
|
+
</AccordionItem>
|
|
68
|
+
</Accordion>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## BottomSheet
|
|
74
|
+
|
|
75
|
+
Mobile bottom sheet overlay.
|
|
76
|
+
|
|
77
|
+
### Props
|
|
78
|
+
| Prop | Type | Default | Description |
|
|
79
|
+
|------|------|---------|-------------|
|
|
80
|
+
| `open` | `boolean` | — | Controlled: whether visible |
|
|
81
|
+
| `defaultExpanded` | `boolean` | — | Initial state |
|
|
82
|
+
| `expandFrom` | `"center" \| "bottom"` | `"center"` | Expand direction |
|
|
83
|
+
| `hasBackdrop` | `boolean` | `true` | Show backdrop overlay |
|
|
84
|
+
| `hideCloseIcon` | `boolean` | — | Hide close icon |
|
|
85
|
+
| `onOpenChange` | `(value: boolean) => void` | — | Callback on open/close |
|
|
86
|
+
|
|
87
|
+
### Example
|
|
88
|
+
```tsx
|
|
89
|
+
const [open, setOpen] = useState(false);
|
|
90
|
+
|
|
91
|
+
<button onClick={() => setOpen(true)}>Open</button>
|
|
92
|
+
<BottomSheet open={open} onOpenChange={setOpen}>
|
|
93
|
+
<p>Content</p>
|
|
94
|
+
</BottomSheet>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Breadcrumb
|
|
100
|
+
|
|
101
|
+
Navigation path display.
|
|
102
|
+
|
|
103
|
+
### Props
|
|
104
|
+
| Prop | Type | Description |
|
|
105
|
+
|------|------|-------------|
|
|
106
|
+
| `locations` | `BreadcrumbLocation[]` | Array with `{ text, href }` |
|
|
107
|
+
| `onClick` | `(event) => void` | Click handler for links |
|
|
108
|
+
|
|
109
|
+
### Example — With locations array
|
|
110
|
+
```tsx
|
|
111
|
+
<Breadcrumb
|
|
112
|
+
locations={[
|
|
113
|
+
{ text: 'Home', href: '/' },
|
|
114
|
+
{ text: 'Products', href: '/products' },
|
|
115
|
+
{ text: 'Current', href: '/products/current' },
|
|
116
|
+
]}
|
|
117
|
+
onClick={(e) => e.preventDefault()}
|
|
118
|
+
/>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Example — With children
|
|
122
|
+
```tsx
|
|
123
|
+
<Breadcrumb>
|
|
124
|
+
<a href="/components">Components</a>
|
|
125
|
+
<a href="/components/breadcrumbs">Breadcrumbs</a>
|
|
126
|
+
</Breadcrumb>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Button
|
|
132
|
+
|
|
133
|
+
Standard buttons in various variants.
|
|
134
|
+
|
|
135
|
+
### Props
|
|
136
|
+
| Prop | Type | Default | Description |
|
|
137
|
+
|------|------|---------|-------------|
|
|
138
|
+
| `primary` | `boolean` | `false` | Primary/hero button |
|
|
139
|
+
| `tertiary` | `boolean` | `false` | Tertiary button (text style) |
|
|
140
|
+
| `icon` | `ReactNode` | — | Icon element |
|
|
141
|
+
| `iconOnly` | `boolean` | `false` | Icon only, no text |
|
|
142
|
+
| `large` | `boolean` | `false` | Larger variant |
|
|
143
|
+
| `floating` | `boolean` | `false` | Floating action button |
|
|
144
|
+
| `disabled` | `boolean` | `false` | Disabled |
|
|
145
|
+
|
|
146
|
+
### Examples
|
|
147
|
+
```tsx
|
|
148
|
+
<Button>Default</Button>
|
|
149
|
+
<Button primary>Primary</Button>
|
|
150
|
+
<Button tertiary>Tertiary</Button>
|
|
151
|
+
<Button large>Large</Button>
|
|
152
|
+
<Button icon={<IconCoreRangeProduct />}>With Icon</Button>
|
|
153
|
+
<Button iconOnly icon={<IconCoreRangeProduct />} />
|
|
154
|
+
<Button iconOnly large primary floating icon={<IconCoreRangeProduct />} />
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Card
|
|
160
|
+
|
|
161
|
+
Card component with header, body, and notifications.
|
|
162
|
+
|
|
163
|
+
### Components
|
|
164
|
+
- `Card` — Container
|
|
165
|
+
- `CardHeader` — Header with optional image, subtitle, and action
|
|
166
|
+
- `CardBody` — Text content with optional children
|
|
167
|
+
- `CardNotification` — Notification bar
|
|
168
|
+
|
|
169
|
+
### Example
|
|
170
|
+
```tsx
|
|
171
|
+
<Card>
|
|
172
|
+
<CardHeader
|
|
173
|
+
title="Card Title"
|
|
174
|
+
subtitle="Description"
|
|
175
|
+
image={{ src: 'image.webp', alt: 'Image', aspectRatio: '16/9' }}
|
|
176
|
+
action={<button className="fwe-btn fwe-btn-icon">...</button>}
|
|
177
|
+
/>
|
|
178
|
+
<CardBody text="Card text">
|
|
179
|
+
<button className="fwe-btn fwe-btn-hero fwe-btn-block">Action</button>
|
|
180
|
+
</CardBody>
|
|
181
|
+
<CardNotification className="fwe-bg-orange" title="Warning" message="Details" />
|
|
182
|
+
</Card>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Chip / ChipContainer
|
|
188
|
+
|
|
189
|
+
Chips for filters, categories, selection, and actions.
|
|
190
|
+
|
|
191
|
+
### Chip Props
|
|
192
|
+
| Prop | Type | Default | Description |
|
|
193
|
+
|------|------|---------|-------------|
|
|
194
|
+
| `type` | `ChipType` | `ChipType.Choice` | Chip type: `Choice`, `Category`, `Filter`, `Action`, `Readonly` |
|
|
195
|
+
| `icon` | `ReactNode` | — | Icon |
|
|
196
|
+
| `selected` | `boolean` | `false` | Selected |
|
|
197
|
+
| `disabled` | `boolean` | `false` | Disabled |
|
|
198
|
+
| `large` | `boolean` | `false` | Large |
|
|
199
|
+
|
|
200
|
+
### ChipContainer Props
|
|
201
|
+
| Prop | Type | Default | Description |
|
|
202
|
+
|------|------|---------|-------------|
|
|
203
|
+
| `large` | `boolean` | `false` | Large chips in container |
|
|
204
|
+
|
|
205
|
+
### Example
|
|
206
|
+
```tsx
|
|
207
|
+
import { Chip, ChipContainer, ChipType } from '@festo-ui/react';
|
|
208
|
+
|
|
209
|
+
<ChipContainer>
|
|
210
|
+
<Chip>Choice</Chip>
|
|
211
|
+
<Chip selected>Selected</Chip>
|
|
212
|
+
<Chip type={ChipType.Filter} selected>Filter</Chip>
|
|
213
|
+
<Chip type={ChipType.Action} icon={<IconCoreRangeProduct />}>Action</Chip>
|
|
214
|
+
<Chip type={ChipType.Readonly}>Readonly</Chip>
|
|
215
|
+
</ChipContainer>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## LoadingIndicator
|
|
221
|
+
|
|
222
|
+
Loading indicator in three sizes.
|
|
223
|
+
|
|
224
|
+
### Props
|
|
225
|
+
| Prop | Type | Default | Description |
|
|
226
|
+
|------|------|---------|-------------|
|
|
227
|
+
| `size` | `"large" \| "medium" \| "small"` | `"large"` | Size |
|
|
228
|
+
|
|
229
|
+
### Example
|
|
230
|
+
```tsx
|
|
231
|
+
<LoadingIndicator />
|
|
232
|
+
<LoadingIndicator size="medium">Loading...</LoadingIndicator>
|
|
233
|
+
<LoadingIndicator size="small">Loading...</LoadingIndicator>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## MobileFlyout
|
|
239
|
+
|
|
240
|
+
Mobile navigation with nested pages.
|
|
241
|
+
|
|
242
|
+
### MobileFlyout Props
|
|
243
|
+
| Prop | Type | Default | Description |
|
|
244
|
+
|------|------|---------|-------------|
|
|
245
|
+
| `open` | `boolean` | — | Controlled: whether open |
|
|
246
|
+
| `defaultOpen` | `boolean` | — | Initial state |
|
|
247
|
+
| `visible` | `string[]` | — | Controlled: visible pages |
|
|
248
|
+
| `defaultVisible` | `string[]` | `['root']` | Initially visible pages |
|
|
249
|
+
| `onOpenChange` | `(value: boolean) => void` | — | Callback |
|
|
250
|
+
| `onVisibleChange` | `(value: string) => void` | — | Callback |
|
|
251
|
+
| `back` | `string` | — | Back text |
|
|
252
|
+
|
|
253
|
+
### MobileFlyoutItem Props
|
|
254
|
+
| Prop | Type | Description |
|
|
255
|
+
|------|------|-------------|
|
|
256
|
+
| `icon` | `ReactNode` | Icon |
|
|
257
|
+
| `pageLink` | `string` | Links to a MobileFlyoutPage |
|
|
258
|
+
| `active` | `boolean` | Active state |
|
|
259
|
+
| `component` | `ElementType` | Alternative HTML element |
|
|
260
|
+
|
|
261
|
+
### MobileFlyoutPage Props
|
|
262
|
+
| Prop | Type | Description |
|
|
263
|
+
|------|------|-------------|
|
|
264
|
+
| `name` | `string` | Unique page name |
|
|
265
|
+
| `root` | `boolean` | Whether this is the root page |
|
|
266
|
+
| `back` | `string` | Back text |
|
|
267
|
+
|
|
268
|
+
### Example
|
|
269
|
+
```tsx
|
|
270
|
+
<MobileFlyout>
|
|
271
|
+
<MobileFlyoutPage root name="root">
|
|
272
|
+
<MobileFlyoutItem href="/page1">Page 1</MobileFlyoutItem>
|
|
273
|
+
<MobileFlyoutItem icon={<IconSettings />} pageLink="settings">
|
|
274
|
+
Settings
|
|
275
|
+
</MobileFlyoutItem>
|
|
276
|
+
</MobileFlyoutPage>
|
|
277
|
+
<MobileFlyoutPage name="settings">
|
|
278
|
+
<h3>Settings</h3>
|
|
279
|
+
<MobileFlyoutItem pageLink="advanced">Advanced</MobileFlyoutItem>
|
|
280
|
+
</MobileFlyoutPage>
|
|
281
|
+
</MobileFlyout>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Modals (AlertModal, ConfirmModal, CustomModal, Prompt)
|
|
287
|
+
|
|
288
|
+
Various dialog types.
|
|
289
|
+
|
|
290
|
+
### AlertModal
|
|
291
|
+
Displays a warning/info dialog.
|
|
292
|
+
```tsx
|
|
293
|
+
<AlertModal
|
|
294
|
+
isOpen={open}
|
|
295
|
+
onClose={() => setOpen(false)}
|
|
296
|
+
onCancel={() => setOpen(false)}
|
|
297
|
+
onOk={() => setOpen(false)}
|
|
298
|
+
title="Title"
|
|
299
|
+
subtitle="Subtitle"
|
|
300
|
+
body="Message"
|
|
301
|
+
alertType="info" // or "warning"
|
|
302
|
+
cancel="Cancel"
|
|
303
|
+
ok="OK"
|
|
304
|
+
strong={false} // true for strong warning
|
|
305
|
+
/>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### ConfirmModal
|
|
309
|
+
Confirmation dialog.
|
|
310
|
+
```tsx
|
|
311
|
+
<ConfirmModal
|
|
312
|
+
isOpen={open}
|
|
313
|
+
onClose={() => setOpen(false)}
|
|
314
|
+
onCancel={() => setOpen(false)}
|
|
315
|
+
onOk={() => setOpen(false)}
|
|
316
|
+
title="Confirmation"
|
|
317
|
+
subtitle="Subtitle"
|
|
318
|
+
body="Do you want to continue?"
|
|
319
|
+
cancel="Cancel"
|
|
320
|
+
ok="OK"
|
|
321
|
+
large={false} // true for large scrollable variant
|
|
322
|
+
/>
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Prompt
|
|
326
|
+
Input dialog.
|
|
327
|
+
```tsx
|
|
328
|
+
<Prompt
|
|
329
|
+
isOpen={open}
|
|
330
|
+
onClose={() => setOpen(false)}
|
|
331
|
+
onCancel={() => setOpen(false)}
|
|
332
|
+
onOk={() => setOpen(false)}
|
|
333
|
+
title="Headline"
|
|
334
|
+
subtitle="Category"
|
|
335
|
+
label="Label"
|
|
336
|
+
hint="Optional hint"
|
|
337
|
+
cancel="Cancel"
|
|
338
|
+
ok="Send"
|
|
339
|
+
/>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### CustomModal
|
|
343
|
+
Freely customizable dialog.
|
|
344
|
+
```tsx
|
|
345
|
+
<CustomModal
|
|
346
|
+
isOpen={open}
|
|
347
|
+
onClose={() => setOpen(false)}
|
|
348
|
+
title="Title"
|
|
349
|
+
subtitle="Subtitle"
|
|
350
|
+
closeOnBackdrop={false}
|
|
351
|
+
>
|
|
352
|
+
<form onSubmit={handleSubmit}>
|
|
353
|
+
<div className="fwe-modal-body">
|
|
354
|
+
{/* Form content */}
|
|
355
|
+
</div>
|
|
356
|
+
<div className="fwe-modal-footer">
|
|
357
|
+
<div className="fwe-modal-buttons">
|
|
358
|
+
<button type="button" className="fwe-btn fwe-btn-lg" onClick={() => setOpen(false)}>
|
|
359
|
+
Cancel
|
|
360
|
+
</button>
|
|
361
|
+
<button className="fwe-btn fwe-btn-lg fwe-btn-hero" type="submit">
|
|
362
|
+
Submit
|
|
363
|
+
</button>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
</form>
|
|
367
|
+
</CustomModal>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Common Modal Props
|
|
371
|
+
| Prop | Type | Description |
|
|
372
|
+
|------|------|-------------|
|
|
373
|
+
| `isOpen` | `boolean` | **Required.** Whether the dialog is visible |
|
|
374
|
+
| `isLoading` | `boolean` | Show loading state |
|
|
375
|
+
| `closeOnBackdrop` | `boolean` | Close by clicking backdrop (default: `true`) |
|
|
376
|
+
| `onClose` | `() => void` | Callback on close |
|
|
377
|
+
| `onCloseDone` | `() => void` | Callback after close animation |
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## ImageGallery
|
|
382
|
+
|
|
383
|
+
Fullscreen image gallery overlay.
|
|
384
|
+
|
|
385
|
+
### Props
|
|
386
|
+
| Prop | Type | Description |
|
|
387
|
+
|------|------|-------------|
|
|
388
|
+
| `isOpen` | `boolean` | **Required.** Whether open |
|
|
389
|
+
| `startIndex` | `number` | **Required.** Starting image index |
|
|
390
|
+
| `images` | `ImageGalleryItemData[]` | Array of images |
|
|
391
|
+
| `thumbnailImages` | `ImageGalleryItemData[]` | Thumbnail images |
|
|
392
|
+
| `pagination` | `number \| boolean` | Show pagination |
|
|
393
|
+
| `showScaleButton` | `boolean` | Show zoom button |
|
|
394
|
+
| `descriptiveContent` | `boolean \| (index: number) => ReactNode` | Descriptive text |
|
|
395
|
+
| `onClose` | `() => void` | Callback on close |
|
|
396
|
+
| `onSlideChanged` | `(index: number) => void` | Callback on slide change |
|
|
397
|
+
|
|
398
|
+
### Example
|
|
399
|
+
```tsx
|
|
400
|
+
const [open, setOpen] = useState(false);
|
|
401
|
+
|
|
402
|
+
<button onClick={() => setOpen(true)}>Open Gallery</button>
|
|
403
|
+
<ImageGallery
|
|
404
|
+
isOpen={open}
|
|
405
|
+
onClose={() => setOpen(false)}
|
|
406
|
+
images={images}
|
|
407
|
+
startIndex={0}
|
|
408
|
+
pagination
|
|
409
|
+
thumbnailImages={images}
|
|
410
|
+
showScaleButton
|
|
411
|
+
descriptiveContent
|
|
412
|
+
/>
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## Pagination
|
|
418
|
+
|
|
419
|
+
Page navigation in three styles.
|
|
420
|
+
|
|
421
|
+
### Props
|
|
422
|
+
| Prop | Type | Default | Description |
|
|
423
|
+
|------|------|---------|-------------|
|
|
424
|
+
| `type` | `"NUMERIC" \| "SIMPLE" \| "DOTS"` | `"NUMERIC"` | Display style |
|
|
425
|
+
| `pageMax` | `number` | — | **Required.** Maximum page count |
|
|
426
|
+
| `pageCurrent` | `number` | — | Controlled: current page |
|
|
427
|
+
| `defaultPageCurrent` | `number` | `1` | Initial page |
|
|
428
|
+
| `onChange` | `(page: number, event) => void` | — | Callback |
|
|
429
|
+
|
|
430
|
+
### Example
|
|
431
|
+
```tsx
|
|
432
|
+
<Pagination pageMax={10} defaultPageCurrent={3} />
|
|
433
|
+
<Pagination pageMax={10} defaultPageCurrent={3} type="SIMPLE" />
|
|
434
|
+
<Pagination pageMax={5} defaultPageCurrent={3} type="DOTS" />
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## Popover / Tooltip / Legend / PopoverMenu
|
|
440
|
+
|
|
441
|
+
Overlay elements for additional information.
|
|
442
|
+
|
|
443
|
+
### Popover Props
|
|
444
|
+
| Prop | Type | Description |
|
|
445
|
+
|------|------|-------------|
|
|
446
|
+
| `content` | `ReactNode` | Popover content |
|
|
447
|
+
| `position` | `Placement` | Position (e.g. `"right"`, `"top"`) |
|
|
448
|
+
| `isOpen` | `boolean` | Controlled |
|
|
449
|
+
| `onChange` | `(isOpen: boolean) => void` | Callback |
|
|
450
|
+
| `openOnHover` | `boolean` | Open on hover |
|
|
451
|
+
| `openByDefault` | `boolean` | Initially open |
|
|
452
|
+
| `onTriggerClick` | `(event) => void` | Custom trigger handler |
|
|
453
|
+
|
|
454
|
+
### Examples
|
|
455
|
+
```tsx
|
|
456
|
+
<Popover content="Content">Click me</Popover>
|
|
457
|
+
<Popover position="right" content="Right">Click</Popover>
|
|
458
|
+
<Popover openOnHover content="Hover info">Hover</Popover>
|
|
459
|
+
<Tooltip content="Tooltip text">Element</Tooltip>
|
|
460
|
+
|
|
461
|
+
<PopoverMenu items={[
|
|
462
|
+
{ text: 'Download', icon: <IconDownload />, onClick: () => {} },
|
|
463
|
+
{ text: 'Delete', icon: <IconDelete />, onClick: () => {} },
|
|
464
|
+
]}>
|
|
465
|
+
<IconMore size={24} />
|
|
466
|
+
</PopoverMenu>
|
|
467
|
+
|
|
468
|
+
<Legend items={[{ name: 'PN', text: 'Part Number' }]}>
|
|
469
|
+
<button className="fwe-btn fwe-btn-link fwe-dark"><IconInfo />Legend</button>
|
|
470
|
+
</Legend>
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Progress
|
|
476
|
+
|
|
477
|
+
Progress bar.
|
|
478
|
+
|
|
479
|
+
### Props
|
|
480
|
+
| Prop | Type | Default | Description |
|
|
481
|
+
|------|------|---------|-------------|
|
|
482
|
+
| `progress` | `number` | — | **Required.** Progress 0–100 |
|
|
483
|
+
| `background` | `"white" \| "background" \| "black"` | `"white"` | Background color |
|
|
484
|
+
| `error` | `boolean` | `false` | Error state (red) |
|
|
485
|
+
|
|
486
|
+
### Example
|
|
487
|
+
```tsx
|
|
488
|
+
<Progress progress={50} />
|
|
489
|
+
<Progress progress={50} error />
|
|
490
|
+
<Progress progress={75} background="background" />
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## SearchInput
|
|
496
|
+
|
|
497
|
+
Search field with autocomplete suggestions.
|
|
498
|
+
|
|
499
|
+
### SearchInput Props
|
|
500
|
+
| Prop | Type | Description |
|
|
501
|
+
|------|------|-------------|
|
|
502
|
+
| `label` | `string` | Placeholder text |
|
|
503
|
+
| `value` | `string` | Controlled |
|
|
504
|
+
| `defaultValue` | `string` | Initial value |
|
|
505
|
+
| `debounceTime` | `number` | Debounce in ms (default: 0) |
|
|
506
|
+
| `onChange` | `(value: string) => void` | Callback on input |
|
|
507
|
+
| `onSearch` | `(value: string) => void` | Callback on Enter/selection |
|
|
508
|
+
|
|
509
|
+
### Example
|
|
510
|
+
```tsx
|
|
511
|
+
const [query, setQuery] = useState('');
|
|
512
|
+
const [results, setResults] = useState([]);
|
|
513
|
+
|
|
514
|
+
function handleChange(value) {
|
|
515
|
+
setQuery(value);
|
|
516
|
+
setResults(allItems.filter(s => s.toLowerCase().includes(value.toLowerCase())));
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
<SearchInput label="Search" onChange={handleChange} onSearch={console.log}>
|
|
520
|
+
{results.map(item => (
|
|
521
|
+
<SearchInputOption key={item} value={item}>
|
|
522
|
+
<SearchResult label={item} query={query} />
|
|
523
|
+
</SearchInputOption>
|
|
524
|
+
))}
|
|
525
|
+
</SearchInput>
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## Snackbar
|
|
531
|
+
|
|
532
|
+
Toast notifications.
|
|
533
|
+
|
|
534
|
+
### Setup
|
|
535
|
+
Wrap with `SnackbarProvider` at the root:
|
|
536
|
+
```tsx
|
|
537
|
+
<SnackbarProvider>
|
|
538
|
+
<App />
|
|
539
|
+
</SnackbarProvider>
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Usage with hook
|
|
543
|
+
```tsx
|
|
544
|
+
const showSnackbar = useSnackbar();
|
|
545
|
+
showSnackbar({ text: 'Successfully saved!' });
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### Usage without hook
|
|
549
|
+
```tsx
|
|
550
|
+
import { addSnackbar } from '@festo-ui/react';
|
|
551
|
+
addSnackbar({ text: 'Saved!' });
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## Stepper (Vertical & Horizontal)
|
|
557
|
+
|
|
558
|
+
Step indicator for multi-step processes.
|
|
559
|
+
|
|
560
|
+
### Example — Vertical
|
|
561
|
+
```tsx
|
|
562
|
+
const [stepIndex, setStepIndex] = useState(0);
|
|
563
|
+
|
|
564
|
+
<StepperVertical stepIndex={stepIndex} onChange={setStepIndex}>
|
|
565
|
+
<StepVertical title="Step 1">
|
|
566
|
+
<p>Content</p>
|
|
567
|
+
<button className="fwe-btn" onClick={() => setStepIndex(stepIndex + 1)}>Next</button>
|
|
568
|
+
</StepVertical>
|
|
569
|
+
<StepVertical title="Step 2">
|
|
570
|
+
<p>Content</p>
|
|
571
|
+
</StepVertical>
|
|
572
|
+
</StepperVertical>
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Example — Horizontal
|
|
576
|
+
```tsx
|
|
577
|
+
<StepperHorizontal stepIndex={stepIndex} onChange={setStepIndex}>
|
|
578
|
+
<StepHorizontal title="Step 1">Content</StepHorizontal>
|
|
579
|
+
<StepHorizontal title="Step 2">Content</StepHorizontal>
|
|
580
|
+
</StepperHorizontal>
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## Tabs
|
|
586
|
+
|
|
587
|
+
Tab navigation with content areas.
|
|
588
|
+
|
|
589
|
+
### Props
|
|
590
|
+
| Prop | Type | Default | Description |
|
|
591
|
+
|------|------|---------|-------------|
|
|
592
|
+
| `config` | `TabsConfiguration` | — | Configuration (tabBar, tabItems) |
|
|
593
|
+
| `viewType` | `"responsive" \| "legacy"` | `"responsive"` | View type |
|
|
594
|
+
| `showDivider` | `boolean` | `false` | Divider line under tabs |
|
|
595
|
+
| `onChange` | `({ previous, current }) => void` | — | Callback on tab change |
|
|
596
|
+
|
|
597
|
+
### TabPane Props
|
|
598
|
+
| Prop | Type | Description |
|
|
599
|
+
|------|------|-------------|
|
|
600
|
+
| `name` | `string` | **Required.** Tab name/label |
|
|
601
|
+
| `icon` | `ReactNode` | Optional: icon in tab |
|
|
602
|
+
|
|
603
|
+
### Example
|
|
604
|
+
```tsx
|
|
605
|
+
<Tabs>
|
|
606
|
+
<TabPane name="Tab 1">Content 1</TabPane>
|
|
607
|
+
<TabPane name="Tab 2">Content 2</TabPane>
|
|
608
|
+
<TabPane name="Tab 3">Content 3</TabPane>
|
|
609
|
+
</Tabs>
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### Configuration example
|
|
613
|
+
```tsx
|
|
614
|
+
<Tabs
|
|
615
|
+
viewType="legacy"
|
|
616
|
+
config={{
|
|
617
|
+
tabBar: { fullWidth: true },
|
|
618
|
+
tabItems: { appearance: 'fill' }, // 'equal' | 'fill'
|
|
619
|
+
}}
|
|
620
|
+
>
|
|
621
|
+
<TabPane name="Tab 1">Content</TabPane>
|
|
622
|
+
</Tabs>
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## TableHeaderCell
|
|
628
|
+
|
|
629
|
+
Sortable table header cell.
|
|
630
|
+
|
|
631
|
+
### Props
|
|
632
|
+
| Prop | Type | Description |
|
|
633
|
+
|------|------|-------------|
|
|
634
|
+
| `active` | `boolean` | Whether this column is actively sorted |
|
|
635
|
+
| `ascending` | `boolean` | **Required.** Sort direction |
|
|
636
|
+
|
|
637
|
+
### Example
|
|
638
|
+
```tsx
|
|
639
|
+
const [orderBy, setOrderBy] = useState('name');
|
|
640
|
+
const [ascending, setAscending] = useState(true);
|
|
641
|
+
|
|
642
|
+
<table className="fwe-table">
|
|
643
|
+
<thead>
|
|
644
|
+
<tr>
|
|
645
|
+
<TableHeaderCell
|
|
646
|
+
ascending={ascending}
|
|
647
|
+
active={orderBy === 'name'}
|
|
648
|
+
onClick={() => {
|
|
649
|
+
setAscending(orderBy === 'name' ? !ascending : true);
|
|
650
|
+
setOrderBy('name');
|
|
651
|
+
}}
|
|
652
|
+
>
|
|
653
|
+
Name
|
|
654
|
+
</TableHeaderCell>
|
|
655
|
+
</tr>
|
|
656
|
+
</thead>
|
|
657
|
+
<tbody>
|
|
658
|
+
<tr className="fwe-tr-md"><td>Entry</td></tr>
|
|
659
|
+
</tbody>
|
|
660
|
+
</table>
|
|
661
|
+
```
|
package/llm-doc/forms.md
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# Form Components
|
|
2
|
+
|
|
3
|
+
## TextInput
|
|
4
|
+
|
|
5
|
+
Text field with label, hint, error display, and icon.
|
|
6
|
+
|
|
7
|
+
### Props
|
|
8
|
+
| Prop | Type | Description |
|
|
9
|
+
|------|------|-------------|
|
|
10
|
+
| `label` | `string` | Label text |
|
|
11
|
+
| `value` | `string` | Controlled |
|
|
12
|
+
| `defaultValue` | `string` | Initial value |
|
|
13
|
+
| `onChange` | `(value: string, event) => void` | Callback |
|
|
14
|
+
| `hint` | `string` | Hint text |
|
|
15
|
+
| `error` | `string` | Error text |
|
|
16
|
+
| `icon` | `ReactNode` | Icon on the right |
|
|
17
|
+
| `readonly` | `boolean` | Read-only |
|
|
18
|
+
|
|
19
|
+
### Example
|
|
20
|
+
```tsx
|
|
21
|
+
<TextInput label="Username" defaultValue="max" hint="Hint" placeholder="Enter..." />
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Controlled
|
|
25
|
+
```tsx
|
|
26
|
+
const [value, setValue] = useState('');
|
|
27
|
+
<TextInput label="Name" value={value} onChange={setValue} />
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Validation
|
|
31
|
+
```tsx
|
|
32
|
+
<TextInput label="Name" defaultValue="" minLength={5} required />
|
|
33
|
+
<TextInput label="Error" aria-invalid error="Required field" icon={<IconClose className="fwe-color-red" />} />
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## TextArea
|
|
39
|
+
|
|
40
|
+
Multi-line text field.
|
|
41
|
+
|
|
42
|
+
### Props
|
|
43
|
+
| Prop | Type | Description |
|
|
44
|
+
|------|------|-------------|
|
|
45
|
+
| `label` | `string` | **Required.** Label |
|
|
46
|
+
| `value` | `string` | Controlled |
|
|
47
|
+
| `defaultValue` | `string` | Initial value |
|
|
48
|
+
| `onChange` | `(value: string, event) => void` | Callback |
|
|
49
|
+
| `hint` | `string` | Hint text |
|
|
50
|
+
| `error` | `string` | Error text |
|
|
51
|
+
| `rows` | `number` | Rows (0 = auto-grow) |
|
|
52
|
+
| `maxLength` | `number` | Max character count |
|
|
53
|
+
|
|
54
|
+
### Example
|
|
55
|
+
```tsx
|
|
56
|
+
<TextArea label="Description" rows={3} hint="Hint" maxLength={200} />
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Select
|
|
62
|
+
|
|
63
|
+
Dropdown selection.
|
|
64
|
+
|
|
65
|
+
### Props
|
|
66
|
+
| Prop | Type | Description |
|
|
67
|
+
|------|------|-------------|
|
|
68
|
+
| `options` | `SelectOption<T>[]` | **Required.** Array with `{ data: T, label: string \| ReactNode }` |
|
|
69
|
+
| `value` | `T` | Controlled |
|
|
70
|
+
| `defaultValue` | `T` | Initial value |
|
|
71
|
+
| `onChange` | `(value: T) => void` | Callback |
|
|
72
|
+
| `label` | `string` | Label |
|
|
73
|
+
| `name` | `string` | FormData name |
|
|
74
|
+
| `hint` | `string` | Hint text |
|
|
75
|
+
| `error` | `string` | Error text |
|
|
76
|
+
| `placeholder` | `string` | Placeholder |
|
|
77
|
+
| `disabled` | `boolean` | Disabled |
|
|
78
|
+
| `required` | `boolean` | Required |
|
|
79
|
+
|
|
80
|
+
### Example
|
|
81
|
+
```tsx
|
|
82
|
+
const options = [
|
|
83
|
+
{ data: 1, label: 'Option A' },
|
|
84
|
+
{ data: 2, label: 'Option B' },
|
|
85
|
+
{ data: 3, label: 'Option C' },
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
<Select label="Selection" options={options} defaultValue={1} />
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Controlled
|
|
92
|
+
```tsx
|
|
93
|
+
const [value, setValue] = useState(1);
|
|
94
|
+
<Select label="Selection" options={options} value={value} onChange={setValue} />
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## ComboBox
|
|
100
|
+
|
|
101
|
+
Searchable dropdown (Select with text input).
|
|
102
|
+
|
|
103
|
+
### Props
|
|
104
|
+
Same as `Select`, plus:
|
|
105
|
+
| Prop | Type | Description |
|
|
106
|
+
|------|------|-------------|
|
|
107
|
+
| `onInputChange` | `(value: string) => void` | Callback on text input |
|
|
108
|
+
| `emptyMessage` | `string` | Text when no results |
|
|
109
|
+
|
|
110
|
+
### Example
|
|
111
|
+
```tsx
|
|
112
|
+
<ComboBox label="Search" options={options} defaultValue={2} />
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## MultiSelect
|
|
118
|
+
|
|
119
|
+
Multi-selection dropdown.
|
|
120
|
+
|
|
121
|
+
### Props
|
|
122
|
+
| Prop | Type | Description |
|
|
123
|
+
|------|------|-------------|
|
|
124
|
+
| `options` | `SelectOption<T>[]` | Options |
|
|
125
|
+
| `value` | `T[]` | Controlled: selected values |
|
|
126
|
+
| `defaultValue` | `T[]` | Initial values |
|
|
127
|
+
| `onChange` | `(value: T[]) => void` | Callback |
|
|
128
|
+
| `label` | `string` | Label |
|
|
129
|
+
| `name` | `string` | FormData name |
|
|
130
|
+
| `hint` | `string` | Hint text |
|
|
131
|
+
| `error` | `string` | Error text |
|
|
132
|
+
| `placeholder` | `string` | Placeholder |
|
|
133
|
+
| `disabled` | `boolean` | Disabled |
|
|
134
|
+
|
|
135
|
+
### Example
|
|
136
|
+
```tsx
|
|
137
|
+
<MultiSelect
|
|
138
|
+
label="Categories"
|
|
139
|
+
options={options}
|
|
140
|
+
defaultValue={[1, 3]}
|
|
141
|
+
placeholder="Select..."
|
|
142
|
+
/>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Checkbox
|
|
148
|
+
|
|
149
|
+
### Props
|
|
150
|
+
| Prop | Type | Default | Description |
|
|
151
|
+
|------|------|---------|-------------|
|
|
152
|
+
| `checked` | `boolean` | — | Controlled |
|
|
153
|
+
| `defaultChecked` | `boolean` | — | Initial value |
|
|
154
|
+
| `onChange` | `(value: boolean, event) => void` | — | Callback |
|
|
155
|
+
| `large` | `boolean` | `false` | Large |
|
|
156
|
+
| `valid` | `boolean` | `true` | Validation status |
|
|
157
|
+
| `labelPosition` | `"after" \| "before"` | `"after"` | Label position |
|
|
158
|
+
| `indeterminate` | `boolean` | `false` | Indeterminate state |
|
|
159
|
+
|
|
160
|
+
### Example
|
|
161
|
+
```tsx
|
|
162
|
+
<Checkbox id="terms" name="acceptTerms">Accept terms</Checkbox>
|
|
163
|
+
<Checkbox id="large" large>Large checkbox</Checkbox>
|
|
164
|
+
<Checkbox valid={false}>Invalid</Checkbox>
|
|
165
|
+
<Checkbox indeterminate>Partial</Checkbox>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## RadioGroup / RadioButton
|
|
171
|
+
|
|
172
|
+
### RadioGroup Props
|
|
173
|
+
| Prop | Type | Default | Description |
|
|
174
|
+
|------|------|---------|-------------|
|
|
175
|
+
| `value` | `string` | — | Controlled |
|
|
176
|
+
| `defaultValue` | `string` | — | Initial value |
|
|
177
|
+
| `onValueChange` | `(value: string) => void` | — | Callback |
|
|
178
|
+
| `name` | `string` | — | FormData name |
|
|
179
|
+
| `large` | `boolean` | `false` | Large |
|
|
180
|
+
| `labelPosition` | `"after" \| "before" \| "below"` | `"after"` | Label position |
|
|
181
|
+
| `disabled` | `boolean` | `false` | Disabled |
|
|
182
|
+
| `required` | `boolean` | `false` | Required |
|
|
183
|
+
|
|
184
|
+
### RadioButton Props
|
|
185
|
+
| Prop | Type | Description |
|
|
186
|
+
|------|------|-------------|
|
|
187
|
+
| `value` | `string` | **Required.** Value |
|
|
188
|
+
| `disabled` | `boolean` | Individually disabled |
|
|
189
|
+
|
|
190
|
+
### Example
|
|
191
|
+
```tsx
|
|
192
|
+
<RadioGroup name="color" defaultValue="blue">
|
|
193
|
+
<RadioButton value="red">Red</RadioButton>
|
|
194
|
+
<RadioButton value="blue">Blue</RadioButton>
|
|
195
|
+
<RadioButton value="green">Green</RadioButton>
|
|
196
|
+
</RadioGroup>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Controlled
|
|
200
|
+
```tsx
|
|
201
|
+
const [value, setValue] = useState('blue');
|
|
202
|
+
<RadioGroup value={value} onValueChange={setValue}>
|
|
203
|
+
<RadioButton value="red">Red</RadioButton>
|
|
204
|
+
<RadioButton value="blue">Blue</RadioButton>
|
|
205
|
+
</RadioGroup>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Segment / SegmentControl
|
|
211
|
+
|
|
212
|
+
Segmented control (toggle button group).
|
|
213
|
+
|
|
214
|
+
### Segment Props
|
|
215
|
+
| Prop | Type | Description |
|
|
216
|
+
|------|------|-------------|
|
|
217
|
+
| `legend` | `string` | **Required.** Description |
|
|
218
|
+
| `config` | `SegmentConfiguration` | `{ outline?: boolean, iconOnly?: boolean }` |
|
|
219
|
+
| `value` | `any` | Controlled |
|
|
220
|
+
| `onChange` | `(value, event) => void` | Callback |
|
|
221
|
+
|
|
222
|
+
### SegmentControl Props
|
|
223
|
+
| Prop | Type | Description |
|
|
224
|
+
|------|------|-------------|
|
|
225
|
+
| `label` | `string` | **Required.** Label |
|
|
226
|
+
| `value` | `any` | **Required.** Value |
|
|
227
|
+
| `icon` | `ReactNode` | Icon |
|
|
228
|
+
| `checked` | `boolean` | Selected |
|
|
229
|
+
| `iconOnly` | `boolean` | Show icon only |
|
|
230
|
+
|
|
231
|
+
### Example
|
|
232
|
+
```tsx
|
|
233
|
+
<Segment legend="Choose view">
|
|
234
|
+
<SegmentControl name="view" label="List" value="list" />
|
|
235
|
+
<SegmentControl name="view" label="Tiles" value="grid" />
|
|
236
|
+
<SegmentControl name="view" label="Table" value="table" />
|
|
237
|
+
</Segment>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Switch
|
|
243
|
+
|
|
244
|
+
Toggle switch.
|
|
245
|
+
|
|
246
|
+
### Props
|
|
247
|
+
| Prop | Type | Default | Description |
|
|
248
|
+
|------|------|---------|-------------|
|
|
249
|
+
| `title` | `string` | — | **Required.** Label (also for accessibility) |
|
|
250
|
+
| `value` | `boolean` | — | Controlled |
|
|
251
|
+
| `defaultValue` | `boolean` | — | Initial value |
|
|
252
|
+
| `onChange` | `(value: boolean, event) => void` | — | Callback |
|
|
253
|
+
| `labelPosition` | `"after" \| "before" \| "below"` | `"after"` | Label position |
|
|
254
|
+
| `large` | `boolean` | `false` | Large |
|
|
255
|
+
|
|
256
|
+
### Example
|
|
257
|
+
```tsx
|
|
258
|
+
<Switch title="Notifications" defaultValue={true} />
|
|
259
|
+
<Switch title="Large" large labelPosition="before" />
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Slider
|
|
265
|
+
|
|
266
|
+
Range slider.
|
|
267
|
+
|
|
268
|
+
### Props
|
|
269
|
+
| Prop | Type | Default | Description |
|
|
270
|
+
|------|------|---------|-------------|
|
|
271
|
+
| `min` | `number` | — | **Required.** Minimum |
|
|
272
|
+
| `max` | `number` | — | **Required.** Maximum |
|
|
273
|
+
| `value` | `number` | — | **Required.** Value |
|
|
274
|
+
| `step` | `number` | — | Step size |
|
|
275
|
+
| `onChange` | `(value: number, event) => void` | — | Callback on change |
|
|
276
|
+
| `onChangeCommitted` | `(value: number) => void` | — | Callback on release |
|
|
277
|
+
| `label` | `string` | — | Label |
|
|
278
|
+
| `showValue` | `boolean` | `true` | Show value |
|
|
279
|
+
|
|
280
|
+
### Example
|
|
281
|
+
```tsx
|
|
282
|
+
const [value, setValue] = useState(50);
|
|
283
|
+
<Slider min={0} max={100} value={value} onChange={setValue} label="Volume" />
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## TimePicker
|
|
289
|
+
|
|
290
|
+
Time selection.
|
|
291
|
+
|
|
292
|
+
### Props
|
|
293
|
+
| Prop | Type | Description |
|
|
294
|
+
|------|------|-------------|
|
|
295
|
+
| `value` | `Date` | Controlled |
|
|
296
|
+
| `defaultValue` | `Date` | Initial value |
|
|
297
|
+
| `name` | `string` | FormData name |
|
|
298
|
+
| `onChange` | `(date: Date) => void` | Callback |
|
|
299
|
+
| `options` | `TimePickerOptions` | Configuration |
|
|
300
|
+
| `formatDate` | `(date: Date) => string` | Formatting |
|
|
301
|
+
| `disabled` | `boolean` | Disabled |
|
|
302
|
+
| `required` | `boolean` | Required |
|
|
303
|
+
| `error` | `string` | Error text |
|
|
304
|
+
| `hint` | `string` | Hint text |
|
|
305
|
+
|
|
306
|
+
### TimePickerOptions
|
|
307
|
+
```ts
|
|
308
|
+
{
|
|
309
|
+
timeFormat?: '12' | '24',
|
|
310
|
+
showSeconds?: boolean,
|
|
311
|
+
minuteStepSize?: number,
|
|
312
|
+
range?: { minValue: Date, maxValue: Date },
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Example
|
|
317
|
+
```tsx
|
|
318
|
+
<TimePicker defaultValue={new Date()} style={{ width: 330 }}>
|
|
319
|
+
Time
|
|
320
|
+
</TimePicker>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### 12h format with seconds
|
|
324
|
+
```tsx
|
|
325
|
+
<TimePicker
|
|
326
|
+
value={date}
|
|
327
|
+
onChange={setDate}
|
|
328
|
+
options={{ timeFormat: '12', showSeconds: true }}
|
|
329
|
+
formatDate={Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: 'numeric', second: 'numeric' }).format}
|
|
330
|
+
>
|
|
331
|
+
Time AM/PM
|
|
332
|
+
</TimePicker>
|
|
333
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Installation & Setup
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @festo-ui/react
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Including CSS
|
|
10
|
+
|
|
11
|
+
The library requires its CSS stylesheet. Import it once at the root of your application:
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import '@festo-ui/react/index.css';
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Imports
|
|
18
|
+
|
|
19
|
+
All components are exported as named exports from the main package:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { Button, TextInput, Select, Modal, Accordion } from '@festo-ui/react';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Complete Export List
|
|
26
|
+
|
|
27
|
+
**Components:**
|
|
28
|
+
- `Accordion`, `AccordionHeader`, `AccordionItem`, `AccordionItemBody`, `AccordionItemHeader`
|
|
29
|
+
- `BottomSheet`
|
|
30
|
+
- `Breadcrumb`
|
|
31
|
+
- `Button`
|
|
32
|
+
- `Card`, `CardBody`, `CardHeader`, `CardNotification`
|
|
33
|
+
- `Chip`, `ChipContainer`, `ChipType`
|
|
34
|
+
- `LoadingIndicator`
|
|
35
|
+
- `MobileFlyout`, `MobileFlyoutItem`, `MobileFlyoutPage`
|
|
36
|
+
- `AlertModal`, `ConfirmModal`, `CustomModal`, `Prompt`
|
|
37
|
+
- `ImageGallery`, `ImageGalleryContent`, `ImageGallerySwiper`, `ImageGalleryThumbsSwiper`
|
|
38
|
+
- `Pagination`
|
|
39
|
+
- `Popover`, `PopoverMenu`, `PopoverMenuItem`, `PopoverMenuContext`, `Tooltip`, `Legend`
|
|
40
|
+
- `Progress`
|
|
41
|
+
- `SearchInput`, `SearchInputOption`, `SearchResult`
|
|
42
|
+
- `Snackbar`, `SnackbarProvider`, `addSnackbar`, `useSnackbar`
|
|
43
|
+
- `StepperVertical`, `StepVertical`, `StepperHorizontal`, `StepHorizontal`
|
|
44
|
+
- `Tabs`, `TabPane`
|
|
45
|
+
- `TableHeaderCell`
|
|
46
|
+
|
|
47
|
+
**Forms:**
|
|
48
|
+
- `Checkbox`
|
|
49
|
+
- `ComboBox`
|
|
50
|
+
- `MultiSelect`
|
|
51
|
+
- `RadioGroup`, `RadioButton`
|
|
52
|
+
- `Segment`, `SegmentControl`
|
|
53
|
+
- `Select`
|
|
54
|
+
- `Slider`
|
|
55
|
+
- `Switch`
|
|
56
|
+
- `TextArea`
|
|
57
|
+
- `TextInput`
|
|
58
|
+
- `TimePicker`
|
|
59
|
+
|
|
60
|
+
**Types (TypeScript):**
|
|
61
|
+
All components export their props types, e.g. `ButtonProps`, `TextInputProps`, `SelectProps`, etc.
|
|
62
|
+
|
|
63
|
+
## CSS Utility Classes
|
|
64
|
+
|
|
65
|
+
The library uses CSS classes with the prefix `fwe-`. Commonly used:
|
|
66
|
+
|
|
67
|
+
- Layout: `fwe-grid`, `fwe-col-{n}`, `fwe-container`, `fwe-d-flex`
|
|
68
|
+
- Spacing: `fwe-m-{size}`, `fwe-p-{size}`, `fwe-mt-{size}`, `fwe-mb-{size}`, `fwe-mr-{size}`, `fwe-ml-{size}`, `fwe-my-{size}`, `fwe-mx-{size}`
|
|
69
|
+
- Sizes: `xxxs`, `xxs`, `xs`, `s`, `m`, `l`, `xl`, `xxl`
|
|
70
|
+
- Buttons: `fwe-btn`, `fwe-btn-hero`, `fwe-btn-icon`, `fwe-btn-link`, `fwe-btn-lg`, `fwe-btn-block`
|
|
71
|
+
- Colors: `fwe-bg-white`, `fwe-bg-background`, `fwe-bg-black`, `fwe-bg-gray-300`, `fwe-color-red`
|
|
72
|
+
- Flex: `fwe-align-items-center`, `fwe-mr-auto`, `fwe-ml-auto`
|
|
73
|
+
- Table: `fwe-table`, `fwe-tr-sm`, `fwe-tr-md`, `fwe-tr-lg`
|
|
74
|
+
- Text: `fwe-font-size-small`
|
|
75
|
+
- Other: `fwe-dark`, `fwe-b`
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Patterns & Best Practices
|
|
2
|
+
|
|
3
|
+
## Controlled vs. Uncontrolled
|
|
4
|
+
|
|
5
|
+
All form components support both modes:
|
|
6
|
+
|
|
7
|
+
### Uncontrolled (simpler)
|
|
8
|
+
State is managed internally. Use `defaultValue` / `defaultChecked`:
|
|
9
|
+
```tsx
|
|
10
|
+
<TextInput label="Name" defaultValue="Max" />
|
|
11
|
+
<Checkbox defaultChecked>Option</Checkbox>
|
|
12
|
+
<Select defaultValue={1} options={options} />
|
|
13
|
+
<RadioGroup defaultValue="a"><RadioButton value="a">A</RadioButton></RadioGroup>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Controlled (full control)
|
|
17
|
+
State is managed externally. Use `value` + `onChange`:
|
|
18
|
+
```tsx
|
|
19
|
+
const [value, setValue] = useState('');
|
|
20
|
+
<TextInput label="Name" value={value} onChange={setValue} />
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Note:** The `onChange` signature varies by component:
|
|
24
|
+
- `TextInput`, `TextArea`: `(value: string, event) => void`
|
|
25
|
+
- `Select`, `ComboBox`: `(value: T) => void`
|
|
26
|
+
- `MultiSelect`: `(value: T[]) => void`
|
|
27
|
+
- `Checkbox`: `(value: boolean, event) => void`
|
|
28
|
+
- `Switch`: `(value: boolean, event) => void`
|
|
29
|
+
- `RadioGroup`: `onValueChange: (value: string) => void`
|
|
30
|
+
- `Slider`: `(value: number, event) => void`
|
|
31
|
+
- `TimePicker`: `(date: Date) => void`
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## FormData Integration
|
|
36
|
+
|
|
37
|
+
All form components support native FormData via the `name` prop:
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
function handleSubmit(event) {
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
const formData = new FormData(event.currentTarget);
|
|
43
|
+
const value = formData.get('fieldName');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
<form onSubmit={handleSubmit}>
|
|
47
|
+
<TextInput name="username" label="Username" />
|
|
48
|
+
<Select name="role" label="Role" options={roleOptions} />
|
|
49
|
+
<Checkbox name="acceptTerms">Accept terms</Checkbox>
|
|
50
|
+
<Switch name="notifications" title="Notifications" />
|
|
51
|
+
<RadioGroup name="color">
|
|
52
|
+
<RadioButton value="red">Red</RadioButton>
|
|
53
|
+
<RadioButton value="blue">Blue</RadioButton>
|
|
54
|
+
</RadioGroup>
|
|
55
|
+
<MultiSelect name="categories" label="Categories" options={catOptions} />
|
|
56
|
+
<TimePicker name="meetingTime">Time</TimePicker>
|
|
57
|
+
<Slider name="volume" min={0} max={100} value={50} label="Volume" />
|
|
58
|
+
<button className="fwe-btn fwe-btn-hero" type="submit">Submit</button>
|
|
59
|
+
</form>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Validation
|
|
65
|
+
|
|
66
|
+
### Error display
|
|
67
|
+
Most form components have an `error` prop:
|
|
68
|
+
```tsx
|
|
69
|
+
<TextInput label="Email" error="Invalid email address" />
|
|
70
|
+
<Select label="Selection" error="Please select" options={options} />
|
|
71
|
+
<TextArea label="Text" error="Required field" />
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### HTML5 validation
|
|
75
|
+
```tsx
|
|
76
|
+
<TextInput label="Name" required minLength={3} />
|
|
77
|
+
<TextArea label="Text" required maxLength={500} />
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Checkbox validation
|
|
81
|
+
```tsx
|
|
82
|
+
<Checkbox valid={false}>Invalid option</Checkbox>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Hint Texts
|
|
88
|
+
|
|
89
|
+
Many components support `hint` for help text:
|
|
90
|
+
```tsx
|
|
91
|
+
<TextInput label="Email" hint="e.g. max@example.com" />
|
|
92
|
+
<Select label="Role" hint="Choose your primary role" options={options} />
|
|
93
|
+
<TimePicker hint="Please select a time">Time</TimePicker>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## CSS Classes for Layout
|
|
99
|
+
|
|
100
|
+
### Grid System
|
|
101
|
+
```tsx
|
|
102
|
+
<div className="fwe-grid">
|
|
103
|
+
<div className="fwe-col-4">1/3 width</div>
|
|
104
|
+
<div className="fwe-col-8">2/3 width</div>
|
|
105
|
+
</div>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Container
|
|
109
|
+
```tsx
|
|
110
|
+
<div className="fwe-container">Centered container</div>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Buttons with CSS classes (alternative to Button component)
|
|
114
|
+
```tsx
|
|
115
|
+
<button className="fwe-btn">Default</button>
|
|
116
|
+
<button className="fwe-btn fwe-btn-hero">Primary</button>
|
|
117
|
+
<button className="fwe-btn fwe-btn-lg">Large</button>
|
|
118
|
+
<button className="fwe-btn fwe-btn-block">Full width</button>
|
|
119
|
+
<button className="fwe-btn fwe-btn-icon"><Icon /></button>
|
|
120
|
+
<button className="fwe-btn fwe-btn-link fwe-dark">Link</button>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Spacing
|
|
124
|
+
Prefix `fwe-m-` (margin) and `fwe-p-` (padding), suffixes:
|
|
125
|
+
- Direction: `t` (top), `b` (bottom), `l` (left), `r` (right), `x` (horizontal), `y` (vertical)
|
|
126
|
+
- Sizes: `xxxs`, `xxs`, `xs`, `s`, `m`, `l`, `xl`, `xxl`
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
<div className="fwe-mt-m fwe-mb-s fwe-px-l">Content with spacing</div>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Navbar Structure
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
<header className="fwe-fixed-header">
|
|
138
|
+
<nav className="fwe-navbar">
|
|
139
|
+
<div className="fwe-container">
|
|
140
|
+
<MobileFlyout className="fwe-mr-auto">...</MobileFlyout>
|
|
141
|
+
<div className="fwe-logo-container">
|
|
142
|
+
<div className="fwe-festo-logo" />
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</nav>
|
|
146
|
+
</header>
|
|
147
|
+
<div className="fwe-navbar-spacer" />
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Tables
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
<table className="fwe-table">
|
|
156
|
+
<thead>
|
|
157
|
+
<tr>
|
|
158
|
+
<TableHeaderCell ascending={ascending} active={orderBy === 'name'} onClick={...}>
|
|
159
|
+
Name
|
|
160
|
+
</TableHeaderCell>
|
|
161
|
+
</tr>
|
|
162
|
+
</thead>
|
|
163
|
+
<tbody>
|
|
164
|
+
<tr className="fwe-tr-md"><td>Content</td></tr>
|
|
165
|
+
<tr className="fwe-tr-lg"><td>Content</td></tr>
|
|
166
|
+
<tr className="fwe-tr-sm"><td>Content</td></tr>
|
|
167
|
+
</tbody>
|
|
168
|
+
</table>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Important Notes
|
|
174
|
+
|
|
175
|
+
1. **Import CSS:** Always include `import '@festo-ui/react/index.css'`
|
|
176
|
+
2. **Icons:** Icons are provided separately (e.g. `IconCoreRangeProduct`, `IconSettings`, `IconMore`, `IconDownload`, `IconDelete`, `IconInfo`, `IconWarning`, `IconClose`, `IconCheckSmall`, `IconWorksheet`, `IconMessage`, `IconGridView`, `IconListView`)
|
|
177
|
+
3. **Snackbar:** Requires `<SnackbarProvider>` as wrapper
|
|
178
|
+
4. **Modals:** All modals are controlled via `isOpen` + `onClose`
|
|
179
|
+
5. **All components** accept standard HTML attributes like `className`, `style`, `data-testid`, etc.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@festo-ui/react",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.1-dev.918",
|
|
4
4
|
"author": "Festo UI (styleguide@festo.com)",
|
|
5
5
|
"copyright": "Copyright (c) 2025 Festo SE & Co. KG. All rights reserved.",
|
|
6
6
|
"license": "apache-2.0",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"module": "./dist/index.js",
|
|
17
17
|
"types": "./dist/index.d.ts",
|
|
18
18
|
"files": [
|
|
19
|
-
"dist"
|
|
19
|
+
"dist",
|
|
20
|
+
"llm-doc"
|
|
20
21
|
],
|
|
21
22
|
"sideEffects": [
|
|
22
23
|
"**/*.css"
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@biomejs/biome": "2.3.2",
|
|
46
47
|
"@chromatic-com/storybook": "^5.0.0",
|
|
48
|
+
"@festo-ui/web-essentials": "*",
|
|
47
49
|
"@rsbuild/core": "~1.6.2",
|
|
48
50
|
"@rsbuild/plugin-react": "^1.4.1",
|
|
49
51
|
"@rsbuild/plugin-sass": "^1.4.0",
|
|
@@ -52,6 +54,7 @@
|
|
|
52
54
|
"@rstest/core": "^0.6.5",
|
|
53
55
|
"@rstest/coverage-istanbul": "^0.0.5",
|
|
54
56
|
"@storybook/addon-docs": "^10.2.3",
|
|
57
|
+
"@storybook/addon-mcp": "^0.6.0",
|
|
55
58
|
"@testing-library/dom": "^10.4.0",
|
|
56
59
|
"@testing-library/jest-dom": "^6.9.1",
|
|
57
60
|
"@testing-library/react": "^16.3.0",
|