@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,199 @@
1
+ $count-size: 60px;
2
+ $action-item-min-width: 64px;
3
+ $action-label-max-width: 84px;
4
+ $divider-height: 40px;
5
+ $bar-gap: 3px;
6
+ $bar-radius: 8px;
7
+ $bar-shadow: 0 3.5px 14px 0 var(--shadow-moderate);
8
+
9
+ .bulkActionsContainer {
10
+ position: relative;
11
+ width: fit-content;
12
+ background-color: var(--color-dap-gray-050);
13
+ border-radius: $bar-radius;
14
+ box-shadow: $bar-shadow;
15
+ }
16
+
17
+ .floating {
18
+ position: absolute;
19
+ bottom: var(--md);
20
+ left: 50%;
21
+ transform: translateX(-50%);
22
+ z-index: 1;
23
+ animation-duration: 0.3s;
24
+ animation-timing-function: ease-out;
25
+ animation-fill-mode: forwards;
26
+
27
+ &.entering {
28
+ animation-name: slideUpFadeIn;
29
+ }
30
+
31
+ &.exiting {
32
+ animation-name: slideDownFadeOut;
33
+ }
34
+ }
35
+
36
+ .selectedCountContainer {
37
+ display: flex;
38
+ flex-shrink: 0;
39
+ align-items: center;
40
+ justify-content: center;
41
+ width: $count-size;
42
+ height: $count-size;
43
+ padding: var(--3xs);
44
+ background-color: var(--color-dap-blue-600);
45
+ border-radius: $bar-radius 0 0 $bar-radius;
46
+ }
47
+
48
+ .selectedCountBadge {
49
+ margin: 0;
50
+ color: var(--color-dap-gray-050);
51
+ text-align: center;
52
+ }
53
+
54
+ .bulkActionsInfo {
55
+ flex-shrink: 0;
56
+ margin: 0;
57
+ padding: 0 var(--xs);
58
+ color: var(--color-dap-gray-600);
59
+ text-wrap: nowrap;
60
+ }
61
+
62
+ .bulkActionsActions {
63
+ min-width: 0;
64
+ }
65
+
66
+ .measurementContainer {
67
+ pointer-events: none;
68
+ position: absolute;
69
+ display: flex;
70
+ align-items: center;
71
+ height: 0;
72
+ overflow: hidden;
73
+ visibility: hidden;
74
+
75
+ > * {
76
+ flex-shrink: 0;
77
+ }
78
+
79
+ * {
80
+ visibility: hidden;
81
+ }
82
+ }
83
+
84
+ .menuTriggerAnchor {
85
+ display: inline-flex;
86
+ }
87
+
88
+ .actionItem {
89
+ display: flex;
90
+ flex-shrink: 0;
91
+ align-items: center;
92
+ gap: var(--3xs);
93
+ min-width: $action-item-min-width;
94
+ padding-right: var(--3xs);
95
+
96
+ &[data-item-width='fit-content'],
97
+ &[data-item-width='custom'] {
98
+ min-width: 0;
99
+ }
100
+
101
+ &::before {
102
+ content: '';
103
+ flex-shrink: 0;
104
+ width: 1px;
105
+ height: $divider-height;
106
+ background-color: var(--color-dap-gray-400);
107
+ }
108
+ }
109
+
110
+ .actionButton {
111
+ display: flex;
112
+ flex-direction: column;
113
+ align-items: center;
114
+ justify-content: center;
115
+ gap: var(--4xs);
116
+ min-width: $action-item-min-width;
117
+ max-width: $action-label-max-width;
118
+ padding: var(--3xs) var(--4xs);
119
+ border: none;
120
+ border-radius: var(--3xs);
121
+ background-color: var(--background);
122
+ color: var(--font-action-secondary);
123
+ cursor: pointer;
124
+ transition:
125
+ background-color 0.2s,
126
+ color 0.2s;
127
+
128
+ &[data-item-width='fit-content'] {
129
+ min-width: 0;
130
+ max-width: none;
131
+ width: fit-content;
132
+ }
133
+
134
+ &[data-item-width='custom'] {
135
+ min-width: 0;
136
+ max-width: none;
137
+ width: var(--ds-bulk-actions-item-width);
138
+ }
139
+
140
+ &:hover:not(:disabled) {
141
+ background-color: var(--background-action-tertiary-hover);
142
+ color: var(--brand-600);
143
+ }
144
+
145
+ &:active:not(:disabled) {
146
+ background-color: var(--background-action-secondary-hover);
147
+ color: var(--font-action-secondary);
148
+ }
149
+
150
+ &:disabled {
151
+ background-color: transparent;
152
+ color: var(--font-disabled);
153
+ cursor: not-allowed;
154
+ }
155
+
156
+ &:focus {
157
+ outline: none;
158
+ }
159
+
160
+ &:focus-visible {
161
+ outline: 2px solid var(--brand-600);
162
+ outline-offset: -2px;
163
+ }
164
+ }
165
+
166
+ .actionLabel {
167
+ min-width: 0;
168
+ max-width: 100%;
169
+ overflow: hidden;
170
+ text-align: center;
171
+ text-overflow: ellipsis;
172
+ white-space: nowrap;
173
+ }
174
+
175
+ .dismissButton {
176
+ flex-shrink: 0;
177
+ }
178
+
179
+ @keyframes slideUpFadeIn {
180
+ 0% {
181
+ opacity: 0;
182
+ transform: translate(-50%, 100%);
183
+ }
184
+ 100% {
185
+ opacity: 1;
186
+ transform: translate(-50%, 0);
187
+ }
188
+ }
189
+
190
+ @keyframes slideDownFadeOut {
191
+ 0% {
192
+ opacity: 1;
193
+ transform: translate(-50%, 0);
194
+ }
195
+ 100% {
196
+ opacity: 0;
197
+ transform: translate(-50%, 100%);
198
+ }
199
+ }
@@ -0,0 +1,220 @@
1
+ import { createContext, useContext, useEffect, useRef, useState } from 'react';
2
+ import classnames from 'classnames';
3
+ import styles from './ds-bulk-actions.module.scss';
4
+ import type {
5
+ BulkActionsMenuPlacement,
6
+ DsBulkActionsItemProps,
7
+ DsBulkActionsProps,
8
+ } from './ds-bulk-actions.types';
9
+ import {
10
+ getBulkActionItems,
11
+ isMenuItemProps,
12
+ itemToOverflowEntry,
13
+ resolveItemWidth,
14
+ } from './ds-bulk-actions.utils';
15
+ import { ActionButton } from './components/action-button';
16
+ import { MenuEntries, TooltipMenuTrigger } from './components/menu-entries';
17
+ import { OverflowMeasure, OverflowMore } from './components/overflow';
18
+ import { useBulkActionsOverflow } from './hooks/use-bulk-actions-overflow';
19
+ import { DsButtonV3 } from '../ds-button-v3';
20
+ import { DsDropdownMenu } from '../ds-dropdown-menu';
21
+ import { DsStack } from '../ds-stack';
22
+ import { DsTooltip } from '../ds-tooltip';
23
+ import { DsTypography } from '../ds-typography';
24
+ import { mergeRefs } from '../../utils/merge-refs';
25
+
26
+ const EXIT_ANIMATION_MS = 300;
27
+
28
+ interface BulkActionsContextValue {
29
+ menuPlacement: BulkActionsMenuPlacement;
30
+ }
31
+
32
+ const BulkActionsContext = createContext<BulkActionsContextValue>({ menuPlacement: 'bottom' });
33
+
34
+ const Item = (props: DsBulkActionsItemProps) => {
35
+ const { className, style, ref, width, icon, label, disabled } = props;
36
+ const { menuPlacement } = useContext(BulkActionsContext);
37
+ const { mode, buttonStyle } = resolveItemWidth(width);
38
+
39
+ return (
40
+ <div ref={ref} className={classnames(styles.actionItem, className)} style={style} data-item-width={mode}>
41
+ {isMenuItemProps(props) ? (
42
+ <DsDropdownMenu.Root positioning={{ placement: menuPlacement }}>
43
+ <TooltipMenuTrigger
44
+ icon={icon}
45
+ label={label}
46
+ disabled={disabled}
47
+ itemWidth={mode}
48
+ buttonStyle={buttonStyle}
49
+ />
50
+ <DsDropdownMenu.Content>
51
+ <MenuEntries entries={props.menu} />
52
+ </DsDropdownMenu.Content>
53
+ </DsDropdownMenu.Root>
54
+ ) : (
55
+ <DsTooltip content={label}>
56
+ <ActionButton
57
+ icon={icon}
58
+ label={label}
59
+ onClick={props.onClick}
60
+ disabled={disabled}
61
+ itemWidth={mode}
62
+ style={buttonStyle}
63
+ />
64
+ </DsTooltip>
65
+ )}
66
+ </div>
67
+ );
68
+ };
69
+
70
+ /**
71
+ * Floating or static toolbar for bulk operations on a selection.
72
+ **/
73
+ const DsBulkActions = ({
74
+ selectedCount,
75
+ children,
76
+ onClearSelection,
77
+ placement = 'static',
78
+ menuPlacement: menuPlacementProp,
79
+ locale,
80
+ className,
81
+ style,
82
+ ref,
83
+ }: DsBulkActionsProps) => {
84
+ const [isRendered, setIsRendered] = useState(false);
85
+ const [animationClass, setAnimationClass] = useState('');
86
+ const isFloating = placement === 'floating';
87
+ const menuPlacement = menuPlacementProp ?? (isFloating ? 'top' : 'bottom');
88
+
89
+ const itemsSelectedLabel = locale?.itemsSelectedLabel ?? 'Items selected';
90
+ const clearSelectionLabel = locale?.clearSelectionLabel ?? 'Clear selection';
91
+ const moreActionsLabel = locale?.moreActionsLabel ?? 'More';
92
+
93
+ const containerRef = useRef<HTMLDivElement>(null);
94
+ const stackRef = useRef<HTMLDivElement>(null);
95
+ const badgeRef = useRef<HTMLDivElement>(null);
96
+ const dismissRef = useRef<HTMLButtonElement>(null);
97
+ const measureRef = useRef<HTMLDivElement>(null);
98
+ const items = getBulkActionItems(children);
99
+
100
+ const { visibleCount, showMore, summaryLabelHidden } = useBulkActionsOverflow({
101
+ active: isRendered,
102
+ itemCount: items.length,
103
+ style,
104
+ containerRef,
105
+ stackRef,
106
+ badgeRef,
107
+ dismissRef,
108
+ measureRef,
109
+ });
110
+
111
+ useEffect(() => {
112
+ if (selectedCount > 0) {
113
+ if (!isRendered) {
114
+ setIsRendered(true);
115
+ setAnimationClass(isFloating ? styles.entering : '');
116
+ }
117
+
118
+ return;
119
+ }
120
+
121
+ if (isRendered) {
122
+ if (isFloating) {
123
+ setAnimationClass(styles.exiting);
124
+ const timer = setTimeout(() => {
125
+ setIsRendered(false);
126
+ setAnimationClass('');
127
+ }, EXIT_ANIMATION_MS);
128
+ return () => clearTimeout(timer);
129
+ }
130
+
131
+ setIsRendered(false);
132
+ setAnimationClass('');
133
+ }
134
+ }, [selectedCount, isRendered, isFloating]);
135
+
136
+ if (!isRendered) {
137
+ return null;
138
+ }
139
+
140
+ const clampedVisibleCount = Math.min(visibleCount, items.length);
141
+ const visibleItems = items.slice(0, clampedVisibleCount);
142
+ const overflowEntries = items
143
+ .slice(clampedVisibleCount)
144
+ .map((item, index) => itemToOverflowEntry(item.props, index));
145
+
146
+ return (
147
+ <BulkActionsContext.Provider value={{ menuPlacement }}>
148
+ <div
149
+ ref={mergeRefs(ref, containerRef)}
150
+ role="toolbar"
151
+ aria-label={itemsSelectedLabel}
152
+ className={classnames(
153
+ styles.bulkActionsContainer,
154
+ isFloating && styles.floating,
155
+ isFloating && animationClass,
156
+ className,
157
+ )}
158
+ style={style}
159
+ >
160
+ <DsStack ref={stackRef} direction="row" alignItems="center" gap="var(--3xs)">
161
+ <div ref={badgeRef} className={styles.selectedCountContainer} aria-live="polite" aria-atomic="true">
162
+ <DsTypography variant="heading1" className={styles.selectedCountBadge}>
163
+ {selectedCount}
164
+ </DsTypography>
165
+ </div>
166
+
167
+ {summaryLabelHidden ? null : (
168
+ <DsTypography variant="body-md-semi-bold" className={styles.bulkActionsInfo}>
169
+ {itemsSelectedLabel}
170
+ </DsTypography>
171
+ )}
172
+
173
+ <DsStack
174
+ direction="row"
175
+ alignItems="center"
176
+ justifyContent="flex-end"
177
+ flex="1 1 auto"
178
+ flexWrap="nowrap"
179
+ className={styles.bulkActionsActions}
180
+ >
181
+ {visibleItems}
182
+ {showMore && overflowEntries.length > 0 ? (
183
+ <OverflowMore
184
+ label={moreActionsLabel}
185
+ menuPlacement={menuPlacement}
186
+ entries={overflowEntries}
187
+ />
188
+ ) : null}
189
+ </DsStack>
190
+
191
+ <DsButtonV3
192
+ ref={dismissRef}
193
+ variant="tertiary"
194
+ size="medium"
195
+ icon="close"
196
+ aria-label={clearSelectionLabel}
197
+ className={styles.dismissButton}
198
+ onClick={onClearSelection}
199
+ />
200
+ </DsStack>
201
+
202
+ {items.length > 0 ? (
203
+ <OverflowMeasure
204
+ items={items}
205
+ moreActionsLabel={moreActionsLabel}
206
+ itemsSelectedLabel={itemsSelectedLabel}
207
+ measureRef={measureRef}
208
+ />
209
+ ) : null}
210
+ </div>
211
+ </BulkActionsContext.Provider>
212
+ );
213
+ };
214
+
215
+ DsBulkActions.displayName = 'DsBulkActions';
216
+ Item.displayName = 'DsBulkActions.Item';
217
+
218
+ DsBulkActions.Item = Item;
219
+
220
+ export default DsBulkActions;
@@ -0,0 +1,170 @@
1
+ import type { CSSProperties, ReactNode, Ref } from 'react';
2
+ import type { IconType } from '../ds-icon';
3
+
4
+ export const bulkActionsPlacements = ['static', 'floating'] as const;
5
+ export type BulkActionsPlacement = (typeof bulkActionsPlacements)[number];
6
+
7
+ export const bulkActionsMenuPlacements = ['top', 'bottom'] as const;
8
+ export type BulkActionsMenuPlacement = (typeof bulkActionsMenuPlacements)[number];
9
+
10
+ export const bulkActionsMenuItemVariants = ['default', 'error'] as const;
11
+ export type BulkActionsMenuItemVariant = (typeof bulkActionsMenuItemVariants)[number];
12
+
13
+ export const bulkActionsItemWidthModes = ['fixed', 'fit-content'] as const;
14
+ export type BulkActionsItemWidthMode = (typeof bulkActionsItemWidthModes)[number];
15
+
16
+ /**
17
+ * `'fixed'` keeps the 64–84px column. `'fit-content'` hugs the label. A number is an exact pixel width and truncates.
18
+ */
19
+ export type DsBulkActionsItemWidth = BulkActionsItemWidthMode | number;
20
+
21
+ export interface DsBulkActionsLocale {
22
+ /**
23
+ * Label shown next to the selection count
24
+ * @default 'Items selected'
25
+ */
26
+ itemsSelectedLabel?: string;
27
+
28
+ /**
29
+ * Accessible name for the dismiss control
30
+ * @default 'Clear selection'
31
+ */
32
+ clearSelectionLabel?: string;
33
+
34
+ /**
35
+ * Label for the overflow trigger that holds collapsed actions
36
+ * @default 'More'
37
+ */
38
+ moreActionsLabel?: string;
39
+ }
40
+
41
+ interface DsBulkActionsMenuEntryBase {
42
+ value: string;
43
+ label: string;
44
+ icon?: IconType;
45
+ disabled?: boolean;
46
+ /**
47
+ * @default 'default'
48
+ */
49
+ variant?: BulkActionsMenuItemVariant;
50
+ }
51
+
52
+ type DsBulkActionsMenuLeafEntry = DsBulkActionsMenuEntryBase & {
53
+ onSelect?: () => void;
54
+ menu?: never;
55
+ };
56
+
57
+ type DsBulkActionsMenuBranchEntry = DsBulkActionsMenuEntryBase & {
58
+ /**
59
+ * Nested submenu entries. Mutually exclusive with `onSelect`.
60
+ */
61
+ menu: DsBulkActionsMenuEntry[];
62
+ onSelect?: never;
63
+ };
64
+
65
+ export type DsBulkActionsMenuEntry = DsBulkActionsMenuLeafEntry | DsBulkActionsMenuBranchEntry;
66
+
67
+ interface DsBulkActionsItemSharedProps {
68
+ /**
69
+ * Action column width.
70
+ * @default 'fixed'
71
+ */
72
+ width?: DsBulkActionsItemWidth;
73
+
74
+ /**
75
+ * Whether the action is non-interactive
76
+ * @default false
77
+ */
78
+ disabled?: boolean;
79
+
80
+ className?: string;
81
+ style?: CSSProperties;
82
+ ref?: Ref<HTMLDivElement>;
83
+ children?: never;
84
+ }
85
+
86
+ type DsBulkActionsItemClickProps = DsBulkActionsItemSharedProps & {
87
+ /**
88
+ * Icon displayed above the action label
89
+ */
90
+ icon: IconType;
91
+
92
+ /**
93
+ * Action label under the icon. Truncates when `width` is `'fixed'` or a pixel length; full text in tooltip.
94
+ */
95
+ label: string;
96
+
97
+ /**
98
+ * Called when the action is activated. Mutually exclusive with `menu`.
99
+ */
100
+ onClick: () => void;
101
+
102
+ menu?: never;
103
+ };
104
+
105
+ type DsBulkActionsItemMenuProps = DsBulkActionsItemSharedProps & {
106
+ /**
107
+ * Icon displayed above the action label
108
+ */
109
+ icon: IconType;
110
+
111
+ /**
112
+ * Action label under the icon. Truncates when `width` is `'fixed'` or a pixel length; full text in tooltip.
113
+ */
114
+ label: string;
115
+
116
+ /**
117
+ * Declarative menu shown from this action. Mutually exclusive with `onClick`.
118
+ */
119
+ menu: DsBulkActionsMenuEntry[];
120
+
121
+ onClick?: never;
122
+ };
123
+
124
+ /**
125
+ * Slot in the bulk-actions toolbar. `onClick` and `menu` are mutually exclusive.
126
+ */
127
+ export type DsBulkActionsItemProps = DsBulkActionsItemClickProps | DsBulkActionsItemMenuProps;
128
+
129
+ export interface DsBulkActionsProps {
130
+ /**
131
+ * Number of selected items. The bar mounts at 1+ and unmounts when cleared to 0.
132
+ */
133
+ selectedCount: number;
134
+
135
+ /**
136
+ * Action slots rendered between the selection summary and the dismiss control
137
+ */
138
+ children?: ReactNode;
139
+
140
+ /**
141
+ * Called when the dismiss control is activated; should clear the selection
142
+ */
143
+ onClearSelection: () => void;
144
+
145
+ /**
146
+ * Placement of the toolbar. `floating` anchors to the bottom center of a positioned
147
+ * ancestor; `static` leaves placement to the consumer via `className` / `style`.
148
+ *
149
+ * Action overflow ("More") only activates when the toolbar width is bounded via
150
+ * `style.width`, `style.maxWidth`, or a CSS `max-width`; a bare `width` set through
151
+ * `className` is not detected (the default `fit-content` width is indistinguishable).
152
+ * @default 'static'
153
+ */
154
+ placement?: BulkActionsPlacement;
155
+
156
+ /**
157
+ * Flyout direction for `menu` items and the overflow More menu.
158
+ * @default 'top' when `placement="floating"`, otherwise `'bottom'`
159
+ */
160
+ menuPlacement?: BulkActionsMenuPlacement;
161
+
162
+ /**
163
+ * Localized strings for user-facing copy
164
+ */
165
+ locale?: DsBulkActionsLocale;
166
+
167
+ className?: string;
168
+ style?: CSSProperties;
169
+ ref?: Ref<HTMLDivElement>;
170
+ }