@dbcdk/react-components 0.0.8 → 0.0.10

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 (88) hide show
  1. package/README.md +167 -0
  2. package/dist/components/__stories__/_data/tabs.d.ts +9 -0
  3. package/dist/components/__stories__/_data/tabs.js +31 -0
  4. package/dist/components/accordion/Accordion.d.ts +27 -0
  5. package/dist/components/accordion/Accordion.js +66 -0
  6. package/dist/components/accordion/Accordion.module.css +87 -0
  7. package/dist/components/button/Button.module.css +1 -0
  8. package/dist/components/circle/Circle.d.ts +4 -1
  9. package/dist/components/circle/Circle.js +2 -2
  10. package/dist/components/circle/Circle.module.css +54 -2
  11. package/dist/components/code-block/CodeBlock.module.css +1 -1
  12. package/dist/components/datetime-picker/DateTimePicker.d.ts +4 -7
  13. package/dist/components/datetime-picker/DateTimePicker.js +117 -64
  14. package/dist/components/datetime-picker/dateTimeHelpers.d.ts +14 -2
  15. package/dist/components/datetime-picker/dateTimeHelpers.js +32 -17
  16. package/dist/components/forms/checkbox/Checkbox.d.ts +2 -8
  17. package/dist/components/forms/checkbox/Checkbox.js +3 -5
  18. package/dist/components/forms/input/Input.d.ts +1 -0
  19. package/dist/components/forms/input/Input.js +2 -4
  20. package/dist/components/forms/input/Input.module.css +9 -11
  21. package/dist/components/forms/input-container/InputContainer.d.ts +2 -1
  22. package/dist/components/forms/input-container/InputContainer.js +3 -3
  23. package/dist/components/forms/input-container/InputContainer.module.css +65 -0
  24. package/dist/components/forms/radio-buttons/RadioButton.d.ts +36 -0
  25. package/dist/components/forms/radio-buttons/RadioButton.js +26 -0
  26. package/dist/components/forms/radio-buttons/RadioButtonGroup.d.ts +25 -0
  27. package/dist/components/forms/radio-buttons/RadioButtonGroup.js +19 -0
  28. package/dist/components/forms/radio-buttons/RadioButtons.module.css +117 -0
  29. package/dist/components/forms/select/Select.d.ts +1 -1
  30. package/dist/components/forms/select/Select.js +3 -3
  31. package/dist/components/forms/text-area/Textarea.js +3 -3
  32. package/dist/components/forms/text-area/Textarea.module.css +8 -1
  33. package/dist/components/headline/Headline.d.ts +2 -7
  34. package/dist/components/headline/Headline.js +5 -2
  35. package/dist/components/headline/Headline.module.css +61 -2
  36. package/dist/components/hyperlink/Hyperlink.d.ts +1 -0
  37. package/dist/components/hyperlink/Hyperlink.js +5 -1
  38. package/dist/components/icon/Icon.module.css +1 -0
  39. package/dist/components/interval-select/IntervalSelect.js +1 -1
  40. package/dist/components/nav-bar/NavBar.d.ts +24 -6
  41. package/dist/components/overlay/modal/provider/ModalProvider.d.ts +2 -2
  42. package/dist/components/overlay/modal/provider/ModalProvider.js +24 -25
  43. package/dist/components/overlay/side-panel/SidePanel.d.ts +12 -4
  44. package/dist/components/overlay/side-panel/SidePanel.js +60 -4
  45. package/dist/components/overlay/side-panel/SidePanel.module.css +151 -28
  46. package/dist/components/overlay/side-panel/useSidePanel.d.ts +1 -1
  47. package/dist/components/overlay/side-panel/useSidePanel.js +2 -2
  48. package/dist/components/page-layout/PageLayout.js +0 -2
  49. package/dist/components/popover/Popover.js +33 -14
  50. package/dist/components/popover/Popover.module.css +0 -4
  51. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.d.ts +5 -5
  52. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +16 -8
  53. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.module.css +0 -3
  54. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.d.ts +3 -1
  55. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +4 -3
  56. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +109 -79
  57. package/dist/components/sidebar/components/sidebar-items/SidebarItems.js +16 -3
  58. package/dist/components/sidebar/components/sidebar-items/SidebarItems.module.css +20 -0
  59. package/dist/components/sidebar/providers/SidebarProvider.js +25 -46
  60. package/dist/components/skeleton-loader/SkeletonLoader.d.ts +1 -1
  61. package/dist/components/skeleton-loader/SkeletonLoader.js +15 -12
  62. package/dist/components/state-page/StatePage.d.ts +9 -0
  63. package/dist/components/state-page/StatePage.js +20 -0
  64. package/dist/components/state-page/StatePage.module.css +9 -0
  65. package/dist/components/state-page/empty.d.ts +2 -0
  66. package/dist/components/state-page/empty.js +2 -0
  67. package/dist/components/state-page/error.d.ts +2 -0
  68. package/dist/components/state-page/error.js +2 -0
  69. package/dist/components/state-page/notFound.d.ts +2 -0
  70. package/dist/components/state-page/notFound.js +2 -0
  71. package/dist/components/sticky-footer-layout/StickyFooterLayout.d.ts +19 -0
  72. package/dist/components/sticky-footer-layout/StickyFooterLayout.js +27 -0
  73. package/dist/components/table/Table.js +4 -4
  74. package/dist/components/table/Table.module.css +168 -60
  75. package/dist/components/table/components/empty-state/EmptyState.d.ts +1 -1
  76. package/dist/components/table/components/empty-state/EmptyState.js +6 -7
  77. package/dist/components/toast/Toast.js +5 -1
  78. package/dist/components/toast/Toast.module.css +40 -15
  79. package/dist/components/toast/provider/ToastProvider.js +1 -0
  80. package/dist/hooks/useTimeDuration.js +9 -3
  81. package/dist/hooks/useViewportFill.js +1 -0
  82. package/dist/index.d.ts +6 -0
  83. package/dist/index.js +6 -1
  84. package/dist/src/styles/styles.css +22 -3
  85. package/dist/styles/styles.css +22 -3
  86. package/dist/styles/themes/dbc/dark.css +1 -1
  87. package/dist/styles/themes/dbc/light.css +2 -1
  88. package/package.json +1 -1
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import type { JSX } from 'react';
3
+ type Variant = 'default' | 'primary' | 'outlined';
4
+ type Size = 'sm' | 'md' | 'lg';
5
+ export interface RadioButtonProps {
6
+ /** group name (required for radio behavior) */
7
+ name: string;
8
+ /** this option's value */
9
+ value: string;
10
+ /** controlled selected value for the group */
11
+ selectedValue?: string;
12
+ /** uncontrolled checked (rare; prefer selectedValue on group) */
13
+ checked?: boolean;
14
+ /** called with (value, event) */
15
+ onChange?: (value: string, event: React.ChangeEvent<HTMLInputElement>) => void;
16
+ disabled?: boolean;
17
+ label?: string;
18
+ variant?: Variant;
19
+ size?: Size;
20
+ containerLabel?: string;
21
+ error?: string;
22
+ helpText?: string;
23
+ orientation?: 'vertical' | 'horizontal';
24
+ labelWidth?: string;
25
+ fullWidth?: boolean;
26
+ required?: boolean;
27
+ /**
28
+ * If true, do NOT wrap with InputContainer.
29
+ * Use inside RadioGroup.
30
+ */
31
+ noContainer?: boolean;
32
+ id?: string;
33
+ 'data-cy'?: string;
34
+ }
35
+ export declare function RadioButton({ name, value, selectedValue, checked, onChange, disabled, label, variant, size, containerLabel, error, helpText, orientation, labelWidth, fullWidth, required, noContainer, id, 'data-cy': dataCy, }: RadioButtonProps): JSX.Element;
36
+ export {};
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId } from 'react';
4
+ import styles from './RadioButtons.module.css';
5
+ import { InputContainer } from '../input-container/InputContainer';
6
+ export function RadioButton({ name, value, selectedValue, checked, onChange, disabled, label, variant = 'outlined', size = 'md', containerLabel, error, helpText, orientation = 'horizontal', labelWidth = '160px', fullWidth = false, required = false, noContainer = false, id, 'data-cy': dataCy, }) {
7
+ const generatedId = useId();
8
+ const controlId = id !== null && id !== void 0 ? id : `radio-${generatedId}`;
9
+ const isChecked = selectedValue !== undefined ? selectedValue === value : Boolean(checked);
10
+ const content = (_jsxs("span", { className: styles.container, "data-cy": dataCy, children: [_jsxs("span", { className: styles.controlWrap, children: [_jsx("input", { id: controlId, className: styles.input, type: "radio", name: name, value: value, checked: isChecked, disabled: disabled, required: required, "aria-invalid": Boolean(error) || undefined, onChange: e => {
11
+ if (disabled)
12
+ return;
13
+ onChange === null || onChange === void 0 ? void 0 : onChange(e.target.value, e);
14
+ } }), _jsx("span", { className: [
15
+ styles.radio,
16
+ isChecked ? styles.checked : '',
17
+ disabled ? styles.disabled : '',
18
+ styles[variant],
19
+ styles[size],
20
+ ]
21
+ .filter(Boolean)
22
+ .join(' '), "aria-hidden": "true", children: _jsx("span", { className: styles.dot }) })] }), label && (_jsx("label", { className: styles.label, htmlFor: controlId, children: label }))] }));
23
+ if (noContainer || (!containerLabel && !error))
24
+ return content;
25
+ return (_jsx(InputContainer, { label: containerLabel, htmlFor: controlId, error: error, helpText: helpText, orientation: orientation, labelWidth: labelWidth, fullWidth: fullWidth, required: required, children: content }));
26
+ }
@@ -0,0 +1,25 @@
1
+ import type { JSX } from 'react';
2
+ export type RadioOption<V extends string = string> = {
3
+ label: string;
4
+ value: V;
5
+ disabled?: boolean;
6
+ };
7
+ export interface RadioGroupProps<V extends string = string> {
8
+ name?: string;
9
+ value: V | null;
10
+ onChange: (value: V) => void;
11
+ options: RadioOption<V>[];
12
+ disabled?: boolean;
13
+ label?: string;
14
+ error?: string;
15
+ helpText?: string;
16
+ required?: boolean;
17
+ orientation?: 'vertical' | 'horizontal';
18
+ labelWidth?: string;
19
+ fullWidth?: boolean;
20
+ modified?: boolean;
21
+ /** layout of radios themselves */
22
+ direction?: 'row' | 'column';
23
+ 'data-cy'?: string;
24
+ }
25
+ export declare function RadioButtonGroup<V extends string = string>({ name, value, onChange, options, disabled, label, error, helpText, required, orientation, labelWidth, fullWidth, modified, direction, 'data-cy': dataCy, }: RadioGroupProps<V>): JSX.Element;
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useId } from 'react';
4
+ import { RadioButton } from './RadioButton';
5
+ import { InputContainer } from '../input-container/InputContainer';
6
+ export function RadioButtonGroup({ name, value, onChange, options, disabled = false, label, error, helpText, required = false, orientation = 'horizontal', labelWidth = '160px', fullWidth = false, modified = false, direction = 'column', 'data-cy': dataCy, }) {
7
+ const generated = useId();
8
+ const groupName = name !== null && name !== void 0 ? name : `radio-group-${generated}`;
9
+ const content = (_jsx("div", { role: "radiogroup", "aria-invalid": Boolean(error) || undefined, "data-cy": dataCy, style: {
10
+ display: 'flex',
11
+ flexDirection: direction,
12
+ gap: '8px',
13
+ }, children: options.map(opt => (_jsx(RadioButton, { noContainer: true, name: groupName, value: String(opt.value), selectedValue: value !== null && value !== void 0 ? value : undefined, disabled: disabled || opt.disabled, label: opt.label, required: required, onChange: v => onChange(v), "data-cy": dataCy ? `${dataCy}-option-${String(opt.value)}` : undefined }, String(opt.value)))) }));
14
+ // If no InputContainer props were provided, just return group content
15
+ if (!label && !error)
16
+ return content;
17
+ // Wrap once as a proper field
18
+ return (_jsx(InputContainer, { label: label, htmlFor: undefined, error: error, helpText: helpText, orientation: orientation, labelWidth: labelWidth, fullWidth: fullWidth, required: required, modified: modified, children: content }));
19
+ }
@@ -0,0 +1,117 @@
1
+ .container {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: 8px;
5
+ }
6
+
7
+ .controlWrap {
8
+ position: relative;
9
+ display: inline-flex;
10
+ align-items: center;
11
+ }
12
+
13
+ /* Real input stays for a11y, but visually hidden */
14
+ .input {
15
+ position: absolute;
16
+ inset: 0;
17
+ width: var(--component-size-xs);
18
+ height: var(--component-size-xs);
19
+ margin: 0;
20
+ opacity: 0;
21
+ cursor: pointer;
22
+ }
23
+
24
+ /* The visible radio */
25
+ .radio {
26
+ width: var(--component-size-xs);
27
+ height: var(--component-size-xs);
28
+ border-radius: 999px;
29
+ display: inline-flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+
33
+ background: var(--color-bg-surface);
34
+ border: 2px solid rgba(0, 0, 0, 0.25);
35
+
36
+ transition:
37
+ border-color 120ms ease,
38
+ box-shadow 120ms ease,
39
+ background-color 120ms ease;
40
+ }
41
+
42
+ .dot {
43
+ width: 10px;
44
+ height: 10px;
45
+ border-radius: 999px;
46
+ transform: scale(0);
47
+ transition: transform 120ms ease;
48
+ background: var(--color-brand);
49
+ }
50
+
51
+ /* checked state */
52
+ .checked {
53
+ border-color: var(--color-brand);
54
+ }
55
+ .checked .dot {
56
+ transform: scale(1);
57
+ }
58
+
59
+ /* hover (only when not disabled) */
60
+ .input:not(:disabled):hover + .radio {
61
+ border-color: var(--color-brand-hover);
62
+ }
63
+
64
+ /* focus ring */
65
+ .input:focus-visible + .radio {
66
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-brand) 30%, transparent);
67
+ border-color: var(--color-brand-strong);
68
+ }
69
+
70
+ /* disabled */
71
+ .disabled {
72
+ opacity: 0.5;
73
+ }
74
+ .input:disabled {
75
+ cursor: not-allowed;
76
+ }
77
+
78
+ .label {
79
+ cursor: pointer;
80
+ user-select: none;
81
+ }
82
+
83
+ .primary.checked {
84
+ background: color-mix(in srgb, var(--color-brand) 10%, var(--color-bg-surface));
85
+ }
86
+
87
+ .default {
88
+ border-color: rgba(0, 0, 0, 0.2);
89
+ }
90
+
91
+ /* Sizes */
92
+ .sm {
93
+ width: 16px;
94
+ height: 16px;
95
+ }
96
+ .sm .dot {
97
+ width: 8px;
98
+ height: 8px;
99
+ }
100
+
101
+ .md {
102
+ width: 20px;
103
+ height: 20px;
104
+ }
105
+ .md .dot {
106
+ width: 10px;
107
+ height: 10px;
108
+ }
109
+
110
+ .lg {
111
+ width: 24px;
112
+ height: 24px;
113
+ }
114
+ .lg .dot {
115
+ width: 12px;
116
+ height: 12px;
117
+ }
@@ -18,5 +18,5 @@ export type SelectProps<T> = Omit<InputContainerProps, 'children' | 'htmlFor' |
18
18
  tooltip?: React.ReactNode;
