@drivenets/design-system 0.18.1 → 0.19.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 (122) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/index.cjs +17138 -12075
  3. package/dist/index.d.cts +783 -238
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +783 -238
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +17123 -12076
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +15 -15
  11. package/src/components/ds-bot-button/ds-bot-button.figma.ts +38 -0
  12. package/src/components/ds-bot-button/ds-bot-button.module.scss +41 -0
  13. package/src/components/ds-bot-button/ds-bot-button.tsx +48 -0
  14. package/src/components/ds-bot-button/ds-bot-button.types.ts +33 -0
  15. package/src/components/ds-bot-button/index.ts +2 -0
  16. package/src/components/ds-bot-button/net-gen-logo.tsx +46 -0
  17. package/src/components/ds-bulk-actions/components/action-button.tsx +42 -0
  18. package/src/components/ds-bulk-actions/components/menu-entries.tsx +76 -0
  19. package/src/components/ds-bulk-actions/components/overflow.tsx +75 -0
  20. package/src/components/ds-bulk-actions/ds-bulk-actions.figma.ts +28 -0
  21. package/src/components/ds-bulk-actions/ds-bulk-actions.module.scss +199 -0
  22. package/src/components/ds-bulk-actions/ds-bulk-actions.tsx +220 -0
  23. package/src/components/ds-bulk-actions/ds-bulk-actions.types.ts +170 -0
  24. package/src/components/ds-bulk-actions/ds-bulk-actions.unit.test.ts +195 -0
  25. package/src/components/ds-bulk-actions/ds-bulk-actions.utils.ts +91 -0
  26. package/src/components/ds-bulk-actions/ds-bulk-actions.utils.unit.test.ts +134 -0
  27. package/src/components/ds-bulk-actions/hooks/compute-bulk-actions-overflow.ts +85 -0
  28. package/src/components/ds-bulk-actions/hooks/use-bulk-actions-overflow.ts +153 -0
  29. package/src/components/ds-bulk-actions/index.ts +18 -0
  30. package/src/components/ds-button-v3/ds-button-v3.module.scss +2 -2
  31. package/src/components/ds-catalog-layout/ds-catalog-layout.figma.ts +1 -1
  32. package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +2 -0
  33. package/src/components/ds-empty-state/ds-empty-state.module.scss +16 -0
  34. package/src/components/ds-empty-state/ds-empty-state.tsx +45 -0
  35. package/src/components/ds-empty-state/ds-empty-state.types.ts +38 -0
  36. package/src/components/ds-empty-state/index.ts +3 -0
  37. package/src/components/ds-file-upload/ds-file-upload.tsx +4 -1
  38. package/src/components/ds-form-control/ds-form-control.tsx +18 -10
  39. package/src/components/ds-illustration/ds-illustration.figma.ts +34 -0
  40. package/src/components/ds-illustration/ds-illustration.module.scss +6 -0
  41. package/src/components/ds-illustration/ds-illustration.tsx +29 -0
  42. package/src/components/ds-illustration/ds-illustration.types.ts +45 -0
  43. package/src/components/ds-illustration/illustrations/configuration.tsx +51 -0
  44. package/src/components/ds-illustration/illustrations/device.tsx +133 -0
  45. package/src/components/ds-illustration/illustrations/done.tsx +107 -0
  46. package/src/components/{ds-catalog-layout/catalog-layout-empty-illustration.tsx → ds-illustration/illustrations/empty-inbox.tsx} +10 -14
  47. package/src/components/ds-illustration/illustrations/error.tsx +86 -0
  48. package/src/components/ds-illustration/illustrations/go-filter.tsx +51 -0
  49. package/src/components/ds-illustration/illustrations/illustration-clouds.tsx +10 -0
  50. package/src/components/ds-illustration/illustrations/index.ts +39 -0
  51. package/src/components/ds-illustration/illustrations/no-connection.tsx +54 -0
  52. package/src/components/ds-illustration/illustrations/no-credit-card.tsx +48 -0
  53. package/src/components/ds-illustration/illustrations/no-device.tsx +61 -0
  54. package/src/components/ds-illustration/illustrations/no-documents.tsx +52 -0
  55. package/src/components/ds-illustration/illustrations/no-gps.tsx +57 -0
  56. package/src/components/ds-illustration/illustrations/no-images.tsx +82 -0
  57. package/src/components/ds-illustration/illustrations/no-items-cart.tsx +86 -0
  58. package/src/components/ds-illustration/illustrations/no-messages.tsx +51 -0
  59. package/src/components/ds-illustration/illustrations/no-search-result.tsx +76 -0
  60. package/src/components/ds-illustration/illustrations/no-tasks.tsx +61 -0
  61. package/src/components/ds-illustration/illustrations/site.tsx +80 -0
  62. package/src/components/ds-illustration/index.ts +3 -0
  63. package/src/components/ds-main-menu/components/expanded-item/expanded-item.tsx +108 -0
  64. package/src/components/ds-main-menu/components/expanded-item/expanded-item.types.ts +8 -0
  65. package/src/components/ds-main-menu/components/expanded-item/index.ts +1 -0
  66. package/src/components/ds-main-menu/components/menu-graphic/index.ts +1 -0
  67. package/src/components/ds-main-menu/components/menu-graphic/menu-graphic.tsx +16 -0
  68. package/src/components/ds-main-menu/components/menu-graphic/menu-graphic.types.ts +7 -0
  69. package/src/components/ds-main-menu/components/tile-link/index.ts +1 -0
  70. package/src/components/ds-main-menu/components/tile-link/tile-link.tsx +85 -0
  71. package/src/components/ds-main-menu/components/tile-link/tile-link.types.ts +8 -0
  72. package/src/components/ds-main-menu/components/utility-link/index.ts +1 -0
  73. package/src/components/ds-main-menu/components/utility-link/utility-link.tsx +37 -0
  74. package/src/components/ds-main-menu/components/utility-link/utility-link.types.ts +7 -0
  75. package/src/components/ds-main-menu/ds-main-menu.figma.ts +17 -0
  76. package/src/components/ds-main-menu/ds-main-menu.module.scss +349 -0
  77. package/src/components/ds-main-menu/ds-main-menu.tsx +102 -0
  78. package/src/components/ds-main-menu/ds-main-menu.types.ts +86 -0
  79. package/src/components/ds-main-menu/ds-main-menu.utils.ts +40 -0
  80. package/src/components/ds-main-menu/index.ts +9 -0
  81. package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +1 -1
  82. package/src/components/ds-popover/ds-popover.tsx +2 -1
  83. package/src/components/ds-popover/ds-popover.types.ts +7 -0
  84. package/src/components/ds-site-node/ds-site-node.tsx +2 -2
  85. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +12 -2
  86. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +2 -3
  87. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +1 -6
  88. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +6 -3
  89. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +18 -7
  90. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +27 -21
  91. package/src/components/ds-table/components/ds-table-header-label.module.scss +12 -0
  92. package/src/components/ds-table/components/ds-table-header-label.tsx +28 -0
  93. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +0 -1
  94. package/src/components/ds-table/context/ds-table-context.tsx +8 -0
  95. package/src/components/ds-table/ds-table.module.scss +26 -16
  96. package/src/components/ds-table/ds-table.tsx +46 -14
  97. package/src/components/ds-table/ds-table.types.ts +33 -3
  98. package/src/components/ds-table/hooks/use-column-resize.ts +114 -11
  99. package/src/components/ds-table/hooks/use-scrollbar-spacer.ts +108 -0
  100. package/src/components/ds-table/hooks/use-scrollbar-spacer.unit.test.ts +26 -0
  101. package/src/components/ds-table/styles/_variables.scss +0 -2
  102. package/src/components/ds-table/utils/column-size.ts +274 -1
  103. package/src/components/ds-table/utils/column-size.unit.test.ts +157 -0
  104. package/src/components/ds-table/utils/constants.ts +13 -5
  105. package/src/components/ds-table/utils/table-columns.tsx +27 -13
  106. package/src/components/ds-table/utils/table-columns.unit.test.ts +44 -0
  107. package/src/components/ds-toggle/ds-toggle.module.scss +18 -0
  108. package/src/components/ds-toggle/ds-toggle.tsx +15 -13
  109. package/src/components/ds-tooltip/ds-tooltip.tsx +6 -2
  110. package/src/components/ds-tooltip/ds-tooltip.types.ts +17 -0
  111. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.figma.ts +35 -0
  112. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.module.scss +150 -0
  113. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.tsx +161 -0
  114. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.types.ts +117 -0
  115. package/src/components/ds-top-bar-navigation/index.ts +2 -0
  116. package/src/index.ts +6 -0
  117. package/src/utils/use-is-horizontally-overflowing.ts +35 -0
  118. package/src/components/ds-site-node/hooks/use-is-overflowing.ts +0 -31
  119. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +0 -112
  120. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx +0 -67
  121. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.types.ts +0 -41
  122. package/src/components/ds-table/components/ds-table-bulk-actions/index.ts +0 -2
