@drivenets/design-system 0.6.1 → 0.7.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 (134) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/index.cjs +12394 -8354
  3. package/dist/index.d.cts +806 -118
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +806 -118
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +12376 -8357
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +25 -23
  11. package/src/components/ds-autocomplete/ds-autocomplete.module.scss +2 -1
  12. package/src/components/ds-autocomplete/ds-autocomplete.tsx +47 -40
  13. package/src/components/ds-autocomplete/ds-autocomplete.types.tsx +37 -14
  14. package/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss +3 -7
  15. package/src/components/ds-comment-bubble/components/ds-comment-thread/ds-comment-thread.module.scss +62 -0
  16. package/src/components/ds-comment-bubble/components/ds-comment-thread/ds-comment-thread.tsx +83 -0
  17. package/src/components/ds-comment-bubble/components/ds-comment-thread/ds-comment-thread.types.ts +12 -0
  18. package/src/components/ds-comment-bubble/components/ds-comment-thread/index.ts +2 -0
  19. package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.module.scss +149 -0
  20. package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.tsx +147 -0
  21. package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.types.ts +15 -0
  22. package/src/components/ds-comment-bubble/components/ds-thread-item/index.ts +2 -0
  23. package/src/components/ds-comment-bubble/ds-comment-bubble.module.scss +298 -0
  24. package/src/components/ds-comment-bubble/ds-comment-bubble.tsx +241 -0
  25. package/src/components/ds-comment-bubble/ds-comment-bubble.types.ts +27 -0
  26. package/src/components/ds-comment-bubble/hooks/index.ts +3 -0
  27. package/src/components/ds-comment-bubble/hooks/use-auto-resize.ts +17 -0
  28. package/src/components/ds-comment-bubble/hooks/use-click-outside.ts +22 -0
  29. package/src/components/ds-comment-bubble/hooks/use-scroll-overflow.ts +27 -0
  30. package/src/components/ds-comment-bubble/index.ts +6 -0
  31. package/src/components/ds-comment-card/ds-comment-card.module.scss +162 -0
  32. package/src/components/ds-comment-card/ds-comment-card.tsx +171 -0
  33. package/src/components/ds-comment-card/ds-comment-card.types.ts +45 -0
  34. package/src/components/ds-comment-card/index.ts +2 -0
  35. package/src/components/ds-comment-indicator/ds-comment-indicator.module.scss +65 -0
  36. package/src/components/ds-comment-indicator/ds-comment-indicator.tsx +48 -0
  37. package/src/components/ds-comment-indicator/ds-comment-indicator.types.ts +13 -0
  38. package/src/components/ds-comment-indicator/index.ts +2 -0
  39. package/src/components/ds-comments-drawer/comments-filters.test.ts +483 -0
  40. package/src/components/ds-comments-drawer/comments-filters.ts +134 -0
  41. package/src/components/ds-comments-drawer/comments-filters.types.ts +14 -0
  42. package/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.module.scss +92 -0
  43. package/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx +219 -0
  44. package/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.types.ts +12 -0
  45. package/src/components/ds-comments-drawer/components/comments-filter-modal/index.ts +2 -0
  46. package/src/components/ds-comments-drawer/ds-comments-drawer.module.scss +67 -0
  47. package/src/components/ds-comments-drawer/ds-comments-drawer.tsx +215 -0
  48. package/src/components/ds-comments-drawer/ds-comments-drawer.types.ts +23 -0
  49. package/src/components/ds-comments-drawer/index.ts +3 -0
  50. package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +2 -0
  51. package/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx +8 -4
  52. package/src/components/ds-file-upload/types/accept-types.ts +0 -1
  53. package/src/components/ds-filter-status-icon/ds-filter-status-icon.module.scss +19 -0
  54. package/src/components/ds-filter-status-icon/ds-filter-status-icon.tsx +35 -0
  55. package/src/components/ds-filter-status-icon/ds-filter-status-icon.types.ts +31 -0
  56. package/src/components/ds-filter-status-icon/index.ts +3 -0
  57. package/src/components/ds-icon/custom-icons/index.ts +10 -0
  58. package/src/components/ds-icon/custom-icons/special-failed.tsx +16 -0
  59. package/src/components/ds-icon/custom-icons/special-paused.tsx +16 -0
  60. package/src/components/ds-icon/custom-icons/special-running.tsx +16 -0
  61. package/src/components/ds-icon/custom-icons/special-warning.tsx +16 -0
  62. package/src/components/ds-panel/ds-panel.module.scss +3 -3
  63. package/src/components/ds-progress-arc/ds-progress-arc.module.scss +57 -0
  64. package/src/components/ds-progress-arc/ds-progress-arc.tsx +111 -0
  65. package/src/components/ds-progress-arc/ds-progress-arc.types.ts +34 -0
  66. package/src/components/ds-progress-arc/index.ts +3 -0
  67. package/src/components/ds-progress-arc/utils.test.ts +79 -0
  68. package/src/components/ds-progress-arc/utils.ts +61 -0
  69. package/src/components/ds-progress-donut/ds-progress-donut.module.scss +67 -0
  70. package/src/components/ds-progress-donut/ds-progress-donut.tsx +83 -0
  71. package/src/components/ds-progress-donut/ds-progress-donut.types.ts +38 -0
  72. package/src/components/ds-progress-donut/index.ts +2 -0
  73. package/src/components/ds-progress-donut/utils.ts +1 -0
  74. package/src/components/ds-progress-linear/ds-progress-linear.module.scss +100 -0
  75. package/src/components/ds-progress-linear/ds-progress-linear.tsx +107 -0
  76. package/src/components/ds-progress-linear/ds-progress-linear.types.ts +72 -0
  77. package/src/components/ds-progress-linear/index.ts +2 -0
  78. package/src/components/ds-progress-linear/utils.test.ts +70 -0
  79. package/src/components/ds-progress-linear/utils.ts +7 -0
  80. package/src/components/ds-radio-group/ds-radio-group.tsx +2 -0
  81. package/src/components/ds-select/ds-select.tsx +2 -1
  82. package/src/components/ds-select/ds-select.types.tsx +7 -0
  83. package/src/components/ds-stepper/components/ds-step.tsx +1 -1
  84. package/src/components/ds-stepper/ds-stepper.module.scss +16 -2
  85. package/src/components/ds-stepper/ds-stepper.tsx +3 -1
  86. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +9 -0
  87. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +137 -0
  88. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.types.ts +22 -0
  89. package/src/components/ds-table/components/ds-table-body-virtualized/index.ts +2 -0
  90. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.module.scss +3 -1
  91. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +7 -10
  92. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +3 -4
  93. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +13 -53
  94. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +32 -85
  95. package/src/components/ds-table/components/ds-table-row/ds-table-row.types.ts +0 -6
  96. package/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.module.scss +10 -0
  97. package/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.tsx +45 -0
  98. package/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.types.ts +7 -0
  99. package/src/components/ds-table/components/ds-table-row-expandable-cell/index.ts +2 -0
  100. package/src/components/ds-table/components/ds-table-row-selectable-cell/ds-table-row-selectable-cell.module.scss +6 -0
  101. package/src/components/ds-table/components/ds-table-row-selectable-cell/ds-table-row-selectable-cell.tsx +30 -0
  102. package/src/components/ds-table/components/ds-table-row-selectable-cell/ds-table-row-selectable-cell.types.ts +6 -0
  103. package/src/components/ds-table/components/ds-table-row-selectable-cell/index.ts +2 -0
  104. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +80 -0
  105. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +95 -0
  106. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.types.ts +11 -0
  107. package/src/components/ds-table/components/ds-table-row-virtualized/index.ts +2 -0
  108. package/src/components/ds-table/context/ds-table-context.tsx +0 -9
  109. package/src/components/ds-table/ds-table.module.scss +4 -17
  110. package/src/components/ds-table/ds-table.tsx +44 -77
  111. package/src/components/ds-table/ds-table.types.ts +57 -12
  112. package/src/components/ds-table/index.ts +1 -0
  113. package/src/components/ds-table/styles/_mixins.scss +15 -0
  114. package/src/components/ds-table/styles/_variables.scss +2 -2
  115. package/src/components/ds-table/utils/{column-styling.ts → column-size.ts} +4 -14
  116. package/src/components/ds-table/utils/constants.ts +4 -0
  117. package/src/components/ds-tag/index.tsx +1 -0
  118. package/src/components/ds-tag-filter/ds-tag-filter.module.scss +13 -15
  119. package/src/components/ds-tag-filter/ds-tag-filter.tsx +38 -72
  120. package/src/components/ds-tag-filter/ds-tag-filter.types.ts +2 -3
  121. package/src/components/ds-tag-filter/hooks/use-tag-overflow-calculation.ts +12 -62
  122. package/src/components/ds-tag-filter/utils.test.ts +2 -108
  123. package/src/components/ds-tag-filter/utils.ts +2 -12
  124. package/src/components/ds-tree/ds-tree.module.scss +283 -0
  125. package/src/components/ds-tree/ds-tree.tsx +172 -0
  126. package/src/components/ds-tree/ds-tree.types.ts +83 -0
  127. package/src/components/ds-tree/ds-tree.utils.ts +11 -0
  128. package/src/components/ds-tree/index.ts +3 -0
  129. package/src/index.ts +9 -0
  130. package/src/utils/format-relative-time.test.ts +74 -0
  131. package/src/utils/format-relative-time.ts +52 -0
  132. package/src/utils/numbers.test.ts +24 -0
  133. package/src/utils/numbers.ts +1 -0
  134. package/src/components/ds-table/utils/create-columns-grid-template.ts +0 -23
