@drivenets/design-system 0.18.1 → 0.20.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 (216) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/index.cjs +17038 -10114
  3. package/dist/index.d.cts +1355 -444
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +1354 -443
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +17073 -10172
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +41 -41
  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-checkbox/ds-checkbox.figma.ts +3 -0
  33. package/src/components/ds-checkbox/ds-checkbox.module.scss +102 -20
  34. package/src/components/ds-checkbox/ds-checkbox.tsx +30 -2
  35. package/src/components/ds-checkbox/ds-checkbox.types.ts +10 -0
  36. package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.module.scss +169 -0
  37. package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.tsx +92 -0
  38. package/src/components/ds-code-input/collapsed-code-field/index.ts +1 -0
  39. package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.module.scss +110 -0
  40. package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.tsx +61 -0
  41. package/src/components/ds-code-input/ds-code-input-panel/index.ts +1 -0
  42. package/src/components/ds-code-input/ds-code-input.module.scss +25 -0
  43. package/src/components/ds-code-input/ds-code-input.tsx +401 -0
  44. package/src/components/ds-code-input/ds-code-input.types.ts +166 -0
  45. package/src/components/ds-code-input/ds-code-input.utils.ts +94 -0
  46. package/src/components/ds-code-input/ds-code-input.utils.unit.test.ts +166 -0
  47. package/src/components/ds-code-input/index.ts +2 -0
  48. package/src/components/ds-code-input/use-collapsed-viewport.ts +289 -0
  49. package/src/components/ds-code-input/use-editor-host.ts +87 -0
  50. package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +2 -0
  51. package/src/components/ds-empty-state/ds-empty-state.module.scss +16 -0
  52. package/src/components/ds-empty-state/ds-empty-state.tsx +45 -0
  53. package/src/components/ds-empty-state/ds-empty-state.types.ts +38 -0
  54. package/src/components/ds-empty-state/index.ts +3 -0
  55. package/src/components/ds-file-upload/ds-file-upload.tsx +4 -1
  56. package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.ts +11 -0
  57. package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.types.ts +41 -0
  58. package/src/components/ds-filters-bar/components/ds-filters-bar-builder/index.ts +1 -0
  59. package/src/components/ds-filters-bar/components/ds-filters-bar-clear-all.ts +11 -0
  60. package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.ts +11 -0
  61. package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.types.ts +30 -0
  62. package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/index.ts +1 -0
  63. package/src/components/ds-filters-bar/components/ds-filters-bar-pinned.ts +31 -0
  64. package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.ts +11 -0
  65. package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.types.ts +22 -0
  66. package/src/components/ds-filters-bar/components/ds-filters-bar-query/index.ts +1 -0
  67. package/src/components/ds-filters-bar/components/ds-filters-bar-saved-filters.ts +19 -0
  68. package/src/components/ds-filters-bar/components/ds-filters-bar-search.ts +11 -0
  69. package/src/components/ds-filters-bar/components/ds-filters-bar-summary.ts +11 -0
  70. package/src/components/ds-filters-bar/components/ds-filters-bar-toolbar.ts +11 -0
  71. package/src/components/ds-filters-bar/components/ds-filters-bar-view-switch.ts +11 -0
  72. package/src/components/ds-filters-bar/components/ds-filters-bar-view.ts +11 -0
  73. package/src/components/ds-filters-bar/components/index.ts +11 -0
  74. package/src/components/ds-filters-bar/ds-filters-bar.context.ts +69 -0
  75. package/src/components/ds-filters-bar/ds-filters-bar.tsx +120 -0
  76. package/src/components/ds-filters-bar/ds-filters-bar.types.ts +440 -0
  77. package/src/components/ds-filters-bar/ds-filters-bar.unit.test.ts +216 -0
  78. package/src/components/ds-filters-bar/ds-filters-bar.utils.ts +141 -0
  79. package/src/components/ds-filters-bar/index.ts +1 -0
  80. package/src/components/ds-form-control/ds-form-control.module.scss +18 -0
  81. package/src/components/ds-form-control/ds-form-control.tsx +61 -19
  82. package/src/components/ds-form-control/ds-form-control.types.ts +2 -1
  83. package/src/components/ds-icon/custom-icons/index.ts +2 -0
  84. package/src/components/ds-icon/custom-icons/special-adv-filters.tsx +28 -0
  85. package/src/components/ds-illustration/ds-illustration.figma.ts +34 -0
  86. package/src/components/ds-illustration/ds-illustration.module.scss +6 -0
  87. package/src/components/ds-illustration/ds-illustration.tsx +29 -0
  88. package/src/components/ds-illustration/ds-illustration.types.ts +45 -0
  89. package/src/components/ds-illustration/illustrations/configuration.tsx +51 -0
  90. package/src/components/ds-illustration/illustrations/device.tsx +133 -0
  91. package/src/components/ds-illustration/illustrations/done.tsx +107 -0
  92. package/src/components/{ds-catalog-layout/catalog-layout-empty-illustration.tsx → ds-illustration/illustrations/empty-inbox.tsx} +10 -14
  93. package/src/components/ds-illustration/illustrations/error.tsx +86 -0
  94. package/src/components/ds-illustration/illustrations/go-filter.tsx +51 -0
  95. package/src/components/ds-illustration/illustrations/illustration-clouds.tsx +10 -0
  96. package/src/components/ds-illustration/illustrations/index.ts +39 -0
  97. package/src/components/ds-illustration/illustrations/no-connection.tsx +54 -0
  98. package/src/components/ds-illustration/illustrations/no-credit-card.tsx +48 -0
  99. package/src/components/ds-illustration/illustrations/no-device.tsx +61 -0
  100. package/src/components/ds-illustration/illustrations/no-documents.tsx +52 -0
  101. package/src/components/ds-illustration/illustrations/no-gps.tsx +57 -0
  102. package/src/components/ds-illustration/illustrations/no-images.tsx +82 -0
  103. package/src/components/ds-illustration/illustrations/no-items-cart.tsx +86 -0
  104. package/src/components/ds-illustration/illustrations/no-messages.tsx +51 -0
  105. package/src/components/ds-illustration/illustrations/no-search-result.tsx +76 -0
  106. package/src/components/ds-illustration/illustrations/no-tasks.tsx +61 -0
  107. package/src/components/ds-illustration/illustrations/site.tsx +80 -0
  108. package/src/components/ds-illustration/index.ts +3 -0
  109. package/src/components/ds-main-menu/components/expanded-item/expanded-item.tsx +108 -0
  110. package/src/components/ds-main-menu/components/expanded-item/expanded-item.types.ts +8 -0
  111. package/src/components/ds-main-menu/components/expanded-item/index.ts +1 -0
  112. package/src/components/ds-main-menu/components/menu-graphic/index.ts +1 -0
  113. package/src/components/ds-main-menu/components/menu-graphic/menu-graphic.tsx +16 -0
  114. package/src/components/ds-main-menu/components/menu-graphic/menu-graphic.types.ts +7 -0
  115. package/src/components/ds-main-menu/components/tile-link/index.ts +1 -0
  116. package/src/components/ds-main-menu/components/tile-link/tile-link.tsx +85 -0
  117. package/src/components/ds-main-menu/components/tile-link/tile-link.types.ts +8 -0
  118. package/src/components/ds-main-menu/components/utility-link/index.ts +1 -0
  119. package/src/components/ds-main-menu/components/utility-link/utility-link.tsx +37 -0
  120. package/src/components/ds-main-menu/components/utility-link/utility-link.types.ts +7 -0
  121. package/src/components/ds-main-menu/ds-main-menu.figma.ts +17 -0
  122. package/src/components/ds-main-menu/ds-main-menu.module.scss +349 -0
  123. package/src/components/ds-main-menu/ds-main-menu.tsx +102 -0
  124. package/src/components/ds-main-menu/ds-main-menu.types.ts +86 -0
  125. package/src/components/ds-main-menu/ds-main-menu.utils.ts +40 -0
  126. package/src/components/ds-main-menu/index.ts +9 -0
  127. package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +1 -1
  128. package/src/components/ds-pin-toggle/ds-pin-toggle.figma.ts +27 -0
  129. package/src/components/ds-pin-toggle/ds-pin-toggle.module.scss +41 -0
  130. package/src/components/ds-pin-toggle/ds-pin-toggle.tsx +43 -0
  131. package/src/components/ds-pin-toggle/ds-pin-toggle.types.ts +29 -0
  132. package/src/components/ds-pin-toggle/index.ts +2 -0
  133. package/src/components/ds-popover/components/popover-trigger/index.ts +1 -0
  134. package/src/components/ds-popover/components/popover-trigger/popover-trigger.tsx +15 -0
  135. package/src/components/ds-popover/ds-popover.context.ts +18 -0
  136. package/src/components/ds-popover/ds-popover.hover-intent.ts +65 -0
  137. package/src/components/ds-popover/ds-popover.tsx +164 -41
  138. package/src/components/ds-popover/ds-popover.types.ts +65 -0
  139. package/src/components/ds-popover/ds-popover.utils.ts +60 -0
  140. package/src/components/ds-popover/ds-popover.utils.unit.test.ts +25 -0
  141. package/src/components/ds-saved-filters/components/ds-saved-filters-delete-confirm.tsx +53 -0
  142. package/src/components/ds-saved-filters/components/ds-saved-filters-name-entry.tsx +64 -0
  143. package/src/components/ds-saved-filters/components/ds-saved-filters-name-modal.tsx +50 -0
  144. package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.module.scss +68 -0
  145. package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.tsx +83 -0
  146. package/src/components/ds-saved-filters/components/ds-saved-filters-row/index.ts +1 -0
  147. package/src/components/ds-saved-filters/components/ds-saved-filters-save.tsx +164 -0
  148. package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.module.scss +21 -0
  149. package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.tsx +237 -0
  150. package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/index.ts +1 -0
  151. package/src/components/ds-saved-filters/components/index.ts +2 -0
  152. package/src/components/ds-saved-filters/ds-saved-filters.ts +6 -0
  153. package/src/components/ds-saved-filters/ds-saved-filters.types.ts +106 -0
  154. package/src/components/ds-saved-filters/ds-saved-filters.utils.ts +35 -0
  155. package/src/components/ds-saved-filters/hooks/index.ts +1 -0
  156. package/src/components/ds-saved-filters/hooks/use-picker-outside-dismiss.ts +42 -0
  157. package/src/components/ds-saved-filters/index.ts +2 -0
  158. package/src/components/ds-segment-group/ds-segment-group.module.scss +65 -37
  159. package/src/components/ds-segment-group/ds-segment-group.tsx +17 -5
  160. package/src/components/ds-segment-group/ds-segment-group.types.ts +20 -5
  161. package/src/components/ds-segment-group/figma/ds-segment-group-v2-item.figma.ts +109 -0
  162. package/src/components/ds-segment-group/figma/ds-segment-group-v2.figma.ts +31 -0
  163. package/src/components/ds-site-node/ds-site-node.tsx +2 -2
  164. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +12 -2
  165. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +2 -3
  166. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +1 -6
  167. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +6 -3
  168. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +18 -7
  169. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +27 -21
  170. package/src/components/ds-table/components/ds-table-header-label.module.scss +12 -0
  171. package/src/components/ds-table/components/ds-table-header-label.tsx +28 -0
  172. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +0 -1
  173. package/src/components/ds-table/context/ds-table-context.tsx +8 -0
  174. package/src/components/ds-table/ds-table.module.scss +26 -16
  175. package/src/components/ds-table/ds-table.tsx +46 -14
  176. package/src/components/ds-table/ds-table.types.ts +33 -3
  177. package/src/components/ds-table/hooks/use-column-resize.ts +114 -11
  178. package/src/components/ds-table/hooks/use-scrollbar-spacer.ts +108 -0
  179. package/src/components/ds-table/hooks/use-scrollbar-spacer.unit.test.ts +26 -0
  180. package/src/components/ds-table/styles/_variables.scss +0 -2
  181. package/src/components/ds-table/utils/column-size.ts +274 -1
  182. package/src/components/ds-table/utils/column-size.unit.test.ts +157 -0
  183. package/src/components/ds-table/utils/constants.ts +13 -5
  184. package/src/components/ds-table/utils/table-columns.tsx +27 -13
  185. package/src/components/ds-table/utils/table-columns.unit.test.ts +44 -0
  186. package/src/components/ds-tag/ds-tag.figma.ts +23 -3
  187. package/src/components/ds-tag/ds-tag.module.scss +71 -7
  188. package/src/components/ds-tag/ds-tag.tsx +115 -14
  189. package/src/components/ds-tag/ds-tag.types.ts +70 -3
  190. package/src/components/ds-tag/ds-tag.unit.test.ts +23 -4
  191. package/src/components/ds-tag/index.ts +2 -2
  192. package/src/components/ds-text-input/ds-input-field-v02.figma.ts +2 -0
  193. package/src/components/ds-toggle/ds-toggle.module.scss +18 -0
  194. package/src/components/ds-toggle/ds-toggle.tsx +15 -9
  195. package/src/components/ds-toggle/ds-toggle.types.ts +6 -0
  196. package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.module.scss +69 -0
  197. package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.tsx +55 -0
  198. package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.types.ts +43 -0
  199. package/src/components/ds-toggle-filter-data/index.ts +1 -0
  200. package/src/components/ds-tooltip/ds-tooltip.module.scss +8 -0
  201. package/src/components/ds-tooltip/ds-tooltip.tsx +7 -3
  202. package/src/components/ds-tooltip/ds-tooltip.types.ts +17 -0
  203. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.figma.ts +35 -0
  204. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.module.scss +150 -0
  205. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.tsx +161 -0
  206. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.types.ts +117 -0
  207. package/src/components/ds-top-bar-navigation/index.ts +2 -0
  208. package/src/components/ds-tree/ds-tree.tsx +10 -10
  209. package/src/components/ds-tree/ds-tree.types.ts +40 -7
  210. package/src/index.ts +8 -0
  211. package/src/utils/use-is-horizontally-overflowing.ts +35 -0
  212. package/src/components/ds-site-node/hooks/use-is-overflowing.ts +0 -31
  213. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +0 -112
  214. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx +0 -67
  215. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.types.ts +0 -41
  216. package/src/components/ds-table/components/ds-table-bulk-actions/index.ts +0 -2
