@drivenets/design-system 0.16.0 → 0.18.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 (176) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/index.cjs +1893 -824
  3. package/dist/index.d.cts +363 -247
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +363 -247
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1896 -829
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +25 -25
  11. package/src/components/ds-alert-banner/ds-alert-banner-global.figma.ts +49 -0
  12. package/src/components/ds-alert-banner/ds-alert-banner.figma.ts +51 -0
  13. package/src/components/ds-alert-banner/ds-alert-banner.tsx +7 -6
  14. package/src/components/ds-autocomplete/ds-autocomplete.figma.ts +2 -2
  15. package/src/components/ds-autocomplete/ds-autocomplete.module.scss +15 -4
  16. package/src/components/ds-autocomplete/ds-autocomplete.tsx +10 -3
  17. package/src/components/ds-autocomplete/ds-autocomplete.types.ts +8 -0
  18. package/src/components/ds-avatar/ds-avatar.figma.ts +2 -2
  19. package/src/components/ds-avatar-group/ds-avatar-group.figma.ts +2 -2
  20. package/src/components/ds-breadcrumb/ds-breadcrumb.figma.ts +2 -2
  21. package/src/components/ds-button-v3/ds-button-v3.figma.ts +7 -5
  22. package/src/components/ds-button-v3/ds-button-v3.module.scss +84 -17
  23. package/src/components/ds-button-v3/ds-button-v3.tsx +2 -0
  24. package/src/components/ds-button-v3/ds-button-v3.types.ts +11 -2
  25. package/src/components/ds-button-v3/ds-button-v4.figma.ts +121 -0
  26. package/src/components/ds-card/figma/ds-card-content.figma.ts +2 -2
  27. package/src/components/ds-card/figma/ds-card-footer.figma.ts +2 -2
  28. package/src/components/ds-card/figma/ds-card-header.figma.ts +2 -2
  29. package/src/components/ds-card/figma/ds-card.figma.ts +2 -2
  30. package/src/components/ds-catalog-layout/ds-catalog-layout.figma.ts +51 -0
  31. package/src/components/ds-catalog-layout/ds-catalog-layout.tsx +9 -8
  32. package/src/components/ds-checkbox/ds-checkbox-level-2.figma.ts +2 -2
  33. package/src/components/ds-checkbox/ds-checkbox.figma.ts +2 -2
  34. package/src/components/ds-checkbox-group/ds-checkbox-group.figma.ts +2 -2
  35. package/src/components/ds-comment-bubble/ds-comment-bubble.figma.ts +52 -0
  36. package/src/components/ds-comment-card/ds-comment-card.figma.ts +36 -0
  37. package/src/components/ds-comment-indicator/ds-comment-indicator.figma.ts +32 -0
  38. package/src/components/ds-comments-drawer/components/comments-filter-modal/index.ts +0 -1
  39. package/src/components/ds-comments-drawer/ds-comments-drawer.figma.ts +19 -0
  40. package/src/components/ds-confirmation/ds-confirmation.tsx +13 -11
  41. package/src/components/ds-date-picker/ds-date-picker.figma.ts +2 -2
  42. package/src/components/ds-divider/ds-divider.figma.ts +2 -2
  43. package/src/components/ds-drawer/ds-drawer.tsx +11 -10
  44. package/src/components/ds-drawer/figma/ds-drawer-content.figma.ts +2 -2
  45. package/src/components/ds-drawer/figma/ds-drawer-footer.figma.ts +2 -2
  46. package/src/components/ds-drawer/figma/ds-drawer-header.figma.ts +2 -2
  47. package/src/components/ds-drawer/figma/ds-drawer.figma.ts +2 -2
  48. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-footer.figma.ts +2 -2
  49. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-group.figma.ts +2 -2
  50. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-item.figma.ts +2 -2
  51. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-separator.figma.ts +2 -2
  52. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu.figma.ts +2 -2
  53. package/src/components/ds-expandable-text-input/ds-expandable-text-input.figma.ts +2 -2
  54. package/src/components/ds-file-upload/components/file-upload-item/index.ts +0 -1
  55. package/src/components/ds-file-upload/ds-file-upload.figma.ts +2 -2
  56. package/src/components/ds-filter-status-icon/ds-filter-status-icon.figma.ts +2 -2
  57. package/src/components/ds-form-control/ds-form-control.figma.ts +2 -2
  58. package/src/components/ds-form-control/ds-form-control.tsx +15 -13
  59. package/src/components/ds-icon/figma/ds-icon-material.figma.batch.ts +1 -1
  60. package/src/components/ds-icon/figma/ds-icon-special.figma.batch.ts +1 -1
  61. package/src/components/ds-key-value-pair/ds-key-value-pair.figma.ts +2 -2
  62. package/src/components/ds-loader/ds-loader.figma.ts +2 -2
  63. package/src/components/ds-modal/ds-modal.tsx +10 -9
  64. package/src/components/ds-modal/figma/ds-modal-content.figma.ts +2 -2
  65. package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +2 -2
  66. package/src/components/ds-modal/figma/ds-modal-header.figma.ts +2 -2
  67. package/src/components/ds-modal/figma/ds-modal.figma.ts +2 -2
  68. package/src/components/ds-number-input/ds-number-input.tsx +1 -0
  69. package/src/components/ds-panel/ds-panel.figma.ts +2 -2
  70. package/src/components/ds-popover/figma/ds-popover-content-item.figma.ts +2 -2
  71. package/src/components/ds-popover/figma/ds-popover-content.figma.ts +2 -2
  72. package/src/components/ds-popover/figma/ds-popover-footer.figma.ts +2 -2
  73. package/src/components/ds-popover/figma/ds-popover-header.figma.ts +2 -2
  74. package/src/components/ds-popover/figma/ds-popover.figma.ts +2 -2
  75. package/src/components/ds-progress-arc/ds-progress-arc.figma.ts +2 -2
  76. package/src/components/ds-progress-donut/ds-progress-donut.figma.ts +2 -2
  77. package/src/components/ds-progress-linear/ds-progress-linear.figma.ts +2 -2
  78. package/src/components/ds-progress-task-bar/ds-progress-task-bar.figma.ts +2 -2
  79. package/src/components/ds-radio-group/figma/ds-radio-group-item.figma.ts +2 -2
  80. package/src/components/ds-radio-group/figma/ds-radio-group.figma.ts +2 -2
  81. package/src/components/ds-segment-group/figma/ds-segment-group-item.figma.ts +2 -2
  82. package/src/components/ds-segment-group/figma/ds-segment-group.figma.ts +2 -2
  83. package/src/components/ds-select/ds-select.figma.ts +4 -3
  84. package/src/components/ds-select/ds-select.tsx +6 -1
  85. package/src/components/ds-select/ds-select.types.ts +2 -1
  86. package/src/components/ds-site-node/ds-site-node.figma.ts +2 -2
  87. package/src/components/ds-slider/ds-slider.figma.ts +29 -0
  88. package/src/components/ds-slider/ds-slider.module.scss +104 -0
  89. package/src/components/ds-slider/ds-slider.tsx +118 -0
  90. package/src/components/ds-slider/ds-slider.types.ts +102 -0
  91. package/src/components/ds-slider/ds-slider.utils.ts +14 -0
  92. package/src/components/ds-slider/index.ts +11 -0
  93. package/src/components/ds-smart-tabs/ds-smart-tabs.tsx +5 -4
  94. package/src/components/ds-smart-tabs/figma/ds-smart-tabs-tab.figma.ts +83 -0
  95. package/src/components/ds-smart-tabs/figma/ds-smart-tabs.figma.ts +23 -0
  96. package/src/components/ds-spinner/ds-spinner.figma.ts +2 -2
  97. package/src/components/ds-split-button/ds-split-button.figma.ts +2 -2
  98. package/src/components/ds-status-badge-v2/ds-status-badge-v2.figma.ts +2 -2
  99. package/src/components/ds-stepper/ds-stepper.figma.ts +2 -2
  100. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +5 -1
  101. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +15 -3
  102. package/src/components/ds-table/components/ds-table-group-header-cell/index.ts +0 -1
  103. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +17 -3
  104. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +30 -9
  105. package/src/components/ds-table/components/ds-table-resize-handle/ds-table-resize-handle.module.scss +23 -0
  106. package/src/components/ds-table/components/ds-table-resize-handle/ds-table-resize-handle.tsx +218 -0
  107. package/src/components/ds-table/components/ds-table-resize-handle/ds-table-resize-handle.types.ts +14 -0
  108. package/src/components/ds-table/components/ds-table-resize-handle/index.ts +1 -0
  109. package/src/components/ds-table/components/ds-table-resize-overlay/ds-table-resize-overlay.module.scss +18 -0
  110. package/src/components/ds-table/components/ds-table-resize-overlay/ds-table-resize-overlay.tsx +16 -0
  111. package/src/components/ds-table/components/ds-table-resize-overlay/ds-table-resize-overlay.types.ts +13 -0
  112. package/src/components/ds-table/components/ds-table-resize-overlay/index.ts +1 -0
  113. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +1 -1
  114. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +9 -2
  115. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +2 -1
  116. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +9 -2
  117. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-checkbox.tsx +2 -2
  118. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-date.tsx +2 -1
  119. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-number.tsx +2 -1
  120. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-select.tsx +2 -1
  121. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-text.tsx +2 -1
  122. package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.tsx +12 -3
  123. package/src/components/ds-table/context/ds-table-context.tsx +208 -0
  124. package/src/components/ds-table/ds-table-defaults.ts +42 -0
  125. package/src/components/ds-table/ds-table.module.scss +19 -1
  126. package/src/components/ds-table/ds-table.tsx +163 -235
  127. package/src/components/ds-table/ds-table.types.ts +59 -47
  128. package/src/components/ds-table/figma/ds-table-expandable.figma.ts +2 -2
  129. package/src/components/ds-table/figma/ds-table.figma.ts +2 -2
  130. package/src/components/ds-table/grouping/utils/column-group.ts +7 -5
  131. package/src/components/ds-table/hooks/use-cell-editor.ts +5 -1
  132. package/src/components/ds-table/hooks/use-column-resize.ts +385 -0
  133. package/src/components/ds-table/hooks/use-editing-state.ts +190 -0
  134. package/src/components/ds-table/index.ts +1 -0
  135. package/src/components/ds-table/styles/_variables.scss +2 -1
  136. package/src/components/ds-table/utils/column-size-meta.augmentation.ts +16 -0
  137. package/src/components/ds-table/utils/column-size.ts +213 -10
  138. package/src/components/ds-table/utils/column-size.unit.test.ts +213 -0
  139. package/src/components/ds-table/utils/constants.ts +28 -0
  140. package/src/components/ds-table/utils/frozen-body.ts +33 -0
  141. package/src/components/ds-table/utils/frozen-body.unit.test.ts +44 -0
  142. package/src/components/ds-table/utils/table-api.ts +42 -0
  143. package/src/components/ds-table/utils/table-api.unit.test.ts +82 -0
  144. package/src/components/ds-table/utils/table-columns.tsx +119 -0
  145. package/src/components/ds-table/utils/table-columns.unit.test.ts +199 -0
  146. package/src/components/ds-tabs/figma/ds-tabs-tab-vertical.figma.ts +2 -2
  147. package/src/components/ds-tabs/figma/ds-tabs-tab.figma.ts +2 -2
  148. package/src/components/ds-tabs/figma/ds-tabs-vertical.figma.ts +2 -2
  149. package/src/components/ds-tabs/figma/ds-tabs.figma.ts +2 -2
  150. package/src/components/ds-tag/ds-tag.figma.ts +51 -0
  151. package/src/components/ds-tag-filter/ds-tag-filter.figma.ts +16 -0
  152. package/src/components/ds-text-input/ds-input-field-v02.figma.ts +31 -6
  153. package/src/components/ds-text-input/ds-text-input.figma.ts +5 -4
  154. package/src/components/ds-text-input/ds-text-input.tsx +1 -0
  155. package/src/components/ds-text-input/ds-text-input.types.ts +1 -1
  156. package/src/components/ds-time-picker/ds-time-picker.figma.ts +2 -2
  157. package/src/components/ds-toast/ds-toast.figma.ts +3 -3
  158. package/src/components/ds-toggle/ds-toggle.figma.ts +2 -2
  159. package/src/components/ds-tooltip/ds-tooltip.figma.ts +2 -2
  160. package/src/components/ds-tree/ds-tree.figma.ts +54 -0
  161. package/src/components/ds-tree/ds-tree.module.scss +2 -2
  162. package/src/components/ds-tree/ds-tree.tsx +6 -0
  163. package/src/components/ds-tree/ds-tree.types.ts +16 -0
  164. package/src/components/ds-typography/ds-typography.figma.ts +2 -2
  165. package/src/components/ds-user-card/ds-user-card.figma.ts +2 -2
  166. package/src/components/ds-vertical-tabs/ds-vertical-tabs.tsx +13 -6
  167. package/src/components/ds-workspace-layout/ds-workspace-layout.figma.ts +99 -0
  168. package/src/components/ds-workspace-layout/ds-workspace-layout.module.scss +1 -8
  169. package/src/components/ds-workspace-layout/ds-workspace-layout.tsx +11 -10
  170. package/src/index.ts +1 -0
  171. package/src/styles/_gradients.scss +19 -0
  172. package/src/styles/_root.scss +39 -15
  173. package/src/styles/_typography.scss +1 -0
  174. package/src/styles/shared/_input.scss +10 -0
  175. package/src/styles/styles.scss +1 -0
  176. package/src/components/ds-table/context/ds-table-context.ts +0 -112
