@dbcdk/react-components 0.0.14 → 0.0.16

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 (52) hide show
  1. package/dist/components/accordion/Accordion.d.ts +2 -1
  2. package/dist/components/accordion/Accordion.js +14 -3
  3. package/dist/components/accordion/Accordion.module.css +1 -1
  4. package/dist/components/accordion/components/AccordionRow.d.ts +2 -1
  5. package/dist/components/accordion/components/AccordionRow.js +8 -6
  6. package/dist/components/accordion/components/AccordionRow.module.css +12 -8
  7. package/dist/components/button/Button.d.ts +1 -0
  8. package/dist/components/button/Button.js +3 -3
  9. package/dist/components/button/Button.module.css +12 -1
  10. package/dist/components/card/Card.module.css +1 -1
  11. package/dist/components/chip/Chip.js +11 -1
  12. package/dist/components/chip/Chip.module.css +92 -30
  13. package/dist/components/circle/Circle.d.ts +1 -1
  14. package/dist/components/circle/Circle.module.css +5 -1
  15. package/dist/components/clear-button/ClearButton.js +1 -1
  16. package/dist/components/clear-button/ClearButton.module.css +3 -0
  17. package/dist/components/code-block/CodeBlock.d.ts +7 -3
  18. package/dist/components/code-block/CodeBlock.js +35 -2
  19. package/dist/components/code-block/CodeBlock.module.css +49 -2
  20. package/dist/components/filter-field/FilterField.d.ts +2 -1
  21. package/dist/components/filter-field/FilterField.js +2 -2
  22. package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.d.ts +5 -3
  23. package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.js +4 -3
  24. package/dist/components/forms/checkbox/Checkbox.d.ts +1 -1
  25. package/dist/components/forms/checkbox/Checkbox.module.css +11 -0
  26. package/dist/components/hyperlink/Hyperlink.module.css +0 -1
  27. package/dist/components/overlay/modal/Modal.js +1 -1
  28. package/dist/components/overlay/side-panel/SidePanel.js +1 -1
  29. package/dist/components/page-layout/PageLayout.module.css +0 -2
  30. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +1 -1
  31. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +1 -1
  32. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +0 -4
  33. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.d.ts +2 -1
  34. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.js +2 -2
  35. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css +9 -0
  36. package/dist/components/split-pane/SplitPane.js +1 -1
  37. package/dist/components/state-page/StatePage.module.css +1 -1
  38. package/dist/components/table/Table.js +18 -3
  39. package/dist/components/table/Table.module.css +35 -30
  40. package/dist/components/table/components/empty-state/EmptyState.d.ts +7 -22
  41. package/dist/components/table/components/empty-state/EmptyState.js +12 -8
  42. package/dist/components/table/components/empty-state/EmptyState.module.css +2 -14
  43. package/dist/components/tabs/Tabs.js +1 -1
  44. package/dist/components/tabs/Tabs.module.css +1 -1
  45. package/dist/components/toast/Toast.js +1 -1
  46. package/dist/hooks/useTableSelection.d.ts +1 -1
  47. package/dist/hooks/useTableSelection.js +97 -77
  48. package/dist/hooks/useViewportFill.js +12 -0
  49. package/dist/src/styles/styles.css +8 -0
  50. package/dist/styles/styles.css +8 -0
  51. package/dist/styles/themes/dbc/base.css +1 -0
  52. package/package.json +1 -1
@@ -2,6 +2,7 @@ import type { JSX, ReactNode } from 'react';
2
2
  import type { Severity } from '../../constants/severity.types';
