@emporix/component-library 1.8.1 → 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.
Files changed (41) hide show
  1. package/README.md +540 -1
  2. package/dist/components/autocomplete/AutoComplete.d.ts +40 -0
  3. package/dist/components/autocomplete/AutoComplete.d.ts.map +1 -0
  4. package/dist/components/autocomplete/index.d.ts +3 -0
  5. package/dist/components/autocomplete/index.d.ts.map +1 -0
  6. package/dist/components/buttons/PrimaryButton.d.ts +1 -1
  7. package/dist/components/buttons/PrimaryButton.d.ts.map +1 -1
  8. package/dist/components/buttons/SecondaryButton.d.ts +1 -1
  9. package/dist/components/buttons/SecondaryButton.d.ts.map +1 -1
  10. package/dist/components/checkbox/Checkbox.d.ts +50 -0
  11. package/dist/components/checkbox/Checkbox.d.ts.map +1 -0
  12. package/dist/components/checkbox/index.d.ts +3 -0
  13. package/dist/components/checkbox/index.d.ts.map +1 -0
  14. package/dist/components/datatable/DataTable.d.ts +6 -0
  15. package/dist/components/datatable/DataTable.d.ts.map +1 -0
  16. package/dist/components/datatable/index.d.ts +5 -0
  17. package/dist/components/datatable/index.d.ts.map +1 -0
  18. package/dist/components/datatable/types.d.ts +60 -0
  19. package/dist/components/datatable/types.d.ts.map +1 -0
  20. package/dist/components/datatable/useDataTablePagination.d.ts +17 -0
  21. package/dist/components/datatable/useDataTablePagination.d.ts.map +1 -0
  22. package/dist/components/dialog/Dialog.d.ts +34 -0
  23. package/dist/components/dialog/Dialog.d.ts.map +1 -0
  24. package/dist/components/dialog/index.d.ts +3 -0
  25. package/dist/components/dialog/index.d.ts.map +1 -0
  26. package/dist/components/dropdown/Dropdown.d.ts +23 -1
  27. package/dist/components/dropdown/Dropdown.d.ts.map +1 -1
  28. package/dist/components/menu/Menu.d.ts +27 -0
  29. package/dist/components/menu/Menu.d.ts.map +1 -0
  30. package/dist/components/menu/index.d.ts +3 -0
  31. package/dist/components/menu/index.d.ts.map +1 -0
  32. package/dist/components/message/Message.d.ts +31 -0
  33. package/dist/components/message/Message.d.ts.map +1 -0
  34. package/dist/components/radiobutton/RadioButton.d.ts +46 -0
  35. package/dist/components/radiobutton/RadioButton.d.ts.map +1 -0
  36. package/dist/index.d.ts +15 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.es.js +1563 -6009
  39. package/dist/index.umd.js +1 -553
  40. package/dist/style.css +1 -1
  41. package/package.json +10 -4
package/README.md CHANGED
@@ -110,6 +110,9 @@ import { InputText } from '@emporix/component-library'
110
110
  // Required field
111
111
  <InputText label="Name" required placeholder="Enter name" />
112
112
 
113
+ // With validation error
114
+ <InputText label="Email" required error="Please enter a valid email address" />
115
+
113
116
  // Disabled
114
117
  <InputText label="Id" placeholder="Enter id" disabled />
