@drivenets/design-system 0.0.1

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 (242) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +201 -0
  3. package/dist/index.cjs +5394 -0
  4. package/dist/index.css +1 -0
  5. package/dist/index.d.cts +9157 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.ts +9157 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +5335 -0
  10. package/dist/index.js.map +1 -0
  11. package/package.json +104 -0
  12. package/src/components/ds-alert-banner/ds-alert-banner.module.scss +116 -0
  13. package/src/components/ds-alert-banner/ds-alert-banner.tsx +115 -0
  14. package/src/components/ds-alert-banner/ds-alert-banner.types.tsx +47 -0
  15. package/src/components/ds-alert-banner/index.tsx +2 -0
  16. package/src/components/ds-breadcrumb/ds-breadcrumb.module.scss +99 -0
  17. package/src/components/ds-breadcrumb/ds-breadcrumb.tsx +94 -0
  18. package/src/components/ds-breadcrumb/ds-breadcrumb.types.tsx +32 -0
  19. package/src/components/ds-breadcrumb/index.tsx +2 -0
  20. package/src/components/ds-button/ds-button.tsx +12 -0
  21. package/src/components/ds-button/ds-button.types.ts +6 -0
  22. package/src/components/ds-button/index.ts +2 -0
  23. package/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.module.scss +257 -0
  24. package/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx +34 -0
  25. package/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.types.ts +41 -0
  26. package/src/components/ds-button/versions/ds-button-legacy/index.ts +2 -0
  27. package/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss +580 -0
  28. package/src/components/ds-button/versions/ds-button-new/ds-button-new.tsx +49 -0
  29. package/src/components/ds-button/versions/ds-button-new/ds-button-new.types.ts +41 -0
  30. package/src/components/ds-button/versions/ds-button-new/index.ts +2 -0
  31. package/src/components/ds-checkbox/ds-checkbox.module.scss +130 -0
  32. package/src/components/ds-checkbox/ds-checkbox.tsx +55 -0
  33. package/src/components/ds-checkbox/ds-checkbox.types.tsx +27 -0
  34. package/src/components/ds-checkbox/index.tsx +2 -0
  35. package/src/components/ds-chip/ds-chip.module.scss +71 -0
  36. package/src/components/ds-chip/ds-chip.tsx +73 -0
  37. package/src/components/ds-chip/ds-chip.types.tsx +46 -0
  38. package/src/components/ds-chip/index.tsx +2 -0
  39. package/src/components/ds-chip-group/ds-chip-group.module.scss +59 -0
  40. package/src/components/ds-chip-group/ds-chip-group.tsx +122 -0
  41. package/src/components/ds-chip-group/ds-chip-group.types.ts +52 -0
  42. package/src/components/ds-chip-group/hooks/use-chip-row-calculation.ts +76 -0
  43. package/src/components/ds-chip-group/index.ts +2 -0
  44. package/src/components/ds-confirmation/ds-confirmation.module.scss +58 -0
  45. package/src/components/ds-confirmation/ds-confirmation.tsx +121 -0
  46. package/src/components/ds-confirmation/ds-confirmation.types.tsx +28 -0
  47. package/src/components/ds-confirmation/index.ts +2 -0
  48. package/src/components/ds-dialog/ds-dialog.module.scss +50 -0
  49. package/src/components/ds-dialog/ds-dialog.tsx +70 -0
  50. package/src/components/ds-dialog/ds-dialog.types.tsx +48 -0
  51. package/src/components/ds-dialog/index.tsx +2 -0
  52. package/src/components/ds-drawer/ds-drawer.module.scss +212 -0
  53. package/src/components/ds-drawer/ds-drawer.tsx +160 -0
  54. package/src/components/ds-drawer/ds-drawer.types.tsx +54 -0
  55. package/src/components/ds-drawer/index.ts +2 -0
  56. package/src/components/ds-dropdown-menu/ds-dropdown-menu.module.scss +123 -0
  57. package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +396 -0
  58. package/src/components/ds-dropdown-menu/ds-dropdown-menu.types.tsx +262 -0
  59. package/src/components/ds-dropdown-menu/index.tsx +2 -0
  60. package/src/components/ds-expandable-text-input/ds-expandable-text-input.module.scss +70 -0
  61. package/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx +106 -0
  62. package/src/components/ds-expandable-text-input/ds-expandable-text-input.types.tsx +19 -0
  63. package/src/components/ds-expandable-text-input/index.ts +2 -0
  64. package/src/components/ds-file-upload/components/file-upload/file-upload.module.scss +90 -0
  65. package/src/components/ds-file-upload/components/file-upload/file-upload.tsx +106 -0
  66. package/src/components/ds-file-upload/components/file-upload/file-upload.types.tsx +94 -0
  67. package/src/components/ds-file-upload/components/file-upload/index.ts +2 -0
  68. package/src/components/ds-file-upload/components/file-upload-item/file-upload-item.module.scss +125 -0
  69. package/src/components/ds-file-upload/components/file-upload-item/file-upload-item.tsx +143 -0
  70. package/src/components/ds-file-upload/components/file-upload-item/file-upload-item.types.tsx +26 -0
  71. package/src/components/ds-file-upload/components/file-upload-item/index.ts +2 -0
  72. package/src/components/ds-file-upload/ds-file-upload-api.types.ts +38 -0
  73. package/src/components/ds-file-upload/ds-file-upload.tsx +46 -0
  74. package/src/components/ds-file-upload/ds-file-upload.types.tsx +54 -0
  75. package/src/components/ds-file-upload/errors/file-upload-errors.ts +27 -0
  76. package/src/components/ds-file-upload/hooks/index.ts +2 -0
  77. package/src/components/ds-file-upload/hooks/use-file-upload.ts +350 -0
  78. package/src/components/ds-file-upload/index.ts +7 -0
  79. package/src/components/ds-file-upload/types/accept-types.ts +106 -0
  80. package/src/components/ds-file-upload/utils/accept-utils.ts +25 -0
  81. package/src/components/ds-file-upload/utils/constants.ts +21 -0
  82. package/src/components/ds-file-upload/utils/file-factory.ts +18 -0
  83. package/src/components/ds-file-upload/utils/format-file-size.ts +15 -0
  84. package/src/components/ds-file-upload/utils/generate-helper-text.ts +39 -0
  85. package/src/components/ds-file-upload/utils/get-error-message.ts +17 -0
  86. package/src/components/ds-file-upload/utils/is-file-equal.ts +8 -0
  87. package/src/components/ds-form-control/ds-form-control.module.scss +135 -0
  88. package/src/components/ds-form-control/ds-form-control.tsx +94 -0
  89. package/src/components/ds-form-control/ds-form-control.types.tsx +58 -0
  90. package/src/components/ds-form-control/index.tsx +2 -0
  91. package/src/components/ds-grid/ds-grid.module.scss +0 -0
  92. package/src/components/ds-grid/ds-grid.tsx +43 -0
  93. package/src/components/ds-grid/ds-grid.types.ts +55 -0
  94. package/src/components/ds-grid/index.ts +2 -0
  95. package/src/components/ds-icon/ds-icon.module.scss +25 -0
  96. package/src/components/ds-icon/ds-icon.tsx +33 -0
  97. package/src/components/ds-icon/ds-icon.types.ts +53 -0
  98. package/src/components/ds-icon/index.ts +2 -0
  99. package/src/components/ds-icon/material-icons.ts +6203 -0
  100. package/src/components/ds-modal/ds-modal.module.scss +100 -0
  101. package/src/components/ds-modal/ds-modal.tsx +136 -0
  102. package/src/components/ds-modal/ds-modal.types.tsx +44 -0
  103. package/src/components/ds-modal/index.tsx +2 -0
  104. package/src/components/ds-number-input/ds-number-input.module.scss +15 -0
  105. package/src/components/ds-number-input/ds-number-input.tsx +74 -0
  106. package/src/components/ds-number-input/ds-number-input.types.tsx +76 -0
  107. package/src/components/ds-number-input/index.tsx +2 -0
  108. package/src/components/ds-panel/ds-panel.module.scss +95 -0
  109. package/src/components/ds-panel/ds-panel.tsx +46 -0
  110. package/src/components/ds-panel/ds-panel.types.ts +17 -0
  111. package/src/components/ds-panel/index.tsx +2 -0
  112. package/src/components/ds-password-input/ds-password-input.module.scss +10 -0
  113. package/src/components/ds-password-input/ds-password-input.tsx +67 -0
  114. package/src/components/ds-password-input/ds-password-input.types.tsx +62 -0
  115. package/src/components/ds-password-input/index.tsx +2 -0
  116. package/src/components/ds-popover/ds-popover.module.scss +23 -0
  117. package/src/components/ds-popover/ds-popover.tsx +33 -0
  118. package/src/components/ds-popover/ds-popover.types.tsx +24 -0
  119. package/src/components/ds-popover/index.tsx +2 -0
  120. package/src/components/ds-radio-group/ds-radio-group.module.scss +138 -0
  121. package/src/components/ds-radio-group/ds-radio-group.tsx +115 -0
  122. package/src/components/ds-radio-group/ds-radio-group.types.tsx +93 -0
  123. package/src/components/ds-radio-group/index.tsx +2 -0
  124. package/src/components/ds-select/ds-select.module.scss +66 -0
  125. package/src/components/ds-select/ds-select.tsx +239 -0
  126. package/src/components/ds-select/ds-select.types.tsx +80 -0
  127. package/src/components/ds-select/index.tsx +2 -0
  128. package/src/components/ds-select/select-items-chips.tsx +56 -0
  129. package/src/components/ds-smart-tabs/ds-smart-tabs.module.scss +69 -0
  130. package/src/components/ds-smart-tabs/ds-smart-tabs.tsx +113 -0
  131. package/src/components/ds-smart-tabs/ds-smart-tabs.types.tsx +74 -0
  132. package/src/components/ds-smart-tabs/index.tsx +2 -0
  133. package/src/components/ds-spinner/ds-spinner.module.scss +39 -0
  134. package/src/components/ds-spinner/ds-spinner.tsx +51 -0
  135. package/src/components/ds-spinner/ds-spinner.types.tsx +20 -0
  136. package/src/components/ds-spinner/index.tsx +2 -0
  137. package/src/components/ds-status-badge/ds-status-badge.module.scss +79 -0
  138. package/src/components/ds-status-badge/ds-status-badge.tsx +42 -0
  139. package/src/components/ds-status-badge/ds-status-badge.types.tsx +46 -0
  140. package/src/components/ds-status-badge/index.tsx +2 -0
  141. package/src/components/ds-stepper/components/ds-step-content.tsx +32 -0
  142. package/src/components/ds-stepper/components/ds-step-description.tsx +6 -0
  143. package/src/components/ds-stepper/components/ds-step-label.tsx +6 -0
  144. package/src/components/ds-stepper/components/ds-step-next-button.tsx +13 -0
  145. package/src/components/ds-stepper/components/ds-step.tsx +59 -0
  146. package/src/components/ds-stepper/ds-stepper.module.scss +134 -0
  147. package/src/components/ds-stepper/ds-stepper.tsx +34 -0
  148. package/src/components/ds-stepper/ds-stepper.types.ts +12 -0
  149. package/src/components/ds-stepper/hooks/use-stepper.ts +20 -0
  150. package/src/components/ds-stepper/hooks/use-steps.ts +18 -0
  151. package/src/components/ds-stepper/index.tsx +6 -0
  152. package/src/components/ds-system-status/ds-system-status.module.scss +87 -0
  153. package/src/components/ds-system-status/ds-system-status.tsx +33 -0
  154. package/src/components/ds-system-status/ds-system-status.types.tsx +37 -0
  155. package/src/components/ds-system-status/index.tsx +10 -0
  156. package/src/components/ds-table/components/core-table/core-table.module.scss +32 -0
  157. package/src/components/ds-table/components/core-table/core-table.tsx +83 -0
  158. package/src/components/ds-table/components/core-table/index.ts +1 -0
  159. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +115 -0
  160. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx +70 -0
  161. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.types.ts +41 -0
  162. package/src/components/ds-table/components/ds-table-bulk-actions/index.ts +2 -0
  163. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.module.scss +53 -0
  164. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.tsx +95 -0
  165. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.types.ts +67 -0
  166. package/src/components/ds-table/components/ds-table-cell/index.ts +2 -0
  167. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +81 -0
  168. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +99 -0
  169. package/src/components/ds-table/components/ds-table-header/ds-table-header.types.ts +11 -0
  170. package/src/components/ds-table/components/ds-table-header/index.ts +2 -0
  171. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +137 -0
  172. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +205 -0
  173. package/src/components/ds-table/components/ds-table-row/ds-table-row.types.ts +22 -0
  174. package/src/components/ds-table/components/ds-table-row/index.ts +2 -0
  175. package/src/components/ds-table/context/ds-table-context.tsx +39 -0
  176. package/src/components/ds-table/ds-table.module.scss +60 -0
  177. package/src/components/ds-table/ds-table.tsx +312 -0
  178. package/src/components/ds-table/ds-table.types.ts +377 -0
  179. package/src/components/ds-table/filters/adapters/create-checkbox-filter-adapter.tsx +116 -0
  180. package/src/components/ds-table/filters/adapters/create-dual-range-filter-adapter.tsx +171 -0
  181. package/src/components/ds-table/filters/adapters/create-filter-adapter.ts +131 -0
  182. package/src/components/ds-table/filters/adapters/index.ts +12 -0
  183. package/src/components/ds-table/filters/components/checkbox-filter/checkbox-filter.tsx +43 -0
  184. package/src/components/ds-table/filters/components/checkbox-filter/index.ts +2 -0
  185. package/src/components/ds-table/filters/components/range-filter/index.ts +2 -0
  186. package/src/components/ds-table/filters/components/range-filter/range-filter.module.scss +25 -0
  187. package/src/components/ds-table/filters/components/range-filter/range-filter.tsx +79 -0
  188. package/src/components/ds-table/filters/hooks/index.ts +1 -0
  189. package/src/components/ds-table/filters/hooks/use-table-filters.ts +234 -0
  190. package/src/components/ds-table/filters/index.ts +50 -0
  191. package/src/components/ds-table/filters/types/filter-adapter.types.ts +111 -0
  192. package/src/components/ds-table/hooks/use-drag-and-drop.tsx +107 -0
  193. package/src/components/ds-table/index.ts +4 -0
  194. package/src/components/ds-table/styles/_variables.scss +17 -0
  195. package/src/components/ds-table/styles/shared/ds-table-shared.module.scss +16 -0
  196. package/src/components/ds-table/utils/column-styling.ts +30 -0
  197. package/src/components/ds-table/utils/create-columns-grid-template.ts +23 -0
  198. package/src/components/ds-table/utils/merge-refs.ts +15 -0
  199. package/src/components/ds-table/utils/story-data-generator.ts +91 -0
  200. package/src/components/ds-text-input/ds-text-input.module.scss +27 -0
  201. package/src/components/ds-text-input/ds-text-input.tsx +75 -0
  202. package/src/components/ds-text-input/ds-text-input.types.tsx +110 -0
  203. package/src/components/ds-text-input/index.ts +2 -0
  204. package/src/components/ds-textarea/ds-textarea.module.scss +5 -0
  205. package/src/components/ds-textarea/ds-textarea.tsx +49 -0
  206. package/src/components/ds-textarea/ds-textarea.types.tsx +67 -0
  207. package/src/components/ds-textarea/index.tsx +2 -0
  208. package/src/components/ds-toast/ds-toast-context.tsx +79 -0
  209. package/src/components/ds-toast/ds-toast.module.scss +134 -0
  210. package/src/components/ds-toast/ds-toast.tsx +54 -0
  211. package/src/components/ds-toast/ds-toast.types.tsx +53 -0
  212. package/src/components/ds-toast/index.ts +3 -0
  213. package/src/components/ds-tooltip/ds-tooltip.module.scss +19 -0
  214. package/src/components/ds-tooltip/ds-tooltip.tsx +33 -0
  215. package/src/components/ds-tooltip/ds-tooltip.types.tsx +12 -0
  216. package/src/components/ds-tooltip/index.tsx +2 -0
  217. package/src/components/ds-typography/ds-typography.config.ts +32 -0
  218. package/src/components/ds-typography/ds-typography.module.scss +93 -0
  219. package/src/components/ds-typography/ds-typography.tsx +27 -0
  220. package/src/components/ds-typography/ds-typography.types.tsx +27 -0
  221. package/src/components/ds-typography/index.tsx +2 -0
  222. package/src/components/ds-vertical-tabs/ds-vertical-tabs.module.scss +60 -0
  223. package/src/components/ds-vertical-tabs/ds-vertical-tabs.tsx +80 -0
  224. package/src/components/ds-vertical-tabs/ds-vertical-tabs.types.ts +69 -0
  225. package/src/components/ds-vertical-tabs/index.ts +7 -0
  226. package/src/index.ts +34 -0
  227. package/src/styles/_colors.scss +65 -0
  228. package/src/styles/_grid.scss +111 -0
  229. package/src/styles/_mixin.scss +1 -0
  230. package/src/styles/_reset.scss +38 -0
  231. package/src/styles/_root.scss +390 -0
  232. package/src/styles/_scrollbars.scss +41 -0
  233. package/src/styles/_spacing.scss +47 -0
  234. package/src/styles/_typography.scss +301 -0
  235. package/src/styles/_utilities.scss +33 -0
  236. package/src/styles/_variables.scss +44 -0
  237. package/src/styles/mixins/_bounding-box.scss +53 -0
  238. package/src/styles/mixins/_list.scss +74 -0
  239. package/src/styles/shared/_dropdown.scss +89 -0
  240. package/src/styles/shared/_input.scss +365 -0
  241. package/src/styles/shared/_modal.scss +123 -0
  242. package/src/styles/styles.scss +12 -0
