@emporix/component-library 1.8.2 → 1.10.0
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/README.md +484 -24
- package/dist/components/autocomplete/AutoComplete.d.ts +40 -0
- package/dist/components/autocomplete/AutoComplete.d.ts.map +1 -0
- package/dist/components/autocomplete/index.d.ts +3 -0
- package/dist/components/autocomplete/index.d.ts.map +1 -0
- package/dist/components/buttons/PrimaryButton.d.ts +1 -1
- package/dist/components/buttons/PrimaryButton.d.ts.map +1 -1
- package/dist/components/buttons/SecondaryButton.d.ts +1 -1
- package/dist/components/buttons/SecondaryButton.d.ts.map +1 -1
- package/dist/components/checkbox/Checkbox.d.ts +50 -0
- package/dist/components/checkbox/Checkbox.d.ts.map +1 -0
- package/dist/components/checkbox/index.d.ts +3 -0
- package/dist/components/checkbox/index.d.ts.map +1 -0
- package/dist/components/datatable/DataTable.d.ts +6 -0
- package/dist/components/datatable/DataTable.d.ts.map +1 -0
- package/dist/components/datatable/index.d.ts +5 -0
- package/dist/components/datatable/index.d.ts.map +1 -0
- package/dist/components/datatable/types.d.ts +60 -0
- package/dist/components/datatable/types.d.ts.map +1 -0
- package/dist/components/datatable/useDataTablePagination.d.ts +17 -0
- package/dist/components/datatable/useDataTablePagination.d.ts.map +1 -0
- package/dist/components/dialog/Dialog.d.ts +34 -0
- package/dist/components/dialog/Dialog.d.ts.map +1 -0
- package/dist/components/dialog/index.d.ts +3 -0
- package/dist/components/dialog/index.d.ts.map +1 -0
- package/dist/components/dropdown/Dropdown.d.ts +19 -1
- package/dist/components/dropdown/Dropdown.d.ts.map +1 -1
- package/dist/components/menu/Menu.d.ts +27 -0
- package/dist/components/menu/Menu.d.ts.map +1 -0
- package/dist/components/menu/index.d.ts +3 -0
- package/dist/components/menu/index.d.ts.map +1 -0
- package/dist/components/message/Message.d.ts +31 -0
- package/dist/components/message/Message.d.ts.map +1 -0
- package/dist/components/radiobutton/RadioButton.d.ts +46 -0
- package/dist/components/radiobutton/RadioButton.d.ts.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +1566 -6019
- package/dist/index.umd.js +1 -553
- package/dist/style.css +1 -1
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -132,6 +132,237 @@ import { InputText } from '@emporix/component-library'
|
|
|
132
132
|
|
|
133
133
|
All standard HTML input attributes (placeholder, value, onChange, etc.) are supported.
|
|
134
134
|
|
|
135
|
+
### AutoComplete
|
|
136
|
+
|
|
137
|
+
Search input with async suggestions. API inspired by [PrimeReact AutoComplete](https://primereact.org/autocomplete/).
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
import { AutoComplete } from '@emporix/component-library'
|
|
141
|
+
;<AutoComplete
|
|
142
|
+
value={query}
|
|
143
|
+
suggestions={vendors}
|
|
144
|
+
field='name'
|
|
145
|
+
dropdown
|
|
146
|
+
completeMethod={({ query }) => searchVendors(query)}
|
|
147
|
+
onChange={e => setQuery(e.value)}
|
|
148
|
+
onSelect={e => setSelectedVendor(e.value)}
|
|
149
|
+
label='Vendor'
|
|
150
|
+
placeholder='Search vendor...'
|
|
151
|
+
/>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
| Prop | Type | Description |
|
|
155
|
+
| -------------- | -------- | ---------------------------------- |
|
|
156
|
+
| value | string | Controlled input text |
|
|
157
|
+
| suggestions | T[] | Suggestion list from parent |
|
|
158
|
+
| completeMethod | function | Called (debounced) when user types |
|
|
159
|
+
| onChange | function | Input text changes |
|
|
160
|
+
| onSelect | function | Suggestion selected |
|
|
161
|
+
| dropdown | boolean | Show dropdown trigger button |
|
|
162
|
+
| field | string | Suggestion label property |
|
|
163
|
+
|
|
164
|
+
### Checkbox
|
|
165
|
+
|
|
166
|
+
A checkbox built on a native `input type="checkbox"` with PrimeReact-compatible `onChange` events (`{ checked, originalEvent }`). API inspired by [PrimeReact Checkbox](https://primereact.org/checkbox/).
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { useState } from 'react'
|
|
170
|
+
import { Checkbox } from '@emporix/component-library'
|
|
171
|
+
|
|
172
|
+
// Controlled single checkbox
|
|
173
|
+
const [checked, setChecked] = useState(false)
|
|
174
|
+
<Checkbox checked={checked} onChange={e => setChecked(e.checked)} />
|
|
175
|
+
|
|
176
|
+
// With label (associate via inputId + htmlFor)
|
|
177
|
+
<Checkbox inputId="remember-me" checked={checked} onChange={e => setChecked(e.checked)} />
|
|
178
|
+
<label htmlFor="remember-me">Remember me</label>
|
|
179
|
+
|
|
180
|
+
// Role toggles (single selection)
|
|
181
|
+
const roles = ['viewer', 'editor', 'manager', 'administrator']
|
|
182
|
+
const [selectedRole, setSelectedRole] = useState<string | null>(null)
|
|
183
|
+
{roles.map(role => (
|
|
184
|
+
<div key={role}>
|
|
185
|
+
<Checkbox
|
|
186
|
+
inputId={role}
|
|
187
|
+
value={role}
|
|
188
|
+
checked={selectedRole === role}
|
|
189
|
+
onChange={e => setSelectedRole(e.checked ? role : null)}
|
|
190
|
+
/>
|
|
191
|
+
<label htmlFor={role}>{role}</label>
|
|
192
|
+
</div>
|
|
193
|
+
))}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Props
|
|
197
|
+
|
|
198
|
+
| Prop | Type | Default | Description |
|
|
199
|
+
| ----------- | -------------------------------- | ------- | ----------------------------------------------- |
|
|
200
|
+
| checked | boolean | false | Whether the checkbox is checked (controlled) |
|
|
201
|
+
| onChange | (e: CheckboxChangeEvent) => void | - | Fired when checked state changes |
|
|
202
|
+
| value | unknown | - | Value associated with the checkbox |
|
|
203
|
+
| inputId | string | - | id for the native input (for label association) |
|
|
204
|
+
| disabled | boolean | false | Whether the checkbox is disabled |
|
|
205
|
+
| invalid | boolean | false | Invalid state for validation styling |
|
|
206
|
+
| name | string | - | Name attribute for form submission |
|
|
207
|
+
| readOnly | boolean | false | Prevents user interaction |
|
|
208
|
+
| required | boolean | false | Marks the field as required |
|
|
209
|
+
| className | string | - | Additional CSS class for the root |
|
|
210
|
+
| data-testid | string | - | Test ID for root and input (suffix `-input`) |
|
|
211
|
+
|
|
212
|
+
Standard HTML input attributes (`aria-label`, `aria-labelledby`, `tabIndex`, etc.) are supported on the native input.
|
|
213
|
+
|
|
214
|
+
### Dialog
|
|
215
|
+
|
|
216
|
+
A modal dialog built on [PrimeReact Dialog](https://primereact.org/dialog/). Use for focused tasks that need a decision or confirmation. Wraps PrimeReact internally with scoped Emporix styles.
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
import { useState } from 'react'
|
|
220
|
+
import {
|
|
221
|
+
Dialog,
|
|
222
|
+
PrimaryButton,
|
|
223
|
+
SecondaryButton,
|
|
224
|
+
} from '@emporix/component-library'
|
|
225
|
+
|
|
226
|
+
function ConfirmDialogExample() {
|
|
227
|
+
const [visible, setVisible] = useState(false)
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<>
|
|
231
|
+
<PrimaryButton onClick={() => setVisible(true)}>
|
|
232
|
+
Open dialog
|
|
233
|
+
</PrimaryButton>
|
|
234
|
+
<Dialog
|
|
235
|
+
visible={visible}
|
|
236
|
+
onHide={() => setVisible(false)}
|
|
237
|
+
header='Confirm changes'
|
|
238
|
+
footer={
|
|
239
|
+
<>
|
|
240
|
+
<SecondaryButton onClick={() => setVisible(false)}>
|
|
241
|
+
Cancel
|
|
242
|
+
</SecondaryButton>
|
|
243
|
+
<PrimaryButton onClick={() => setVisible(false)}>
|
|
244
|
+
Save
|
|
245
|
+
</PrimaryButton>
|
|
246
|
+
</>
|
|
247
|
+
}
|
|
248
|
+
style={{ width: '100%', maxWidth: '480px' }}
|
|
249
|
+
>
|
|
250
|
+
<p>Your changes will be applied immediately.</p>
|
|
251
|
+
</Dialog>
|
|
252
|
+
</>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
#### Props
|
|
258
|
+
|
|
259
|
+
| Prop | Type | Default | Description |
|
|
260
|
+
| --------------- | ------------- | ------- | -------------------------------------------------------- |
|
|
261
|
+
| visible | boolean | - | Whether the dialog is visible |
|
|
262
|
+
| onHide | () => void | - | Called when the dialog closes (overlay, Escape, or icon) |
|
|
263
|
+
| header | ReactNode | - | Dialog title area |
|
|
264
|
+
| footer | ReactNode | - | Footer slot (e.g. action buttons) |
|
|
265
|
+
| children | ReactNode | - | Dialog body content |
|
|
266
|
+
| className | string | - | Additional CSS class on the dialog root |
|
|
267
|
+
| style | CSSProperties | - | Inline styles on the dialog root (e.g. width) |
|
|
268
|
+
| modal | boolean | true | Blocks interaction with the page behind the dialog |
|
|
269
|
+
| dismissableMask | boolean | modal | When true, clicking the mask closes the dialog |
|
|
270
|
+
| closable | boolean | true | Shows the header close icon |
|
|
271
|
+
| draggable | boolean | false | Allows dragging the dialog by its header |
|
|
272
|
+
| data-testid | string | - | Test ID on the dialog root |
|
|
273
|
+
|
|
274
|
+
Requires PrimeReact base CSS and a theme in the host app.
|
|
275
|
+
|
|
276
|
+
### DataTable
|
|
277
|
+
|
|
278
|
+
Encapsulated data table built on [PrimeReact DataTable](https://primereact.org/datatable/). Styles are scoped via CSS modules. Pass `columns`, optional controlled `pagination`, selection, `rowActions`, and an `emptyTemplate` for empty results.
|
|
279
|
+
|
|
280
|
+
Import `@emporix/component-library/styles` once in your app entry, plus PrimeReact base CSS, a theme, and `primeicons` (required for paginator/sort icons).
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
import { useMemo, useState } from 'react'
|
|
284
|
+
import { FilterMatchMode } from 'primereact/api'
|
|
285
|
+
import { DataTable } from '@emporix/component-library'
|
|
286
|
+
import type { DataTablePaginationState } from '@emporix/component-library'
|
|
287
|
+
import '@emporix/component-library/styles'
|
|
288
|
+
import 'primereact/resources/primereact.css'
|
|
289
|
+
import 'primereact/resources/themes/lara-light-cyan/theme.css'
|
|
290
|
+
import 'primeicons/primeicons.css'
|
|
291
|
+
|
|
292
|
+
const columns = [
|
|
293
|
+
{
|
|
294
|
+
columnKey: 'name',
|
|
295
|
+
field: 'name',
|
|
296
|
+
header: 'Name',
|
|
297
|
+
sortable: true,
|
|
298
|
+
filter: true,
|
|
299
|
+
filterMatchMode: FilterMatchMode.CONTAINS,
|
|
300
|
+
showFilterMenu: false,
|
|
301
|
+
showClearButton: false,
|
|
302
|
+
},
|
|
303
|
+
{ columnKey: 'email', field: 'email', header: 'Email', sortable: true },
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
function UsersTable({
|
|
307
|
+
users,
|
|
308
|
+
totalCount,
|
|
309
|
+
loading,
|
|
310
|
+
paginationParams,
|
|
311
|
+
onPage,
|
|
312
|
+
onSort,
|
|
313
|
+
onFilter,
|
|
314
|
+
}) {
|
|
315
|
+
const [selected, setSelected] = useState([])
|
|
316
|
+
|
|
317
|
+
const pagination = useMemo<DataTablePaginationState>(
|
|
318
|
+
() => ({
|
|
319
|
+
...paginationParams,
|
|
320
|
+
totalRecords: totalCount,
|
|
321
|
+
}),
|
|
322
|
+
[paginationParams, totalCount]
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
return (
|
|
326
|
+
<DataTable
|
|
327
|
+
value={users}
|
|
328
|
+
columns={columns}
|
|
329
|
+
dataKey='id'
|
|
330
|
+
loading={loading}
|
|
331
|
+
pagination={pagination}
|
|
332
|
+
onPage={onPage}
|
|
333
|
+
onSort={onSort}
|
|
334
|
+
onFilter={onFilter}
|
|
335
|
+
selectionMode='multiple'
|
|
336
|
+
selection={selected}
|
|
337
|
+
onSelectionChange={setSelected}
|
|
338
|
+
rowActions={user => <button type='button'>Edit</button>}
|
|
339
|
+
currentPageReportTemplate='{first} - {last} of {totalRecords}'
|
|
340
|
+
emptyTemplate={<span>No users found</span>}
|
|
341
|
+
/>
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### Props
|
|
347
|
+
|
|
348
|
+
| Prop | Type | Default | Description |
|
|
349
|
+
| ----------------------------- | --------------------------------- | ------- | ---------------------------------------------------------- |
|
|
350
|
+
| value | T[] | - | Row data array (required) |
|
|
351
|
+
| columns | ColumnProps[] | - | Column definitions; `rowActions` appends an actions column |
|
|
352
|
+
| dataKey | string | - | Unique row identifier field |
|
|
353
|
+
| loading | boolean | false | Shows loading overlay |
|
|
354
|
+
| pagination | DataTablePaginationState | - | Controlled pagination/sort/filter state |
|
|
355
|
+
| onPage / onSort / onFilter | function | - | Pagination, sort, and filter event handlers |
|
|
356
|
+
| selectionMode | `'single' \| 'multiple' \| null` | null | Row selection mode |
|
|
357
|
+
| selection / onSelectionChange | T \| T[number] \| null / function | - | Controlled selection state |
|
|
358
|
+
| rowActions | (item) => ReactNode | - | Renders an actions column per row |
|
|
359
|
+
| emptyTemplate | ReactNode \| () => ReactNode | - | Content when `value` is empty |
|
|
360
|
+
| lazy / totalRecords | boolean / number | - | Server-driven data with external total count |
|
|
361
|
+
| className / style | string / CSSProperties | - | Root table styling |
|
|
362
|
+
| data-testid | string | - | Test ID on the table root |
|
|
363
|
+
|
|
364
|
+
Omit `pagination` to use the built-in `useDataTablePagination` hook for local client-side paging.
|
|
365
|
+
|
|
135
366
|
### Dropdown
|
|
136
367
|
|
|
137
368
|
A select-style dropdown for choosing one or more items from a list. Supports optional filter/search, editable input, label, required indicator, and validation errors. Hover and focus styles match the InputText component. API inspired by [PrimeReact Dropdown](https://primereact.org/dropdown/).
|
|
@@ -179,33 +410,49 @@ const options = [
|
|
|
179
410
|
multiple
|
|
180
411
|
filter
|
|
181
412
|
/>
|
|
413
|
+
|
|
414
|
+
// Multi-select with removable chips (PrimeReact MultiSelect display="chip" parity)
|
|
415
|
+
<Dropdown
|
|
416
|
+
value={groupIds}
|
|
417
|
+
onChange={e => setGroupIds(Array.isArray(e.value) ? e.value : [])}
|
|
418
|
+
options={groupOptions}
|
|
419
|
+
multiple
|
|
420
|
+
display="chip"
|
|
421
|
+
filter
|
|
422
|
+
placeholder="Select groups"
|
|
423
|
+
label="User groups"
|
|
424
|
+
/>
|
|
182
425
|
```
|
|
183
426
|
|
|
184
427
|
#### Props
|
|
185
428
|
|
|
186
|
-
| Prop
|
|
187
|
-
|
|
|
188
|
-
| value
|
|
189
|
-
| onChange
|
|
190
|
-
| options
|
|
191
|
-
| optionLabel
|
|
192
|
-
| optionValue
|
|
193
|
-
| optionDisabled
|
|
194
|
-
| placeholder
|
|
195
|
-
| filter
|
|
196
|
-
| editable
|
|
197
|
-
| onEditEnd
|
|
198
|
-
| multiple
|
|
199
|
-
|
|
|
200
|
-
|
|
|
201
|
-
|
|
|
202
|
-
|
|
|
203
|
-
|
|
|
204
|
-
|
|
|
205
|
-
|
|
|
206
|
-
|
|
|
207
|
-
|
|
|
208
|
-
|
|
|
429
|
+
| Prop | Type | Default | Description |
|
|
430
|
+
| ------------------ | ------------------------- | ---------------------- | --------------------------------------------- |
|
|
431
|
+
| value | unknown | - | Selected value (array when `multiple`) |
|
|
432
|
+
| onChange | (e: ChangeEvent) => void | - | Callback when selection changes |
|
|
433
|
+
| options | T[] | - | Array of options |
|
|
434
|
+
| optionLabel | string | `"label"` | Property name for option label |
|
|
435
|
+
| optionValue | string | `"value"` | Property name for option value |
|
|
436
|
+
| optionDisabled | string | - | Property name for disabled flag on options |
|
|
437
|
+
| placeholder | string | `"Select"` | Placeholder when no value selected |
|
|
438
|
+
| filter | boolean | false | Enable search filter in the overlay |
|
|
439
|
+
| editable | boolean | false | Allow typing in the trigger to filter/select |
|
|
440
|
+
| onEditEnd | (e: EditEndEvent) => void | - | Fired when Enter is pressed in editable mode |
|
|
441
|
+
| multiple | boolean | false | Allow multiple selection |
|
|
442
|
+
| display | `'comma' \| 'chip'` | `'comma'` | Multi-select trigger display mode |
|
|
443
|
+
| maxSelectedLabels | number | - | Max chips before showing `selectedItemsLabel` |
|
|
444
|
+
| selectedItemsLabel | string | `'{0} items selected'` | Overflow label; `{0}` = count |
|
|
445
|
+
| removeIcon | ReactNode | - | Custom icon for chip remove buttons |
|
|
446
|
+
| label | ReactNode | - | Label text above the trigger |
|
|
447
|
+
| error | ReactNode | - | Error text displayed below the trigger |
|
|
448
|
+
| required | boolean | false | Shows asterisk on label |
|
|
449
|
+
| inputId | string | - | id for the trigger (for label association) |
|
|
450
|
+
| disabled | boolean | false | Whether the dropdown is disabled |
|
|
451
|
+
| valueTemplate | (option) => ReactNode | - | Custom render for selected value in trigger |
|
|
452
|
+
| itemTemplate | (option) => ReactNode | - | Custom render for each option in the list |
|
|
453
|
+
| className | string | - | Additional CSS class for the root |
|
|
454
|
+
| panelClassName | string | - | Additional CSS class for the overlay panel |
|
|
455
|
+
| data-testid | string | - | Test ID for root, trigger, panel, and options |
|
|
209
456
|
|
|
210
457
|
### SelectButton
|
|
211
458
|
|
|
@@ -252,6 +499,70 @@ const options = [
|
|
|
252
499
|
| className | string | - | Additional CSS class |
|
|
253
500
|
| data-testid | string | - | Test ID for root and option buttons |
|
|
254
501
|
|
|
502
|
+
### RadioButton
|
|
503
|
+
|
|
504
|
+
Single-choice selection using native `input[type="radio"]`. API inspired by [PrimeReact RadioButton](https://primereact.org/radiobutton/). Compose multiple `RadioButton` instances with the same `name` to form a group.
|
|
505
|
+
|
|
506
|
+
```tsx
|
|
507
|
+
import { useState } from 'react'
|
|
508
|
+
import { RadioButton } from '@emporix/component-library'
|
|
509
|
+
|
|
510
|
+
function ContentTypePicker() {
|
|
511
|
+
const [contentType, setContentType] = useState('standard')
|
|
512
|
+
|
|
513
|
+
return (
|
|
514
|
+
<div style={{ display: 'flex', gap: '1.5rem' }}>
|
|
515
|
+
<RadioButton
|
|
516
|
+
name='content-type'
|
|
517
|
+
inputId='content-standard'
|
|
518
|
+
value='standard'
|
|
519
|
+
label='Standard'
|
|
520
|
+
checked={contentType === 'standard'}
|
|
521
|
+
onChange={e => setContentType(String(e.value))}
|
|
522
|
+
/>
|
|
523
|
+
<RadioButton
|
|
524
|
+
name='content-type'
|
|
525
|
+
inputId='content-templates'
|
|
526
|
+
value='templates'
|
|
527
|
+
label='Templates'
|
|
528
|
+
checked={contentType === 'templates'}
|
|
529
|
+
onChange={e => setContentType(String(e.value))}
|
|
530
|
+
/>
|
|
531
|
+
<RadioButton
|
|
532
|
+
name='content-type'
|
|
533
|
+
inputId='content-service'
|
|
534
|
+
value='service'
|
|
535
|
+
label='Service'
|
|
536
|
+
checked={contentType === 'service'}
|
|
537
|
+
onChange={e => setContentType(String(e.value))}
|
|
538
|
+
/>
|
|
539
|
+
<RadioButton
|
|
540
|
+
name='content-type'
|
|
541
|
+
inputId='content-vendor'
|
|
542
|
+
value='vendor'
|
|
543
|
+
label='Vendor'
|
|
544
|
+
checked={contentType === 'vendor'}
|
|
545
|
+
onChange={e => setContentType(String(e.value))}
|
|
546
|
+
/>
|
|
547
|
+
</div>
|
|
548
|
+
)
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
#### Props
|
|
553
|
+
|
|
554
|
+
| Prop | Type | Default | Description |
|
|
555
|
+
| ----------- | ------------------------ | ------- | -------------------------------------------------------- |
|
|
556
|
+
| inputId | string | - | id for the native input (auto-generated if omitted) |
|
|
557
|
+
| name | string | - | Shared name for radios in the same group (required) |
|
|
558
|
+
| value | unknown | - | Value of this radio option |
|
|
559
|
+
| checked | boolean | false | Whether this option is selected (controlled) |
|
|
560
|
+
| onChange | (e: ChangeEvent) => void | - | Callback when selected; `e.value` is this option's value |
|
|
561
|
+
| disabled | boolean | false | Whether the radio is disabled |
|
|
562
|
+
| label | ReactNode | - | Optional label beside the control |
|
|
563
|
+
| className | string | - | Additional CSS class for the root |
|
|
564
|
+
| data-testid | string | - | Test ID for root and input |
|
|
565
|
+
|
|
255
566
|
### Tabs
|
|
256
567
|
|
|
257
568
|
A flexible tabs component for organizing content into multiple panels.
|
|
@@ -307,6 +618,35 @@ const tabs: TabItem[] = [
|
|
|
307
618
|
| label | string | Display label for the tab |
|
|
308
619
|
| content | ReactNode | Content to display when active |
|
|
309
620
|
|
|
621
|
+
### Menu
|
|
622
|
+
|
|
623
|
+
Popup and inline action menus. Wraps [PrimeReact Menu](https://primereact.org/menu/) with scoped Emporix styles.
|
|
624
|
+
|
|
625
|
+
```tsx
|
|
626
|
+
import { useRef } from 'react'
|
|
627
|
+
import { Menu, type MenuRef, PrimaryButton } from '@emporix/component-library'
|
|
628
|
+
|
|
629
|
+
function RowActionsMenu() {
|
|
630
|
+
const menuRef = useRef<MenuRef>(null)
|
|
631
|
+
|
|
632
|
+
return (
|
|
633
|
+
<>
|
|
634
|
+
<PrimaryButton onClick={e => menuRef.current?.toggle(e)}>
|
|
635
|
+
Actions
|
|
636
|
+
</PrimaryButton>
|
|
637
|
+
<Menu
|
|
638
|
+
ref={menuRef}
|
|
639
|
+
popup
|
|
640
|
+
model={[
|
|
641
|
+
{ label: 'Edit', command: () => onEdit() },
|
|
642
|
+
{ label: 'Delete', disabled: true },
|
|
643
|
+
]}
|
|
644
|
+
/>
|
|
645
|
+
</>
|
|
646
|
+
)
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
310
650
|
### Tooltip
|
|
311
651
|
|
|
312
652
|
Floating overlays on hover or focus, similar to [PrimeReact Tooltip](https://primereact.org/tooltip/). Use **children mode** to wrap a button, input, or other component, or **selector mode** with a CSS `target` and `data-pr-tooltip` / `data-pr-position` on matching elements.
|
|
@@ -356,6 +696,43 @@ import { Tooltip } from '@emporix/component-library'
|
|
|
356
696
|
|
|
357
697
|
Selector targets can override position per element with `data-pr-position`. Escape closes the tooltip.
|
|
358
698
|
|
|
699
|
+
### Message
|
|
700
|
+
|
|
701
|
+
Inline alert banners for contextual feedback, similar to [PrimeReact Message](https://primereact.org/message/). Use `text` for plain copy or `children` for richer content. Default severity icons come from `react-icons`.
|
|
702
|
+
|
|
703
|
+
```tsx
|
|
704
|
+
import { Message } from '@emporix/component-library'
|
|
705
|
+
|
|
706
|
+
// Predefined entity warning (management-dashboard pattern)
|
|
707
|
+
{isPredefined && (
|
|
708
|
+
<Message
|
|
709
|
+
severity="info"
|
|
710
|
+
text="This group is predefined and cannot be modified."
|
|
711
|
+
/>
|
|
712
|
+
)}
|
|
713
|
+
|
|
714
|
+
// Other severities
|
|
715
|
+
<Message severity="success" text="Your changes were saved." />
|
|
716
|
+
<Message severity="warn" text="Review settings before continuing." />
|
|
717
|
+
<Message severity="error" text="Unable to complete the request." />
|
|
718
|
+
|
|
719
|
+
// Rich content
|
|
720
|
+
<Message severity="info">
|
|
721
|
+
<strong>Note:</strong> editing is disabled for system groups.
|
|
722
|
+
</Message>
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
#### Props
|
|
726
|
+
|
|
727
|
+
| Prop | Type | Default | Description |
|
|
728
|
+
| ----------- | ---------------------------------------- | ------- | ----------------------------------------------- |
|
|
729
|
+
| severity | `info` \| `success` \| `warn` \| `error` | `info` | Visual severity of the banner |
|
|
730
|
+
| text | string | - | Plain text content; ignored when `children` set |
|
|
731
|
+
| children | ReactNode | - | Rich message body |
|
|
732
|
+
| icon | ReactNode | - | Custom icon; default icon shown per severity |
|
|
733
|
+
| className | string | - | Additional CSS class on the root element |
|
|
734
|
+
| data-testid | string | - | Test ID for testing |
|
|
735
|
+
|
|
359
736
|
### Accordion
|
|
360
737
|
|
|
361
738
|
Collapsible content panels grouped in tabs.
|
|
@@ -485,7 +862,7 @@ The component library provides a minimal set of CSS variables that are actually
|
|
|
485
862
|
|
|
486
863
|
#### Focus Ring
|
|
487
864
|
|
|
488
|
-
Reusable focus outline for inputs, dropdowns, SelectButton, etc. Override to change focus appearance globally.
|
|
865
|
+
Reusable focus outline for inputs, dropdowns, SelectButton, RadioButton, etc. Override to change focus appearance globally.
|
|
489
866
|
|
|
490
867
|
```css
|
|
491
868
|
--focus-ring-box-shadow: 0 0 0 2px rgba(38, 101, 183, 0.35);
|
|
@@ -498,6 +875,23 @@ Reusable focus outline for inputs, dropdowns, SelectButton, etc. Override to cha
|
|
|
498
875
|
--color-tooltip-text: #ffffff;
|
|
499
876
|
```
|
|
500
877
|
|
|
878
|
+
#### Message
|
|
879
|
+
|
|
880
|
+
```css
|
|
881
|
+
--color-message-info-bg: #e9e9ff;
|
|
882
|
+
--color-message-info-border: #696cff;
|
|
883
|
+
--color-message-info-icon: #696cff;
|
|
884
|
+
--color-message-success-bg: #e4f8f0;
|
|
885
|
+
--color-message-success-border: #1ea97c;
|
|
886
|
+
--color-message-success-icon: #1ea97c;
|
|
887
|
+
--color-message-warn-bg: #fff2e2;
|
|
888
|
+
--color-message-warn-border: #cc8925;
|
|
889
|
+
--color-message-warn-icon: #cc8925;
|
|
890
|
+
--color-message-error-bg: #ffe7e6;
|
|
891
|
+
--color-message-error-border: #ff5757;
|
|
892
|
+
--color-message-error-icon: #ff5757;
|
|
893
|
+
```
|
|
894
|
+
|
|
501
895
|
#### Spacing
|
|
502
896
|
|
|
503
897
|
```css
|
|
@@ -674,6 +1068,72 @@ npm run format:check
|
|
|
674
1068
|
|
|
675
1069
|
The project also includes pre-commit hooks that automatically format staged files before commits.
|
|
676
1070
|
|
|
1071
|
+
## Migrating components from management-dashboard (Cursor skill)
|
|
1072
|
+
|
|
1073
|
+
This repository includes a **Cursor Agent Skill** that guides extracting shared UI from [management-dashboard](https://github.com/emporix/management-dashboard) into `@emporix/component-library`. It covers audits, implementation patterns, CSS encapsulation, MD thin wrappers, tests, and rollout.
|
|
1074
|
+
|
|
1075
|
+
**Skill location:** [`.cursor/skills/migrate-to-component-library/`](.cursor/skills/migrate-to-component-library/)
|
|
1076
|
+
|
|
1077
|
+
| File | Purpose |
|
|
1078
|
+
| -------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
1079
|
+
| [`SKILL.md`](.cursor/skills/migrate-to-component-library/SKILL.md) | Step-by-step workflow (phases, checklists, anti-patterns) |
|
|
1080
|
+
| [`reference.md`](.cursor/skills/migrate-to-component-library/reference.md) | Code templates, token mapping, Jira ticket outline, verification checklist |
|
|
1081
|
+
|
|
1082
|
+
Related Cursor rules: [`.cursor/rules/component-development.mdc`](.cursor/rules/component-development.mdc), [`.cursor/rules/version-bump-for-features.mdc`](.cursor/rules/version-bump-for-features.mdc).
|
|
1083
|
+
|
|
1084
|
+
### When to use it
|
|
1085
|
+
|
|
1086
|
+
Use the skill when you:
|
|
1087
|
+
|
|
1088
|
+
- Move or extract a component from management-dashboard into this library
|
|
1089
|
+
- Migrate PrimeReact-based MD components (e.g. tables, dialogs) with scoped styles
|
|
1090
|
+
- Work on [COP-5597 MD Modularization](https://emporix.atlassian.net/browse/COP-5597) stories
|
|
1091
|
+
|
|
1092
|
+
**Reference migration:** `DataTable` (from MD `MdDataTable`) — library export is `DataTable`; MD keeps the existing `MdDataTable` implementation unchanged. Pilot screens import `DataTable` directly from `@emporix/component-library` (or use a thin `Md*` wrapper if preferred).
|
|
1093
|
+
|
|
1094
|
+
### How to invoke the skill in Cursor
|
|
1095
|
+
|
|
1096
|
+
**Option 1 — Mention the skill in chat (recommended)**
|
|
1097
|
+
|
|
1098
|
+
```
|
|
1099
|
+
Migrate [ComponentName] from management-dashboard to component-library
|
|
1100
|
+
using the migrate-to-component-library skill.
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
**Option 2 — Reference by name**
|
|
1104
|
+
|
|
1105
|
+
Ask Cursor to read and follow `migrate-to-component-library` before starting the task.
|
|
1106
|
+
|
|
1107
|
+
**Option 3 — Explicit file reference**
|
|
1108
|
+
|
|
1109
|
+
```
|
|
1110
|
+
Follow .cursor/skills/migrate-to-component-library/SKILL.md to migrate X.
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
Cursor loads the skill automatically when your request matches its description (component extraction, MD → library migration, COP modularization).
|
|
1114
|
+
|
|
1115
|
+
### What the skill walks you through
|
|
1116
|
+
|
|
1117
|
+
1. **Audit** — find consumers, related SCSS, and PrimeReact dependencies in MD
|
|
1118
|
+
2. **Ask you to choose a pattern** (required before any implementation)
|
|
1119
|
+
- **Pattern A (custom):** boilerplate + SCSS modules + **same API as PrimeReact** — e.g. `Dropdown`, `InputText`, `SelectButton` (no `primereact` dependency)
|
|
1120
|
+
- **Pattern B (PrimeReact wrapper):** encapsulated `primereact/*` + scoped overrides — e.g. `Toast`, `DataTable`
|
|
1121
|
+
3. **Implement in component-library** — component folder, tests, Storybook, exports, version bump
|
|
1122
|
+
4. **Wire consumer app** — import library component directly in pilot views **or** add a thin `Md*` wrapper; leave existing `Md*` unchanged for unmigrated screens; import `@emporix/component-library/styles`
|
|
1123
|
+
5. **Verify** — `npm run lint`, `npm run test -- --run`, `npm run build:lib` (library); build and manual QA in MD
|
|
1124
|
+
|
|
1125
|
+
See [`reference.md`](.cursor/skills/migrate-to-component-library/reference.md) for copy-paste templates (`MdDataTable.tsx` wrapper, `PrimeDataTableOverrides.module.scss`, Jira acceptance criteria).
|
|
1126
|
+
|
|
1127
|
+
### Naming convention (library vs MD)
|
|
1128
|
+
|
|
1129
|
+
| Layer | Name | Example |
|
|
1130
|
+
| ----------------------- | --------------------------------------- | ---------------------------------------------------------------------- |
|
|
1131
|
+
| Component library | Generic, reusable name | `DataTable`, `PrimaryButton` |
|
|
1132
|
+
| Consumer app (existing) | Keep original `Md*` component unchanged | `MdDataTable` (legacy implementation) |
|
|
1133
|
+
| Consumer app (pilot) | Direct library import or thin wrapper | `import { DataTable } from '@emporix/component-library'` in pilot view |
|
|
1134
|
+
|
|
1135
|
+
This keeps existing import paths stable (`import MdDataTable from 'components/MdDataTable'`) while pilot screens opt in via direct library import or an optional wrapper.
|
|
1136
|
+
|
|
677
1137
|
## Contributing
|
|
678
1138
|
|
|
679
1139
|
1. Fork the repository
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export interface AutoCompleteCompleteEvent {
|
|
3
|
+
query: string;
|
|
4
|
+
}
|
|
5
|
+
export interface AutoCompleteChangeEvent {
|
|
6
|
+
value: string;
|
|
7
|
+
originalEvent: React.SyntheticEvent;
|
|
8
|
+
}
|
|
9
|
+
export interface AutoCompleteSelectEvent<T> {
|
|
10
|
+
value: T;
|
|
11
|
+
originalEvent: React.SyntheticEvent;
|
|
12
|
+
}
|
|
13
|
+
export interface AutoCompleteProps<T extends Record<string, unknown>> {
|
|
14
|
+
/** Display text in the input (controlled). */
|
|
15
|
+
value: string;
|
|
16
|
+
/** Suggestion list from the parent (updated via completeMethod). */
|
|
17
|
+
suggestions: T[];
|
|
18
|
+
/** Called when the user types; parent should fetch/filter suggestions. */
|
|
19
|
+
completeMethod: (e: AutoCompleteCompleteEvent) => void;
|
|
20
|
+
/** Property on suggestion objects used for display (default: first string field). */
|
|
21
|
+
field?: keyof T | string;
|
|
22
|
+
/** Fired when the input text changes. */
|
|
23
|
+
onChange: (e: AutoCompleteChangeEvent) => void;
|
|
24
|
+
/** Fired when a suggestion is selected. */
|
|
25
|
+
onSelect?: (e: AutoCompleteSelectEvent<T>) => void;
|
|
26
|
+
/** Show dropdown button to open suggestions. */
|
|
27
|
+
dropdown?: boolean;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
placeholder?: string;
|
|
30
|
+
label?: React.ReactNode;
|
|
31
|
+
error?: React.ReactNode;
|
|
32
|
+
className?: string;
|
|
33
|
+
inputId?: string;
|
|
34
|
+
'data-testid'?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare const AutoComplete: {
|
|
37
|
+
<T extends Record<string, unknown>>({ value, suggestions, completeMethod, field, onChange, onSelect, dropdown, disabled, placeholder, label, error, className, inputId: inputIdProp, "data-testid": testId, }: AutoCompleteProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
38
|
+
displayName: string;
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=AutoComplete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AutoComplete.d.ts","sourceRoot":"","sources":["../../../src/components/autocomplete/AutoComplete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAA;AAId,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,KAAK,CAAC,cAAc,CAAA;CACpC;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACxC,KAAK,EAAE,CAAC,CAAA;IACR,aAAa,EAAE,KAAK,CAAC,cAAc,CAAA;CACpC;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClE,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,WAAW,EAAE,CAAC,EAAE,CAAA;IAChB,0EAA0E;IAC1E,cAAc,EAAE,CAAC,CAAC,EAAE,yBAAyB,KAAK,IAAI,CAAA;IACtD,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACxB,yCAAyC;IACzC,QAAQ,EAAE,CAAC,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAC9C,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,uBAAuB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAClD,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAgBD,eAAO,MAAM,YAAY;KAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,6KAe3D,iBAAiB,CAAC,CAAC,CAAC;;CA8NtB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/autocomplete/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,gBAAgB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PrimaryButton.d.ts","sourceRoot":"","sources":["../../../src/components/buttons/PrimaryButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,OAAO,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"PrimaryButton.d.ts","sourceRoot":"","sources":["../../../src/components/buttons/PrimaryButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAA;IACpD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,aAAa,GAAI,sFAQ3B,kBAAkB,4CAsBpB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecondaryButton.d.ts","sourceRoot":"","sources":["../../../src/components/buttons/SecondaryButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"SecondaryButton.d.ts","sourceRoot":"","sources":["../../../src/components/buttons/SecondaryButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAA;IACpD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,eAAe,GAAI,6EAO7B,oBAAoB,4CAqBtB,CAAA"}
|