@@ -0,0 +1,349 @@
1
+ $z-index: 1000;
2
+ $tile-size: 92px;
3
+ $badge-size: 24px;
4
+ $utility-min-height: 28px;
5
+ $utility-icon-size: 20px;
6
+ $transition-duration: 0.2s;
7
+ $focus-outline-width: 2px;
8
+ $disabled-icon-opacity: 0.55;
9
+ $shadow: 0 6px 18px rgb(4 69 204 / 30%);
10
+ $expanded-card-width: 290px;
11
+ $icon-size: 42px;
12
+
13
+ @mixin interactive-surface {
14
+ background: var(--background-action-secondary-hover);
15
+ color: var(--font-action);
16
+ }
17
+
18
+ @mixin expanded-surface {
19
+ background: var(--background-action-secondary-hover);
20
+ }
21
+
22
+ .root {
23
+ --ds-main-menu-z-index: #{$z-index};
24
+
25
+ position: relative;
26
+ z-index: var(--ds-main-menu-z-index);
27
+ display: flex;
28
+ flex-direction: column;
29
+ width: max-content;
30
+ padding: var(--sm) var(--lg);
31
+ border-radius: var(--4xs);
32
+ background: var(--background);
33
+ box-shadow: $shadow;
34
+ }
35
+
36
+ .grid {
37
+ display: grid;
38
+ gap: var(--sm);
39
+ margin: 0;
40
+ padding: 0;
41
+ list-style: none;
42
+
43
+ &[data-variant='compact'] {
44
+ grid-template-columns: repeat(3, 1fr);
45
+ }
46
+
47
+ &[data-variant='expanded'] {
48
+ grid-template-columns: repeat(2, $expanded-card-width);
49
+ }
50
+ }
51
+
52
+ .tile {
53
+ position: relative;
54
+ }
55
+
56
+ .tileLink {
57
+ position: relative;
58
+ box-sizing: content-box;
59
+ display: flex;
60
+ flex-direction: column;
61
+ align-items: center;
62
+ gap: var(--xs);
63
+ width: $tile-size;
64
+ height: $tile-size;
65
+ padding: var(--standard);
66
+ border: none;
67
+ border-radius: var(--3xs);
68
+ background: var(--background);
69
+ color: var(--font-secondary);
70
+ text-align: center;
71
+ text-decoration: none;
72
+ cursor: pointer;
73
+ transition:
74
+ background-color $transition-duration ease,
75
+ color $transition-duration ease,
76
+ box-shadow $transition-duration ease;
77
+
78
+ &:hover:not(:disabled):not([aria-disabled='true']) {
79
+ @include interactive-surface;
80
+ }
81
+
82
+ &:active:not(:disabled):not([aria-disabled='true']) {
83
+ @include interactive-surface;
84
+ }
85
+
86
+ &:focus {
87
+ outline: none;
88
+ }
89
+
90
+ &:focus-visible {
91
+ @include interactive-surface;
92
+
93
+ outline: $focus-outline-width solid var(--border-action-primary);
94
+ outline-offset: -$focus-outline-width;
95
+ }
96
+ }
97
+
98
+ .tileLinkSelected {
99
+ @include interactive-surface;
100
+
101
+ box-shadow: inset 0 0 0 1px var(--border-action-primary);
102
+ }
103
+
104
+ .tileLinkDisabled {
105
+ background: var(--background);
106
+ box-shadow: none;
107
+ color: var(--font-disabled);
108
+ cursor: not-allowed;
109
+
110
+ .tileIcon {
111
+ opacity: $disabled-icon-opacity;
112
+ }
113
+ }
114
+
115
+ .tileLinkComingSoon {
116
+ cursor: not-allowed;
117
+ }
118
+
119
+ .tileIcon {
120
+ flex: 1;
121
+ min-height: 0;
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ width: $icon-size;
126
+ height: $icon-size;
127
+ }
128
+
129
+ .graphic {
130
+ display: block;
131
+ width: 100%;
132
+ height: 100%;
133
+ }
134
+
135
+ .tileLabel {
136
+ flex: 1;
137
+ min-height: 0;
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ width: 100%;
142
+ text-align: center;
143
+ padding: 0 var(--3xs);
144
+ }
145
+
146
+ .badge {
147
+ position: absolute;
148
+ z-index: 1;
149
+ top: var(--xs);
150
+ right: var(--xs);
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: center;
154
+ width: $badge-size;
155
+ height: $badge-size;
156
+ border: 1px solid var(--border);
157
+ border-radius: var(--3xs);
158
+ background: var(--background);
159
+ color: var(--font-main);
160
+ }
161
+
162
+ .expandedItem {
163
+ position: relative;
164
+ }
165
+
166
+ .expandedLink {
167
+ position: relative;
168
+ display: flex;
169
+ flex-direction: column;
170
+ gap: var(--standard);
171
+ width: 100%;
172
+ padding: var(--md) var(--xl);
173
+ border: 1px solid var(--border);
174
+ border-radius: var(--3xs);
175
+ background: var(--background);
176
+ color: var(--font-main);
177
+ text-align: left;
178
+ text-decoration: none;
179
+ cursor: pointer;
180
+ transition:
181
+ background-color $transition-duration ease,
182
+ box-shadow $transition-duration ease;
183
+
184
+ &:hover:not([aria-disabled='true']) {
185
+ @include expanded-surface;
186
+ }
187
+
188
+ &:active:not([aria-disabled='true']) {
189
+ @include expanded-surface;
190
+ }
191
+
192
+ &:focus {
193
+ outline: none;
194
+ }
195
+
196
+ &:focus-visible {
197
+ @include expanded-surface;
198
+
199
+ outline: $focus-outline-width solid var(--border-action-primary);
200
+ outline-offset: -$focus-outline-width;
201
+ }
202
+ }
203
+
204
+ .expandedLinkSelected {
205
+ @include expanded-surface;
206
+
207
+ box-shadow: inset 0 0 0 1px var(--border-action-primary);
208
+ }
209
+
210
+ .expandedLinkDisabled {
211
+ background: var(--background);
212
+ box-shadow: none;
213
+ color: var(--font-disabled);
214
+ cursor: not-allowed;
215
+
216
+ .expandedIcon {
217
+ opacity: $disabled-icon-opacity;
218
+ }
219
+ }
220
+
221
+ .expandedLinkComingSoon {
222
+ cursor: not-allowed;
223
+ }
224
+
225
+ .expandedIcon {
226
+ display: flex;
227
+ align-items: center;
228
+ justify-content: center;
229
+ width: $icon-size;
230
+ height: $icon-size;
231
+ color: inherit;
232
+ }
233
+
234
+ .expandedText {
235
+ display: flex;
236
+ flex-direction: column;
237
+ gap: var(--xs);
238
+ width: 100%;
239
+ word-break: break-word;
240
+ }
241
+
242
+ .expandedDescription {
243
+ color: var(--font-secondary);
244
+ }
245
+
246
+ .expandedAction {
247
+ position: absolute;
248
+ z-index: 1;
249
+ top: var(--md);
250
+ right: var(--xl);
251
+ display: flex;
252
+ align-items: center;
253
+ height: $icon-size;
254
+ }
255
+
256
+ .expandedActionBadge {
257
+ display: flex;
258
+ align-items: center;
259
+ justify-content: center;
260
+ width: $badge-size;
261
+ height: $badge-size;
262
+ border: 1px solid var(--border);
263
+ border-radius: var(--3xs);
264
+ background: var(--background);
265
+ color: var(--font-main);
266
+ }
267
+
268
+ .footer {
269
+ display: flex;
270
+ flex-direction: column;
271
+ gap: var(--xs);
272
+ margin-top: var(--xs);
273
+ }
274
+
275
+ .utilityList {
276
+ display: flex;
277
+ gap: var(--xs);
278
+ margin: 0;
279
+ padding: 0;
280
+ list-style: none;
281
+ }
282
+
283
+ .utilityItem {
284
+ flex: 1 1 0;
285
+ min-width: 0;
286
+ }
287
+
288
+ .utilityLink {
289
+ display: flex;
290
+ align-items: center;
291
+ gap: var(--xs);
292
+ width: 100%;
293
+ min-height: $utility-min-height;
294
+ padding: var(--3xs) var(--xs);
295
+ border: $focus-outline-width solid transparent;
296
+ border-radius: var(--3xs);
297
+ background: transparent;
298
+ color: var(--font-secondary);
299
+ text-align: left;
300
+ text-decoration: none;
301
+ cursor: pointer;
302
+ transition:
303
+ background-color $transition-duration ease,
304
+ color $transition-duration ease,
305
+ border-color $transition-duration ease;
306
+
307
+ &:hover {
308
+ @include interactive-surface;
309
+ }
310
+
311
+ &:active {
312
+ @include interactive-surface;
313
+
314
+ border-color: var(--border-action-primary);
315
+ }
316
+
317
+ &:focus {
318
+ outline: none;
319
+ }
320
+
321
+ &:focus-visible {
322
+ @include interactive-surface;
323
+
324
+ border-color: var(--border-action-primary);
325
+ }
326
+ }
327
+
328
+ .utilityIcon {
329
+ display: flex;
330
+ align-items: center;
331
+ justify-content: center;
332
+ width: $utility-icon-size;
333
+ height: $utility-icon-size;
334
+ flex-shrink: 0;
335
+ color: inherit;
336
+ }
337
+
338
+ .utilityLabel {
339
+ min-width: 0;
340
+ }
341
+
342
+ // Reset the DsPopover.Panel container so the nav's own visual styles (bg, shadow, radius) render,
343
+ // and size it to the menu content instead of DsPopover's fixed default width.
344
+ .panel {
345
+ background: transparent !important;
346
+ box-shadow: none !important;
347
+ width: max-content !important;
348
+ min-width: max-content !important;
349
+ }
@@ -0,0 +1,102 @@
1
+ import { useState } from 'react';
2
+ import classNames from 'classnames';
3
+ import { DsDivider } from '../ds-divider';
4
+ import { DsPopover } from '../ds-popover';
5
+ import { ExpandedItem } from './components/expanded-item';
6
+ import { TileLink } from './components/tile-link';
7
+ import { UtilityLink } from './components/utility-link';
8
+ import type { DsMainMenuProps } from './ds-main-menu.types';
9
+ import styles from './ds-main-menu.module.scss';
10
+
11
+ const DsMainMenu = ({
12
+ trigger,
13
+ items,
14
+ utilityLinks,
15
+ variant = 'compact',
16
+ selectedId,
17
+ onItemSelect,
18
+ className,
19
+ style,
20
+ ref,
21
+ 'aria-label': ariaLabel = 'Main menu',
22
+ side = 'bottom',
23
+ align = 'start',
24
+ gutter = 8,
25
+ getAnchorElement,
26
+ open: openProp,
27
+ onOpenChange,
28
+ }: DsMainMenuProps) => {
29
+ const isControlled = openProp !== undefined;
30
+ const [internalOpen, setInternalOpen] = useState(false);
31
+ const open = isControlled ? openProp : internalOpen;
32
+
33
+ const handleOpenChange = (nextOpen: boolean) => {
34
+ if (!isControlled) {
35
+ setInternalOpen(nextOpen);
36
+ }
37
+
38
+ onOpenChange?.(nextOpen);
39
+ };
40
+
41
+ const closePanel = () => handleOpenChange(false);
42
+
43
+ return (
44
+ <DsPopover.Root
45
+ open={open}
46
+ side={side}
47
+ align={align}
48
+ gutter={gutter}
49
+ getAnchorElement={getAnchorElement}
50
+ onOpenChange={handleOpenChange}
51
+ >
52
+ <DsPopover.Trigger>{trigger}</DsPopover.Trigger>
53
+ <DsPopover.Panel className={styles.panel}>
54
+ <nav
55
+ ref={ref}
56
+ className={classNames(styles.root, className)}
57
+ style={style}
58
+ aria-label={ariaLabel}
59
+ data-variant={variant}
60
+ >
61
+ <ul className={styles.grid} data-variant={variant}>
62
+ {items.map((item) => (
63
+ <li key={item.id} className={variant === 'expanded' ? styles.expandedItem : styles.tile}>
64
+ {variant === 'expanded' ? (
65
+ <ExpandedItem
66
+ item={item}
67
+ isSelected={item.id === selectedId}
68
+ onItemSelect={onItemSelect}
69
+ onClose={closePanel}
70
+ />
71
+ ) : (
72
+ <TileLink
73
+ item={item}
74
+ isSelected={item.id === selectedId}
75
+ onItemSelect={onItemSelect}
76
+ onClose={closePanel}
77
+ />
78
+ )}
79
+ </li>
80
+ ))}
81
+ </ul>
82
+ {utilityLinks.length > 0 && (
83
+ <div className={styles.footer}>
84
+ <DsDivider />
85
+ <ul className={styles.utilityList}>
86
+ {utilityLinks.map((link) => (
87
+ <li key={link.id} className={styles.utilityItem}>
88
+ <UtilityLink link={link} onItemSelect={onItemSelect} onClose={closePanel} />
89
+ </li>
90
+ ))}
91
+ </ul>
92
+ </div>
93
+ )}
94
+ </nav>
95
+ </DsPopover.Panel>
96
+ </DsPopover.Root>
97
+ );
98
+ };
99
+
100
+ DsMainMenu.displayName = 'DsMainMenu';
101
+
102
+ export default DsMainMenu;
@@ -0,0 +1,86 @@
1
+ import type { CSSProperties, ReactElement, ReactNode, Ref } from 'react';
2
+ import type { DsPopoverAlign, DsPopoverSide } from '../ds-popover';
3
+ import type { IconType } from '../ds-icon';
4
+
5
+ export const mainMenuItemStates = ['default', 'disabled', 'comingSoon', 'extend'] as const;
6
+ export type DsMainMenuItemState = (typeof mainMenuItemStates)[number];
7
+
8
+ export const mainMenuVariants = ['compact', 'expanded'] as const;
9
+ export type DsMainMenuVariant = (typeof mainMenuVariants)[number];
10
+
11
+ export interface DsMainMenuItem {
12
+ id: string;
13
+ label: string;
14
+ /**
15
+ * Tile graphic. Material/custom icon name (`'speed'`) or an SVG component for branded marks.
16
+ */
17
+ icon: IconType;
18
+ /**
19
+ * Supporting copy shown beneath the label in the `'expanded'` variant. Ignored in `'compact'`.
20
+ */
21
+ description?: string;
22
+ href?: string;
23
+ state?: DsMainMenuItemState;
24
+ /**
25
+ * Element rendered in the top-right badge slot. When omitted, a default badge is shown for
26
+ * `'comingSoon'` (wrench icon) and `'extend'` (fullscreen icon). Pass `null` to suppress it.
27
+ */
28
+ statusBadge?: ReactElement | null;
29
+ }
30
+
31
+ export interface DsMainMenuUtilityLink {
32
+ id: string;
33
+ label: string;
34
+ /** Leading graphic. Material/custom icon name or an SVG component. */
35
+ icon: IconType;
36
+ href?: string;
37
+ }
38
+
39
+ export interface DsMainMenuProps {
40
+ /** Single focusable element that toggles the menu panel. */
41
+ trigger: ReactNode;
42
+ items: DsMainMenuItem[];
43
+ utilityLinks: DsMainMenuUtilityLink[];
44
+ /**
45
+ * Layout of the menu items. `'compact'` renders square icon tiles in a grid;
46
+ * `'expanded'` renders wide cards with an icon, heading, and `description`.
47
+ * Chosen once per menu — the two layouts are never mixed.
48
+ * @default 'compact'
49
+ */
50
+ variant?: DsMainMenuVariant;
51
+ /** Currently selected app tile. Matched against `items[].id`. */
52
+ selectedId?: string;
53
+ onItemSelect?: (id: string) => void;
54
+ /** Accessible name for the navigation landmark. */
55
+ 'aria-label'?: string;
56
+ className?: string;
57
+ style?: CSSProperties;
58
+ ref?: Ref<HTMLElement>;
59
+ /**
60
+ * Preferred side of the trigger to render the panel against.
61
+ * @default 'bottom'
62
+ */
63
+ side?: DsPopoverSide;
64
+ /**
65
+ * Alignment of the panel along the trigger edge.
66
+ * @default 'start'
67
+ */
68
+ align?: DsPopoverAlign;
69
+ /**
70
+ * Gap in pixels between the trigger and the panel.
71
+ * @default 8
72
+ */
73
+ gutter?: number;
74
+ /**
75
+ * Returns the element the panel should position against instead of the trigger.
76
+ * Useful when the trigger lives in a sidebar and the panel should open beside the content area.
77
+ */
78
+ getAnchorElement?: () => HTMLElement | null;
79
+ /** Controlled open state. Pair with `onOpenChange`. */
80
+ open?: boolean;
81
+ /** Fires when the popover opens or closes. */
82
+ onOpenChange?: (open: boolean) => void;
83
+ }
84
+
85
+ export const COMING_SOON_TOOLTIP = 'Coming soon — this app is still under construction';
86
+ export const EXTEND_TOOLTIP = 'Click to open the app';
@@ -0,0 +1,40 @@
1
+ import type { KeyboardEvent, MouseEvent } from 'react';
2
+ import type { DsMainMenuItem, DsMainMenuProps } from './ds-main-menu.types';
3
+
4
+ export const isNonActivatable = (state: DsMainMenuItem['state']) =>
5
+ state === 'disabled' || state === 'comingSoon';
6
+
7
+ export const handleActivatableClick = (
8
+ event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>,
9
+ id: string,
10
+ href: string | undefined,
11
+ onItemSelect: DsMainMenuProps['onItemSelect'],
12
+ onClose: () => void,
13
+ ) => {
14
+ if (!href) {
15
+ event.preventDefault();
16
+ }
17
+
18
+ onItemSelect?.(id);
19
+ onClose();
20
+ };
21
+
22
+ export const handleActivatableKeyDown = (
23
+ event: KeyboardEvent<HTMLAnchorElement | HTMLButtonElement>,
24
+ id: string,
25
+ href: string | undefined,
26
+ onItemSelect: DsMainMenuProps['onItemSelect'],
27
+ onClose: () => void,
28
+ ) => {
29
+ if (event.key !== 'Enter' && event.key !== ' ') {
30
+ return;
31
+ }
32
+
33
+ event.preventDefault();
34
+ onItemSelect?.(id);
35
+ onClose();
36
+
37
+ if (href) {
38
+ (event.currentTarget as HTMLAnchorElement).click();
39
+ }
40
+ };
@@ -0,0 +1,9 @@
1
+ export { default as DsMainMenu } from './ds-main-menu';
2
+ export type {
3
+ DsMainMenuItem,
4
+ DsMainMenuItemState,
5
+ DsMainMenuProps,
6
+ DsMainMenuUtilityLink,
7
+ DsMainMenuVariant,
8
+ } from './ds-main-menu.types';
9
+ export { COMING_SOON_TOOLTIP, mainMenuItemStates, mainMenuVariants } from './ds-main-menu.types';
@@ -1,4 +1,4 @@
1
- // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=32014-16402
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=43101-8753
2
2
  // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-modal
