@emporix/component-library 2.4.2 → 2.5.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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A modern React component library built with TypeScript, Vite, and SCSS modules.
4
4
 
5
+ 🔗 [Live demo](https://emporix.github.io/component-library/) · [Storybook](https://emporix.github.io/component-library/storybook/)
6
+
5
7
  ## Features
6
8
 
7
9
  - 🚀 **Modern Stack**: Built with Vite, TypeScript, and React 18
@@ -307,6 +309,46 @@ const [selectedRole, setSelectedRole] = useState<string | null>(null)
307
309
 
308
310
  Standard HTML input attributes (`aria-label`, `aria-labelledby`, `tabIndex`, etc.) are supported on the native input.
309
311
 
312
+ ### InputSwitch
313
+
314
+ A binary on/off toggle for boolean fields, built on a native `input type="checkbox"` with `role="switch"`. API inspired by [PrimeReact InputSwitch](https://primereact.org/inputswitch/) — the change event carries `{ value, originalEvent, target }`, so a `react-hook-form` `field.onChange` can be passed straight through.
315
+
316
+ ```tsx
317
+ import { useState } from 'react'
318
+ import { InputSwitch } from '@emporix/component-library'
319
+
320
+ // Controlled
321
+ const [received, setReceived] = useState(false)
322
+ <InputSwitch checked={received} onChange={e => setReceived(e.value)} />
323
+
324
+ // With label (associate via inputId + htmlFor)
325
+ <InputSwitch inputId="received" checked={received} onChange={e => setReceived(e.value)} />
326
+ <label htmlFor="received">Received</label>
327
+
328
+ // With react-hook-form
329
+ <Controller
330
+ control={control}
331
+ name="received"
332
+ render={({ field }) => (
333
+ <InputSwitch checked={field.value} onChange={field.onChange} />
334
+ )}
335
+ />
336
+ ```
337
+
338
+ #### Props
339
+
340
+ | Prop | Type | Default | Description |
341
+ | ----------- | ----------------------------------- | ------- | ----------------------------------------------- |
342
+ | checked | boolean | false | Whether the switch is on (controlled) |
343
+ | onChange | (e: InputSwitchChangeEvent) => void | - | Fired when the checked state changes |
344
+ | inputId | string | - | id for the native input (for label association) |
345
+ | disabled | boolean | false | Whether the switch is disabled |
346
+ | invalid | boolean | false | Invalid state for validation styling |
347
+ | name | string | - | Name attribute for form submission |
348
+ | readOnly | boolean | false | Prevents user interaction |
349
+ | className | string | - | Additional CSS class for the root |
350
+ | data-testid | string | - | Test ID for root and input (suffix `-input`) |
351
+
310
352
  ### Dialog
311
353
 
312
354
  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.
@@ -457,6 +499,59 @@ import { DateValue } from '@emporix/component-library'
457
499
  | className | string | - | Additional CSS class on the root |
458
500
  | data-testid | string | - | Test ID on the root |
459
501
 
502
+ ### MoneyValue
503
+
504
+ Locale-aware currency display helper for order, quote, and return totals. Context-free like `DateValue` — the app passes the locale in.
505
+
506
+ ```tsx
507
+ import { MoneyValue } from '@emporix/component-library'
508
+
509
+ <MoneyValue value={1234.5} currency='EUR' locale='en-GB' />
510
+ <MoneyValue value={1234.5} currency='EUR' locale='de-DE' />
511
+ <MoneyValue value={undefined} currency={undefined} nullText='No amount' />
512
+ ```
513
+
514
+ Always renders exactly two fraction digits. `0` formats as a real amount; only `undefined`/`NaN` values, a missing currency, or an invalid currency code fall back to `nullText`.
515
+
516
+ #### Props
517
+
518
+ | Prop | Type | Default | Description |
519
+ | ----------- | --------------------- | ------- | -------------------------------------------------- |
520
+ | value | `number \| undefined` | - | Numeric amount to format |
521
+ | currency | `string \| undefined` | - | ISO 4217 currency code (e.g. `EUR`) |
522
+ | locale | string | `en` | Locale used for number formatting |
523
+ | nullText | string | `--` | Fallback when value/currency is missing or invalid |
524
+ | className | string | - | Additional CSS class on the root |
525
+ | data-testid | string | - | Test ID on the root |
526
+
527
+ ### StatusBadge
528
+
529
+ Outlined status pill for table cells and detail headers. The `color` prop drives both the border and the text, so callers map their own status enum to colours.
530
+
531
+ ```tsx
532
+ import { StatusBadge } from '@emporix/component-library'
533
+
534
+ <StatusBadge status='pending' />
535
+ <StatusBadge status='approved' color='#1ea97c' />
536
+ <StatusBadge
537
+ status={item.approvalStatus}
538
+ color={ReturnStatusColor[item.approvalStatus]}
539
+ icon={<i className='pi pi-check' />}
540
+ />
541
+ ```
542
+
543
+ The status text is rendered upper-cased. When `color` is omitted the badge uses the neutral `--color-border-medium` token.
544
+
545
+ #### Props
546
+
547
+ | Prop | Type | Default | Description |
548
+ | ----------- | --------- | ------- | ---------------------------------------------- |
549
+ | status | string | - | Status text, rendered upper-cased |
550
+ | color | string | - | Border and text colour (hex or `var(--token)`) |
551
+ | icon | ReactNode | - | Optional icon rendered before the status text |
552
+ | className | string | - | Additional CSS class on the root |
553
+ | data-testid | string | - | Test ID on the root |
554
+
460
555
  ### DataTable
461
556
 
462
557
  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.
@@ -778,25 +873,42 @@ const tabs: TabItem[] = [
778
873
  className="custom-tabs"
779
874
  data-testid="my-tabs"
780
875
  />
876
+
877
+ // Wizard: gate later steps and keep panel state across switches
878
+ <Tabs
879
+ tabs={[
880
+ { id: 'customer', label: 'Customer', content: <CustomerStep /> },
881
+ { id: 'orders', label: 'Orders', content: <OrdersStep /> },
882
+ { id: 'review', label: 'Review', content: <ReviewStep />, disabled: !isValid },
883
+ ]}
884
+ activeTabId={activeTabId}
885
+ onTabChange={setActiveTabId}
886
+ keepMounted
887
+ />
781
888
  ```
782
889
 
783
890
  #### Props
784
891
 
785
- | Prop | Type | Default | Description |
786
- | ----------- | ----------------------- | ------- | ------------------------------ |
787
- | tabs | TabItem[] | - | Array of tab items |
788
- | activeTabId | string | - | ID of the currently active tab |
789
- | onTabChange | (tabId: string) => void | - | Callback when tab is changed |
790
- | className | string | - | Additional CSS class |
791
- | data-testid | string | - | Test ID for testing |
892
+ | Prop | Type | Default | Description |
893
+ | ----------- | ----------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- |
894
+ | tabs | TabItem[] | - | Array of tab items |
895
+ | activeTabId | string | - | ID of the currently active tab |
896
+ | onTabChange | (tabId: string) => void | - | Callback when tab is changed. Not fired for disabled tabs |
897
+ | keepMounted | boolean | false | Keep all panels mounted and hide inactive ones, so panel state survives switches (like PrimeReact `renderActiveOnly={false}`) |
898
+ | fullWidth | boolean | false | Stretch tabs to evenly fill the available width |
899
+ | className | string | - | Additional CSS class |
900
+ | data-testid | string | - | Test ID for testing |
901
+
902
+ With `keepMounted`, each panel also gets a `{data-testid}-panel-{tabId}` test ID.
792
903
 
793
904
  #### TabItem Interface
794
905
 
795
- | Prop | Type | Description |
796
- | ------- | --------- | ------------------------------ |
797
- | id | string | Unique identifier for the tab |
798
- | label | string | Display label for the tab |
799
- | content | ReactNode | Content to display when active |
906
+ | Prop | Type | Description |
907
+ | -------- | --------- | ------------------------------------------------- |
908
+ | id | string | Unique identifier for the tab |
909
+ | label | string | Display label for the tab |
910
+ | content | ReactNode | Content to display when active |
911
+ | disabled | boolean | Dims the tab and ignores clicks (default `false`) |
800
912
 
801
913
  ### Menu
802
914
 
@@ -0,0 +1,49 @@
1
+ import { default as React } from 'react';
2
+ export interface InputSwitchChangeEvent {
3
+ /**
4
+ * React change event from the native checkbox input backing the switch
5
+ */
6
+ originalEvent: React.ChangeEvent<HTMLInputElement>;
7
+ /**
8
+ * Checked state after the change
9
+ */
10
+ value: boolean;
11
+ /**
12
+ * PrimeReact-compatible target shape, so `react-hook-form` `field.onChange`
13
+ * can be passed straight through and store the boolean.
14
+ */
15
+ target: {
16
+ name?: string;
17
+ id: string;
18
+ value: boolean;
19
+ };
20
+ }
21
+ export interface InputSwitchProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'checked' | 'className' | 'type' | 'value'> {
22
+ /**
23
+ * Whether the switch is on (controlled)
24
+ * @see https://primereact.org/inputswitch/
25
+ */
26
+ checked?: boolean;
27
+ /**
28
+ * Callback when the checked state changes. Receives `{ value, originalEvent, target }`.
29
+ */
30
+ onChange?: (e: InputSwitchChangeEvent) => void;
31
+ /**
32
+ * Id for the native input (for label association via htmlFor)
33
+ */
34
+ inputId?: string;
35
+ /**
36
+ * Invalid state for validation styling
37
+ */
38
+ invalid?: boolean;
39
+ /**
40
+ * Additional CSS class name for the root
41
+ */
42
+ className?: string;
43
+ /**
44
+ * Test ID for testing
45
+ */
46
+ 'data-testid'?: string;
47
+ }
48
+ export declare const InputSwitch: ({ checked, onChange, inputId: inputIdProp, disabled, invalid, className, name, readOnly, required, tabIndex, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "data-testid": testId, ...rest }: InputSwitchProps) => import("react/jsx-runtime").JSX.Element;
49
+ //# sourceMappingURL=InputSwitch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputSwitch.d.ts","sourceRoot":"","sources":["../../../src/components/inputswitch/InputSwitch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgB,MAAM,OAAO,CAAA;AAGpC,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,aAAa,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAA;IAClD;;OAEG;IACH,KAAK,EAAE,OAAO,CAAA;IACd;;;OAGG;IACH,MAAM,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,EAAE,EAAE,MAAM,CAAA;QACV,KAAK,EAAE,OAAO,CAAA;KACf,CAAA;CACF;AAED,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAC5C,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAC3C,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,CACxD;IACC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,sBAAsB,KAAK,IAAI,CAAA;IAC9C;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,WAAW,GAAI,2MAezB,gBAAgB,4CAgElB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export { InputSwitch } from './InputSwitch';
2
+ export type { InputSwitchProps, InputSwitchChangeEvent } from './InputSwitch';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/inputswitch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,30 @@
1
+ export interface MoneyValueProps {
2
+ /**
3
+ * Numeric amount to format.
4
+ */
5
+ value: number | undefined;
6
+ /**
7
+ * ISO 4217 currency code (e.g. `EUR`, `USD`).
8
+ */
9
+ currency: string | undefined;
10
+ /**
11
+ * Locale used for number formatting.
12
+ * @default 'en'
13
+ */
14
+ locale?: string;
15
+ /**
16
+ * Fallback text shown when `value` or `currency` is missing or invalid.
17
+ * @default '--'
18
+ */
19
+ nullText?: string;
20
+ /**
21
+ * Additional CSS class name for the root.
22
+ */
23
+ className?: string;
24
+ /**
25
+ * Test ID for testing.
26
+ */
27
+ 'data-testid'?: string;
28
+ }
29
+ export declare const MoneyValue: ({ value, currency, locale, nullText, className, "data-testid": testId, }: MoneyValueProps) => import("react/jsx-runtime").JSX.Element;
30
+ //# sourceMappingURL=MoneyValue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MoneyValue.d.ts","sourceRoot":"","sources":["../../../src/components/moneyvalue/MoneyValue.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB;;OAEG;IACH,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AA0BD,eAAO,MAAM,UAAU,GAAI,0EAOxB,eAAe,4CAcjB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export { MoneyValue } from './MoneyValue';
2
+ export type { MoneyValueProps } from './MoneyValue';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/moneyvalue/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,26 @@
1
+ import { ReactNode } from 'react';
2
+ export interface StatusBadgeProps {
3
+ /**
4
+ * Status text. Rendered upper-cased.
5
+ */
6
+ status: string;
7
+ /**
8
+ * Border and text colour. Any CSS colour value (hex or `var(--token)`).
9
+ * Falls back to the neutral border colour when omitted.
10
+ */
11
+ color?: string;
12
+ /**
13
+ * Optional icon rendered before the status text.
14
+ */
15
+ icon?: ReactNode;
16
+ /**
17
+ * Additional CSS class name for the root.
18
+ */
19
+ className?: string;
20
+ /**
21
+ * Test ID for testing.
22
+ */
23
+ 'data-testid'?: string;
24
+ }
25
+ export declare const StatusBadge: ({ status, color, icon, className, "data-testid": testId, }: StatusBadgeProps) => import("react/jsx-runtime").JSX.Element;
26
+ //# sourceMappingURL=StatusBadge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusBadge.d.ts","sourceRoot":"","sources":["../../../src/components/statusbadge/StatusBadge.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGjC,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,WAAW,GAAI,4DAMzB,gBAAgB,4CAiBlB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export { StatusBadge } from './StatusBadge';
2
+ export type { StatusBadgeProps } from './StatusBadge';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/statusbadge/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA"}
@@ -12,6 +12,11 @@ export interface TabItem {
12
12
  * Content to display when tab is active
13
13
  */
14
14
  content: ReactNode;
15
+ /**
16
+ * Whether the tab can be selected. Disabled tabs are dimmed and ignore clicks.
17
+ * @default false
18
+ */
19
+ disabled?: boolean;
15
20
  }
16
21
  export interface TabsProps {
17
22
  /**
@@ -23,9 +28,16 @@ export interface TabsProps {
23
28
  */
24
29
  activeTabId: string;
25
30
  /**
26
- * Callback function when tab is changed
31
+ * Callback function when tab is changed. Not fired for disabled tabs.
27
32
  */
28
33
  onTabChange: (tabId: string) => void;
34
+ /**
35
+ * Keep every tab panel mounted and hide the inactive ones with CSS, so panel
36
+ * state (form inputs, scroll position, in-flight requests) survives tab
37
+ * switches. Equivalent to PrimeReact's `renderActiveOnly={false}`.
38
+ * @default false
39
+ */
40
+ keepMounted?: boolean;
29
41
  /**
30
42
  * Additional CSS class name
31
43
  */
@@ -39,5 +51,5 @@ export interface TabsProps {
39
51
  */
40
52
  'data-testid'?: string;
41
53
  }
42
- export declare const Tabs: ({ tabs, activeTabId, onTabChange, className, fullWidth, "data-testid": testId, ...props }: TabsProps) => import("react/jsx-runtime").JSX.Element;
54
+ export declare const Tabs: ({ tabs, activeTabId, onTabChange, keepMounted, className, fullWidth, "data-testid": testId, ...props }: TabsProps) => import("react/jsx-runtime").JSX.Element;
43
55
  //# sourceMappingURL=Tabs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../src/components/tabs/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGjC,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAA;IACV;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,OAAO,EAAE,SAAS,CAAA;CACnB;AAED,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,IAAI,EAAE,OAAO,EAAE,CAAA;IACf;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,IAAI,GAAI,2FAQlB,SAAS,4CA2CX,CAAA"}
1
+ {"version":3,"file":"Tabs.d.ts","sourceRoot":"","sources":["../../../src/components/tabs/Tabs.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGjC,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAA;IACV;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,OAAO,EAAE,SAAS,CAAA;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,IAAI,EAAE,OAAO,EAAE,CAAA;IACf;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,IAAI,GAAI,wGASlB,SAAS,4CAiEX,CAAA"}