3
3
  export interface AccordionItem {
4
4
  header: string;
5
+ headerAddition?: ReactNode;
5
6
  headerIcon?: ReactNode;
6
7
  severity?: Severity;
7
8
  children: ReactNode;
@@ -15,7 +16,7 @@ export interface AccordionProps {
15
16
  size?: Size;
16
17
  /** Uncontrolled defaults */
17
18
  defaultOpenIndex?: number | null;
18
- defaultOpenIndexes?: number[];
19
+ defaultOpenIndexes?: number[] | 'all';
19
20
  /** Controlled state */
20
21
  openIndex?: number | null;
21
22
  openIndexes?: number[];
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { useId, useMemo, useState } from 'react';
3
+ import { useEffect, useId, useMemo, useRef, useState } from 'react';
4
4
  import styles from './Accordion.module.css';
5
5
  import { AccordionRow } from './components/AccordionRow';
6
6
  function uniqSorted(nums) {
@@ -12,8 +12,19 @@ function normalizeMultiple(indexes) {
12
12
  export function Accordion({ items, mode = 'single', size = 'md', defaultOpenIndex = null, defaultOpenIndexes = [], openIndex, openIndexes, onOpenIndexChange, onOpenIndexesChange, }) {
13
13
  const uid = useId();
14
14
  const isControlled = mode === 'single' ? openIndex !== undefined : openIndexes !== undefined;
15
+ // Disable animation on initial render (for default open state).
16
+ const hasMountedRef = useRef(false);
17
+ useEffect(() => {
18
+ hasMountedRef.current = true;
19
+ }, []);
15
20
  const [internalSingle, setInternalSingle] = useState(mode === 'single' ? defaultOpenIndex : null);
16
- const [internalMultiple, setInternalMultiple] = useState(mode === 'multiple' ? normalizeMultiple(defaultOpenIndexes) : []);
21
+ const [internalMultiple, setInternalMultiple] = useState(() => {
22
+ if (mode !== 'multiple')
23
+ return [];
24
+ if (defaultOpenIndexes === 'all')
25
+ return items.map((_, i) => i);
26
+ return normalizeMultiple(defaultOpenIndexes);
27
+ });
17
28
  const currentOpenIndexes = useMemo(() => {
18
29
  if (mode === 'single') {
19
30
  const current = isControlled ? (openIndex !== null && openIndex !== void 0 ? openIndex : null) : internalSingle;
@@ -55,5 +66,5 @@ export function Accordion({ items, mode = 'single', size = 'md', defaultOpenInde
55
66
  else
56
67
  commit([...currentOpenIndexes, index]);
57
68
  }
58
- return (_jsx("div", { className: `${styles.container} ${styles[size]}`, children: items.map((item, i) => (_jsx(AccordionRow, { uid: uid, index: i, item: item, isOpen: openSet.has(i), onToggle: toggle }, i))) }));
69
+ return (_jsx("div", { className: `${styles.container} ${styles[size]}`, children: items.map((item, i) => (_jsx(AccordionRow, { uid: uid, index: i, item: item, isOpen: openSet.has(i), onToggle: toggle, shouldAnimate: hasMountedRef.current }, i))) }));
59
70
  }
@@ -5,7 +5,7 @@
5
5
  display: flex;
6
6
  flex-direction: column;
7
7
  overflow: hidden;
8
- gap: var(--spacing-xxs);
8
+ gap: var(--spacing-xs);
9
9
  }
10
10
 
11
11
  /* Size variables (consumed by AccordionRow) */
@@ -6,5 +6,6 @@ export type AccordionRowProps = {
6
6
  item: AccordionItem;
7
7
  isOpen: boolean;
8
8
  onToggle: (index: number) => void;
9
+ shouldAnimate?: boolean;
9
10
  };
10
- export declare function AccordionRow({ uid, index, item, isOpen, onToggle, }: AccordionRowProps): JSX.Element;
11
+ export declare function AccordionRow({ uid, index, item, isOpen, onToggle, shouldAnimate, }: AccordionRowProps): JSX.Element;
@@ -3,7 +3,7 @@ import { ChevronDown } from 'lucide-react';
3
3
  import { useLayoutEffect, useRef, useState } from 'react';
4
4
  import { Headline } from '../../../components/headline/Headline';
5
5
  import styles from './AccordionRow.module.css';
6
- function useCollapsibleHeight(isOpen) {
6
+ function useCollapsibleHeight(isOpen, shouldAnimate) {
7
7
  const innerRef = useRef(null);
8
8
  const [height, setHeight] = useState('0px');
9
9
  useLayoutEffect(() => {
@@ -13,7 +13,9 @@ function useCollapsibleHeight(isOpen) {
13
13
  return;
14
14
  const prefersReduced = typeof window !== 'undefined' &&
15
15
  ((_b = (_a = window.matchMedia) === null || _a === void 0 ? void 0 : _a.call(window, '(prefers-reduced-motion: reduce)')) === null || _b === void 0 ? void 0 : _b.matches);
16
- if (prefersReduced) {
16
+ // No animation on first render when default-open is used:
17
+ // just set the final height.
18
+ if (!shouldAnimate || prefersReduced) {
17
19
  setHeight(isOpen ? 'auto' : '0px');
18
20
  return;
19
21
  }
@@ -31,7 +33,7 @@ function useCollapsibleHeight(isOpen) {
31
33
  setHeight('0px');
32
34
  });
33
35
  }
34
- }, [isOpen]);
36
+ }, [isOpen, shouldAnimate]);
35
37
  function onTransitionEnd(e) {
36
38
  if (e.propertyName !== 'height')
37
39
  return;
@@ -42,10 +44,10 @@ function useCollapsibleHeight(isOpen) {
42
44
  }
43
45
  return { innerRef, height, onTransitionEnd };
44
46
  }
45
- export function AccordionRow({ uid, index, item, isOpen, onToggle, }) {
47
+ export function AccordionRow({ uid, index, item, isOpen, onToggle, shouldAnimate = true, }) {
46
48
  const isDisabled = !!item.disabled;
47
49
  const buttonId = `${uid}-acc-btn-${index}`;
48
50
  const panelId = `${uid}-acc-panel-${index}`;
49
- const { innerRef, height, onTransitionEnd } = useCollapsibleHeight(isOpen);
50
- return (_jsxs("section", { className: `${styles.item} ${isOpen ? styles.open : ''} ${isDisabled ? styles.disabled : ''}`, children: [_jsx("h3", { className: styles.heading, children: _jsxs("button", { type: "button", id: buttonId, className: styles.trigger, "aria-expanded": isOpen, "aria-controls": panelId, onClick: () => onToggle(index), disabled: isDisabled, children: [_jsxs("span", { className: styles.title, children: [item.headerIcon ? _jsx("span", { className: styles.icon, children: item.headerIcon }) : null, _jsx(Headline, { disableMargin: true, size: 4, weight: 500, severity: item.severity, allowWrap: isOpen, children: item.header })] }), _jsx("span", { className: styles.chevron, "aria-hidden": "true", children: _jsx(ChevronDown, {}) })] }) }), _jsx("div", { id: panelId, role: "region", "aria-labelledby": buttonId, className: styles.panel, style: { height }, onTransitionEnd: onTransitionEnd, children: _jsx("div", { ref: innerRef, className: styles.content, children: item.children }) })] }));
51
+ const { innerRef, height, onTransitionEnd } = useCollapsibleHeight(isOpen, shouldAnimate);
52
+ return (_jsxs("section", { className: `${styles.item} ${isOpen ? styles.open : ''} ${isDisabled ? styles.disabled : ''}`, children: [_jsx("div", { children: _jsxs("button", { type: "button", id: buttonId, className: styles.trigger, "aria-expanded": isOpen, "aria-controls": panelId, onClick: () => onToggle(index), disabled: isDisabled, children: [_jsxs("span", { className: styles.title, children: [item.headerIcon ? _jsx("span", { className: styles.icon, children: item.headerIcon }) : null, _jsx(Headline, { disableMargin: true, size: 4, weight: 500, severity: item.severity, allowWrap: isOpen, children: item.header }), item.headerAddition] }), _jsx("span", { className: styles.chevron, "aria-hidden": "true", children: _jsx(ChevronDown, {}) })] }) }), _jsx("div", { id: panelId, role: "region", "aria-labelledby": buttonId, className: `${styles.panel} ${shouldAnimate ? styles.animate : styles.noAnimate}`, style: { height }, onTransitionEnd: onTransitionEnd, children: _jsx("div", { ref: innerRef, className: styles.content, children: item.children }) })] }));
51
53
  }
@@ -1,7 +1,3 @@
1
- .heading {
2
- margin: 0;
3
- }
4
-
5
1
  .trigger {
6
2
  all: unset;
7
3
  box-sizing: border-box;
@@ -16,7 +12,7 @@
16
12
  user-select: none;
17
13
 
18
14
  padding: var(--acc-trigger-py) var(--acc-trigger-px);
19
- background: var(--color-bg-contextual);
15
+ background: var(--color-bg-contextual-subtle);
20
16
 
21
17
  min-width: 0;
22
18
  }
@@ -35,10 +31,10 @@
35
31
  display: flex;
36
32
  align-items: center;
37
33
  gap: var(--spacing-xs);
38
-
39
34
  min-width: 0;
40
35
  flex: 1 1 auto;
41
36
  overflow: hidden;
37
+ justify-content: space-between;
42
38
  }
43
39
 
44
40
  .icon {
@@ -67,12 +63,20 @@
67
63
  .panel {
68
64
  overflow: hidden;
69
65
  height: 0px;
66
+ }
67
+
68
+ /* Default: animate only when class is present */
69
+ .animate {
70
70
  transition: height var(--transition-slow) var(--ease-decelerate);
71
71
  }
72
72
 
73
- /* Padding inside so scrollHeight includes it */
73
+ /* No animation (initial render default-open) */
74
+ .noAnimate {
75
+ transition: none;
76
+ }
77
+
74
78
  .content {
75
- padding: var(--acc-content-py) var(--acc-trigger-px);
79
+ padding: var(--acc-content-py) 0;
76
80
  }
77
81
 
78
82
  @media (prefers-reduced-motion: reduce) {
@@ -4,6 +4,7 @@ export type ButtonVariant = 'primary' | 'secondary' | 'outlined' | 'default' | '
4
4
  export type ButtonSize = Exclude<Size, 'xl'>;
5
5
  export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
6
6
  variant?: ButtonVariant;
7
+ shape?: 'default' | 'rounded' | 'round';
7
8
  size?: ButtonSize;
8
9
  fullWidth?: boolean;
9
10
  icon?: React.ReactNode;
@@ -19,9 +19,9 @@ function mergeRefs(...refs) {
19
19
  }
20
20
  };
21
21
  }
22
- export const Button = React.forwardRef(function Button({ variant = 'outlined', size = 'md', fullWidth, icon, children, loading, active, spinIcon, tooltip, tooltipPlacement = 'top', isLink, type = 'button', ...rest }, ref) {
22
+ export const Button = React.forwardRef(function Button({ variant = 'outlined', shape = 'default', size = 'md', fullWidth, icon, children, loading, active, spinIcon, tooltip, tooltipPlacement = 'top', isLink, type = 'button', ...rest }, ref) {
23
23
  const { className: userClassName, ...buttonProps } = rest;
24
- const computedClassName = cx(styles.button, styles[variant], styles[size], fullWidth ? styles.fullWidth : '', active ? styles.active : '', loading ? styles.loading : '', isLink ? styles.link : '', userClassName);
24
+ const computedClassName = cx(styles.button, styles[variant], styles[size], fullWidth ? styles.fullWidth : '', active ? styles.active : '', loading ? styles.loading : '', shape !== 'default' ? styles[shape] : '', userClassName);
25
25
  const tooltipEnabled = Boolean(tooltip);
26
26
  // Tooltip anchored to the actual clickable element (button or link element)
27
27
  const { triggerProps, id: tooltipId } = useTooltipTrigger({
@@ -46,7 +46,7 @@ export const Button = React.forwardRef(function Button({ variant = 'outlined', s
46
46
  buttonEl = React.cloneElement(children, {
47
47
  ...buttonProps,
48
48
  ref: mergeRefs(childRef, ref),
49
- className: cx(childClassName, computedClassName),
49
+ className: cx(childClassName, computedClassName, styles.buttonLink),
50
50
  ...(tooltipEnabled ? triggerProps : {}),
51
51
  'aria-describedby': describedBy,
52
52
  children: (_jsxs(_Fragment, { children: [icon && _jsx("span", { className: cx(styles.icon, spinIcon ? 'spin' : ''), children: icon }), children.props.children, loading && (_jsx("span", { style: { display: 'flex', opacity: 0.5 }, className: "spin", children: _jsx(LoaderCircle, {}) }))] })),
@@ -27,6 +27,10 @@
27
27
  user-select: none;
28
28
  white-space: nowrap;
29
29
  }
30
+ .buttonLink {
31
+ color: inherit;
32
+ text-decoration: none;
33
+ }
30
34
 
31
35
  .button:hover {
32
36
  transition:
@@ -40,6 +44,13 @@
40
44
  box-shadow: var(--focus-ring);
41
45
  }
42
46
 
47
+ .round {
48
+ border-radius: 100%;
49
+ }
50
+
51
+ .rounded {
52
+ border-radius: var(--border-radius-rounded);
53
+ }
43
54
  .button:disabled,
44
55
  .button[aria-disabled='true'] {
45
56
  cursor: not-allowed;
@@ -93,7 +104,7 @@
93
104
  .button.sm {
94
105
  height: var(--component-size-sm);
95
106
  min-block-size: var(--component-size-sm);
96
- padding-inline: var(--spacing-sm);
107
+ padding-inline: var(--spacing-xs);
97
108
  }
98
109
 
99
110
  .button.xs {
@@ -28,7 +28,7 @@
28
28
  }
29
29
 
30
30
  .variantSubtle {
31
- background-color: var(--card-bg-subtle, var(--color-bg-surface-subtle));
31
+ background-color: var(--card-bg-subtle, var(--color-bg-contextual-subtle));
32
32
  }
33
33
 
34
34
  .variantStrong {
@@ -16,5 +16,15 @@ export function Chip({ children, severity = 'neutral', loading, disableIcon = tr
16
16
  if (loading) {
17
17
  return _jsx(SkeletonLoaderItem, { width: chipWidth !== null && chipWidth !== void 0 ? chipWidth : '100px', height: "26px", radius: "25px" });
18
18
  }
19
- return (_jsxs("div", { ref: chipRef, className: `${styles.container} ${severity ? styles[severity] : ''} ${fullWidth ? styles.fullWidth : ''} ${styles[size]} ${styles[type]}`, children: [severity && !disableIcon && _jsx(Icon, { severity: severity }), customIcon, children, onClose && (_jsx("button", { onClick: onClose, className: styles.close, children: _jsx(X, {}) }))] }));
19
+ const hasLeading = Boolean(customIcon) || (Boolean(severity) && !disableIcon);
20
+ const hasClose = typeof onClose === 'function';
21
+ return (_jsxs("div", { ref: chipRef, className: [
22
+ styles.container,
23
+ severity ? styles[severity] : '',
24
+ fullWidth ? styles.fullWidth : '',
25
+ styles[size],
26
+ styles[type],
27
+ hasLeading ? styles.hasLeading : '',
28
+ hasClose ? styles.hasClose : '',
29
+ ].join(' '), children: [hasLeading ? (_jsxs("span", { className: styles.leading, children: [severity && !disableIcon && _jsx(Icon, { severity: severity }), customIcon] })) : null, _jsx("span", { className: styles.label, children: children }), hasClose && (_jsx("button", { type: "button", onClick: onClose, className: styles.close, "aria-label": "Luk", children: _jsx(X, {}) }))] }));
20
30
  }
@@ -1,18 +1,23 @@
1
1
  .container {
2
+ /* local layout tokens */
3
+ --chip-gap: var(--spacing-xxs);
4
+ --chip-pad-y: var(--spacing-xxs);
5
+ --chip-pad-x: var(--spacing-md);
6
+
7
+ /* when no icon, compensate by half the gap on each side */
8
+ --chip-pad-x-noicon: calc(var(--chip-pad-x) + (var(--chip-gap) / 2));
9
+
2
10
  display: inline-flex;
3
11
  align-items: center;
4
- gap: var(--space-inline-sm);
12
+ gap: var(--chip-gap);
5
13
  white-space: nowrap;
6
14
 
7
- /* Neutral pill background + text */
8
-
9
15
  color: var(--chip-fg-default, var(--color-fg-default));
10
-
11
- border-radius: var(--border-radius-default);
12
16
  border: var(--border-width-thin) solid transparent;
17
+ border-radius: var(--border-radius-default);
13
18
 
14
- padding-block: var(--spacing-xxs);
15
- padding-inline: var(--spacing-md);
19
+ padding-block: var(--chip-pad-y);
20
+ padding-inline: var(--chip-pad-x-noicon);
16
21
 
17
22
  font-family: var(--font-family);
18
23
  font-size: var(--font-size-sm);
@@ -25,71 +30,126 @@
25
30
  box-shadow var(--transition-fast) var(--ease-standard);
26
31
  }
27
32
 
28
- .container.rounded {
33
+ /* Keep your preferred pill look */
34
+ .rounded {
29
35
  border-radius: var(--border-radius-rounded);
30
36
  }
31
37
 
32
- .container.outlined {
38
+ .outlined {
33
39
  border-radius: var(--border-radius-rounded);
34
40
  border: var(--border-width-thin) solid var(--color-border-default);
41
+ background-color: transparent;
35
42
  }
36
43
 
37
- .container svg {
38
- inline-size: var(--icon-size-md);
39
- block-size: var(--icon-size-md);
44
+ .default {
45
+ border-radius: var(--border-radius-default);
46
+ }
47
+
48
+ /* ---- Leading icon logic ---- */
49
+ .hasLeading {
50
+ /* symmetric tighter padding when icon exists */
51
+ padding-left: var(--spacing-xs);
52
+ padding-right: var(--spacing-xs);
53
+ }
54
+
55
+ /* Leading slot */
56
+ .leading {
57
+ display: inline-flex;
58
+ align-items: center;
59
+ justify-content: center;
60
+ flex: 0 0 auto;
61
+ }
62
+
63
+ .label {
64
+ display: inline-flex;
65
+ align-items: center;
66
+ min-width: 0;
40
67
  }
41
68
 
42
- .sm svg {
69
+ /* ---- Icon sizing ---- */
70
+ .leading svg,
71
+ .close svg {
43
72
  inline-size: var(--icon-size-sm);
44
73
  block-size: var(--icon-size-sm);
74
+ color: currentColor;
75
+ }
76
+
77
+ .sm .leading svg,
78
+ .sm .close svg {
79
+ inline-size: 14px;
80
+ block-size: 14px;
81
+ }
82
+
83
+ .lg .leading svg,
84
+ .lg .close svg {
85
+ inline-size: var(--icon-size-md);
86
+ block-size: var(--icon-size-md);
45
87
  }
46
88
 
47
- /* Small size */
89
+ /* ---- Sizes ---- */
48
90
  .sm {
91
+ --chip-pad-y: var(--spacing-2xs);
92
+ --chip-pad-x: var(--spacing-xs);
93
+ /* keep same gap unless you explicitly want tighter; if you do, change only here */
94
+ /* --chip-gap: var(--spacing-2xs); */
95
+
49
96
  height: var(--component-size-xs);
50
- padding-block: var(--spacing-xxs);
51
- padding-inline: var(--spacing-xs);
52
97
  font-size: var(--font-size-xs);
98
+
99
+ /* recompute derived value */
100
+ --chip-pad-x-noicon: calc(var(--chip-pad-x) + (var(--chip-gap) / 2));
101
+
102
+ padding-block: var(--chip-pad-y);
103
+ padding-inline: var(--chip-pad-x-noicon);
53
104
  }
54
105
 
55
- /* Inverted variant: for dark / strong backgrounds */
56
- .container.invert {
57
- background-color: transparent;
58
- color: var(--color-fg-on-strong);
59
- border-color: transparent;
106
+ .sm.hasLeading {
107
+ padding-left: var(--spacing-xxs);
108
+ padding-right: var(--spacing-xxs);
60
109
  }
61
110
 
62
- /* Status variants (use status tokens) */
111
+ .md {
112
+ height: var(--component-size-sm);
113
+ }
114
+
115
+ .lg {
116
+ --chip-pad-y: var(--spacing-xs);
117
+
118
+ height: var(--component-size-md);
119
+ padding-block: var(--chip-pad-y);
120
+ }
63
121
 
64
- .container.neutral {
122
+ /* ---- Variants / severities ---- */
123
+ .neutral {
65
124
  background-color: var(--color-bg-contextual);
125
+ border-color: transparent;
66
126
  }
67
- .container.success {
127
+
128
+ .success {
68
129
  background-color: var(--color-status-success-bg);
69
130
  color: var(--color-status-success-fg);
70
131
  border-color: var(--color-status-success-border);
71
132
  }
72
133
 
73
- .container.warning {
134
+ .warning {
74
135
  background-color: var(--color-status-warning-bg);
75
136
  color: var(--color-status-warning-fg);
76
137
  border-color: var(--color-status-warning-border);
77
138
  }
78
139
 
79
- .container.error {
140
+ .error {
80
141
  background-color: var(--color-status-error-bg);
81
142
  color: var(--color-status-error-fg);
82
143
  border-color: var(--color-status-error-border);
83
144
  }
84
145
 
85
- .container.info {
146
+ .info {
86
147
  background-color: var(--color-status-info-bg);
87
148
  color: var(--color-status-info-fg);
88
149
  border-color: var(--color-status-info-border);
89
150
  }
90
151
 
91
- /* Brand variant */
92
- .container.brand {
152
+ .brand {
93
153
  background-color: var(--color-brand);
94
154
  color: var(--color-fg-on-brand);
95
155
  border-color: transparent;
@@ -101,7 +161,7 @@
101
161
  justify-content: center;
102
162
  }
103
163
 
104
- /* Close button inside chip */
164
+ /* Close button */
105
165
  .close {
106
166
  all: unset;
107
167
  display: inline-flex;
@@ -109,9 +169,11 @@
109
169
  justify-content: center;
110
170
  cursor: pointer;
111
171
  color: inherit;
172
+
112
173
  inline-size: var(--icon-size-md);
113
174
  block-size: var(--icon-size-md);
114
175
  border-radius: var(--border-radius-round);
176
+
115
177
  transition: background-color var(--transition-fast) var(--ease-standard);
116
178
  }
117
179
 
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode, JSX } from 'react';
2
2
  import { Severity } from '../../constants/severity.types';
3
- type CircleSize = 'xs' | 'sm' | 'md' | 'lg';
3
+ type CircleSize = '2xs' | 'xs' | 'sm' | 'md' | 'lg';
4
4
  interface CircleProps {
5
5
  severity: Severity;
6
6
  children?: ReactNode;
@@ -5,7 +5,6 @@
5
5
  gap: var(--spacing-xs);
6
6
  white-space: nowrap;
7
7
  font-family: var(--font-family);
8
- font-size: var(--font-size-sm);
9
8
  color: var(--color-fg-default);
10
9
  }
11
10
 
@@ -62,6 +61,11 @@
62
61
  box-shadow: 0 0 0 2px var(--color-status-info-bg);
63
62
  }
64
63
 
64
+ .circle[data-size='2xs'] {
65
+ inline-size: var(--component-size-2xs);
66
+ block-size: var(--component-size-2xs);
67
+ }
68
+
65
69
  .circle[data-size='xs'] {
66
70
  inline-size: var(--component-size-xxs);
67
71
  block-size: var(--component-size-xxs);
@@ -5,5 +5,5 @@ export function ClearButton({ onClick, absolute }) {
5
5
  return (_jsx("span", { className: `${styles.clearButton} ${absolute ? styles.absolute : ''}`, children: _jsx("span", { className: styles.button, role: "button", onClick: e => {
6
6
  e.stopPropagation();
7
7
  onClick();
8
- }, children: _jsx(X, { size: 20 }) }) }));
8
+ }, children: _jsx(X, { size: 16 }) }) }));
9
9
  }
@@ -3,6 +3,9 @@
3
3
  align-items: center;
4
4
  justify-content: center;
5
5
  color: var(--color-fg-subtle);
6
+ padding: var(--spacing-xxs);
7
+ cursor: pointer;
8
+ border-radius: 100%;
6
9
  }
7
10
 
8
11
  .clearButton .button:hover {
@@ -1,7 +1,11 @@
1
- import type { JSX } from 'react';
1
+ import type { JSX, ReactNode } from 'react';
2
2
  export interface CodeBlockProps {
3
- code: string;
3
+ code?: string;
4
+ children?: ReactNode;
4
5
  copyButton?: boolean;
6
+ copyText?: string;
5
7
  size?: 'sm' | 'md' | 'lg';
8
+ smart?: boolean;
9
+ wrap?: boolean;
6
10
  }
7
- export declare function CodeBlock({ code, copyButton, size }: CodeBlockProps): JSX.Element;
11
+ export declare function CodeBlock({ code, children, copyButton, copyText, size, smart, wrap, }: CodeBlockProps): JSX.Element;
@@ -1,6 +1,39 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import styles from './CodeBlock.module.css';
3
3
  import { CopyButton } from '../copy-button/CopyButton';
4
- export function CodeBlock({ code, copyButton, size = 'md' }) {
5
- return (_jsxs("pre", { className: `${styles.container} ${styles[size]}`, tabIndex: 0, children: [copyButton && (_jsx("span", { className: styles.copyButton, children: _jsx(CopyButton, { style: "link", text: code }) })), _jsx("code", { className: styles.code, children: code })] }));
4
+ const looksLikeStackFrame = (line) => {
5
+ const t = line.trim();
6
+ return (t.startsWith('at ') || // Java, JS
7
+ t.startsWith('...') ||
8
+ t.startsWith('Caused by:') ||
9
+ t.startsWith('Suppressed:') ||
10
+ t.startsWith('Traceback') || // Python header
11
+ t.startsWith('File ') || // Python
12
+ t.startsWith('↳') || // some tools
13
+ /^at\s+\w/.test(t));
14
+ };
15
+ export function CodeBlock({ code, children, copyButton, copyText, size = 'md', smart = true, wrap = true, }) {
16
+ var _a;
17
+ const text = typeof code === 'string' ? code : undefined;
18
+ const copy = (_a = copyText !== null && copyText !== void 0 ? copyText : text) !== null && _a !== void 0 ? _a : '';
19
+ // If children are provided, render them as-is (no line processing).
20
+ const hasChildren = children !== undefined && children !== null;
21
+ // Smart rendering only when we have plain text + no children.
22
+ const lines = smart && !hasChildren && typeof text === 'string' ? text.split('\n') : null;
23
+ return (_jsxs("pre", { className: [styles.container, styles[size], wrap ? styles.wrap : styles.noWrap].join(' '), tabIndex: 0, children: [copyButton && (_jsx("span", { className: styles.copyButton, children: _jsx(CopyButton, { shape: "round", variant: "inline", text: copy }) })), _jsx("code", { className: styles.code, children: hasChildren
24
+ ? children
25
+ : lines
26
+ ? lines.map((line, i) => {
27
+ const isFirst = i === 0;
28
+ const isFrame = looksLikeStackFrame(line);
29
+ const cls = [
30
+ styles.line,
31
+ isFirst ? styles.lineFirst : '',
32
+ isFrame ? styles.lineFrame : '',
33
+ ]
34
+ .filter(Boolean)
35
+ .join(' ');
36
+ return (_jsxs("span", { className: cls, children: [line, '\n'] }, i));
37
+ })
38
+ : text })] }));
6
39
  }
@@ -47,8 +47,6 @@
47
47
  margin: 0;
48
48
  font-family: var(--font-family-mono);
49
49
  color: var(--color-fg-default);
50
-
51
- /* ✅ Preserve formatting but avoid ugly breaking */
52
50
  white-space: pre-wrap;
53
51
  overflow-wrap: anywhere;
54
52
  word-break: normal; /* <- not break-all */
@@ -71,3 +69,52 @@
71
69
  opacity: 1;
72
70
  pointer-events: auto;
73
71
  }
72
+
73
+ /* --- New: wrap control --- */
74
+ .wrap .code {
75
+ white-space: pre-wrap;
76
+ overflow-wrap: anywhere;
77
+ word-break: normal;
78
+ }
79
+
80
+ .noWrap .code {
81
+ white-space: pre;
82
+ overflow-wrap: normal;
83
+ word-break: normal;
84
+ }
85
+
86
+ /* --- New: per-line styling (for smart mode) --- */
87
+ .line {
88
+ display: inline; /* keep selection/copy behavior natural */
89
+ }
90
+
91
+ .lineFirst {
92
+ font-family: inherit;
93
+ font-weight: 500;
94
+ color: var(--color-fg-default);
95
+ }
96
+
97
+ /* Common stack frames are “noise”; deemphasize without hiding */
98
+ .lineFrame {
99
+ color: var(--color-fg-subtle);
100
+ }
101
+
102
+ /* Optional: make the container a bit denser for logs */
103
+ .container {
104
+ /* keep your existing properties ... */
105
+
106
+ /* Easy win: stacktraces feel less tall */
107
+ line-height: 1.35; /* instead of relaxed */
108
+ }
109
+
110
+ /* Optional: differentiate the code area slightly from surrounding UI */
111
+ .container {
112
+ background: var(--color-bg-surface-strong); /* a touch more neutral than contextual */
113
+ border-color: var(--color-border-subtle);
114
+ }
115
+
116
+ /* Keep focus ring behavior */
117
+ .container:focus-within {
118
+ border-color: var(--color-border-selected);
119
+ box-shadow: var(--shadow-xs), var(--focus-ring);
120
+ }
@@ -23,9 +23,10 @@ export interface FilterFieldProps extends Omit<React.InputHTMLAttributes<HTMLInp
23
23
  label?: string;
24
24
  placeholder?: string;
25
25
  disabled?: boolean;
26
+ width?: string;
26
27
  }
27
28
  export declare const NUMBER_OPERATORS: Operator[];
28
- export declare function FilterField({ field, control, operator, value, onChange, operators, options, single, size, label, placeholder, disabled, 'data-cy': dataCy, ...inputProps }: FilterFieldProps & {
29
+ export declare function FilterField({ field, control, operator, value, onChange, operators, options, single, size, label, placeholder, disabled, 'data-cy': dataCy, width, ...inputProps }: FilterFieldProps & {
29
30
  'data-cy'?: string;
30
31
  }): React.ReactElement;
31
32
  export {};