3
3
  // component=DsModal.Footer
4
4
  //
@@ -29,6 +29,7 @@ const DsPopoverRoot = ({
29
29
  align = 'center',
30
30
  gutter = 8,
31
31
  modal = false,
32
+ getAnchorElement,
32
33
  children,
33
34
  onOpenChange,
34
35
  }: DsPopoverRootProps) => (
@@ -36,7 +37,7 @@ const DsPopoverRoot = ({
36
37
  open={open}
37
38
  defaultOpen={defaultOpen}
38
39
  modal={modal}
39
- positioning={{ placement: toPlacement(side, align), gutter }}
40
+ positioning={{ placement: toPlacement(side, align), gutter, getAnchorElement }}
40
41
  onOpenChange={(details) => onOpenChange?.(details.open)}
41
42
  >
42
43
  {children}
@@ -31,6 +31,13 @@ export interface DsPopoverRootProps {
31
31
  * @default false
32
32
  */
33
33
  modal?: boolean;
34
+ /**
35
+ * Returns the element the panel should position against.
36
+ * When provided, the panel anchors to this element instead of the trigger —
37
+ * useful when the trigger lives in one place (e.g. a sidebar) and the panel
38
+ * should appear relative to a different reference point.
39
+ */
40
+ getAnchorElement?: () => HTMLElement | null;
34
41
  children: ReactNode;
35
42
  /** Fires when the popover opens or closes. */
36
43
  onOpenChange?: (open: boolean) => void;
@@ -1,6 +1,6 @@
1
1
  import classNames from 'classnames';
2
2
  import type { DsSiteNodeProps } from './ds-site-node.types';
3
- import { useIsOverflowing } from './hooks/use-is-overflowing';
3
+ import { useIsHorizontallyOverflowing } from '../../utils/use-is-horizontally-overflowing';
4
4
  import { DsStack } from '../ds-stack';
5
5
  import { DsTooltip } from '../ds-tooltip';
6
6
  import { DsTypography } from '../ds-typography';
@@ -21,7 +21,7 @@ const DsSiteNode = ({
21
21
  const isSelected = state === 'selected';
22
22
  const isMuted = state === 'muted';
23
23
 
24
- const { ref: siteCodeRef, isOverflowing } = useIsOverflowing<HTMLSpanElement>(siteCode);
24
+ const { ref: siteCodeRef, isOverflowing } = useIsHorizontallyOverflowing<HTMLSpanElement>(siteCode);
25
25
 
26
26
  return (
27
27
  <button
@@ -4,9 +4,10 @@
4
4
  display: block;
5
5
  position: relative;
6
6
  overflow-y: auto;
7
+ overflow-x: hidden;
7
8
  flex: 1;
8
9
  min-height: 0;
9
- scrollbar-gutter: stable;
10
+ scrollbar-gutter: auto;
10
11
 
11
12
  @include scrollbars.scrollbar-on-hover;
12
13
  }
@@ -22,7 +23,16 @@
22
23
  visibility: hidden;
23
24
  }
24
25
 
25
- .emptyState {
26
+ .emptyStateRow {
26
27
  position: absolute;
27
28
  inset: 0;
28
29
  }
30
+
31
+ .emptyState {
32
+ display: flex;
33
+ align-items: center;
34
+ justify-content: center;
35
+ width: 100%;
36
+ height: 100%;
37
+ text-align: center;
38
+ }
@@ -5,7 +5,6 @@ import styles from './ds-table-body-virtualized.module.scss';
5
5
  import { DsTableRowVirtualized } from '../ds-table-row-virtualized';
6
6
  import type { DsTableBodyVirtualizedProps } from './ds-table-body-virtualized.types';
7
7
  import { TableBody, TableRow, TableCell } from '../core-table';
8
- import { EMPTY_TABLE_STATE_TEXT } from '../../utils/constants';
9
8
  import { useInfiniteScroll } from './use-infinite-scroll';
10
9
 
11
10
  export const DsTableBodyVirtualized = <TData,>({
@@ -150,8 +149,8 @@ export function DsTableBody<TData>({
150
149
  );
151
150
  })
152
151
  ) : hasNoData ? (
153
- <TableRow>
154
- <TableCell className={styles.emptyState}>{emptyState || EMPTY_TABLE_STATE_TEXT}</TableCell>
152
+ <TableRow className={styles.emptyStateRow}>
153
+ <TableCell className={styles.emptyState}>{emptyState}</TableCell>
155
154
  </TableRow>
156
155
  ) : null}
157
156
 
@@ -20,16 +20,11 @@
20
20
  justify-content: space-between;
21
21
  gap: vars.$header-sort-gap;
22
22
  width: 100%;
23
+ min-width: 0;
23
24
  height: 100%;
24
25
  padding: 0 var(--standard);
25
26
  }
26
27
 
27
- .groupLabel {
28
- overflow: hidden;
29
- text-overflow: ellipsis;
30
- white-space: nowrap;
31
- }
32
-
33
28
  .collapseToggle {
34
29
  flex-shrink: 0;
35
30
  }