@@ -0,0 +1,283 @@
1
+ @use '../../styles/typography';
2
+
3
+ $indent-per-level: 12px;
4
+ $half-icon: calc(var(--icon-width-tiny) * 0.5);
5
+ $transition-duration: 0.2s;
6
+
7
+ @mixin node-row {
8
+ display: flex;
9
+ align-items: center;
10
+ gap: var(--spacing-3xs);
11
+ border-radius: 4px;
12
+ cursor: pointer;
13
+ user-select: none;
14
+ width: 100%;
15
+ background: transparent;
16
+ color: var(--color-font-secondary);
17
+
18
+ --ds-tree-depth: calc(var(--depth) - 1);
19
+ --ds-tree-indent-offset: calc(#{$indent-per-level} * var(--ds-tree-depth));
20
+ --ds-tree-icon-offset: calc(#{$half-icon} * var(--ds-tree-depth));
21
+ --ds-tree-offset: calc(var(--spacing-3xs) + var(--ds-tree-indent-offset) + var(--ds-tree-icon-offset));
22
+
23
+ padding-inline-start: var(--ds-tree-offset);
24
+ padding-inline-end: var(--spacing-3xs);
25
+
26
+ &:hover {
27
+ background: var(--color-background-action-hover-weak);
28
+ }
29
+
30
+ &[data-focus] {
31
+ position: relative;
32
+ outline: none;
33
+ background: var(--color-background-action-hover-weak);
34
+
35
+ &::after {
36
+ content: '';
37
+ position: absolute;
38
+ inset: 0;
39
+ z-index: 2;
40
+ border: 2px solid var(--color-border-action-primary-hover);
41
+ border-radius: inherit;
42
+ pointer-events: none;
43
+ }
44
+ }
45
+
46
+ &[data-selected] {
47
+ background: var(--color-background-action-secondary-hover);
48
+ }
49
+
50
+ &[data-disabled] {
51
+ cursor: not-allowed;
52
+ pointer-events: none;
53
+ .itemText {
54
+ color: var(--color-secondary-300);
55
+ }
56
+ }
57
+
58
+ &:hover .itemAction,
59
+ &[data-focus-visible] .itemAction {
60
+ opacity: 1;
61
+ }
62
+ }
63
+
64
+ @mixin node-text {
65
+ flex: 1;
66
+ display: inline-flex;
67
+ align-items: center;
68
+ gap: var(--spacing-xs);
69
+ min-width: 0;
70
+
71
+ @include typography.ellipsis;
72
+ }
73
+
74
+ .root {
75
+ display: flex;
76
+ flex-direction: column;
77
+ width: 100%;
78
+
79
+ &[data-size='medium'] {
80
+ .branchControl,
81
+ .item {
82
+ padding-block: var(--spacing-xs);
83
+ }
84
+
85
+ .branchText,
86
+ .itemText {
87
+ @include typography.body-sm-reg;
88
+ }
89
+ }
90
+
91
+ &[data-size='small'] {
92
+ .branchControl,
93
+ .item {
94
+ padding-block: var(--spacing-2xs);
95
+ }
96
+
97
+ .branchText,
98
+ .itemText {
99
+ @include typography.body-xs-reg;
100
+ }
101
+ }
102
+ }
103
+
104
+ .tree {
105
+ display: flex;
106
+ flex-direction: column;
107
+ }
108
+
109
+ .branch {
110
+ position: relative;
111
+ }
112
+
113
+ .branchControl {
114
+ @include node-row;
115
+ }
116
+
117
+ .branchIndicator {
118
+ display: inline-flex;
119
+ align-items: center;
120
+ justify-content: center;
121
+ flex-shrink: 0;
122
+ width: var(--icon-width-tiny);
123
+ height: var(--icon-width-tiny);
124
+ color: var(--color-font-main);
125
+ transform-origin: center;
126
+ transition: transform $transition-duration;
127
+
128
+ &[data-state='open'] {
129
+ transform: rotate(90deg);
130
+ }
131
+ }
132
+
133
+ .branchText {
134
+ @include node-text;
135
+ color: var(--color-font-secondary);
136
+ }
137
+
138
+ .branchContent {
139
+ position: relative;
140
+ overflow: hidden;
141
+
142
+ &[data-state='open'] {
143
+ animation: ds-tree-expand $transition-duration ease-out;
144
+ }
145
+
146
+ &[data-state='closed'] {
147
+ animation: ds-tree-collapse $transition-duration ease-out;
148
+ }
149
+ }
150
+
151
+ .branchIndentGuide {
152
+ position: absolute;
153
+ z-index: 1;
154
+ width: 1px;
155
+ height: 100%;
156
+ background: var(--color-border-secondary);
157
+
158
+ --ds-tree-depth: calc(var(--depth) - 1);
159
+ --ds-tree-indent-offset: calc(#{$indent-per-level} * var(--ds-tree-depth));
160
+ --ds-tree-guide-offset: calc(var(--spacing-3xs) + var(--ds-tree-indent-offset));
161
+ --ds-tree-icon-offset: calc(#{$half-icon} * var(--depth));
162
+
163
+ inset-inline-start: calc(var(--ds-tree-guide-offset) + var(--ds-tree-icon-offset));
164
+ }
165
+
166
+ .item {
167
+ @include node-row;
168
+ }
169
+
170
+ .itemText {
171
+ @include node-text;
172
+ color: var(--color-font-secondary);
173
+ }
174
+
175
+ .itemIndicator {
176
+ display: inline-flex;
177
+ align-items: center;
178
+ justify-content: center;
179
+ flex-shrink: 0;
180
+ width: var(--icon-width-tiny);
181
+ height: var(--icon-width-tiny);
182
+ }
183
+
184
+ .itemDot {
185
+ width: 4px;
186
+ height: 4px;
187
+ border-radius: 50%;
188
+ background-color: var(--color-font-main);
189
+ }
190
+
191
+ .itemAction {
192
+ display: inline-flex;
193
+ align-items: center;
194
+ justify-content: center;
195
+ flex-shrink: 0;
196
+ border: none;
197
+ background: transparent;
198
+ padding: 0;
199
+ opacity: 0;
200
+ cursor: pointer;
201
+ color: var(--color-font-secondary);
202
+ transition: opacity $transition-duration;
203
+
204
+ &:hover {
205
+ color: var(--color-font-main);
206
+ }
207
+ }
208
+
209
+ .nodeCheckbox {
210
+ display: inline-flex;
211
+ align-items: center;
212
+ justify-content: center;
213
+ flex-shrink: 0;
214
+ cursor: pointer;
215
+
216
+ &[data-state='checked'],
217
+ &[data-state='indeterminate'] {
218
+ .nodeCheckboxBox {
219
+ background-color: var(--color-background-selected);
220
+ border-color: var(--color-background-selected);
221
+ }
222
+ }
223
+
224
+ &[data-disabled] {
225
+ cursor: not-allowed;
226
+
227
+ .nodeCheckboxBox {
228
+ border-color: var(--color-background-disable);
229
+ }
230
+
231
+ &[data-state='checked'] .nodeCheckboxBox,
232
+ &[data-state='indeterminate'] .nodeCheckboxBox {
233
+ background-color: var(--color-background-disable);
234
+ border-color: var(--color-background-disable);
235
+ }
236
+ }
237
+ }
238
+
239
+ .nodeCheckboxBox {
240
+ display: flex;
241
+ flex-direction: column;
242
+ justify-content: center;
243
+ align-items: center;
244
+ width: 16px;
245
+ height: 16px;
246
+ border: 1.5px solid var(--color-background-deselected);
247
+ border-radius: 2px;
248
+ color: var(--color-secondary-white);
249
+ transition:
250
+ background-color $transition-duration,
251
+ border-color $transition-duration;
252
+ }
253
+
254
+ .nodeCheckboxIndicator {
255
+ flex: 1;
256
+ display: flex;
257
+ align-items: center;
258
+ justify-content: center;
259
+ }
260
+
261
+ @keyframes ds-tree-expand {
262
+ from {
263
+ height: 0;
264
+ opacity: 0;
265
+ }
266
+
267
+ to {
268
+ height: var(--height);
269
+ opacity: 1;
270
+ }
271
+ }
272
+
273
+ @keyframes ds-tree-collapse {
274
+ from {
275
+ height: var(--height);
276
+ opacity: 1;
277
+ }
278
+
279
+ to {
280
+ height: 0;
281
+ opacity: 0;
282
+ }
283
+ }
@@ -0,0 +1,172 @@
1
+ import { TreeView } from '@ark-ui/react/tree-view';
2
+ import classNames from 'classnames';
3
+
4
+ import { DsIcon } from '../ds-icon';
5
+ import styles from './ds-tree.module.scss';
6
+ import type {
7
+ DsTreeBranchContentProps,
8
+ DsTreeBranchControlProps,
9
+ DsTreeBranchIndentGuideProps,
10
+ DsTreeBranchIndicatorProps,
11
+ DsTreeBranchProps,
12
+ DsTreeBranchTextProps,
13
+ DsTreeItemActionProps,
14
+ DsTreeItemIndicatorProps,
15
+ DsTreeItemProps,
16
+ DsTreeItemTextProps,
17
+ DsTreeNode,
18
+ DsTreeNodeCheckboxProps,
19
+ DsTreeRootProps,
20
+ DsTreeTreeProps,
21
+ } from './ds-tree.types';
22
+
23
+ const DsTreeRoot = <T extends DsTreeNode = DsTreeNode>({
24
+ collection,
25
+ size = 'medium',
26
+ ref,
27
+ className,
28
+ style,
29
+ children,
30
+ selectedValue,
31
+ defaultSelectedValue,
32
+ onSelectionChange,
33
+ selectionMode = 'single',
34
+ expandedValue,
35
+ defaultExpandedValue,
36
+ onExpandedChange,
37
+ expandOnClick = true,
38
+ checkedValue,
39
+ defaultCheckedValue,
40
+ onCheckedChange,
41
+ typeahead = true,
42
+ lazyMount = true,
43
+ unmountOnExit,
44
+ }: DsTreeRootProps<T>) => (
45
+ <TreeView.Root
46
+ ref={ref}
47
+ collection={collection}
48
+ selectedValue={selectedValue}
49
+ defaultSelectedValue={defaultSelectedValue}
50
+ onSelectionChange={onSelectionChange}
51
+ selectionMode={selectionMode}
52
+ expandedValue={expandedValue}
53
+ defaultExpandedValue={defaultExpandedValue}
54
+ onExpandedChange={onExpandedChange}
55
+ expandOnClick={expandOnClick}
56
+ checkedValue={checkedValue}
57
+ defaultCheckedValue={defaultCheckedValue}
58
+ onCheckedChange={onCheckedChange}
59
+ typeahead={typeahead}
60
+ lazyMount={lazyMount}
61
+ unmountOnExit={unmountOnExit}
62
+ className={classNames(styles.root, className)}
63
+ style={style}
64
+ data-size={size}
65
+ >
66
+ {children}
67
+ </TreeView.Root>
68
+ );
69
+
70
+ const DsTreeTree = ({ className, style, children }: DsTreeTreeProps) => (
71
+ <TreeView.Tree className={classNames(styles.tree, className)} style={style}>
72
+ {children}
73
+ </TreeView.Tree>
74
+ );
75
+
76
+ const DsTreeBranch = ({ className, style, children }: DsTreeBranchProps) => (
77
+ <TreeView.Branch className={classNames(styles.branch, className)} style={style}>
78
+ {children}
79
+ </TreeView.Branch>
80
+ );
81
+
82
+ const DsTreeBranchControl = ({ className, style, children }: DsTreeBranchControlProps) => (
83
+ <TreeView.BranchControl className={classNames(styles.branchControl, className)} style={style}>
84
+ {children}
85
+ </TreeView.BranchControl>
86
+ );
87
+
88
+ const DsTreeBranchIndicator = ({ className, style, children }: DsTreeBranchIndicatorProps) => (
89
+ <TreeView.BranchIndicator className={classNames(styles.branchIndicator, className)} style={style}>
90
+ {children ?? <DsIcon icon="keyboard_arrow_right" size="tiny" />}
91
+ </TreeView.BranchIndicator>
92
+ );
93
+
94
+ const DsTreeBranchText = ({ className, style, children }: DsTreeBranchTextProps) => (
95
+ <TreeView.BranchText className={classNames(styles.branchText, className)} style={style}>
96
+ {children}
97
+ </TreeView.BranchText>
98
+ );
99
+
100
+ const DsTreeBranchContent = ({ className, style, children }: DsTreeBranchContentProps) => (
101
+ <TreeView.BranchContent className={classNames(styles.branchContent, className)} style={style}>
102
+ {children}
103
+ </TreeView.BranchContent>
104
+ );
105
+
106
+ const DsTreeBranchIndentGuide = ({ className, style }: DsTreeBranchIndentGuideProps) => (
107
+ <TreeView.BranchIndentGuide className={classNames(styles.branchIndentGuide, className)} style={style} />
108
+ );
109
+
110
+ const DsTreeItem = ({ className, style, children, onClick }: DsTreeItemProps) => (
111
+ <TreeView.Item className={classNames(styles.item, className)} style={style} onClick={onClick}>
112
+ {children}
113
+ </TreeView.Item>
114
+ );
115
+
116
+ const DsTreeItemText = ({ className, style, children }: DsTreeItemTextProps) => (
117
+ <TreeView.ItemText className={classNames(styles.itemText, className)} style={style}>
118
+ {children}
119
+ </TreeView.ItemText>
120
+ );
121
+
122
+ const DsTreeItemIndicator = ({ className, style }: DsTreeItemIndicatorProps) => (
123
+ <span className={classNames(styles.itemIndicator, className)} style={style} aria-hidden="true">
124
+ <span className={styles.itemDot} />
125
+ </span>
126
+ );
127
+
128
+ const DsTreeNodeCheckbox = ({ className, style }: DsTreeNodeCheckboxProps) => (
129
+ <TreeView.NodeCheckbox className={classNames(styles.nodeCheckbox, className)} style={style}>
130
+ <div className={styles.nodeCheckboxBox}>
131
+ <TreeView.NodeCheckboxIndicator
132
+ className={styles.nodeCheckboxIndicator}
133
+ indeterminate={<DsIcon icon="check_indeterminate_small" size="tiny" variant="outlined" />}
134
+ >
135
+ <DsIcon icon="check_small" size="tiny" variant="outlined" />
136
+ </TreeView.NodeCheckboxIndicator>
137
+ </div>
138
+ </TreeView.NodeCheckbox>
139
+ );
140
+
141
+ const DsTreeItemAction = ({ className, style, children, onClick }: DsTreeItemActionProps) => (
142
+ <button
143
+ className={classNames(styles.itemAction, className)}
144
+ style={style}
145
+ type="button"
146
+ tabIndex={-1}
147
+ onClick={(e) => {
148
+ e.stopPropagation();
149
+ onClick?.(e);
150
+ }}
151
+ >
152
+ {children}
153
+ </button>
154
+ );
155
+
156
+ export const DsTree = {
157
+ Root: DsTreeRoot,
158
+ Tree: DsTreeTree,
159
+ NodeProvider: TreeView.NodeProvider,
160
+ NodeContext: TreeView.NodeContext,
161
+ Branch: DsTreeBranch,
162
+ BranchControl: DsTreeBranchControl,
163
+ BranchIndicator: DsTreeBranchIndicator,
164
+ BranchText: DsTreeBranchText,
165
+ BranchContent: DsTreeBranchContent,
166
+ BranchIndentGuide: DsTreeBranchIndentGuide,
167
+ Item: DsTreeItem,
168
+ ItemText: DsTreeItemText,
169
+ ItemIndicator: DsTreeItemIndicator,
170
+ ItemAction: DsTreeItemAction,
171
+ NodeCheckbox: DsTreeNodeCheckbox,
172
+ };
@@ -0,0 +1,83 @@
1
+ import type { CSSProperties, MouseEvent, ReactNode, Ref } from 'react';
2
+ import type { TreeView as ArkTreeView } from '@ark-ui/react/tree-view';
3
+ import type { IconType } from '../ds-icon';
4
+ import type { FilterStatus } from '../ds-filter-status-icon';
5
+
6
+ export interface DsTreeNode {
7
+ id: string;
8
+ name: string;
9
+ disabled?: boolean;
10
+ icon?: IconType;
11
+ status?: FilterStatus;
12
+ children?: DsTreeNode[];
13
+ }
14
+
15
+ export const dsTreeSizes = ['medium', 'small'] as const;
16
+ export type DsTreeSize = (typeof dsTreeSizes)[number];
17
+
18
+ interface DsTreeBaseProps {
19
+ className?: string;
20
+ style?: CSSProperties;
21
+ }
22
+
23
+ interface DsTreeBasePropsWithChildren extends DsTreeBaseProps {
24
+ children?: ReactNode;
25
+ }
26
+
27
+ export interface DsTreeRootProps<T extends DsTreeNode = DsTreeNode> extends DsTreeBasePropsWithChildren {
28
+ collection: ArkTreeView.RootProps<T>['collection'];
29
+ size?: DsTreeSize;
30
+ ref?: Ref<HTMLDivElement>;
31
+
32
+ selectedValue?: string[];
33
+ defaultSelectedValue?: string[];
34
+ onSelectionChange?: ArkTreeView.RootProps<T>['onSelectionChange'];
35
+ selectionMode?: 'single' | 'multiple';
36
+
37
+ expandedValue?: string[];
38
+ defaultExpandedValue?: string[];
39
+ onExpandedChange?: ArkTreeView.RootProps<T>['onExpandedChange'];
40
+ expandOnClick?: boolean;
41
+
42
+ checkedValue?: string[];
43
+ defaultCheckedValue?: string[];
44
+ onCheckedChange?: ArkTreeView.RootProps<T>['onCheckedChange'];
45
+
46
+ /**
47
+ * Whether to enable typeahead navigation — typing characters focuses matching nodes
48
+ */
49
+ typeahead?: boolean;
50
+ /**
51
+ * Whether to defer mounting of branch content until a branch is first expanded
52
+ */
53
+ lazyMount?: boolean;
54
+ unmountOnExit?: boolean;
55
+ }
56
+
57
+ export type DsTreeTreeProps = DsTreeBasePropsWithChildren;
58
+
59
+ export type DsTreeBranchProps = DsTreeBasePropsWithChildren;
60
+
61
+ export type DsTreeBranchControlProps = DsTreeBasePropsWithChildren;
62
+
63
+ export type DsTreeBranchIndicatorProps = DsTreeBasePropsWithChildren;
64
+
65
+ export type DsTreeBranchTextProps = DsTreeBasePropsWithChildren;
66
+
67
+ export type DsTreeBranchContentProps = DsTreeBasePropsWithChildren;
68
+
69
+ export type DsTreeBranchIndentGuideProps = DsTreeBaseProps;
70
+
71
+ export interface DsTreeItemProps extends DsTreeBasePropsWithChildren {
72
+ onClick?: (event: MouseEvent<HTMLDivElement>) => void;
73
+ }
74
+
75
+ export type DsTreeItemTextProps = DsTreeBasePropsWithChildren;
76
+
77
+ export type DsTreeItemIndicatorProps = DsTreeBaseProps;
78
+
79
+ export type DsTreeNodeCheckboxProps = DsTreeBaseProps;
80
+
81
+ export interface DsTreeItemActionProps extends DsTreeBasePropsWithChildren {
82
+ onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
83
+ }
@@ -0,0 +1,11 @@
1
+ import { createTreeCollection } from '@ark-ui/react/tree-view';
2
+
3
+ import type { DsTreeNode } from './ds-tree.types';
4
+
5
+ export const createDsTreeCollection = <T extends DsTreeNode>(nodes: T[]) =>
6
+ createTreeCollection<T>({
7
+ nodeToValue: (node) => node.id,
8
+ nodeToString: (node) => node.name,
9
+ // Ark requires rootNode typed as T; the root is a synthetic container never exposed to consumers
10
+ rootNode: { id: 'ROOT', name: '', children: nodes } as unknown as T,
11
+ });
@@ -0,0 +1,3 @@
1
+ export { DsTree } from './ds-tree';
2
+ export { createDsTreeCollection } from './ds-tree.utils';
3
+ export type * from './ds-tree.types';
package/src/index.ts CHANGED
@@ -8,6 +8,10 @@ export * from './components/ds-card';
8
8
  export * from './components/ds-checkbox';
9
9
  export * from './components/ds-chip';
10
10
  export * from './components/ds-chip-group';
11
+ export * from './components/ds-comment-bubble';
12
+ export * from './components/ds-comment-card';
13
+ export * from './components/ds-comment-indicator';
14
+ export * from './components/ds-comments-drawer';
11
15
  export * from './components/ds-confirmation';
12
16
  export * from './components/ds-date-input';
13
17
  export * from './components/ds-dialog';
@@ -16,6 +20,7 @@ export * from './components/ds-drawer';
16
20
  export * from './components/ds-dropdown-menu';
17
21
  export * from './components/ds-expandable-text-input';
18
22
  export * from './components/ds-file-upload';
23
+ export * from './components/ds-filter-status-icon';
19
24
  export * from './components/ds-form-control';
20
25
  export * from './components/ds-grid';
21
26
  export * from './components/ds-icon';
@@ -25,6 +30,9 @@ export * from './components/ds-number-input';
25
30
  export * from './components/ds-panel';
26
31
  export * from './components/ds-password-input';
27
32
  export * from './components/ds-popover';
33
+ export * from './components/ds-progress-arc';
34
+ export * from './components/ds-progress-donut';
35
+ export * from './components/ds-progress-linear';
28
36
  export * from './components/ds-radio-group';
29
37
  export * from './components/ds-segment-group';
30
38
  export * from './components/ds-select';
@@ -43,5 +51,6 @@ export * from './components/ds-textarea';
43
51
  export * from './components/ds-toast';
44
52
  export * from './components/ds-toggle';
45
53
  export * from './components/ds-tooltip';
54
+ export * from './components/ds-tree';
46
55
  export * from './components/ds-typography';
47
56
  export * from './components/ds-vertical-tabs';
@@ -0,0 +1,74 @@
1
+ import { describe, expect, it, vi, afterEach, beforeEach } from 'vitest';
2
+ import { formatRelativeTime } from './format-relative-time';
3
+
4
+ const NOW = new Date('2025-06-15T12:00:00Z');
5
+
6
+ const minutesAgo = (minutes: number) => new Date(NOW.getTime() - minutes * 60 * 1000);
7
+ const hoursAgo = (hours: number) => new Date(NOW.getTime() - hours * 60 * 60 * 1000);
8
+ const daysAgo = (days: number) => new Date(NOW.getTime() - days * 24 * 60 * 60 * 1000);
9
+
10
+ describe('formatRelativeTime', () => {
11
+ beforeEach(() => {
12
+ vi.useFakeTimers();
13
+ vi.setSystemTime(NOW);
14
+ });
15
+
16
+ afterEach(() => {
17
+ vi.useRealTimers();
18
+ });
19
+
20
+ it('should return "Just now" for future dates', () => {
21
+ const futureDate = new Date(NOW.getTime() + 60_000);
22
+
23
+ expect(formatRelativeTime(futureDate)).toBe('Just now');
24
+ });
25
+
26
+ it('should return "Just now" for less than 60 seconds ago', () => {
27
+ const recent = new Date(NOW.getTime() - 30_000);
28
+
29
+ expect(formatRelativeTime(recent)).toBe('Just now');
30
+ });
31
+
32
+ it('should return "Just now" at exactly 0 seconds', () => {
33
+ expect(formatRelativeTime(NOW)).toBe('Just now');
34
+ });
35
+
36
+ it('should return minutes for 1-59 minutes ago', () => {
37
+ expect(formatRelativeTime(minutesAgo(1))).toBe('1m ago');
38
+ expect(formatRelativeTime(minutesAgo(30))).toBe('30m ago');
39
+ expect(formatRelativeTime(minutesAgo(59))).toBe('59m ago');
40
+ });
41
+
42
+ it('should return hours for 1-23 hours ago', () => {
43
+ expect(formatRelativeTime(hoursAgo(1))).toBe('1h ago');
44
+ expect(formatRelativeTime(hoursAgo(12))).toBe('12h ago');
45
+ expect(formatRelativeTime(hoursAgo(23))).toBe('23h ago');
46
+ });
47
+
48
+ it('should return days for 1-6 days ago', () => {
49
+ expect(formatRelativeTime(daysAgo(1))).toBe('1d ago');
50
+ expect(formatRelativeTime(daysAgo(6))).toBe('6d ago');
51
+ });
52
+
53
+ it('should return weeks for 1-3 weeks ago', () => {
54
+ expect(formatRelativeTime(daysAgo(7))).toBe('1w ago');
55
+ expect(formatRelativeTime(daysAgo(14))).toBe('2w ago');
56
+ expect(formatRelativeTime(daysAgo(21))).toBe('3w ago');
57
+ });
58
+
59
+ it('should return months for 1-11 months ago', () => {
60
+ expect(formatRelativeTime(daysAgo(30))).toBe('1mo ago');
61
+ expect(formatRelativeTime(daysAgo(60))).toBe('2mo ago');
62
+ expect(formatRelativeTime(daysAgo(330))).toBe('11mo ago');
63
+ });
64
+
65
+ it('should return years for 12+ months ago', () => {
66
+ expect(formatRelativeTime(daysAgo(365))).toBe('1y ago');
67
+ expect(formatRelativeTime(daysAgo(730))).toBe('2y ago');
68
+ });
69
+
70
+ it('should transition from weeks to months at 4 weeks (weeks cap at 3)', () => {
71
+ expect(formatRelativeTime(daysAgo(27))).toBe('3w ago');
72
+ expect(formatRelativeTime(daysAgo(30))).toBe('1mo ago');
73
+ });
74
+ });