@@ -0,0 +1,118 @@
1
+ import { Slider, type SliderValueChangeDetails } from '@ark-ui/react/slider';
2
+ import classNames from 'classnames';
3
+ import { useControlled } from '../../utils/use-controlled';
4
+ import { DsTypography } from '../ds-typography';
5
+ import type { DsSliderProps, DsSliderValue } from './ds-slider.types';
6
+ import { toArray } from './ds-slider.utils';
7
+ import styles from './ds-slider.module.scss';
8
+
9
+ /**
10
+ * @summary Selects a numeric value or a numeric interval by dragging thumbs along a track.
11
+ *
12
+ * Use for picking a number or interval from a known continuous range where visual
13
+ * adjustment beats typing (filter ranges, thresholds, capacity limits). For exact
14
+ * numeric entry use DsNumberInput; for a few discrete options use DsSegmentGroup.
15
+ */
16
+ const DsSlider = ({
17
+ ref,
18
+ className,
19
+ style,
20
+ label,
21
+ type = 'value',
22
+ size = 'medium',
23
+ min = 0,
24
+ max = 100,
25
+ step = 1,
26
+ value,
27
+ defaultValue,
28
+ name,
29
+ disabled = false,
30
+ thumbLabels,
31
+ formatValue = String,
32
+ onValueChange,
33
+ onValueChangeEnd,
34
+ }: DsSliderProps) => {
35
+ const isRange = type === 'range';
36
+
37
+ const controlledValue = toArray(value);
38
+ const fallbackValue = isRange ? [min, max] : [min];
39
+
40
+ const toPublicValue = (next: number[]): DsSliderValue =>
41
+ isRange ? [next[0] ?? min, next[1] ?? max] : (next[0] ?? min);
42
+
43
+ const [values, setValues] = useControlled<number[]>(
44
+ controlledValue,
45
+ controlledValue !== undefined ? (next) => onValueChange?.(toPublicValue(next)) : undefined,
46
+ toArray(defaultValue) ?? fallbackValue,
47
+ );
48
+
49
+ const handleValueChange = (details: SliderValueChangeDetails) => {
50
+ setValues(details.value);
51
+
52
+ if (controlledValue === undefined) {
53
+ onValueChange?.(toPublicValue(details.value));
54
+ }
55
+ };
56
+
57
+ const valueText = isRange
58
+ ? `${formatValue(values[0] ?? min)}-${formatValue(values[1] ?? max)}`
59
+ : formatValue(values[0] ?? min);
60
+
61
+ const textColor = disabled ? 'disabled' : 'main';
62
+
63
+ const thumbAriaLabels = isRange ? thumbLabels : label ? [label] : thumbLabels;
64
+
65
+ const rootClass = classNames(
66
+ styles.root,
67
+ {
68
+ [styles.small]: size === 'small',
69
+ [styles.large]: size === 'large',
70
+ },
71
+ className,
72
+ );
73
+
74
+ return (
75
+ <Slider.Root
76
+ ref={ref}
77
+ className={rootClass}
78
+ style={style}
79
+ value={values}
80
+ min={min}
81
+ max={max}
82
+ step={step}
83
+ name={name}
84
+ disabled={disabled}
85
+ aria-label={thumbAriaLabels}
86
+ onValueChange={handleValueChange}
87
+ onValueChangeEnd={(details) => onValueChangeEnd?.(toPublicValue(details.value))}
88
+ >
89
+ <div className={styles.header}>
90
+ {label && (
91
+ <DsTypography variant="body-sm-md" color={textColor}>
92
+ {label}
93
+ </DsTypography>
94
+ )}
95
+
96
+ <DsTypography variant="body-sm-md" color={textColor} className={styles.valueText}>
97
+ {valueText}
98
+ </DsTypography>
99
+ </div>
100
+
101
+ <Slider.Control className={styles.control}>
102
+ <Slider.Track className={styles.track}>
103
+ <Slider.Range className={styles.range} />
104
+ </Slider.Track>
105
+
106
+ {values.map((_, index) => (
107
+ <Slider.Thumb key={index} index={index} className={styles.thumb}>
108
+ <Slider.HiddenInput />
109
+ </Slider.Thumb>
110
+ ))}
111
+ </Slider.Control>
112
+ </Slider.Root>
113
+ );
114
+ };
115
+
116
+ DsSlider.displayName = 'DsSlider';
117
+
118
+ export default DsSlider;
@@ -0,0 +1,102 @@
1
+ import type { CSSProperties, Ref } from 'react';
2
+
3
+ export const sliderTypes = ['value', 'range'] as const;
4
+ export type DsSliderType = (typeof sliderTypes)[number];
5
+
6
+ export const sliderSizes = ['small', 'medium', 'large'] as const;
7
+ export type DsSliderSize = (typeof sliderSizes)[number];
8
+
9
+ /**
10
+ * Value for a `value` slider (single number) or a `range` slider ([min, max] tuple).
11
+ */
12
+ export type DsSliderValue = number | [number, number];
13
+
14
+ /**
15
+ * Props for the DsSlider component.
16
+ */
17
+ export type DsSliderProps = {
18
+ /**
19
+ * Forwarded ref for the slider root element.
20
+ */
21
+ ref?: Ref<HTMLDivElement>;
22
+
23
+ className?: string;
24
+
25
+ style?: CSSProperties;
26
+
27
+ /**
28
+ * Visible label rendered above the track. Becomes the accessible name of the control.
29
+ */
30
+ label?: string;
31
+
32
+ /**
33
+ * Selection mode: a single value or a `[min, max]` interval.
34
+ * @default 'value'
35
+ */
36
+ type?: DsSliderType;
37
+
38
+ /**
39
+ * Drives thumb and track sizing together (S/M/L → 16/20/24px thumb, 2/4/8px track).
40
+ * Accepts a responsive value (e.g. `{ md: 'small', lg: 'medium' }`).
41
+ * @default 'medium'
42
+ */
43
+ size?: DsSliderSize;
44
+
45
+ /**
46
+ * Lowest selectable value.
47
+ * @default 0
48
+ */
49
+ min?: number;
50
+
51
+ /**
52
+ * Highest selectable value.
53
+ * @default 100
54
+ */
55
+ max?: number;
56
+
57
+ /**
58
+ * Granularity that the value snaps to. Must be greater than 0.
59
+ * Accepts a responsive value (e.g. `{ md: 5, lg: 1 }`).
60
+ * @default 1
61
+ */
62
+ step?: number;
63
+
64
+ /**
65
+ * Controlled value. A number for `type="value"`, a `[min, max]` tuple for `type="range"`.
66
+ */
67
+ value?: DsSliderValue;
68
+
69
+ /**
70
+ * Uncontrolled initial value. Defaults to `min` for `value` and `[min, max]` for `range`.
71
+ */
72
+ defaultValue?: DsSliderValue;
73
+
74
+ /**
75
+ * Form field name applied to the hidden inputs.
76
+ */
77
+ name?: string;
78
+
79
+ disabled?: boolean;
80
+
81
+ /**
82
+ * Accessible name per thumb. Required for `type="range"` (e.g. `['Minimum', 'Maximum']`).
83
+ * Accepts a responsive value (e.g. `{ md: ['Min', 'Max'], lg: ['Minimum', 'Maximum'] }`).
84
+ */
85
+ thumbLabels?: string[];
86
+
87
+ /**
88
+ * Formats each number shown in the output above the track. Range values are joined with `-`.
89
+ * @default String
90
+ */
91
+ formatValue?: (value: number) => string;
92
+
93
+ /**
94
+ * Called live while the value changes (drag or keyboard).
95
+ */
96
+ onValueChange?: (value: DsSliderValue) => void;
97
+
98
+ /**
99
+ * Called once an interaction settles (pointer released or key committed).
100
+ */
101
+ onValueChangeEnd?: (value: DsSliderValue) => void;
102
+ };
@@ -0,0 +1,14 @@
1
+ import type { DsSliderValue } from './ds-slider.types';
2
+
3
+ /**
4
+ * Normalizes the public value (a number or a `[min, max]` tuple) into the
5
+ * `number[]` shape Ark's slider works with. Returns `undefined` when no value
6
+ * is provided so controlled/uncontrolled detection stays intact.
7
+ */
8
+ export const toArray = (value: DsSliderValue | undefined): number[] | undefined => {
9
+ if (value === undefined) {
10
+ return undefined;
11
+ }
12
+
13
+ return Array.isArray(value) ? value : [value];
14
+ };
@@ -0,0 +1,11 @@
1
+ import type { ComponentProps } from 'react';
2
+
3
+ import { withResponsiveProps } from '../../utils/responsive';
4
+ import DsSliderBase from './ds-slider';
5
+
6
+ export const DsSlider = withResponsiveProps(DsSliderBase, ['size', 'step', 'thumbLabels']);
7
+
8
+ DsSlider.displayName = 'DsSlider';
9
+
10
+ export type DsSliderProps = ComponentProps<typeof DsSlider>;
11
+ export type { DsSliderValue, DsSliderType, DsSliderSize } from './ds-slider.types';
@@ -86,7 +86,7 @@ const DsSmartTab: React.FC<DsSmartTabProps> = ({
86
86
  /**
87
87
  * Design system SmartTabs component
88
88
  */
89
- const DsSmartTabs = ({
89
+ const DsSmartTabsRoot = ({
90
90
  activeTab,
91
91
  onTabClick,
92
92
  className,
@@ -110,8 +110,9 @@ const DsSmartTabs = ({
110
110
 
111
111
  DsSmartTab.displayName = 'DsSmartTabs.Tab';
112
112
 
113
- DsSmartTabs.Tab = DsSmartTab;
114
-
115
- DsSmartTabs.displayName = 'DsSmartTabs';
113
+ const DsSmartTabs = Object.assign(DsSmartTabsRoot, {
114
+ displayName: 'DsSmartTabs',
115
+ Tab: DsSmartTab,
116
+ });
116
117
 
117
118
  export default DsSmartTabs;
@@ -0,0 +1,83 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=15707-16204
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-smart-tabs
3
+ // component=DsSmartTabs.Tab
4
+ //
5
+ // `Part_SmartTabV1` maps to `DsSmartTabs.Tab`, resolved inside the group's
6
+ // `Smart tabs slot` (nestable). Each item = colored icon badge + label + count.
7
+ //
8
+ // Variant divergence — the Figma `state` axis (selected/unselected/hover/focus)
9
+ // is mostly visual: selection is owned by `DsSmartTabs` (`activeTab`), and
10
+ // hover/focus are driven by CSS. Only `disabled` maps to a `DsSmartTabs.Tab` prop.
11
+ //
12
+ // `color` is intentionally not emitted: it is a fixed semantic per-category value
13
+ // (encoded in the nested badge's `category` variant, not a tab-level property) and
14
+ // the category→color mapping is owned by the design system, so the developer sets
15
+ // it explicitly. `value` is data, not derivable from Figma, so slugify the label
16
+ // as a sensible default for the developer to confirm/replace.
17
+ import figma from 'figma';
18
+
19
+ const instance = figma.selectedInstance;
20
+
21
+ const state = instance.getEnum('state', {
22
+ selected: 'selected',
23
+ unselected: 'unselected',
24
+ hover: 'hover',
25
+ focus: 'focus',
26
+ disabled: 'disabled',
27
+ });
28
+
29
+ // Label and count are plain text layers (not component properties): the first
30
+ // text child is the label, the second is the right-aligned count.
31
+ const texts = instance.children.filter((child): child is figma.TextHandle => child.type === 'TEXT');
32
+ const label = texts[0]?.textContent.trim() || 'Label';
33
+ const count = texts[1]?.textContent.trim() ?? '';
34
+
35
+ const value = label
36
+ .toLowerCase()
37
+ .replace(/[^a-z0-9]+/g, '-')
38
+ .replace(/^-+|-+$/g, '');
39
+
40
+ // The badge glyph maps to `DsSmartTabs.Tab.icon` (an icon-name string). Prefer a
41
+ // connected ds-icon's Code Connect id and fall back to the glyph layer name.
42
+ const resolveIcon = (): string => {
43
+ const node = instance
44
+ .findConnectedInstances((n) => n.codeConnectId()?.startsWith('ds-icon-') ?? false, {
45
+ traverseInstances: true,
46
+ })
47
+ .find((n): n is figma.InstanceHandle => n.type === 'INSTANCE');
48
+
49
+ const id = node?.codeConnectId();
50
+ if (id?.startsWith('ds-icon-o-')) {
51
+ return id.slice('ds-icon-o-'.length);
52
+ }
53
+ if (id?.startsWith('ds-icon-s-')) {
54
+ return id.slice('ds-icon-s-'.length);
55
+ }
56
+
57
+ return (node?.name ?? 'circle')
58
+ .replace(/^DAP_GM_[OS]_/, '')
59
+ .replace(/^DAP_/, '')
60
+ .replace(/[\s/]+/g, '_')
61
+ .toLowerCase();
62
+ };
63
+
64
+ // `content` is number|string and is not auto-formatted: emit a numeric literal for
65
+ // a plain integer, otherwise keep the pre-formatted string (e.g. "1,273").
66
+ const contentAttr = /^\d+$/.test(count) ? `content={${count}}` : `content="${count}"`;
67
+
68
+ const attrs = [
69
+ `value="${value}"`,
70
+ `label="${label}"`,
71
+ `icon="${resolveIcon()}"`,
72
+ contentAttr,
73
+ state === 'disabled' ? 'disabled' : '',
74
+ ]
75
+ .filter(Boolean)
76
+ .join(' ');
77
+
78
+ export default {
79
+ example: figma.code`<DsSmartTabs.Tab ${attrs} />`,
80
+ imports: ["import { DsSmartTabs } from '@drivenets/design-system';"],
81
+ id: 'ds-smart-tabs-tab',
82
+ metadata: { nestable: true },
83
+ } satisfies figma.Template;
@@ -0,0 +1,23 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=34864-129238
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-smart-tabs
3
+ // component=DsSmartTabs
4
+ //
5
+ // `DsSmartTabsV1` is the smart-tabs filter row. It maps to `DsSmartTabs`, whose
6
+ // `Smart tabs slot` resolves each item via its own template (`DsSmartTabs.Tab`),
7
+ // so added/removed tabs stay in sync.
8
+ //
9
+ // `activeTab` and `onTabClick` are the controlled selection pair. Figma models
10
+ // only the row chrome — selection state is authored in code, so the snippet emits
11
+ // identifier placeholders for the developer to wire to component state.
12
+ import figma from 'figma';
13
+
14
+ const tabs = figma.selectedInstance.getSlot('Smart tabs slot');
15
+
16
+ export default {
17
+ example: figma.code`<DsSmartTabs activeTab={activeTab} onTabClick={setActiveTab}>
18
+ ${tabs}
19
+ </DsSmartTabs>`,
20
+ imports: ["import { DsSmartTabs } from '@drivenets/design-system';"],
21
+ id: 'ds-smart-tabs',
22
+ metadata: { nestable: false },
23
+ } satisfies figma.Template;
@@ -13,7 +13,7 @@ const size = instance.getEnum('Size', {
13
13
 
14
14
  export default {
15
15
  example: figma.code`<DsSpinner size="${size}" />`,
16
- imports: ["import { DsSpinner } from '@drivenets/design-system'"],
16
+ imports: ["import { DsSpinner } from '@drivenets/design-system';"],
17
17
  id: 'ds-spinner',
18
18
  metadata: { nestable: true },
19
- };
19
+ } satisfies figma.Template;
@@ -89,7 +89,7 @@ const buttonProps = [
89
89
 
90
90
  export default {
91
91
  example: figma.code`<DsSplitButton size="${size}"${disabled ? ' disabled' : ''} slotProps={{ button: { ${buttonProps} }, select: { options: [], value: '' } }} />`,
92
- imports: ["import { DsSplitButton } from '@drivenets/design-system'"],
92
+ imports: ["import { DsSplitButton } from '@drivenets/design-system';"],
93
93
  id: 'ds-split-button',
94
94
  metadata: { nestable: false },
95
- };
95
+ } satisfies figma.Template;
@@ -35,7 +35,7 @@ export default {
35
35
  : structure === 'text-only'
36
36
  ? figma.code`<DsStatusBadgeV2 phase="${phase}" label="${label}" variant="${variant}" size="${size}" icon={null} />`
37
37
  : figma.code`<DsStatusBadgeV2 phase="${phase}" label="${label}" variant="${variant}" size="${size}" />`,
38
- imports: ["import { DsStatusBadgeV2 } from '@drivenets/design-system'"],
38
+ imports: ["import { DsStatusBadgeV2 } from '@drivenets/design-system';"],
39
39
  id: 'ds-status-badge-v2',
40
40
  metadata: { nestable: true },
41
- };
41
+ } satisfies figma.Template;
@@ -32,7 +32,7 @@ export default {
32
32
  </DsStep>
33
33
  {/* Add a <DsStep> per step */}
34
34
  </DsStepper>`,
35
- imports: ["import { DsStepper, DsStep, DsStepContent, DsNextStepButton } from '@drivenets/design-system'"],
35
+ imports: ["import { DsStepper, DsStep, DsStepContent, DsNextStepButton } from '@drivenets/design-system';"],
36
36
  id: 'ds-stepper',
37
37
  metadata: { nestable: true },
38
- };
38
+ } satisfies figma.Template;
@@ -3,11 +3,15 @@
3
3
 
4
4
  .groupHeaderCell {
5
5
  @include typography.body-sm-md;
6
+ position: relative;
6
7
  color: var(--font-main);
7
8
  height: var(--ds-table-header-height);
8
9
  padding: 0;
9
10
  border-bottom: vars.$border-width solid var(--color-dap-gray-400);
10
- border-left: vars.$group-border-width solid var(--border);
11
+
12
+ &:not(:first-child) {
13
+ border-left: vars.$group-border-width solid var(--border);
14
+ }
11
15
  }
12
16
 
13
17
  .groupHeaderContent {
@@ -3,21 +3,27 @@ import { DsButtonV3 } from '../../../ds-button-v3';
3
3
  import { TableHead } from '../core-table';
4
4
  import { useDsTableContext } from '../../context/ds-table-context';
5
5
  import { getGroupColumnSizeStyle } from '../../grouping';
6
+ import { getResizableHeaderStyle } from '../../utils/column-size';
6
7
  import { defaultDsTableLocale } from '../../ds-table.types';
8
+ import { DsTableResizeHandle } from '../ds-table-resize-handle';
7
9
  import type { DsTableGroupHeaderCellProps } from './ds-table-group-header-cell.types';
8
10
  import styles from './ds-table-group-header-cell.module.scss';
9
11
 
10
12
  export const DsTableGroupHeaderCell = <TData, TValue>({
11
13
  header,
12
14
  }: DsTableGroupHeaderCellProps<TData, TValue>) => {
13
- const { collapsedColumnGroups, onToggleColumnGroup, locale } = useDsTableContext<TData, TValue>();
15
+ const { collapsedColumnGroups, onToggleColumnGroup, locale, resizableColumns, resizeSizingReady } =
16
+ useDsTableContext<TData, TValue>();
14
17
 
15
18
  const groupId = header.column.id;
16
19
  const visibleLeafColumns = header.column.getLeafColumns().filter((leaf) => leaf.getIsVisible());
17
- const groupStyle = getGroupColumnSizeStyle(visibleLeafColumns);
20
+ const groupStyle = resizeSizingReady
21
+ ? getResizableHeaderStyle(header.id)
22
+ : getGroupColumnSizeStyle(visibleLeafColumns, {});
18
23
 
19
24
  const isCollapsible = header.column.columnDef.meta?.group?.collapsible ?? false;
20
25
  const isCollapsed = collapsedColumnGroups?.includes(groupId) ?? false;
26
+ const canResize = resizableColumns && !isCollapsed && header.column.getCanResize();
21
27
 
22
28
  const mergedLocale = { ...defaultDsTableLocale, ...locale };
23
29
  const toggleLabel = isCollapsed ? mergedLocale.expandColumnGroup : mergedLocale.collapseColumnGroup;
@@ -25,7 +31,12 @@ export const DsTableGroupHeaderCell = <TData, TValue>({
25
31
  const handleToggle = () => onToggleColumnGroup?.(groupId);
26
32
 
27
33
  return (
28
- <TableHead className={styles.groupHeaderCell} style={groupStyle} colSpan={header.colSpan}>
34
+ <TableHead
35
+ data-column-id={groupId}
36
+ className={styles.groupHeaderCell}
37
+ style={groupStyle}
38
+ colSpan={header.colSpan}
39
+ >
29
40
  <div className={styles.groupHeaderContent}>
30
41
  <span className={styles.groupLabel}>
31
42
  {flexRender(header.column.columnDef.header, header.getContext())}
@@ -43,6 +54,7 @@ export const DsTableGroupHeaderCell = <TData, TValue>({
43
54
  />
44
55
  )}
45
56
  </div>
57
+ {canResize && <DsTableResizeHandle header={header} />}
46
58
  </TableHead>
47
59
  );
48
60
  };
@@ -1,2 +1 @@
1
1
  export { DsTableGroupHeaderCell } from './ds-table-group-header-cell';
2
- export type { DsTableGroupHeaderCellProps } from './ds-table-group-header-cell.types';
@@ -1,8 +1,9 @@
1
1
  @use '../../../../styles/typography';
2
2
  @use '../../styles/variables' as vars;
3
3
 
4
- .stickyHeader {
5
- background-color: var(--background);
4
+ .header {
5
+ background-color: var(--background-secondary);
6
+ overflow: hidden;
6
7
  }
7
8
 
8
9
  .virtualizedHeader {
@@ -28,6 +29,7 @@
28
29
 
29
30
  .headerCell {
30
31
  @include typography.body-sm-md;
32
+ position: relative;
31
33
  color: var(--font-main);
32
34
  text-align: left;
33
35
  padding: 0 var(--standard);
@@ -57,7 +59,7 @@
57
59
  justify-content: center;
58
60
  }
59
61
 
60
- .groupStart {
62
+ .groupStart:not(:first-child) {
61
63
  border-left: vars.$group-border-width solid var(--border);
62
64
  }
63
65
 
@@ -69,6 +71,18 @@
69
71
  height: 100%;
70
72
  }
71
73
 
74
+ .headerLabelStack {
75
+ min-width: 0;
76
+ overflow: hidden;
77
+ }
78
+
79
+ .headerLabel {
80
+ display: block;
81
+ overflow: hidden;
82
+ text-overflow: ellipsis;
83
+ white-space: nowrap;
84
+ }
85
+
72
86
  .filterTriggerContainer {
73
87
  display: inline-flex;
74
88
  align-items: center;
@@ -6,21 +6,20 @@ import styles from './ds-table-header.module.scss';
6
6
  import stylesShared from '../../styles/shared/ds-table-shared.module.scss';
7
7
  import type { DsTableHeaderProps } from './ds-table-header.types';
8
8
  import { useDsTableContext } from '../../context/ds-table-context';
9
- import { getColumnSizeStyle } from '../../utils/column-size';
9
+ import { getHeaderCellSizeStyle, isExplicitColumnWidth } from '../../utils/column-size';
10
10
  import { SELECT_COLUMN_ID } from '../../utils/constants';
11
11
  import { DsStack } from '../../../ds-stack';
12
12
  import { DsTableColumnFilterPopover } from '../../filters/components/column-filter-popover';
13
13
  import type { ResolvedColumnFilter } from '../../filters/types/filter-adapter.types';
14
14
  import { DsTableGroupHeaderCell } from '../ds-table-group-header-cell';
15
+ import { DsTableResizeHandle } from '../ds-table-resize-handle';
15
16
  import { isFirstLeafColumnOfGroup } from '../../grouping';
16
17
 
17
18
  const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
18
- const { stickyHeader, bordered, virtualized } = useDsTableContext<TData, unknown>();
19
+ const { bordered, virtualized, resizableColumns, resizeSizingReady } = useDsTableContext<TData, unknown>();
19
20
 
20
21
  return (
21
- <TableHeader
22
- className={classnames(stickyHeader && styles.stickyHeader, virtualized && styles.virtualizedHeader)}
23
- >
22
+ <TableHeader className={classnames(styles.header, virtualized && styles.virtualizedHeader)}>
24
23
  {table.getHeaderGroups().map((headerGroup) => (
25
24
  <TableRow
26
25
  key={headerGroup.id}
@@ -31,12 +30,19 @@ const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
31
30
  )}
32
31
  >
33
32
  {headerGroup.headers.map((header) => {
33
+ const columnSizing = table.getState().columnSizing;
34
+
34
35
  if (header.isPlaceholder) {
35
36
  return (
36
37
  <TableHead
37
38
  key={header.id}
38
39
  className={styles.headerCell}
39
- style={getColumnSizeStyle(header.getSize())}
40
+ style={getHeaderCellSizeStyle(
41
+ header.id,
42
+ header.getSize(),
43
+ isExplicitColumnWidth(header.column, resizeSizingReady ? columnSizing : {}),
44
+ !!resizeSizingReady,
45
+ )}
40
46
  />
41
47
  );
42
48
  }
@@ -45,8 +51,14 @@ const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
45
51
  return <DsTableGroupHeaderCell key={header.id} header={header} />;
46
52
  }
47
53
 
48
- const headerStyle = getColumnSizeStyle(header.column.getSize());
54
+ const headerStyle = getHeaderCellSizeStyle(
55
+ header.id,
56
+ header.column.getSize(),
57
+ isExplicitColumnWidth(header.column, resizeSizingReady ? columnSizing : {}),
58
+ !!resizeSizingReady,
59
+ );
49
60
  const canSort = header.column.getCanSort();
61
+ const canResize = resizableColumns && header.column.getCanResize();
50
62
  const isSelectColumn = header.column.id === SELECT_COLUMN_ID;
51
63
  const isGroupStart = isFirstLeafColumnOfGroup(header.column);
52
64
 
@@ -58,6 +70,7 @@ const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
58
70
  return (
59
71
  <TableHead
60
72
  key={header.id}
73
+ data-column-id={header.column.id}
61
74
  className={classnames(
62
75
  styles.headerCell,
63
76
  canSort && styles.sortableHeader,
@@ -70,8 +83,15 @@ const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
70
83
  style={headerStyle}
71
84
  >
72
85
  <div className={styles.headerSortContainer}>
73
- <DsStack direction="column" justifyContent="center" width="100%">
74
- {flexRender(header.column.columnDef.header, header.getContext())}
86
+ <DsStack
87
+ direction="column"
88
+ justifyContent="center"
89
+ width="100%"
90
+ className={styles.headerLabelStack}
91
+ >
92
+ <span className={styles.headerLabel}>
93
+ {flexRender(header.column.columnDef.header, header.getContext())}
94
+ </span>
75
95
  </DsStack>
76
96
  {canSort && (
77
97
  <div className={styles.pageButtonIconContainer}>
@@ -106,6 +126,7 @@ const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
106
126
  </div>
107
127
  )}
108
128
  </div>
129
+ {canResize && <DsTableResizeHandle header={header} />}
109
130
  </TableHead>
110
131
  );
111
132
  })}
@@ -0,0 +1,23 @@
1
+ @use '../../styles/variables' as vars;
2
+
3
+ .resizeHandle {
4
+ position: absolute;
5
+ top: 0;
6
+ // Overlay paints 2px from the cell right edge; center this hit target on that band.
7
+ right: vars.$resize-handle-hit-width * -0.5 + vars.$resize-divider-width * -0.5;
8
+ height: 100%;
9
+ width: vars.$resize-handle-hit-width;
10
+ padding: 0;
11
+ border: 0;
12
+ background: transparent;
13
+ cursor: col-resize;
14
+ touch-action: none;
15
+ z-index: 1;
16
+
17
+ // Focusable for keyboard resize (WCAG 2.1.1). Negative offset keeps the ring
18
+ // clear of the scroll container's overflow clip on the last column.
19
+ &:focus-visible {
20
+ outline: vars.$resize-divider-width solid var(--border-action-primary);
21
+ outline-offset: -1px;
22
+ }
23
+ }