@festo-ui/react 10.1.0 → 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/pagination/Pagination.d.ts +1 -5
- package/dist/components/pagination/Pagination.js +6 -12
- package/dist/components/popovers/popover/Popover.js +11 -4
- package/dist/components/search-input/SearchInput.css +12 -0
- package/dist/components/search-input/SearchInput.d.ts +18 -6
- package/dist/components/search-input/SearchInput.js +82 -58
- package/dist/components/search-input/SearchInputOption.d.ts +5 -0
- package/dist/components/search-input/SearchInputOption.js +18 -0
- package/dist/components/search-input/SearchResult.d.ts +8 -0
- package/dist/components/search-input/SearchResult.js +48 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -2
- 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
- package/dist/components/search-input/ClearButton.d.ts +0 -1
- package/dist/components/search-input/ClearButton.js +0 -11
- package/dist/components/search-input/SearchSuggestion.d.ts +0 -17
- package/dist/components/search-input/SearchSuggestion.js +0 -21
- package/dist/components/search-input/useSearchInput.d.ts +0 -13
- package/dist/components/search-input/useSearchInput.js +0 -85
|
@@ -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
|
+
```
|