19
19
  tooltipPlacement?: 'top' | 'right' | 'bottom' | 'left';
20
20
  };
21
- export declare function Select<T extends string | number | Record<string, any>>({ label, error, helpText, orientation, labelWidth, fullWidth, required, tooltip, tooltipPlacement, id, options, selectedValue, onChange, placeholder, size, variant, onClear, datakey, dataCy, disabled, }: SelectProps<T>): React.ReactNode;
21
+ export declare function Select<T extends string | number | Record<string, any>>({ label, error, helpText, orientation, labelWidth, fullWidth, required, tooltip, tooltipPlacement, modified, id, options, selectedValue, onChange, placeholder, size, variant, onClear, datakey, dataCy, disabled, }: SelectProps<T>): React.ReactNode;
22
22
  export {};
@@ -10,7 +10,7 @@ import { Popover } from '../../popover/Popover';
10
10
  import { InputContainer } from '../input-container/InputContainer';
11
11
  export function Select({
12
12
  // InputContainer props
13
- label, error, helpText, orientation = 'vertical', labelWidth = '120px', fullWidth = true, required, tooltip, tooltipPlacement = 'right',
13
+ label, error, helpText, orientation = 'vertical', labelWidth = '160px', fullWidth = true, required, tooltip, tooltipPlacement = 'right', modified = false,
14
14
  // Select props
15
15
  id, options, selectedValue, onChange, placeholder = 'Vælg', size, variant = 'outlined', onClear, datakey, dataCy, disabled, }) {
16
16
  const generatedId = useId();
@@ -72,10 +72,10 @@ id, options, selectedValue, onChange, placeholder = 'Vælg', size, variant = 'ou
72
72
  ids.push(tooltipId);
73
73
  return ids.length ? ids.join(' ') : undefined;
74
74
  })();