@@ -0,0 +1,85 @@
1
+ import type { KeyboardEvent, MouseEvent } from 'react';
2
+ import classNames from 'classnames';
3
+ import { DsIcon } from '../../../ds-icon';
4
+ import { DsTooltip } from '../../../ds-tooltip';
5
+ import { DsTypography } from '../../../ds-typography';
6
+ import { COMING_SOON_TOOLTIP, EXTEND_TOOLTIP } from '../../ds-main-menu.types';
7
+ import styles from '../../ds-main-menu.module.scss';
8
+ import { handleActivatableClick, handleActivatableKeyDown, isNonActivatable } from '../../ds-main-menu.utils';
9
+ import { MenuGraphic } from '../menu-graphic';
10
+ import type { TileLinkProps } from './tile-link.types';
11
+
12
+ export const TileLink = ({ item, isSelected, onItemSelect, onClose }: TileLinkProps) => {
13
+ const state = item.state ?? 'default';
14
+ const nonActivatable = isNonActivatable(state);
15
+ const tileClassName = classNames(styles.tileLink, {
16
+ [styles.tileLinkSelected]: isSelected,
17
+ [styles.tileLinkDisabled]: state === 'disabled',
18
+ [styles.tileLinkComingSoon]: state === 'comingSoon',
19
+ });
20
+
21
+ const isDisabled = state === 'disabled';
22
+
23
+ const commonProps = {
24
+ className: tileClassName,
25
+ 'aria-current': isSelected ? ('page' as const) : undefined,
26
+ 'aria-disabled': nonActivatable ? true : undefined,
27
+ tabIndex: isDisabled ? -1 : undefined,
28
+ onClick: nonActivatable
29
+ ? (event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>) => event.preventDefault()
30
+ : (event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>) =>
31
+ handleActivatableClick(event, item.id, item.href, onItemSelect, onClose),
32
+ onKeyDown: nonActivatable
33
+ ? undefined
34
+ : (event: KeyboardEvent<HTMLAnchorElement | HTMLButtonElement>) =>
35
+ handleActivatableKeyDown(event, item.id, item.href, onItemSelect, onClose),
36
+ };
37
+
38
+ const badgeContent =
39
+ item.statusBadge !== undefined ? (
40
+ item.statusBadge
41
+ ) : state === 'comingSoon' ? (
42
+ <DsIcon icon="handyman" size="tiny" color="main" />
43
+ ) : state === 'extend' ? (
44
+ <DsIcon icon="open_in_full" size="tiny" color="main" />
45
+ ) : null;
46
+
47
+ const badgeTooltip =
48
+ state === 'comingSoon' ? COMING_SOON_TOOLTIP : state === 'extend' ? EXTEND_TOOLTIP : undefined;
49
+
50
+ const badge = badgeContent !== null && <span className={styles.badge}>{badgeContent}</span>;
51
+
52
+ const content = (
53
+ <>
54
+ <span className={styles.tileIcon}>
55
+ <MenuGraphic icon={item.icon} svgClassName={styles.graphic} size="extra-large" />
56
+ </span>
57
+ <DsTypography
58
+ variant={isSelected ? 'body-sm-md' : 'body-sm-reg'}
59
+ className={styles.tileLabel}
60
+ truncate={2}
61
+ tooltip
62
+ >
63
+ {item.label}
64
+ </DsTypography>
65
+ {badge &&
66
+ (badgeTooltip ? (
67
+ <DsTooltip content={badgeTooltip} placement="top">
68
+ {badge}
69
+ </DsTooltip>
70
+ ) : (
71
+ badge
72
+ ))}
73
+ </>
74
+ );
75
+
76
+ return item.href ? (
77
+ <a href={item.href} {...commonProps}>
78
+ {content}
79
+ </a>
80
+ ) : (
81
+ <button type="button" {...commonProps}>
82
+ {content}
83
+ </button>
84
+ );
85
+ };
@@ -0,0 +1,8 @@
1
+ import type { DsMainMenuItem, DsMainMenuProps } from '../../ds-main-menu.types';
2
+
3
+ export interface TileLinkProps {
4
+ item: DsMainMenuItem;
5
+ isSelected: boolean;
6
+ onItemSelect?: DsMainMenuProps['onItemSelect'];
7
+ onClose: () => void;
8
+ }
@@ -0,0 +1 @@
1
+ export * from './utility-link';
@@ -0,0 +1,37 @@
1
+ import type { KeyboardEvent, MouseEvent } from 'react';
2
+ import { DsTypography } from '../../../ds-typography';
3
+ import styles from '../../ds-main-menu.module.scss';
4
+ import { handleActivatableClick, handleActivatableKeyDown } from '../../ds-main-menu.utils';
5
+ import { MenuGraphic } from '../menu-graphic';
6
+ import type { UtilityLinkProps } from './utility-link.types';
7
+
8
+ export const UtilityLink = ({ link, onItemSelect, onClose }: UtilityLinkProps) => {
9
+ const commonProps = {
10
+ className: styles.utilityLink,
11
+ onClick: (event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>) =>
12
+ handleActivatableClick(event, link.id, link.href, onItemSelect, onClose),
13
+ onKeyDown: (event: KeyboardEvent<HTMLAnchorElement | HTMLButtonElement>) =>
14
+ handleActivatableKeyDown(event, link.id, link.href, onItemSelect, onClose),
15
+ };
16
+
17
+ const content = (
18
+ <>
19
+ <span className={styles.utilityIcon}>
20
+ <MenuGraphic icon={link.icon} svgClassName={styles.graphic} size="small" />
21
+ </span>
22
+ <DsTypography variant="body-xs-reg" className={styles.utilityLabel} truncate tooltip>
23
+ {link.label}
24
+ </DsTypography>
25
+ </>
26
+ );
27
+
28
+ return link.href ? (
29
+ <a href={link.href} {...commonProps}>
30
+ {content}
31
+ </a>
32
+ ) : (
33
+ <button type="button" {...commonProps}>
34
+ {content}
35
+ </button>
36
+ );
37
+ };
@@ -0,0 +1,7 @@
1
+ import type { DsMainMenuProps, DsMainMenuUtilityLink } from '../../ds-main-menu.types';
2
+
3
+ export interface UtilityLinkProps {
4
+ link: DsMainMenuUtilityLink;
5
+ onItemSelect?: DsMainMenuProps['onItemSelect'];
6
+ onClose: () => void;
7
+ }
@@ -0,0 +1,17 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=20895-43484
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-main-menu
3
+ // component=DsMainMenu
4
+ import figma from 'figma';
5
+
6
+ export default {
7
+ example: figma.code`<DsMainMenu
8
+ trigger={/* header trigger */}
9
+ items={items}
10
+ utilityLinks={utilityLinks}
11
+ selectedId={selectedId}
12
+ onItemSelect={onItemSelect}
13
+ />`,
14
+ imports: ["import { DsMainMenu } from '@drivenets/design-system';"],
15
+ id: 'ds-main-menu',
16
+ metadata: { nestable: false },
17
+ } satisfies figma.Template;
@@ -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';