@drivenets/design-system 0.17.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 (94) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/index.cjs +1727 -701
  3. package/dist/index.d.cts +115 -52
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +115 -52
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1729 -705
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +2 -2
  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-autocomplete/ds-autocomplete.module.scss +15 -4
  14. package/src/components/ds-autocomplete/ds-autocomplete.tsx +10 -3
  15. package/src/components/ds-autocomplete/ds-autocomplete.types.ts +8 -0
  16. package/src/components/ds-button-v3/ds-button-v3.figma.ts +5 -3
  17. package/src/components/ds-button-v3/ds-button-v3.module.scss +84 -17
  18. package/src/components/ds-button-v3/ds-button-v3.tsx +2 -0
  19. package/src/components/ds-button-v3/ds-button-v3.types.ts +11 -2
  20. package/src/components/ds-button-v3/ds-button-v4.figma.ts +121 -0
  21. package/src/components/ds-comment-bubble/ds-comment-bubble.figma.ts +52 -0
  22. package/src/components/ds-comment-card/ds-comment-card.figma.ts +36 -0
  23. package/src/components/ds-comment-indicator/ds-comment-indicator.figma.ts +32 -0
  24. package/src/components/ds-comments-drawer/ds-comments-drawer.figma.ts +19 -0
  25. package/src/components/ds-select/ds-select.figma.ts +2 -1
  26. package/src/components/ds-select/ds-select.tsx +6 -1
  27. package/src/components/ds-select/ds-select.types.ts +2 -1
  28. package/src/components/ds-slider/ds-slider.figma.ts +29 -0
  29. package/src/components/ds-slider/ds-slider.module.scss +104 -0
  30. package/src/components/ds-slider/ds-slider.tsx +118 -0
  31. package/src/components/ds-slider/ds-slider.types.ts +102 -0
  32. package/src/components/ds-slider/ds-slider.utils.ts +14 -0
  33. package/src/components/ds-slider/index.ts +11 -0
  34. package/src/components/ds-smart-tabs/figma/ds-smart-tabs-tab.figma.ts +83 -0
  35. package/src/components/ds-smart-tabs/figma/ds-smart-tabs.figma.ts +23 -0
  36. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +5 -1
  37. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +15 -3
  38. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +15 -1
  39. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +29 -6
  40. package/src/components/ds-table/components/ds-table-resize-handle/ds-table-resize-handle.module.scss +23 -0
  41. package/src/components/ds-table/components/ds-table-resize-handle/ds-table-resize-handle.tsx +218 -0
  42. package/src/components/ds-table/components/ds-table-resize-handle/ds-table-resize-handle.types.ts +14 -0
  43. package/src/components/ds-table/components/ds-table-resize-handle/index.ts +1 -0
  44. package/src/components/ds-table/components/ds-table-resize-overlay/ds-table-resize-overlay.module.scss +18 -0
  45. package/src/components/ds-table/components/ds-table-resize-overlay/ds-table-resize-overlay.tsx +16 -0
  46. package/src/components/ds-table/components/ds-table-resize-overlay/ds-table-resize-overlay.types.ts +13 -0
  47. package/src/components/ds-table/components/ds-table-resize-overlay/index.ts +1 -0
  48. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +1 -1
  49. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +9 -2
  50. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +2 -1
  51. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +9 -2
  52. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-checkbox.tsx +2 -2
  53. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-date.tsx +2 -1
  54. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-number.tsx +2 -1
  55. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-select.tsx +2 -1
  56. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-text.tsx +2 -1
  57. package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.tsx +12 -3
  58. package/src/components/ds-table/context/ds-table-context.tsx +208 -0
  59. package/src/components/ds-table/ds-table-defaults.ts +42 -0
  60. package/src/components/ds-table/ds-table.module.scss +4 -1
  61. package/src/components/ds-table/ds-table.tsx +139 -214
  62. package/src/components/ds-table/ds-table.types.ts +53 -47
  63. package/src/components/ds-table/grouping/utils/column-group.ts +7 -5
  64. package/src/components/ds-table/hooks/use-cell-editor.ts +5 -1
  65. package/src/components/ds-table/hooks/use-column-resize.ts +385 -0
  66. package/src/components/ds-table/hooks/use-editing-state.ts +190 -0
  67. package/src/components/ds-table/index.ts +1 -0
  68. package/src/components/ds-table/styles/_variables.scss +2 -1
  69. package/src/components/ds-table/utils/column-size-meta.augmentation.ts +16 -0
  70. package/src/components/ds-table/utils/column-size.ts +213 -10
  71. package/src/components/ds-table/utils/column-size.unit.test.ts +213 -0
  72. package/src/components/ds-table/utils/constants.ts +28 -0
  73. package/src/components/ds-table/utils/frozen-body.ts +33 -0
  74. package/src/components/ds-table/utils/frozen-body.unit.test.ts +44 -0
  75. package/src/components/ds-table/utils/table-api.ts +42 -0
  76. package/src/components/ds-table/utils/table-api.unit.test.ts +82 -0
  77. package/src/components/ds-table/utils/table-columns.tsx +119 -0
  78. package/src/components/ds-table/utils/table-columns.unit.test.ts +199 -0
  79. package/src/components/ds-tag/ds-tag.figma.ts +51 -0
  80. package/src/components/ds-tag-filter/ds-tag-filter.figma.ts +16 -0
  81. package/src/components/ds-text-input/ds-input-field-v02.figma.ts +29 -4
  82. package/src/components/ds-text-input/ds-text-input.figma.ts +3 -2
  83. package/src/components/ds-text-input/ds-text-input.tsx +1 -0
  84. package/src/components/ds-text-input/ds-text-input.types.ts +1 -1
  85. package/src/components/ds-tree/ds-tree.figma.ts +54 -0
  86. package/src/components/ds-workspace-layout/ds-workspace-layout.figma.ts +99 -0
  87. package/src/components/ds-workspace-layout/ds-workspace-layout.module.scss +1 -8
  88. package/src/index.ts +1 -0
  89. package/src/styles/_gradients.scss +19 -0
  90. package/src/styles/_root.scss +39 -15
  91. package/src/styles/_typography.scss +1 -0
  92. package/src/styles/shared/_input.scss +10 -0
  93. package/src/styles/styles.scss +1 -0
  94. package/src/components/ds-table/context/ds-table-context.ts +0 -112