75
- return (_jsxs(InputContainer, { label: label, htmlFor: controlId, fullWidth: fullWidth, error: error, helpText: helpText, orientation: orientation, labelWidth: labelWidth, required: required, children: [_jsx(Popover, { ref: popoverRef, trigger: (onClick, icon) => (_jsx(Button, { disabled: disabled, ...(tooltipEnabled ? triggerProps : {}), id: controlId, "data-cy": dataCy !== null && dataCy !== void 0 ? dataCy : 'select-button', onKeyDown: handleKeyDown, fullWidth: fullWidth, variant: variant, onClick: e => {
75
+ return (_jsxs(InputContainer, { label: label, htmlFor: controlId, fullWidth: fullWidth, error: error, helpText: helpText, orientation: orientation, labelWidth: labelWidth, required: required, modified: modified, children: [_jsx(Popover, { ref: popoverRef, trigger: (onClick, icon) => (_jsx(Button, { disabled: disabled, ...(tooltipEnabled ? triggerProps : {}), id: controlId, "data-cy": dataCy !== null && dataCy !== void 0 ? dataCy : 'select-button', onKeyDown: handleKeyDown, fullWidth: fullWidth, variant: variant, onClick: e => {
76
76
  setActiveIndex(selectedIndex >= 0 ? selectedIndex : 0);
77
77
  onClick(e);
78
- }, size: size, type: "button", "aria-haspopup": "listbox", "aria-invalid": Boolean(error) || undefined, "aria-describedby": describedBy, children: _jsxs("span", { className: "dbc-flex dbc-justify-between dbc-items-center dbc-gap-xxs", style: { width: '100%' }, children: [_jsx("span", { children: selected ? selected.label : placeholder }), onClear && selected && _jsx(ClearButton, { onClick: onClear }), icon] }) })), children: _jsx(Menu, { onKeyDown: handleKeyDown, role: "listbox", children: options.map((opt, index) => {
78
+ }, size: size, type: "button", "data-forminput": true, "aria-haspopup": "listbox", "aria-invalid": Boolean(error) || undefined, "aria-describedby": describedBy, children: _jsxs("span", { className: "dbc-flex dbc-justify-between dbc-items-center dbc-gap-xxs", style: { width: '100%' }, children: [_jsx("span", { children: selected ? selected.label : placeholder }), onClear && selected && _jsx(ClearButton, { onClick: onClear }), icon] }) })), children: _jsx(Menu, { onKeyDown: handleKeyDown, role: "listbox", children: options.map((opt, index) => {
79
79
  const isSelected = typeof opt.value === 'object' && typeof selectedValue === 'object' && datakey
80
80
  ? (selectedValue === null || selectedValue === void 0 ? void 0 : selectedValue[datakey]) === opt.value[datakey]
81
81
  : opt.value === selectedValue;
@@ -4,9 +4,9 @@ import { useCallback, useId, useMemo } from 'react';
4
4
  import { useTooltipTrigger } from '../../../components/overlay/tooltip/useTooltipTrigger';
5
5
  import styles from './Textarea.module.css';
6
6
  import { InputContainer } from '../input-container/InputContainer';
7
- export const Textarea = function Textarea({ value, inputChanged, disabled, rows = 3, showCount, tooltip, tooltipPlacement = 'right', showTooltip, placeholder, adjustHeight, id,
7
+ export const Textarea = function Textarea({ value, inputChanged, disabled, rows = 3, showCount, tooltip, tooltipPlacement = 'right', showTooltip, placeholder, adjustHeight, id, modified = false,
8
8
  // InputContainer props
9
- label, error, helpText, orientation = 'horizontal', labelWidth = '120px', fullWidth = false, required,
9
+ label, error, helpText, orientation = 'horizontal', labelWidth = '160px', fullWidth = false, required,
10
10
  // Native textarea props
11
11
  className, ...rest }) {
12
12
  const generatedId = useId();
@@ -28,6 +28,6 @@ className, ...rest }) {
28
28
  placement: tooltipPlacement,
29
29
  offset: 8,
30
30
  });
31
- return (_jsx(InputContainer, { label: label, htmlFor: textareaId, error: error, helpText: helpText, helpTextAddition: showCount ? `${value === null || value === void 0 ? void 0 : value.length} tegn i denne boks` : undefined, orientation: orientation, labelWidth: labelWidth, fullWidth: fullWidth, required: required, children: _jsx("div", { className: styles.container, children: _jsx("div", { ...(tooltipEnabled ? triggerProps : {}), children: inputField }) }) }));
31
+ return (_jsx(InputContainer, { modified: modified, label: label, htmlFor: textareaId, error: error, helpText: helpText, helpTextAddition: showCount ? `${value === null || value === void 0 ? void 0 : value.length} tegn i denne boks` : undefined, orientation: orientation, labelWidth: labelWidth, fullWidth: fullWidth, required: required, children: _jsx("div", { className: styles.container, children: _jsx("div", { ...(tooltipEnabled ? triggerProps : {}), children: inputField }) }) }));
32
32
  };
33
33
  Textarea.displayName = 'Textarea';
@@ -9,6 +9,14 @@
9
9
  font-family: var(--font-family);
10
10
  }
11
11
 
12
+ .container textarea:disabled {
13
+ background-color: var(--color-disabled-bg);
14
+ border: 0;
15
+ color: var(--color-disabled-fg);
16
+ cursor: not-allowed;
17
+ opacity: 0.5;
18
+ }
19
+
12
20
  .internalCount {
13
21
  text-align: right;
14
22
  font-size: var(--font-size-sm);
@@ -22,5 +30,4 @@
22
30
  .container textarea:focus-visible {
23
31
  outline: none;
24
32
  border-color: var(--color-border-selected);
25
- box-shadow: var(--focus-ring);
26
33
  }
@@ -10,13 +10,8 @@ interface HeadlineProps extends React.AriaAttributes {
10
10
  subHeadline?: string | JSX.Element;
11
11
  addition?: React.ReactNode;
12
12
  icon?: React.ReactNode;
13
- /**
14
- * Optional visual tone override:
15
- * - dark: force normal foreground colours
16
- * - light: force on-strong/light text
17
- * If omitted, the headline simply inherits its colour from its parent.
18
- */
13
+ allowWrap?: boolean;
19
14
  tone?: HeadlineTone;
20
15
  }
21
- export declare function Headline({ size, marker, disableMargin, children, severity, weight, subHeadline, addition, icon, tone, }: PropsWithChildren<HeadlineProps>): React.ReactNode;
16
+ export declare function Headline({ size, marker, disableMargin, children, severity, weight, subHeadline, addition, icon, tone, allowWrap, }: PropsWithChildren<HeadlineProps>): React.ReactNode;
22
17
  export {};
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import styles from './Headline.module.css';
4
4
  import { SeverityBgColor } from '../../constants/severity';
5
5
  import { Icon } from '../icon/Icon';
6
- export function Headline({ size = 2, marker, disableMargin, children, severity, weight = 600, subHeadline, addition, icon, tone, }) {
6
+ export function Headline({ size = 2, marker, disableMargin, children, severity, weight = 600, subHeadline, addition, icon, tone, allowWrap = true, }) {
7
7
  const Tag = `h${size}`;
8
8
  const containerClassName = [styles.headlineContainer, tone ? styles[`tone-${tone}`] : '']
9
9
  .filter(Boolean)
@@ -15,8 +15,11 @@ export function Headline({ size = 2, marker, disableMargin, children, severity,
15
15
  ]
16
16
  .filter(Boolean)
17
17
  .join(' ');
18
+ const textClassName = [styles.text, allowWrap ? styles.wrap : styles.truncate]
19
+ .filter(Boolean)
20
+ .join(' ');
18
21
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: containerClassName, children: [_jsxs(Tag, { style: {
19
22
  '--font-weight': weight,
20
23
  '--marker-color': severity ? SeverityBgColor[severity] : undefined,
21
- }, className: headlineClassName, children: [icon || (severity && !marker) ? _jsx(Icon, { customIcon: icon, severity: severity }) : null, children] }), addition] }), subHeadline && _jsx("div", { className: styles.subHeadline, children: subHeadline })] }));
24
+ }, className: headlineClassName, children: [icon || (severity && !marker) ? (_jsx("span", { className: styles.icon, children: _jsx(Icon, { customIcon: icon, severity: severity }) })) : null, _jsx("span", { className: textClassName, children: children })] }), addition] }), subHeadline && _jsx("div", { className: styles.subHeadline, children: subHeadline })] }));
22
25
  }
@@ -3,6 +3,7 @@
3
3
  align-items: center;
4
4
  gap: var(--spacing-lg);
5
5
  flex-wrap: wrap;
6
+ max-width: 100%;
6
7
  }
7
8
 
8
9
  /* Base headline: inherit colour from parent surface */
@@ -18,8 +19,6 @@
18
19
  transition: color var(--transition-fast) var(--ease-standard);
19
20
  }
20
21
 
21
- /* Optional tone overrides for special cases (hero sections, etc.) */
22
-
23
22
  .tone-dark .headline {
24
23
  color: var(--color-fg-default);
25
24
  }
@@ -58,3 +57,63 @@
58
57
  margin-block-start: calc(var(--spacing-2xs) * -1);
59
58
  line-height: var(--line-height-normal);
60
59
  }
60
+
61
+ .headline {
62
+ position: relative;
63
+ display: inline-flex;
64
+ align-items: center;
65
+ gap: var(--spacing-xs);
66
+ font-weight: var(--font-weight, var(--font-weight-bold));
67
+ letter-spacing: var(--letter-spacing-tight);
68
+ color: inherit;
69
+ line-height: var(--line-height-tight);
70
+ transition: color var(--transition-fast) var(--ease-standard);
71
+
72
+ /* Needed so truncation works inside flex parents */
73
+ min-width: 0;
74
+ }
75
+
76
+ /* Truncated variant (single line with ellipsis) */
77
+ .truncate {
78
+ white-space: nowrap;
79
+ overflow: hidden;
80
+ text-overflow: ellipsis;
81
+ }
82
+
83
+ .headline {
84
+ position: relative;
85
+ display: inline-flex;
86
+ align-items: center;
87
+ gap: var(--spacing-xs);
88
+ font-weight: var(--font-weight, var(--font-weight-bold));
89
+ letter-spacing: var(--letter-spacing-tight);
90
+ color: inherit;
91
+ line-height: var(--line-height-tight);
92
+ transition: color var(--transition-fast) var(--ease-standard);
93
+
94
+ /* helps inside flex parents */
95
+ min-width: 0;
96
+ }
97
+
98
+ .icon {
99
+ flex: 0 0 auto;
100
+ display: inline-flex;
101
+ align-items: center;
102
+ }
103
+
104
+ /* text wrapper must be the shrinkable element */
105
+ .text {
106
+ min-width: 0;
107
+ flex: 1 1 auto;
108
+ }
109
+
110
+ .truncate {
111
+ overflow: hidden;
112
+ white-space: nowrap;
113
+ text-overflow: ellipsis;
114
+ }
115
+
116
+ .wrap {
117
+ white-space: normal;
118
+ overflow: visible;
119
+ }
@@ -4,6 +4,7 @@ interface HyperlinkProps {
4
4
  className?: string;
5
5
  icon?: React.ReactNode;
6
6
  disableIcon?: boolean;
7
+ onClick?: (e: React.MouseEvent) => void;
7
8
  }
8
9
  export declare function Hyperlink({ component, icon }: HyperlinkProps): React.ReactElement;
9
10
  export {};
@@ -6,6 +6,10 @@ export function Hyperlink({ component, icon }) {
6
6
  return React.cloneElement(component, {
7
7
  ...originalProps,
8
8
  className: styles.link,
9
- onClick: (e) => e.stopPropagation(),
9
+ onClick: (e) => {
10
+ var _a;
11
+ e.stopPropagation();
12
+ (_a = originalProps === null || originalProps === void 0 ? void 0 : originalProps.onClick) === null || _a === void 0 ? void 0 : _a.call(originalProps, e);
13
+ },
10
14
  }, _jsxs(_Fragment, { children: [_jsx("span", { className: styles.content, children: originalProps.children }), icon && _jsx("span", { className: styles.icon, children: icon })] }));
11
15
  }
@@ -4,6 +4,7 @@
4
4
  vertical-align: middle;
5
5
  gap: var(--spacing-xxs);
6
6
  color: var(--color-fg-subtle);
7
+ font-size: var(--font-size-sm);
7
8
  }
8
9
 
9
10
  .icon {
@@ -10,7 +10,7 @@ import { useTooltipTrigger } from '../../components/overlay/tooltip/useTooltipTr
10
10
  import { Popover } from '../../components/popover/Popover';
11
11
  export function IntervalSelect({
12
12
  // InputContainer props
13
- label, error, helpText, orientation = 'vertical', labelWidth = '120px', fullWidth = true, required,
13
+ label, error, helpText, orientation = 'vertical', labelWidth = '160px', fullWidth = true, required,
14
14
  // tooltip
15
15
  tooltip, tooltipPlacement = 'right',
16
16
  // IntervalSelect props
@@ -1,18 +1,36 @@
1
1
  import type { ElementType, ReactNode, JSX } from 'react';
2
- export type NavBarItem = {
3
- component?: ElementType<any>;
2
+ export type NavBarItem = NavBarLinkItem | NavBarExpandableItem | NavBarGroupItem;
3
+ type NavBarBase = {
4
4
  label: string;
5
5
  icon?: ReactNode;
6
+ enabled?: boolean;
7
+ tags?: string[];
8
+ };
9
+ /** Simple clickable item */
10
+ export type NavBarLinkItem = NavBarBase & {
11
+ type?: 'item';
12
+ href: string;
13
+ component?: ElementType<any>;
6
14
  active?: boolean;
7
15
  external?: boolean;
8
- enabled?: boolean;
16
+ };
17
+ /** Clickable + expandable item (has href AND children) */
18
+ export type NavBarExpandableItem = NavBarBase & {
19
+ type: 'expandable';
9
20
  href: string;
10
- tags?: string[];
11
- children?: NavBarItem[];
21
+ component?: ElementType<any>;
22
+ active?: boolean;
23
+ external?: boolean;
24
+ children: NavBarItem[];
25
+ };
26
+ /** Non-clickable group header */
27
+ export type NavBarGroupItem = NavBarBase & {
28
+ type: 'group';
29
+ children: NavBarItem[];
12
30
  };
13
31
  interface NavBarProps {
14
32
  logo?: ReactNode;
15
- items: NavBarItem[];
33
+ items: NavBarLinkItem[];
16
34
  productName?: string;
17
35
  addition?: ReactNode;
18
36
  }
@@ -1,5 +1,5 @@
1
- import React, { ReactNode } from 'react';
2
- import { ModalProps } from '../Modal';
1
+ import React, { type ReactNode } from 'react';
2
+ import { type ModalProps } from '../Modal';
3
3
  type ModalConfig = Omit<ModalProps, 'isOpen' | 'onRequestClose'> & {
4
4
  onRequestClose?: () => void;
5
5
  };
@@ -1,11 +1,14 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { createContext, useCallback, useContext, useState, useRef } from 'react';
3
+ import { createContext, useCallback, useContext, useEffect, useRef, useState, } from 'react';
4
+ import { createPortal } from 'react-dom';
4
5
  import { Modal } from '../Modal';
5
6
  const ModalContext = createContext(undefined);
6
7
  export function ModalProvider({ children }) {
7
8
  const [isOpen, setIsOpen] = useState(false);
8
9
  const [config, setConfig] = useState(null);
10
+ const [mounted, setMounted] = useState(false);
11
+ useEffect(() => setMounted(true), []);
9
12
  // Holds the resolver for the current "confirm" call, if any
10
13
  const pendingResolverRef = useRef(null);
11
14
  const resolvePending = useCallback((value) => {
@@ -24,47 +27,43 @@ export function ModalProvider({ children }) {
24
27
  setIsOpen(true);
25
28
  }, [resolvePending]);
26
29
  const handleRequestClose = useCallback(() => {
27
- if (config === null || config === void 0 ? void 0 : config.onRequestClose) {
28
- config.onRequestClose();
29
- }
30
- // Any non-explicit confirm click counts as "false"
30
+ var _a;
31
+ (_a = config === null || config === void 0 ? void 0 : config.onRequestClose) === null || _a === void 0 ? void 0 : _a.call(config);
31
32
  resolvePending(false);
32
33
  closeModal();
33
34
  }, [config, closeModal, resolvePending]);
34
35
  const confirm = useCallback((confirmConfig) => {
35
36
  return new Promise(resolve => {
36
- // cancel any previous pending confirm
37
37
  resolvePending(false);
38
38
  pendingResolverRef.current = resolve;
39
39
  const { confirmLabel = 'Ok', cancelLabel = 'Annuller', ...rest } = confirmConfig;
40
- const primaryAction = {
41
- label: confirmLabel,
42
- onClick: () => {
43
- resolvePending(true);
44
- closeModal();
45
- },
46
- };
47
- const secondaryAction = {
48
- label: cancelLabel,
49
- onClick: () => {
50
- resolvePending(false);
51
- closeModal();
52
- },
53
- };
54
40
  setConfig({
55
41
  ...rest,
56
- primaryAction,
57
- secondaryAction,
42
+ primaryAction: {
43
+ label: confirmLabel,
44
+ onClick: () => {
45
+ resolvePending(true);
46
+ closeModal();
47
+ },
48
+ },
49
+ secondaryAction: {
50
+ label: cancelLabel,
51
+ onClick: () => {
52
+ resolvePending(false);
53
+ closeModal();
54
+ },
55
+ },
58
56
  });
59
57
  setIsOpen(true);
60
58
  });
61
59
  }, [closeModal, resolvePending]);
62
- return (_jsxs(ModalContext.Provider, { value: { openModal, closeModal, confirm }, children: [children, _jsx(Modal, { ...(config !== null && config !== void 0 ? config : {}), isOpen: isOpen, onRequestClose: handleRequestClose, children: config === null || config === void 0 ? void 0 : config.children })] }));
60
+ const modalNode = (_jsxs(ModalContext.Provider, { value: { openModal, closeModal, confirm }, children: [children, mounted &&
61
+ createPortal(_jsx(Modal, { ...(config !== null && config !== void 0 ? config : {}), isOpen: isOpen, onRequestClose: handleRequestClose, children: config === null || config === void 0 ? void 0 : config.children }), document.body)] }));
62
+ return modalNode;
63
63
  }
64
64
  export function useModal() {
65
65
  const ctx = useContext(ModalContext);
66
- if (!ctx) {
66
+ if (!ctx)
67
67
  throw new Error('useModal must be used within a ModalProvider');
68
- }
69
68
  return ctx;
70
69
  }
@@ -1,16 +1,24 @@
1
- import type { HTMLAttributes, ReactNode } from 'react';
1
+ import React, { type HTMLAttributes, type ReactNode } from 'react';
2
2
  import { Severity } from '../../../constants/severity.types';
3
3
  interface SidePanelProps {
4
4
  children?: ReactNode;
5
5
  header: ReactNode;
6
6
  headerAddition?: ReactNode;
7
- actions: ReactNode;
8
- onClose: () => void;
7
+ actions?: ReactNode;
8
+ onClose: (event?: React.MouseEvent<HTMLButtonElement> | React.MouseEvent<HTMLDivElement>) => void;
9
9
  isOpen: boolean;
10
10
  showBackdrop?: boolean;
11
11
  severity?: Severity;
12
12
  showHeaderMarker?: boolean;
13
13
  width?: number | string;
14
+ /**
15
+ * Optional details pane (separate column).
16
+ */
17
+ details?: ReactNode;
18
+ detailsHeader?: ReactNode;
19
+ detailsWidth?: number | string;
20
+ onCloseDetails?: () => void;
21
+ detailsHeaderAddition?: ReactNode;
14
22
  }
15
- export declare function SidePanel({ isOpen, onClose, children, header, headerAddition, actions, showBackdrop, severity, showHeaderMarker, width, ...props }: SidePanelProps & HTMLAttributes<HTMLElement>): ReactNode;
23
+ export declare function SidePanel({ isOpen, onClose, children, header, headerAddition, actions, showBackdrop, severity, showHeaderMarker, width, details, detailsHeader, detailsWidth, onCloseDetails, detailsHeaderAddition, ...props }: SidePanelProps & HTMLAttributes<HTMLElement>): ReactNode;
16
24
  export {};