115
118
  ```
@@ -122,12 +125,335 @@ import { InputText } from '@emporix/component-library'
122
125
  | tooltip | string | - | If set, shows info icon with this tooltip |
123
126
  | required | boolean | false | Shows asterisk on label |
124
127
  | inputId | string | - | id for the input (for label association) |
128
+ | error | ReactNode | - | Error text displayed below the input |
125
129
  | disabled | boolean | false | Whether the input is disabled |
126
130
  | className | string | - | Additional CSS class for the root |
127
131
  | data-testid | string | - | Test ID for root and input (suffix `-input`) |
128
132
 
129
133
  All standard HTML input attributes (placeholder, value, onChange, etc.) are supported.
130
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
+
366
+ ### Dropdown
367
+
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/).
369
+
370
+ ```tsx
371
+ import { Dropdown } from '@emporix/component-library'
372
+
373
+ const options = [
374
+ { label: 'GBP - British Pound Sterling', value: 'GBP' },
375
+ { label: 'EUR - Euro', value: 'EUR' },
376
+ { label: 'USD - US Dollar', value: 'USD' },
377
+ ]
378
+
379
+ // With label, filter, and required
380
+ <Dropdown
381
+ value={currency}
382
+ onChange={e => setCurrency(e.value)}
383
+ options={options}
384
+ optionLabel="label"
385
+ optionValue="value"
386
+ placeholder="Select a currency"
387
+ label="Preferred Currency"
388
+ required
389
+ filter
390
+ />
391
+
392
+ // With validation error (same styling as InputText)
393
+ <Dropdown
394
+ value={currency}
395
+ onChange={e => setCurrency(e.value)}
396
+ options={options}
397
+ placeholder="Select a currency"
398
+ label="Preferred Currency"
399
+ required
400
+ error="Currency is required"
401
+ />
402
+
403
+ // Multiple selection
404
+ <Dropdown
405
+ value={currencies}
406
+ onChange={e => setCurrencies(Array.isArray(e.value) ? e.value : [])}
407
+ options={options}
408
+ placeholder="Select currencies"
409
+ label="Currencies"
410
+ multiple
411
+ filter
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
+ />
425
+ ```
426
+
427
+ #### Props
428
+
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 |
456
+
131
457
  ### SelectButton
132
458
 
133
459
  Choose single or multiple options using buttons. API inspired by [PrimeReact SelectButton](https://primereact.org/selectbutton/). Selected state uses project primary colors.
@@ -173,6 +499,70 @@ const options = [
173
499
  | className | string | - | Additional CSS class |
174
500
  | data-testid | string | - | Test ID for root and option buttons |
175
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
+
176
566
  ### Tabs
177
567
 
178
568
  A flexible tabs component for organizing content into multiple panels.
@@ -228,6 +618,35 @@ const tabs: TabItem[] = [
228
618
  | label | string | Display label for the tab |
229
619
  | content | ReactNode | Content to display when active |
230
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
+
231
650
  ### Tooltip
232
651
 
233
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.
@@ -277,6 +696,43 @@ import { Tooltip } from '@emporix/component-library'
277
696
 
278
697
  Selector targets can override position per element with `data-pr-position`. Escape closes the tooltip.
279
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
+
280
736
  ### Accordion
281
737
 
282
738
  Collapsible content panels grouped in tabs.
@@ -406,7 +862,7 @@ The component library provides a minimal set of CSS variables that are actually
406
862
 
407
863
  #### Focus Ring
408
864
 
409
- 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.
410
866
 
411
867
  ```css
412
868
  --focus-ring-box-shadow: 0 0 0 2px rgba(38, 101, 183, 0.35);
@@ -419,6 +875,23 @@ Reusable focus outline for inputs, dropdowns, SelectButton, etc. Override to cha
419
875
  --color-tooltip-text: #ffffff;
420
876
  ```
421
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
+
422
895
  #### Spacing
423
896
 
424
897
  ```css
@@ -595,6 +1068,72 @@ npm run format:check
595
1068
 
596
1069
  The project also includes pre-commit hooks that automatically format staged files before commits.
597
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
+
598
1137
  ## Contributing
599
1138
 
600
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,3 @@
1
+ export { AutoComplete } from './AutoComplete';
2
+ export type { AutoCompleteProps, AutoCompleteChangeEvent, AutoCompleteCompleteEvent, AutoCompleteSelectEvent, } from './AutoComplete';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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"}
@@ -15,7 +15,7 @@ export interface PrimaryButtonProps {
15
15
  /**
16
16
  * Click handler
17
17
  */
18
- onClick?: () => void;
18
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
19
19
  /**
20
20
  * Additional CSS class name
21
21
  */
@@ -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,MAAM,IAAI,CAAA;IACpB;;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
+ {"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"}