@@ -0,0 +1,32 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=19208-6889
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-comment-indicator
3
+ // component=DsCommentIndicator
4
+ //
5
+ // The linked node is `Structure_CommentIndicatorV1`, the inner structure shared by the
6
+ // indicator variants. The `type` axis (placeholder / default / action-required) maps
7
+ // directly to the `type` prop; on the bare structure node it has no value, so it falls
8
+ // back to `default`. For the `placeholder` state no avatar is shown; the `default` /
9
+ // `action-required` states carry the initiator's avatar. `avatarSrc` is a photo URL
10
+ // and `avatarName` is content — neither is derivable from Figma — so `avatarName` is
11
+ // emitted as a placeholder for the developer to fill in (see `ds-avatar.figma.ts`).
12
+ import figma from 'figma';
13
+
14
+ const instance = figma.selectedInstance;
15
+
16
+ const type =
17
+ instance.getEnum('type', {
18
+ placeholder: 'placeholder',
19
+ default: 'default',
20
+ 'action-required': 'action-required',
21
+ 'action required': 'action-required',
22
+ }) ?? 'default';
23
+
24
+ export default {
25
+ example:
26
+ type === 'placeholder'
27
+ ? figma.code`<DsCommentIndicator type="placeholder" onClick={() => {}} />`
28
+ : figma.code`<DsCommentIndicator type="${type}" avatarName="Full Name" onClick={() => {}} />`,
29
+ imports: ["import { DsCommentIndicator } from '@drivenets/design-system';"],
30
+ id: 'ds-comment-indicator',
31
+ metadata: { nestable: true },
32
+ } satisfies figma.Template;
@@ -0,0 +1,19 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=19346-10511
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-comments-drawer
3
+ // component=DsCommentsDrawer
4
+ //
5
+ // `DsCommentsDrawerV1` is the comments side panel. Unlike `ds-drawer` / `ds-modal` it
6
+ // does not compose its children through slots: the toolbar, search, and the comment
7
+ // list are all internal, and the cards are rendered from the `comments` data array.
8
+ // There is therefore nothing to resolve via `executeTemplate` — the snippet emits
9
+ // `comments` / `onOpenChange` identifier placeholders for the developer to wire to
10
+ // state. The Figma `Type` axis (`Default` / `Regular`) has no `DsCommentsDrawer` prop
11
+ // equivalent — both render the same composition.
12
+ import figma from 'figma';
13
+
14
+ export default {
15
+ example: figma.code`<DsCommentsDrawer open comments={comments} onOpenChange={() => {}} />`,
16
+ imports: ["import { DsCommentsDrawer } from '@drivenets/design-system';"],
17
+ id: 'ds-comments-drawer',
18
+ metadata: { nestable: false },
19
+ } satisfies figma.Template;
@@ -8,6 +8,7 @@ const instance = figma.selectedInstance;
8
8
  const size = instance.getEnum('Size', {
9
9
  Default: 'default',
10
10
  Small: 'small',
11
+ Large: 'large',
11
12
  });