@@ -0,0 +1,396 @@
1
+ import type React from 'react';
2
+ import { createContext, Fragment, useContext, useState } from 'react';
3
+ import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
4
+ import { Menu } from '@ark-ui/react/menu';
5
+ import { Portal } from '@ark-ui/react/portal';
6
+ import classNames from 'classnames';
7
+ import styles from './ds-dropdown-menu.module.scss';
8
+ import { DsIcon } from '../ds-icon';
9
+ import DsTypography from '../ds-typography/ds-typography';
10
+ import DsTextInput from '../ds-text-input/ds-text-input';
11
+ import type {
12
+ DsDropdownMenuActionsProps,
13
+ DsDropdownMenuContentProps,
14
+ DsDropdownMenuHeaderProps,
15
+ DsDropdownMenuItemGroupContentProps,
16
+ DsDropdownMenuItemGroupLabelProps,
17
+ DsDropdownMenuItemGroupProps,
18
+ DsDropdownMenuItemIndicatorProps,
19
+ DsDropdownMenuItemProps,
20
+ DsDropdownMenuLegacyProps,
21
+ DsDropdownMenuPositioning,
22
+ DsDropdownMenuRootProps,
23
+ DsDropdownMenuSeparatorProps,
24
+ DsDropdownMenuTriggerProps,
25
+ } from './ds-dropdown-menu.types';
26
+
27
+ /**
28
+ * Context to share state between Group and GroupLabel
29
+ */
30
+ interface GroupContextValue {
31
+ collapsed: boolean;
32
+ toggle: () => void;
33
+ }
34
+
35
+ const GroupContext = createContext<GroupContextValue | null>(null);
36
+
37
+ const DEFAULT_POSITIONING: DsDropdownMenuPositioning = {
38
+ placement: 'bottom-start' as const,
39
+ gutter: 4,
40
+ };
41
+
42
+ /**
43
+ * Root component - manages dropdown state
44
+ */
45
+ const Root: React.FC<DsDropdownMenuRootProps> = ({
46
+ open,
47
+ onOpenChange,
48
+ onSelect,
49
+ onHighlightChange,
50
+ positioning: customPositioning,
51
+ preventCloseOnSelect = false,
52
+ children,
53
+ }) => {
54
+ const positioning = {
55
+ ...DEFAULT_POSITIONING,
56
+ ...customPositioning,
57
+ };
58
+
59
+ return (
60
+ <Menu.Root
61
+ open={open}
62
+ onOpenChange={(details) => onOpenChange?.(details.open)}
63
+ onSelect={(details) => onSelect?.(details.value)}
64
+ onHighlightChange={(details) => onHighlightChange?.(details.highlightedValue)}
65
+ positioning={positioning}
66
+ closeOnSelect={!preventCloseOnSelect}
67
+ >
68
+ {children}
69
+ </Menu.Root>
70
+ );
71
+ };
72
+
73
+ /**
74
+ * Trigger component - wraps the trigger element
75
+ */
76
+ const Trigger: React.FC<DsDropdownMenuTriggerProps> = Menu.Trigger;
77
+
78
+ /**
79
+ * Content component - wraps the dropdown content
80
+ */
81
+ const Content: React.FC<DsDropdownMenuContentProps> = ({
82
+ disablePortal = false,
83
+ children,
84
+ className,
85
+ style,
86
+ }) => {
87
+ const Wrapper = disablePortal ? Fragment : Portal;
88
+
89
+ return (
90
+ <Wrapper>
91
+ <Menu.Positioner>
92
+ <Menu.Content className={classNames(styles.content, className)} style={style}>
93
+ {children}
94
+ </Menu.Content>
95
+ </Menu.Positioner>
96
+ </Wrapper>
97
+ );
98
+ };
99
+
100
+ /**
101
+ * Item component - renders a menu item
102
+ */
103
+ const Item: React.FC<DsDropdownMenuItemProps> = ({
104
+ disabled,
105
+ selected,
106
+ value,
107
+ asChild,
108
+ onClick,
109
+ onSelect,
110
+ children,
111
+ className,
112
+ style,
113
+ }) => {
114
+ return (
115
+ <Menu.Item
116
+ disabled={disabled}
117
+ className={classNames(
118
+ styles.item,
119
+ {
120
+ [styles.selected]: selected,
121
+ },
122
+ className,
123
+ )}
124
+ style={style}
125
+ value={value}
126
+ asChild={asChild}
127
+ onClick={onClick}
128
+ onSelect={onSelect}
129
+ >
130
+ {children}
131
+ </Menu.Item>
132
+ );
133
+ };
134
+
135
+ /**
136
+ * ItemIndicator component - renders a selection indicator (check icon by default)
137
+ */
138
+ const ItemIndicator: React.FC<DsDropdownMenuItemIndicatorProps> = ({ children, className, style }) => {
139
+ return (
140
+ <span className={classNames(styles.indicator, className)} style={style}>
141
+ {children ?? <DsIcon icon="check" />}
142
+ </span>
143
+ );
144
+ };
145
+
146
+ /**
147
+ * Header component - sticky header container for search, filters, or other controls
148
+ */
149
+ const Header: React.FC<DsDropdownMenuHeaderProps> = ({ children, className, style }) => {
150
+ return (
151
+ <div className={classNames(styles.header, className)} style={style}>
152
+ {children}
153
+ </div>
154
+ );
155
+ };
156
+
157
+ /**
158
+ * Actions component - footer container for action buttons
159
+ */
160
+ const Actions: React.FC<DsDropdownMenuActionsProps> = ({ children, className, style }) => {
161
+ return (
162
+ <div
163
+ className={classNames(styles.actions, className)}
164
+ style={style}
165
+ role="menu"
166
+ aria-label="Menu actions"
167
+ >
168
+ {children}
169
+ </div>
170
+ );
171
+ };
172
+
173
+ /**
174
+ * ItemGroup component - collapsible group container
175
+ */
176
+ const ItemGroup: React.FC<DsDropdownMenuItemGroupProps> = ({
177
+ children,
178
+ collapsed: controlledCollapsed,
179
+ onCollapsedChange,
180
+ className,
181
+ style,
182
+ }) => {
183
+ const [internalCollapsed, setInternalCollapsed] = useState(false);
184
+
185
+ const isControlled = controlledCollapsed !== undefined;
186
+ const collapsed = isControlled ? controlledCollapsed : internalCollapsed;
187
+
188
+ const toggle = () => {
189
+ const newCollapsed = !collapsed;
190
+ if (!isControlled) {
191
+ setInternalCollapsed(newCollapsed);
192
+ }
193
+ onCollapsedChange?.(newCollapsed);
194
+ };
195
+
196
+ return (
197
+ <GroupContext.Provider value={{ collapsed, toggle }}>
198
+ <Menu.ItemGroup className={classNames(styles.group, className)} style={style}>
199
+ {children}
200
+ </Menu.ItemGroup>
201
+ </GroupContext.Provider>
202
+ );
203
+ };
204
+
205
+ /**
206
+ * ItemGroupLabel component - clickable group header with collapse indicator
207
+ */
208
+ const ItemGroupLabel: React.FC<DsDropdownMenuItemGroupLabelProps> = ({ children, className, style }) => {
209
+ const context = useContext(GroupContext);
210
+
211
+ if (!context) {
212
+ // If not inside a Group, just render static label
213
+ return (
214
+ <Menu.ItemGroupLabel className={classNames(styles.groupLabel, className)} style={style}>
215
+ <DsTypography variant="body-sm-md">{children}</DsTypography>
216
+ </Menu.ItemGroupLabel>
217
+ );
218
+ }
219
+
220
+ const { collapsed, toggle } = context;
221
+
222
+ return (
223
+ <Menu.ItemGroupLabel asChild>
224
+ <button
225
+ type="button"
226
+ className={classNames(styles.groupLabel, styles.groupLabelCollapsible, className)}
227
+ style={style}
228
+ onClick={toggle}
229
+ onKeyDown={(e) => e.stopPropagation()}
230
+ >
231
+ <DsTypography variant="body-sm-md">{children}</DsTypography>
232
+ <DsIcon
233
+ icon="keyboard_arrow_down"
234
+ className={classNames(styles.groupLabelIcon, {
235
+ [styles.groupLabelIconRotated]: !collapsed,
236
+ })}
237
+ />
238
+ </button>
239
+ </Menu.ItemGroupLabel>
240
+ );
241
+ };
242
+
243
+ /**
244
+ * ItemGroupContent component - collapsible content container
245
+ */
246
+ const ItemGroupContent: React.FC<DsDropdownMenuItemGroupContentProps> = ({ children, className, style }) => {
247
+ const context = useContext(GroupContext);
248
+
249
+ if (context?.collapsed) {
250
+ return null;
251
+ }
252
+
253
+ return (
254
+ <div className={className} style={style}>
255
+ {children}
256
+ </div>
257
+ );
258
+ };
259
+
260
+ /**
261
+ * Separator component - renders a visual divider
262
+ */
263
+ const Separator: React.FC<DsDropdownMenuSeparatorProps> = ({ className, style }) => {
264
+ return <Menu.Separator className={classNames(styles.separator, className)} style={style} />;
265
+ };
266
+
267
+ /**
268
+ * DEPRECATED: Legacy DsDropdownMenu component with options array
269
+ * Use compound component pattern instead
270
+ * @deprecated
271
+ */
272
+ export const DsDropdownMenuLegacy: React.FC<DsDropdownMenuLegacyProps> = ({
273
+ options,
274
+ children,
275
+ contentGap = 0,
276
+ className,
277
+ style,
278
+ align = 'center',
279
+ side = 'bottom',
280
+ disablePortal = false,
281
+ disableSearch = true,
282
+ selected,
283
+ onSelect,
284
+ }) => {
285
+ const [open, setOpen] = useState(false);
286
+ const [searchTerm, setSearchTerm] = useState('');
287
+
288
+ const Wrapper = disablePortal ? Fragment : DropdownMenu.Portal;
289
+
290
+ const filteredOptions = disableSearch
291
+ ? options
292
+ : options.filter((option) => option.label.toLowerCase().includes(searchTerm.toLowerCase()));
293
+
294
+ return (
295
+ <DropdownMenu.Root open={open} onOpenChange={setOpen}>
296
+ <DropdownMenu.Trigger asChild>{children}</DropdownMenu.Trigger>
297
+ <Wrapper>
298
+ <DropdownMenu.Content
299
+ className={classNames(styles.contentLegacy, styles.viewportLegacy)}
300
+ sideOffset={contentGap}
301
+ align={align}
302
+ side={side}
303
+ >
304
+ {!disableSearch && (
305
+ <DsTextInput
306
+ placeholder="Search"
307
+ value={searchTerm}
308
+ onValueChange={setSearchTerm}
309
+ slots={{
310
+ startAdornment: <DsIcon icon="search" size="tiny" />,
311
+ }}
312
+ onKeyDown={(e) => e.stopPropagation()}
313
+ />
314
+ )}
315
+ {filteredOptions.map((option, i) => (
316
+ <DropdownMenu.Item
317
+ key={i}
318
+ disabled={option.disabled}
319
+ className={classNames(
320
+ styles.itemLegacy,
321
+ {
322
+ [styles.selected]: selected === option.value,
323
+ },
324
+ className,
325
+ )}
326
+ style={style}
327
+ onClick={(e) => {
328
+ e.stopPropagation();
329
+
330
+ if (!option.disabled) {
331
+ if (option.value) {
332
+ onSelect?.(option.value);
333
+ }
334
+ option.onClick?.(e);
335
+ setOpen(false);
336
+ }
337
+ }}
338
+ >
339
+ {option.icon && <DsIcon icon={option.icon} className={styles.itemIcon} />}
340
+ <span className={styles.label}>{option.label}</span>
341
+ {option.value && selected === option.value && (
342
+ <span className={styles.indicator}>
343
+ <DsIcon icon="check" />
344
+ </span>
345
+ )}
346
+ </DropdownMenu.Item>
347
+ ))}
348
+ </DropdownMenu.Content>
349
+ </Wrapper>
350
+ </DropdownMenu.Root>
351
+ );
352
+ };
353
+
354
+ /**
355
+ * Design system DsDropdownMenu component
356
+ *
357
+ * @example
358
+ * <DsDropdownMenu.Root>
359
+ * <DsDropdownMenu.Trigger>
360
+ * <button>Open</button>
361
+ * </DsDropdownMenu.Trigger>
362
+ * <DsDropdownMenu.Content>
363
+ * <DsDropdownMenu.Header>
364
+ * <DsTextInput placeholder="Search..." />
365
+ * </DsDropdownMenu.Header>
366
+ * <DsDropdownMenu.Item value="item1">
367
+ * Item 1
368
+ * <DsDropdownMenu.ItemIndicator />
369
+ * </DsDropdownMenu.Item>
370
+ * <DsDropdownMenu.ItemGroup>
371
+ * <DsDropdownMenu.ItemGroupLabel>Group Name</DsDropdownMenu.ItemGroupLabel>
372
+ * <DsDropdownMenu.ItemGroupContent>
373
+ * <DsDropdownMenu.Item value="item2">Item 2</DsDropdownMenu.Item>
374
+ * </DsDropdownMenu.ItemGroupContent>
375
+ * </DsDropdownMenu.ItemGroup>
376
+ * <DsDropdownMenu.Separator />
377
+ * <DsDropdownMenu.Actions>
378
+ * <DsButton>Cancel</DsButton>
379
+ * <DsButton>Apply</DsButton>
380
+ * </DsDropdownMenu.Actions>
381
+ * </DsDropdownMenu.Content>
382
+ * </DsDropdownMenu.Root>
383
+ */
384
+ export const DsDropdownMenu = {
385
+ Root,
386
+ Trigger,
387
+ Content,
388
+ Item,
389
+ ItemIndicator,
390
+ Header,
391
+ Actions,
392
+ ItemGroup,
393
+ ItemGroupLabel,
394
+ ItemGroupContent,
395
+ Separator,
396
+ };
@@ -0,0 +1,262 @@
1
+ import type { CSSProperties, MouseEvent, ReactNode } from 'react';
2
+ import type { Menu } from '@ark-ui/react/menu';
3
+ import type { IconType } from '../ds-icon';
4
+
5
+ /**
6
+ * Base positioning type
7
+ */
8
+ type BasePositioning = NonNullable<Menu.RootProps['positioning']>;
9
+
10
+ /**
11
+ * Positioning options for dropdown menu
12
+ */
13
+ export type DsDropdownMenuPositioning = Pick<BasePositioning, 'placement' | 'gutter' | 'sameWidth'>;
14
+
15
+ /**
16
+ * DEPRECATED: Legacy dropdown menu option configuration
17
+ * Use compound component pattern instead
18
+ * @deprecated
19
+ */
20
+ export interface DsDropdownMenuOptionLegacy {
21
+ /**
22
+ * Display label for the menu option
23
+ */
24
+ label: string;
25
+ /**
26
+ * Optional icon to display
27
+ */
28
+ icon?: IconType;
29
+ /**
30
+ * Whether this option is disabled
31
+ */
32
+ disabled?: boolean;
33
+ /**
34
+ * Click handler for the option
35
+ */
36
+ onClick?: (e: MouseEvent<HTMLElement>) => void;
37
+ /**
38
+ * Optional value for selection tracking
39
+ */
40
+ value?: string;
41
+ }
42
+
43
+ /**
44
+ * DEPRECATED: Legacy props for DsDropdownMenuLegacy component
45
+ * Use compound component pattern instead
46
+ * @deprecated
47
+ */
48
+ export interface DsDropdownMenuLegacyProps {
49
+ /**
50
+ * The options to be displayed in the dropdown menu
51
+ */
52
+ options: DsDropdownMenuOptionLegacy[];
53
+ /**
54
+ * Optional children to be rendered inside the component
55
+ * Typically used for the trigger element
56
+ */
57
+ children?: ReactNode | undefined;
58
+ /**
59
+ * The gap between the trigger and dropdown content in pixels
60
+ * @default 0
61
+ */
62
+ contentGap?: number;
63
+ /**
64
+ * Additional CSS class names
65
+ */
66
+ className?: string;
67
+ /**
68
+ * Optional inline styles to apply to the component
69
+ */
70
+ style?: CSSProperties;
71
+ /**
72
+ * The alignment of the dropdown content
73
+ * @default 'center'
74
+ */
75
+ align?: 'start' | 'center' | 'end';
76
+ /**
77
+ * The side of the dropdown content
78
+ * @default 'bottom'
79
+ */
80
+ side?: 'top' | 'right' | 'bottom' | 'left';
81
+ /**
82
+ * Whether to render in place instead of using portals
83
+ * @default false
84
+ */
85
+ disablePortal?: boolean;
86
+ /**
87
+ * Whether to disable the search functionality
88
+ * @default false
89
+ */
90
+ disableSearch?: boolean;
91
+ /**
92
+ * Currently selected value (for selection tracking)
93
+ */
94
+ selected?: string;
95
+ /**
96
+ * Callback when an option with a value is selected
97
+ */
98
+ onSelect?: (value: string) => void;
99
+ }
100
+
101
+ /**
102
+ * Props for the DsDropdownMenu Root component
103
+ */
104
+ export interface DsDropdownMenuRootProps {
105
+ /**
106
+ * Whether the dropdown is open (controlled mode)
107
+ */
108
+ open?: boolean;
109
+ /**
110
+ * Callback when open state changes
111
+ */
112
+ onOpenChange?: (open: boolean) => void;
113
+ /**
114
+ * Callback when an item is selected
115
+ */
116
+ onSelect?: (value: string) => void;
117
+ /**
118
+ * Callback when the highlighted item changes
119
+ */
120
+ onHighlightChange?: (value: string | null) => void;
121
+ /**
122
+ * Positioning options for the dropdown
123
+ */
124
+ positioning?: DsDropdownMenuPositioning;
125
+ /**
126
+ * Prevents the menu from closing when any item is selected
127
+ * @default false
128
+ */
129
+ preventCloseOnSelect?: boolean;
130
+ /**
131
+ * Root content
132
+ */
133
+ children?: ReactNode;
134
+ }
135
+
136
+ /**
137
+ * Props for the DsDropdownMenu Trigger component
138
+ */
139
+ export type DsDropdownMenuTriggerProps = Menu.TriggerProps;
140
+
141
+ /**
142
+ * Props for the DsDropdownMenu Content component
143
+ */
144
+ export interface DsDropdownMenuContentProps extends Pick<
145
+ Menu.ContentProps,
146
+ 'className' | 'style' | 'children'
147
+ > {
148
+ /**
149
+ * Whether to render in place instead of using portals
150
+ * @default false
151
+ */
152
+ disablePortal?: boolean;
153
+ }
154
+
155
+ /**
156
+ * Props for the DsDropdownMenu Item component
157
+ */
158
+ export interface DsDropdownMenuItemProps extends Pick<
159
+ Menu.ItemProps,
160
+ 'disabled' | 'asChild' | 'className' | 'style' | 'onSelect' | 'onClick' | 'value' | 'children'
161
+ > {
162
+ /**
163
+ * Whether the item is selected (applies selected styling)
164
+ */
165
+ selected?: boolean;
166
+ }
167
+
168
+ /**
169
+ * Props for the DsDropdownMenu ItemIndicator component
170
+ */
171
+ export interface DsDropdownMenuItemIndicatorProps {
172
+ /**
173
+ * Custom content to render as the indicator (defaults to check icon)
174
+ */
175
+ children?: ReactNode;
176
+ /**
177
+ * Optional CSS class name
178
+ */
179
+ className?: string;
180
+ /**
181
+ * Optional inline styles
182
+ */
183
+ style?: CSSProperties;
184
+ }
185
+
186
+ /**
187
+ * Props for the DsDropdownMenu Header component
188
+ */
189
+ export interface DsDropdownMenuHeaderProps {
190
+ /**
191
+ * The header content (typically a search input or filter controls)
192
+ */
193
+ children: ReactNode;
194
+ /**
195
+ * Optional CSS class name
196
+ */
197
+ className?: string;
198
+ /**
199
+ * Optional inline styles
200
+ */
201
+ style?: CSSProperties;
202
+ }
203
+
204
+ /**
205
+ * Props for the DsDropdownMenu Actions component
206
+ */
207
+ export interface DsDropdownMenuActionsProps {
208
+ /**
209
+ * The action buttons or elements
210
+ */
211
+ children: ReactNode;
212
+ /**
213
+ * Optional CSS class name
214
+ */
215
+ className?: string;
216
+ /**
217
+ * Optional inline styles
218
+ */
219
+ style?: CSSProperties;
220
+ }
221
+
222
+ /**
223
+ * Props for the DsDropdownMenu ItemGroup component
224
+ */
225
+ export interface DsDropdownMenuItemGroupProps extends Menu.ItemGroupProps {
226
+ /**
227
+ * Whether the group is collapsed
228
+ */
229
+ collapsed?: boolean;
230
+ /**
231
+ * Callback when collapse state changes
232
+ */
233
+ onCollapsedChange?: (collapsed: boolean) => void;
234
+ }
235
+
236
+ /**
237
+ * Props for the DsDropdownMenu ItemGroupLabel component
238
+ */
239
+ export type DsDropdownMenuItemGroupLabelProps = Menu.ItemGroupLabelProps;
240
+
241
+ /**
242
+ * Props for the DsDropdownMenu ItemGroupContent component
243
+ */
244
+ export interface DsDropdownMenuItemGroupContentProps {
245
+ /**
246
+ * The content to show/hide based on collapsed state
247
+ */
248
+ children: ReactNode;
249
+ /**
250
+ * Optional CSS class name
251
+ */
252
+ className?: string;
253
+ /**
254
+ * Optional inline styles
255
+ */
256
+ style?: CSSProperties;
257
+ }
258
+
259
+ /**
260
+ * Props for the DsDropdownMenu Separator component
261
+ */
262
+ export type DsDropdownMenuSeparatorProps = Menu.SeparatorProps;
@@ -0,0 +1,2 @@
1
+ export { DsDropdownMenu, DsDropdownMenuLegacy } from './ds-dropdown-menu';
2
+ export * from './ds-dropdown-menu.types';