@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,312 @@
1
+ import * as React from 'react';
2
+ import { useImperativeHandle } from 'react';
3
+ import {
4
+ type RowSelectionState,
5
+ type ColumnFiltersState,
6
+ getCoreRowModel,
7
+ getFilteredRowModel,
8
+ getSortedRowModel,
9
+ type SortingState,
10
+ useReactTable,
11
+ type VisibilityState,
12
+ } from '@tanstack/react-table';
13
+ import { useVirtualizer, type VirtualItem } from '@tanstack/react-virtual';
14
+ import classnames from 'classnames';
15
+ import { Table, TableBody, TableCell, TableRow } from './components/core-table';
16
+ import { DsTableBulkActions } from './components/ds-table-bulk-actions';
17
+ import { DsTableHeader } from './components/ds-table-header';
18
+ import styles from './ds-table.module.scss';
19
+ import type { DsDataTableProps, DsTableRowSize } from './ds-table.types';
20
+ import { DsTableRow } from './components/ds-table-row';
21
+ import { useDragAndDrop } from './hooks/use-drag-and-drop';
22
+ import { type DsTableContextType, DsTableContext } from './context/ds-table-context';
23
+ import { createColumnsGridTemplate } from './utils/create-columns-grid-template';
24
+
25
+ // Row size to pixel height mapping (matches CSS variables)
26
+ const ROW_SIZE_HEIGHT_MAP: Record<DsTableRowSize, number> = {
27
+ small: 36,
28
+ medium: 48,
29
+ large: 64,
30
+ };
31
+
32
+ /**
33
+ * Design system Table component
34
+ */
35
+ const DsTable = <TData extends { id: string }, TValue>({
36
+ ref,
37
+ columns,
38
+ data: tableData,
39
+ virtualized = false,
40
+ virtualizedOptions,
41
+ className,
42
+ onRowClick,
43
+ emptyState,
44
+ stickyHeader = true,
45
+ bordered = true,
46
+ fullWidth = true,
47
+ highlightOnHover = true,
48
+ rowSize = 'medium',
49
+ expandable = false,
50
+ renderExpandedRow,
51
+ selectable = false,
52
+ showSelectAllCheckbox = true,
53
+ onSelectionChange,
54
+ onSortingChange,
55
+ onScroll,
56
+ actions = [],
57
+ onRowDoubleClick,
58
+ primaryRowActions = [],
59
+ secondaryRowActions,
60
+ reorderable = false,
61
+ onOrderChange,
62
+ columnFilters: externalColumnFilters,
63
+ onColumnFiltersChange,
64
+ columnVisibility: externalColumnVisibility,
65
+ onColumnVisibilityChange,
66
+ activeRowId,
67
+ }: DsDataTableProps<TData, TValue>) => {
68
+ const [data, setData] = React.useState(tableData);
69
+ const [sorting, setSorting] = React.useState<SortingState>([]);
70
+ const [internalColumnFilters, setInternalColumnFilters] = React.useState<ColumnFiltersState>([]);
71
+ const [internalColumnVisibility, setInternalColumnVisibility] = React.useState<VisibilityState>({});
72
+ const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});
73
+ const [expandedRows, setExpandedRows] = React.useState<Record<string, boolean>>({});
74
+
75
+ const { DragWrapper, SortableWrapper } = useDragAndDrop<TData>({
76
+ isDragEnabled: reorderable && !virtualized,
77
+ onOrderChange: (newData) => {
78
+ setData(newData);
79
+ onOrderChange?.(newData);
80
+ },
81
+ items: data,
82
+ });
83
+
84
+ const tableContainerRef = React.useRef<HTMLDivElement>(null);
85
+
86
+ const columnFilters = externalColumnFilters ?? internalColumnFilters;
87
+ const handleColumnFiltersChange = (
88
+ updaterOrValue: ColumnFiltersState | ((old: ColumnFiltersState) => ColumnFiltersState),
89
+ ) => {
90
+ const newFilters = typeof updaterOrValue === 'function' ? updaterOrValue(columnFilters) : updaterOrValue;
91
+ if (onColumnFiltersChange) {
92
+ onColumnFiltersChange(newFilters);
93
+ } else {
94
+ setInternalColumnFilters(newFilters);
95
+ }
96
+ };
97
+
98
+ const columnVisibility = externalColumnVisibility ?? internalColumnVisibility;
99
+ const handleColumnVisibilityChange = (
100
+ updaterOrValue: VisibilityState | ((old: VisibilityState) => VisibilityState),
101
+ ) => {
102
+ const newVisibility =
103
+ typeof updaterOrValue === 'function' ? updaterOrValue(columnVisibility) : updaterOrValue;
104
+
105
+ if (onColumnVisibilityChange) {
106
+ onColumnVisibilityChange(newVisibility);
107
+ } else {
108
+ setInternalColumnVisibility(newVisibility);
109
+ }
110
+ };
111
+
112
+ const handleSortingChange = (updaterOrValue: SortingState | ((old: SortingState) => SortingState)) => {
113
+ const newSorting = typeof updaterOrValue === 'function' ? updaterOrValue(sorting) : updaterOrValue;
114
+ setSorting(newSorting);
115
+ onSortingChange?.(newSorting);
116
+ };
117
+
118
+ const handleRowSelectionChange = (
119
+ updaterOrValue: RowSelectionState | ((old: RowSelectionState) => RowSelectionState),
120
+ ) => {
121
+ const newRowSelection =
122
+ typeof updaterOrValue === 'function' ? updaterOrValue(rowSelection) : updaterOrValue;
123
+ setRowSelection(newRowSelection);
124
+ onSelectionChange?.(newRowSelection);
125
+ };
126
+
127
+ const table = useReactTable({
128
+ data: reorderable ? data : tableData,
129
+ columns,
130
+ getCoreRowModel: getCoreRowModel(),
131
+ onSortingChange: handleSortingChange,
132
+ getSortedRowModel: getSortedRowModel(),
133
+ onColumnFiltersChange: handleColumnFiltersChange,
134
+ getFilteredRowModel: getFilteredRowModel(),
135
+ onColumnVisibilityChange: handleColumnVisibilityChange,
136
+ onRowSelectionChange: handleRowSelectionChange,
137
+ getRowId: (row) => row.id,
138
+ state: {
139
+ sorting,
140
+ columnFilters,
141
+ columnVisibility,
142
+ rowSelection,
143
+ },
144
+ enableRowSelection: typeof selectable === 'function' ? (row) => selectable(row.original) : selectable,
145
+ });
146
+
147
+ useImperativeHandle(
148
+ ref,
149
+ () => ({
150
+ selectRow: (rowId: string) => {
151
+ table.getRow(rowId).toggleSelected(true);
152
+ },
153
+ deselectRow: (rowId: string) => {
154
+ table.getRow(rowId).toggleSelected(false);
155
+ },
156
+ selectAllRows: () => {
157
+ table.toggleAllRowsSelected(true);
158
+ },
159
+ deselectAllRows: () => {
160
+ table.toggleAllRowsSelected(false);
161
+ },
162
+ selectRows: (rowIds: string[]) => {
163
+ const selection: Record<string, boolean> = {};
164
+ rowIds.forEach((id) => (selection[id] = true));
165
+ table.setRowSelection(selection);
166
+ },
167
+ getSelectedRows: () => {
168
+ return table.getFilteredSelectedRowModel().rows.map((r) => r.original);
169
+ },
170
+ }),
171
+ [table],
172
+ );
173
+
174
+ const { rows, rowsById } = table.getRowModel();
175
+
176
+ const rowVirtualizer = useVirtualizer({
177
+ count: rows.length,
178
+ getScrollElement: () => tableContainerRef.current,
179
+ // estimate row height for accurate scrollbar dragging
180
+ estimateSize: () => virtualizedOptions?.estimateSize || ROW_SIZE_HEIGHT_MAP[rowSize],
181
+ overscan: virtualizedOptions?.overscan || 5,
182
+ onChange: (instance, sync) => {
183
+ if (sync && onScroll) {
184
+ const scrollOffset = instance.scrollOffset || 0;
185
+ const totalContentHeight = instance.getTotalSize();
186
+ const viewportHeight = instance.scrollElement?.clientHeight;
187
+
188
+ if (viewportHeight) {
189
+ onScroll({ scrollOffset, totalContentHeight, viewportHeight });
190
+ }
191
+ }
192
+ },
193
+ });
194
+
195
+ const toggleRowExpanded = (rowId: string) => {
196
+ setExpandedRows((prev) => ({
197
+ ...prev,
198
+ [rowId]: !prev[rowId],
199
+ }));
200
+ };
201
+
202
+ const renderEmptyState = () =>
203
+ virtualized ? (
204
+ <div className={styles.emptyStateVirtualized}>{emptyState || 'No results.'}</div>
205
+ ) : (
206
+ <TableRow>
207
+ <TableCell
208
+ colSpan={columns.length + (expandable ? 1 : 0) + (selectable ? 1 : 0)}
209
+ className={styles.emptyState}
210
+ >
211
+ {emptyState || 'No results.'}
212
+ </TableCell>
213
+ </TableRow>
214
+ );
215
+
216
+ const selectedRows = Object.entries(rowSelection)
217
+ .filter(([, selected]) => selected)
218
+ // This is not necessarily a redundant condition. It depends on `noUncheckedIndexedAccess`.
219
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
220
+ .map(([key]) => rowsById[key]?.original)
221
+ .filter(Boolean);
222
+
223
+ const contextValue: DsTableContextType<TData, TValue> = {
224
+ stickyHeader,
225
+ bordered,
226
+ fullWidth,
227
+ highlightOnHover,
228
+ rowSize,
229
+ expandable,
230
+ selectable,
231
+ reorderable,
232
+ showSelectAllCheckbox,
233
+ onRowClick,
234
+ onRowDoubleClick,
235
+ primaryRowActions,
236
+ secondaryRowActions,
237
+ renderExpandedRow,
238
+ virtualized,
239
+ expandedRows,
240
+ toggleRowExpanded,
241
+ activeRowId,
242
+ };
243
+
244
+ return (
245
+ <DsTableContext.Provider value={contextValue}>
246
+ <div
247
+ ref={tableContainerRef}
248
+ className={classnames(
249
+ styles.container,
250
+ !virtualized && styles.dataTableContainer,
251
+ virtualized && styles.virtualizedContainer,
252
+ className,
253
+ )}
254
+ >
255
+ <DragWrapper>
256
+ <Table
257
+ style={
258
+ virtualized
259
+ ? ({
260
+ '--ds-table-columns-template': createColumnsGridTemplate({
261
+ columns,
262
+ selectable: !!selectable,
263
+ }),
264
+ } as React.CSSProperties)
265
+ : undefined
266
+ }
267
+ className={classnames(
268
+ fullWidth && styles.fullWidth,
269
+ !bordered && styles.tableNoBorder,
270
+ virtualized && styles.virtualized,
271
+ )}
272
+ >
273
+ <DsTableHeader table={table} />
274
+ <TableBody
275
+ style={{ height: virtualized ? `${String(rowVirtualizer.getTotalSize())}px` : undefined }}
276
+ className={classnames(virtualized && styles.virtualizedBody)}
277
+ >
278
+ {virtualized ? (
279
+ rowVirtualizer.getVirtualItems().length ? (
280
+ rowVirtualizer.getVirtualItems().map((virtualRow: VirtualItem) => {
281
+ const row = rows[virtualRow.index];
282
+ return <DsTableRow key={row.id} row={row} virtualRow={virtualRow} />;
283
+ })
284
+ ) : (
285
+ renderEmptyState()
286
+ )
287
+ ) : (
288
+ <SortableWrapper>
289
+ {rows.length
290
+ ? rows.map((row) => <DsTableRow key={row.id} row={row} />)
291
+ : renderEmptyState()}
292
+ </SortableWrapper>
293
+ )}
294
+ </TableBody>
295
+ </Table>
296
+ </DragWrapper>
297
+ </div>
298
+ {selectable && actions.length > 0 && (
299
+ <DsTableBulkActions
300
+ numSelectedRows={selectedRows.length}
301
+ actions={actions.map((action) => ({
302
+ ...action,
303
+ onClick: () => action.onClick(selectedRows),
304
+ }))}
305
+ onClearSelection={table.resetRowSelection}
306
+ />
307
+ )}
308
+ </DsTableContext.Provider>
309
+ );
310
+ };
311
+
312
+ export default DsTable;
@@ -0,0 +1,377 @@
1
+ import type React from 'react';
2
+ import type {
3
+ ColumnDef,
4
+ ColumnFiltersState,
5
+ SortingState,
6
+ Table,
7
+ VisibilityState,
8
+ } from '@tanstack/react-table';
9
+ import type { IconType } from '../ds-icon';
10
+ import type { RowAction, SecondaryRowAction } from './components/ds-table-cell';
11
+
12
+ /**
13
+ * Row size variants based on Figma design specifications
14
+ */
15
+ export type DsTableRowSize = 'small' | 'medium' | 'large';
16
+
17
+ /**
18
+ * Parameters passed to the onScroll callback in virtualized tables.
19
+ */
20
+ export interface ScrollParams {
21
+ /** The current vertical scroll position from the top of the scrollable content (in pixels) */
22
+ scrollOffset: number;
23
+ /** The total height of all content in the virtualized table (in pixels) */
24
+ totalContentHeight: number;
25
+ /** The visible height of the table container (in pixels) */
26
+ viewportHeight: number;
27
+ }
28
+
29
+ /**
30
+ * API for programmatically controlling a DsTable component.
31
+ * This interface provides methods to interact with the table's selection, sorting, filtering, and pagination features.
32
+ *
33
+ * @template TData - The type of data items in the table
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * const tableRef = useRef<DsTableApi<Person>>(null);
38
+ *
39
+ * // Select a specific row
40
+ * tableRef.current?.selectRow('row-1');
41
+ *
42
+ * // Select multiple rows
43
+ * tableRef.current?.selectRows(['row-1', 'row-2', 'row-3']);
44
+ *
45
+ * // Get all selected rows
46
+ * const selected = tableRef.current?.getSelectedRows();
47
+ * ```
48
+ */
49
+ export interface DsTableApi<TData> {
50
+ /**
51
+ * Selects a single row by its ID.
52
+ *
53
+ * @param rowId - The unique identifier of the row to select
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * tableRef.current?.selectRow('user-123');
58
+ * ```
59
+ */
60
+ selectRow: (rowId: string) => void;
61
+
62
+ /**
63
+ * Deselects a single row by its ID.
64
+ *
65
+ * @param rowId - The unique identifier of the row to deselect
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * tableRef.current?.deselectRow('user-123');
70
+ * ```
71
+ */
72
+ deselectRow: (rowId: string) => void;
73
+
74
+ /**
75
+ * Selects all visible rows in the table.
76
+ * This includes rows that are currently filtered or paginated.
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * tableRef.current?.selectAllRows();
81
+ * ```
82
+ */
83
+ selectAllRows: () => void;
84
+
85
+ /**
86
+ * Deselects all rows in the table.
87
+ * Clears the current selection state.
88
+ *
89
+ * @example
90
+ * ```tsx
91
+ * tableRef.current?.deselectAllRows();
92
+ * ```
93
+ */
94
+ deselectAllRows: () => void;
95
+
96
+ /**
97
+ * Selects multiple rows by their IDs.
98
+ *
99
+ * @param rowIds - Array of unique identifiers for the rows to select
100
+ *
101
+ * @example
102
+ * ```tsx
103
+ * tableRef.current?.selectRows(['user-1', 'user-2', 'user-3']);
104
+ * ```
105
+ */
106
+ selectRows: (rowIds: string[]) => void;
107
+
108
+ /**
109
+ * Returns an array of all currently selected row data.
110
+ *
111
+ * @returns Array of selected row data objects
112
+ *
113
+ * @example
114
+ * ```tsx
115
+ * const selectedUsers = tableRef.current?.getSelectedRows();
116
+ * console.log('Selected users:', selectedUsers);
117
+ * ```
118
+ */
119
+ getSelectedRows: () => TData[];
120
+ }
121
+
122
+ /**
123
+ * Represents a bulk action that can be performed on multiple selected rows
124
+ */
125
+ export interface Action<TData> {
126
+ /**
127
+ * Icon to be displayed for the action
128
+ */
129
+ icon: IconType;
130
+
131
+ /**
132
+ * Label text for the action
133
+ */
134
+ label: string;
135
+
136
+ /**
137
+ * Function to be called when the action is clicked, receives the selected rows as parameter
138
+ */
139
+ onClick: (rows: TData[]) => void;
140
+ }
141
+
142
+ export interface DsDataTableProps<TData, TValue> {
143
+ /**
144
+ * Ref to the table API
145
+ */
146
+ ref?: React.RefObject<DsTableApi<TData> | null>;
147
+
148
+ /**
149
+ * Columns of the table
150
+ */
151
+ columns: ColumnDef<TData, TValue>[];
152
+
153
+ /**
154
+ * Data of the table
155
+ */
156
+ data: TData[];
157
+
158
+ /**
159
+ * Whether the table is virtualized
160
+ * @default false
161
+ */
162
+ virtualized?: boolean;
163
+
164
+ /**
165
+ * Options for the virtualized table
166
+ */
167
+ virtualizedOptions?: {
168
+ /**
169
+ * Estimate size of the table
170
+ */
171
+ estimateSize?: number;
172
+
173
+ /**
174
+ * Overscan of the table
175
+ */
176
+ overscan?: number;
177
+ };
178
+
179
+ /**
180
+ * Class name of the table
181
+ */
182
+ className?: string;
183
+
184
+ /**
185
+ * Function to handle row click
186
+ */
187
+ onRowClick?: (row: TData) => void;
188
+
189
+ /**
190
+ * Function to handle row double click
191
+ */
192
+ onRowDoubleClick?: (row: TData) => void;
193
+
194
+ /**
195
+ * ID of the currently active row (useful for showing drawer/panel associations)
196
+ * When set, the row with this ID will be visually highlighted with a persistent active state
197
+ * @example
198
+ * ```tsx
199
+ * const [activeRowId, setActiveRowId] = useState<string | null>(null);
200
+ * const [isDrawerOpen, setIsDrawerOpen] = useState(false);
201
+ *
202
+ * <DsTable
203
+ * activeRowId={isDrawerOpen ? activeRowId : null}
204
+ * onRowClick={(row) => {
205
+ * setActiveRowId(row.id);
206
+ * setIsDrawerOpen(true);
207
+ * }}
208
+ * />
209
+ * ```
210
+ */
211
+ activeRowId?: string | null;
212
+
213
+ /**
214
+ * Empty state of the table
215
+ */
216
+ emptyState?: React.ReactNode;
217
+
218
+ /**
219
+ * Whether the table has sticky header
220
+ */
221
+ stickyHeader?: boolean;
222
+
223
+ /**
224
+ * Whether the table has zebra stripes
225
+ */
226
+ zebra?: boolean;
227
+
228
+ /**
229
+ * Whether the table has bordered cells
230
+ */
231
+ bordered?: boolean;
232
+
233
+ /**
234
+ * Whether the table is full width
235
+ */
236
+ fullWidth?: boolean;
237
+
238
+ /**
239
+ * Whether the table has highlight on hover
240
+ */
241
+ highlightOnHover?: boolean;
242
+
243
+ /**
244
+ * Row size variant (small: 36px, medium: 48px, large: 64px)
245
+ * @default 'medium'
246
+ */
247
+ rowSize?: DsTableRowSize;
248
+
249
+ /**
250
+ * Whether the table is expandable or if an individual row should be expandable
251
+ */
252
+ expandable?: boolean | ((row: TData) => boolean);
253
+
254
+ /**
255
+ * Function to render the expanded row
256
+ */
257
+ renderExpandedRow?: (row: TData) => React.ReactNode;
258
+
259
+ /**
260
+ * Function to handle table creation
261
+ */
262
+ onTableCreated?: (table: Table<TData>) => void;
263
+
264
+ /**
265
+ * Whether the table rows are selectable. Can be a boolean or a function that receives row data and returns whether it can be selected.
266
+ * @default false
267
+ * @example
268
+ * ```tsx
269
+ * // Enable selection for all rows
270
+ * selectable={true}
271
+ *
272
+ * // Disable selection for rows where status is 'archived'
273
+ * selectable={(rowData) => rowData.status !== 'archived'}
274
+ *
275
+ * // Limit selection to max 3 rows (note: use state to track current selection)
276
+ * selectable={(rowData) => {
277
+ * // This will be called for each row during render
278
+ * const selectedIds = Object.keys(rowSelection).filter(id => rowSelection[id]);
279
+ * return selectedIds.includes(rowData.id) || selectedIds.length < 3;
280
+ * }}
281
+ * ```
282
+ */
283
+ selectable?: boolean | ((rowData: TData) => boolean);
284
+
285
+ /**
286
+ * Whether to show the select/deselect all checkbox in the header
287
+ * @default true
288
+ */
289
+ showSelectAllCheckbox?: boolean;
290
+
291
+ /**
292
+ * Function to handle selection change
293
+ */
294
+ onSelectionChange?: (selectedRows: Record<string, boolean>) => void;
295
+
296
+ /**
297
+ * Function to handle sorting change
298
+ */
299
+ onSortingChange?: (sorting: SortingState) => void;
300
+
301
+ /**
302
+ * Function to handle scroll events in virtualized tables.
303
+ * Called when the user scrolls within the table container.
304
+ *
305
+ * @param params - Scroll parameters containing scroll position and dimensions
306
+ *
307
+ * @example
308
+ * ```tsx
309
+ * const handleScroll = ({ scrollOffset, totalContentHeight, viewportHeight }) => {
310
+ * const distanceFromBottom = totalContentHeight - scrollOffset - viewportHeight;
311
+ * if (distanceFromBottom < 500) {
312
+ * // Fetch more data when within 500px of bottom
313
+ * fetchNextPage();
314
+ * }
315
+ * };
316
+ * ```
317
+ */
318
+ onScroll?: (params: ScrollParams) => void;
319
+
320
+ /**
321
+ * Actions to be shown in the bulk actions
322
+ */
323
+ actions?: Action<TData>[];
324
+
325
+ /**
326
+ * Primary actions to be shown on each row (on hover)
327
+ */
328
+ primaryRowActions?: RowAction<TData>[];
329
+
330
+ /**
331
+ * Secondary actions to be shown in a dropdown on each row (on hover)
332
+ */
333
+ secondaryRowActions?: SecondaryRowAction<TData>[];
334
+
335
+ /**
336
+ * Whether the table rows are reorderable via drag & drop
337
+ * @note This feature does not work when virtualization is enabled
338
+ */
339
+ reorderable?: boolean;
340
+
341
+ /**
342
+ * Callback when the order of rows changes via drag & drop
343
+ */
344
+ onOrderChange?: (newData: TData[]) => void;
345
+
346
+ /**
347
+ * External column filters state
348
+ */
349
+ columnFilters?: ColumnFiltersState;
350
+
351
+ /**
352
+ * Callback when column filters change
353
+ */
354
+ onColumnFiltersChange?: (filters: ColumnFiltersState) => void;
355
+
356
+ /**
357
+ * External column visibility state
358
+ * @example
359
+ * ```tsx
360
+ * const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
361
+ * age: false, // hide age column
362
+ * status: true, // show status column
363
+ * });
364
+ *
365
+ * <DsTable
366
+ * columnVisibility={columnVisibility}
367
+ * onColumnVisibilityChange={setColumnVisibility}
368
+ * />
369
+ * ```
370
+ */
371
+ columnVisibility?: VisibilityState;
372
+
373
+ /**
374
+ * Callback when column visibility changes
375
+ */
376
+ onColumnVisibilityChange?: (visibility: VisibilityState) => void;
377
+ }