12
13
 
13
14
  const state = instance.getEnum('State', {
@@ -22,7 +23,7 @@ const state = instance.getEnum('State', {
22
23
 
23
24
  const disabled = state === 'disable';
24
25
 
25
- const sizeAttr = size === 'small' ? 'size="small"' : '';
26
+ const sizeAttr = size === 'small' || size === 'large' ? `size="${size}"` : '';
26
27
 
27
28
  // Multi-select is driven by the nested dropdown menu's `Type` (Checkbox = multi). The menu
28
29
  // lives inside the select tree even when collapsed, so the designer's intent is read in any
@@ -135,7 +135,12 @@ const DsSelect = ({
135
135
  lazyMount
136
136
  >
137
137
  <Select.Control
138
- className={classNames(styles.control, size === 'small' && styles.small, className)}
138
+ className={classNames(
139
+ styles.control,
140
+ size === 'small' && styles.small,
141
+ size === 'large' && styles.large,
142
+ className,
143
+ )}
139
144
  style={style}
140
145
  onBlur={onBlur}
141
146
  onKeyDown={(e) => {
@@ -2,7 +2,8 @@ import type React from 'react';
2
2
  import type { ReactNode } from 'react';
3
3
  import type { IconName } from '../ds-icon';
4
4
 
5
- export type SelectSize = 'default' | 'small';
5
+ export const selectSizes = ['small', 'default', 'large'] as const;
6
+ export type SelectSize = (typeof selectSizes)[number];
6
7
 
7
8
  export interface DsSelectOption {
8
9
  /**
@@ -0,0 +1,29 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=29879-165
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-slider
3
+ // component=DsSlider
4
+ import figma from 'figma';
5
+
6
+ const instance = figma.selectedInstance;
7
+
8
+ const type = instance.getEnum('type', {
9
+ value: 'value',
10
+ range: 'range',
11
+ });
12
+
13
+ const isRange = type === 'range';
14
+
15
+ const attrs = [
16
+ 'label="Input label"',
17
+ isRange ? 'type="range"' : '',
18
+ isRange ? 'defaultValue={[36, 60]}' : 'defaultValue={36}',
19
+ isRange ? "thumbLabels={['Minimum', 'Maximum']}" : '',
20
+ ]
21
+ .filter(Boolean)
22
+ .join(' ');
23
+
24
+ export default {
25
+ example: figma.code`<DsSlider ${attrs} />`,
26
+ imports: ["import { DsSlider } from '@drivenets/design-system';"],
27
+ id: 'ds-slider',
28
+ metadata: { nestable: false },
29
+ } satisfies figma.Template;
@@ -0,0 +1,104 @@
1
+ .root {
2
+ --ds-slider-thumb-size: var(--md);
3
+ --ds-slider-thumb-size-active: var(--lg);
4
+ --ds-slider-thumb-border: 2px;
5
+ --ds-slider-thumb-border-active: 4px;
6
+ --ds-slider-track-height: 4px;
7
+ --ds-slider-focus-ring: 3px;
8
+ --ds-slider-radius: 9999px;
9
+ --ds-slider-transition: 0.2s;
10
+
11
+ display: flex;
12
+ flex-direction: column;
13
+ gap: var(--sm);
14
+ width: 100%;
15
+
16
+ &.small {
17
+ --ds-slider-thumb-size: var(--standard);
18
+ --ds-slider-thumb-size-active: var(--md);
19
+ --ds-slider-track-height: 2px;
20
+ }
21
+
22
+ &.large {
23
+ --ds-slider-thumb-size: var(--lg);
24
+ --ds-slider-thumb-size-active: 28px;
25
+ --ds-slider-thumb-border: 3px;
26
+ --ds-slider-thumb-border-active: 5px;
27
+ --ds-slider-track-height: 8px;
28
+ }
29
+
30
+ &[data-disabled] {
31
+ cursor: not-allowed;
32
+
33
+ .range {
34
+ background: var(--background-disable);
35
+ }
36
+
37
+ .thumb {
38
+ cursor: not-allowed;
39
+ border-color: var(--background-disable);
40
+ }
41
+ }
42
+ }
43
+
44
+ .header {
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: space-between;
48
+ gap: var(--xs);
49
+ padding-bottom: var(--4xs);
50
+ }
51
+
52
+ .valueText {
53
+ margin-left: auto;
54
+ text-align: right;
55
+ }
56
+
57
+ .control {
58
+ position: relative;
59
+ display: flex;
60
+ align-items: center;
61
+ min-height: var(--ds-slider-thumb-size-active);
62
+ }
63
+
64
+ .track {
65
+ flex: 1;
66
+ height: var(--ds-slider-track-height);
67
+ background: var(--background-secondary-hover);
68
+ border-radius: var(--ds-slider-radius);
69
+ }
70
+
71
+ .range {
72
+ height: 100%;
73
+ background: var(--background-primary);
74
+ border-radius: var(--ds-slider-radius);
75
+ }
76
+
77
+ .thumb {
78
+ box-sizing: border-box;
79
+ width: var(--ds-slider-thumb-size);
80
+ height: var(--ds-slider-thumb-size);
81
+ background: var(--background);
82
+ border: var(--ds-slider-thumb-border) solid var(--background-primary);
83
+ border-radius: var(--ds-slider-radius);
84
+ outline: none;
85
+ cursor: pointer;
86
+ transition:
87
+ width var(--ds-slider-transition),
88
+ height var(--ds-slider-transition),
89
+ border-width var(--ds-slider-transition),
90
+ border-color var(--ds-slider-transition),
91
+ box-shadow var(--ds-slider-transition);
92
+
93
+ &:not([data-disabled]):hover,
94
+ &:focus-visible {
95
+ width: var(--ds-slider-thumb-size-active);
96
+ height: var(--ds-slider-thumb-size-active);
97
+ border-width: var(--ds-slider-thumb-border-active);
98
+ border-color: var(--background-primary-hover);
99
+ }
100
+
101
+ &:focus-visible {
102
+ box-shadow: 0 0 0 var(--ds-slider-focus-ring) var(--background-secondary-hover);
103
+ }
104
+ }
@@ -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';
@@ -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;
@@ -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
  };