@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,234 @@
1
+ import { type ReactNode, useState } from 'react';
2
+ import type { CellContext, ColumnDef } from '@tanstack/react-table';
3
+ import type { ChipItem } from '../../../ds-chip-group';
4
+ import type {
5
+ AnyAdapter,
6
+ ColumnFilterState,
7
+ FilterAdapter,
8
+ FilterNavItem,
9
+ FilterState,
10
+ } from '../types/filter-adapter.types';
11
+
12
+ export interface UseTableFiltersResult<TData, TValue> {
13
+ /**
14
+ * Current filter state
15
+ */
16
+ filterState: FilterState<TValue>;
17
+
18
+ /**
19
+ * Column filters for TanStack Table
20
+ */
21
+ columnFilters: ColumnFilterState<TValue>[];
22
+
23
+ /**
24
+ * Filter chips for display
25
+ */
26
+ filterChips: ChipItem[];
27
+
28
+ /**
29
+ * Filter navigation items with counts
30
+ */
31
+ filterNavItems: FilterNavItem[];
32
+
33
+ /**
34
+ * Enhanced column definitions with filter functions
35
+ */
36
+ enhancedColumns: ColumnDef<TData>[];
37
+
38
+ /**
39
+ * Handlers
40
+ */
41
+ handlers: {
42
+ /**
43
+ * Update a specific filter's value
44
+ */
45
+ updateFilter: (filterId: string, value: TValue) => void;
46
+
47
+ /**
48
+ * Apply all filters (converts to TanStack format)
49
+ */
50
+ applyFilters: () => void;
51
+
52
+ /**
53
+ * Clear all filters
54
+ */
55
+ clearAll: () => void;
56
+
57
+ /**
58
+ * Delete a specific chip
59
+ */
60
+ deleteChip: (chip: ChipItem) => void;
61
+ };
62
+
63
+ /**
64
+ * Render function for filter modal content
65
+ */
66
+ renderFilterContent: (selectedFilter: FilterNavItem) => ReactNode;
67
+ }
68
+
69
+ /**
70
+ * Hook to orchestrate table filtering with adapters
71
+ * Manages filter state, generates chips, handles column definitions
72
+ *
73
+ * @param filterAdapters Array of filter adapters
74
+ * @param baseColumns Base column definitions (optional)
75
+ * @returns Complete filter management system
76
+ */
77
+ export function useTableFilters<TData, TValue, TCellValue>(
78
+ filterAdapters: FilterAdapter<TData, TValue, TCellValue>[] | AnyAdapter[],
79
+ baseColumns?: ColumnDef<TData>[],
80
+ ): UseTableFiltersResult<TData, TValue> {
81
+ // Internal variable just to avoid assignment issues of `any`.
82
+ const _filterAdapters = filterAdapters as FilterAdapter<TData, TValue, TCellValue>[];
83
+
84
+ // Initialize filter state from adapters
85
+ const initialState: FilterState<TValue> = {};
86
+ _filterAdapters.forEach((adapter) => {
87
+ initialState[adapter.id] = adapter.initialValue;
88
+ });
89
+
90
+ const [filterState, setFilterState] = useState<FilterState<TValue>>(initialState);
91
+ const [columnFilters, setColumnFilters] = useState<ColumnFilterState<TValue>[]>([]);
92
+ const [filterChips, setFilterChips] = useState<ChipItem[]>([]);
93
+
94
+ // Generate filter nav items with counts (updates as user changes filters in modal)
95
+ const filterNavItems: FilterNavItem[] = _filterAdapters.map((adapter) => ({
96
+ id: adapter.id,
97
+ label: adapter.label,
98
+ count: adapter.getActiveFiltersCount(filterState[adapter.id]),
99
+ }));
100
+
101
+ // Enhance column definitions with filter functions and renderers
102
+ const enhancedColumns: ColumnDef<TData>[] = !baseColumns
103
+ ? []
104
+ : baseColumns.map((col) => {
105
+ const adapter = _filterAdapters.find((a) => a.id === col.id);
106
+
107
+ if (adapter) {
108
+ const cellRenderer = adapter.cellRenderer;
109
+ return {
110
+ ...col,
111
+ filterFn: adapter.columnFilterFn,
112
+ ...(cellRenderer && {
113
+ cell: (info: CellContext<TData, TCellValue>) => cellRenderer(info.getValue()),
114
+ }),
115
+ };
116
+ }
117
+
118
+ return col;
119
+ });
120
+
121
+ // Handlers
122
+ const updateFilter = (filterId: string, value: TValue) => {
123
+ setFilterState((prev) => ({
124
+ ...prev,
125
+ [filterId]: value,
126
+ }));
127
+ };
128
+
129
+ const applyFilters = () => {
130
+ const filters: ColumnFilterState<TValue>[] = [];
131
+ const chips: ChipItem[] = [];
132
+
133
+ _filterAdapters.forEach((adapter) => {
134
+ const value = filterState[adapter.id];
135
+
136
+ if (adapter.getActiveFiltersCount(value) > 0) {
137
+ filters.push({
138
+ id: adapter.id,
139
+ value,
140
+ });
141
+ }
142
+
143
+ // Generate chips from current filter value
144
+ const adapterChips = adapter.toChips(value);
145
+ chips.push(...adapterChips);
146
+ });
147
+
148
+ setColumnFilters(filters);
149
+ setFilterChips(chips);
150
+ };
151
+
152
+ const clearAll = () => {
153
+ const resetState: FilterState<TValue> = {};
154
+ _filterAdapters.forEach((adapter) => {
155
+ resetState[adapter.id] = adapter.reset();
156
+ });
157
+ setFilterState(resetState);
158
+ setColumnFilters([]);
159
+ setFilterChips([]);
160
+ };
161
+
162
+ const deleteChip = (chip: ChipItem) => {
163
+ const filterKey = typeof chip.metadata?.key === 'string' ? chip.metadata.key : undefined;
164
+ if (!filterKey) {
165
+ return;
166
+ }
167
+
168
+ const adapter = _filterAdapters.find((a) => a.id === filterKey);
169
+ if (!adapter) {
170
+ return;
171
+ }
172
+
173
+ const currentValue = filterState[filterKey];
174
+ const newValue = adapter.fromChip(chip, currentValue);
175
+
176
+ // Regenerate chips to check if this was the last one
177
+ const chips: ChipItem[] = [];
178
+ _filterAdapters.forEach((adapter) => {
179
+ const value = adapter.id === filterKey ? newValue : filterState[adapter.id];
180
+ const adapterChips = adapter.toChips(value);
181
+ chips.push(...adapterChips);
182
+ });
183
+
184
+ // If no chips left, clear all filters
185
+ if (chips.length === 0) {
186
+ clearAll();
187
+ return;
188
+ }
189
+
190
+ // Otherwise, update state and filters
191
+ setFilterState((prev) => ({
192
+ ...prev,
193
+ [filterKey]: newValue,
194
+ }));
195
+
196
+ // Update column filters
197
+ if (adapter.getActiveFiltersCount(newValue) === 0) {
198
+ setColumnFilters((prev) => prev.filter((cf) => cf.id !== filterKey));
199
+ } else {
200
+ setColumnFilters((prev) =>
201
+ prev.map((cf) => (cf.id === filterKey ? { id: filterKey, value: newValue } : cf)),
202
+ );
203
+ }
204
+
205
+ setFilterChips(chips);
206
+ };
207
+
208
+ const renderFilterContent = (selectedFilter: FilterNavItem) => {
209
+ const adapter = _filterAdapters.find((a) => a.id === selectedFilter.id);
210
+ if (!adapter) {
211
+ return null;
212
+ }
213
+
214
+ const value = filterState[adapter.id];
215
+ const onChange = (newValue: TValue) => updateFilter(adapter.id, newValue);
216
+
217
+ return adapter.renderFilter(value, onChange);
218
+ };
219
+
220
+ return {
221
+ filterState,
222
+ columnFilters,
223
+ filterChips,
224
+ filterNavItems,
225
+ enhancedColumns,
226
+ handlers: {
227
+ updateFilter,
228
+ applyFilters,
229
+ clearAll,
230
+ deleteChip,
231
+ },
232
+ renderFilterContent,
233
+ };
234
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Table Filters - Filter Adapter Pattern
3
+ *
4
+ * A plug-and-play filter system that eliminates boilerplate and centralizes filter logic.
5
+ *
6
+ * ## Quick Start:
7
+ *
8
+ * ```typescript
9
+ * // 1. Create filter adapters in a config file
10
+ * import { createCheckboxFilterAdapter, createDualRangeFilterAdapter } from '../filters';
11
+ *
12
+ * export const statusFilter = createCheckboxFilterAdapter({
13
+ * id: 'status',
14
+ * label: 'Status',
15
+ * items: [{ value: 'active', label: 'Active' }],
16
+ * });
17
+ *
18
+ * export const myFilters = [statusFilter];
19
+ *
20
+ * // 2. Use in component with useTableFilters hook
21
+ * import { useTableFilters } from '../filters';
22
+ *
23
+ * const { columnFilters, filterChips, enhancedColumns, handlers } =
24
+ * useTableFilters(myFilters, columns);
25
+ * ```
26
+ *
27
+ * ## Available Exports:
28
+ *
29
+ * ### Factory Functions:
30
+ * - `createCheckboxFilterAdapter` - Multi-select checkbox filters
31
+ * - `createDualRangeFilterAdapter` - Numeric range filters (with multiple fields)
32
+ * - `createFilterAdapter` - Base function for custom filters (full control)
33
+ *
34
+ * ### Hook:
35
+ * - `useTableFilters` - Orchestrates multiple filters, manages state
36
+ *
37
+ * ### Types:
38
+ * - `FilterAdapter` - Core adapter interface
39
+ * - `CheckboxFilterItem` - Item type for checkbox filters
40
+ * - `RangeFilterValue` - Value type for range filters
41
+ *
42
+ * ## Example:
43
+ * See `stories/filters-panel.stories.tsx` and `stories/filters-panel/workflow-filters.config.tsx`
44
+ */
45
+
46
+ export * from './adapters';
47
+ export * from './types/filter-adapter.types';
48
+ export * from './hooks';
49
+ export type { CheckboxFilterItem, CheckboxFilterProps } from './components/checkbox-filter';
50
+ export type { RangeFilterValue, RangeFilterProps } from './components/range-filter';
@@ -0,0 +1,111 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Row } from '@tanstack/react-table';
3
+ import type { ChipItem } from '../../../ds-chip-group';
4
+
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ export type AnyAdapter = FilterAdapter<any, any, any>;
7
+
8
+ /**
9
+ * Base filter adapter interface that all filters must implement
10
+ * This provides a consistent contract for filter behavior
11
+ *
12
+ * @template TData - The row data type
13
+ * @template TFilterValue - The filter state value type
14
+ * @template TCellValue - The actual cell value type from the table (defaults to unknown)
15
+ */
16
+ export interface FilterAdapter<TData, TFilterValue, TCellValue = unknown> {
17
+ /**
18
+ * Unique identifier for the filter (should match column accessorKey)
19
+ */
20
+ id: string;
21
+
22
+ /**
23
+ * Display label for the filter in the navigation
24
+ */
25
+ label: string;
26
+
27
+ /**
28
+ * Initial/default value for the filter state
29
+ */
30
+ initialValue: TFilterValue;
31
+
32
+ /**
33
+ * TanStack Table filter function
34
+ * Determines if a row matches the current filter value
35
+ */
36
+ columnFilterFn: (row: Row<TData>, columnId: string, filterValue: TFilterValue) => boolean;
37
+
38
+ /**
39
+ * Optional custom cell renderer for the table column
40
+ * Receives the actual cell value from the table, not the filter value
41
+ * If not provided, default rendering will be used
42
+ */
43
+ cellRenderer?: (value: TCellValue) => ReactNode;
44
+
45
+ /**
46
+ * Convert filter value to filter chips for display
47
+ * Returns empty array if no chips should be shown
48
+ */
49
+ toChips: (value: TFilterValue) => ChipItem[];
50
+
51
+ /**
52
+ * Remove a chip from the filter value
53
+ * Returns updated filter value with the chip's effect removed
54
+ */
55
+ fromChip: (chip: ChipItem, currentValue: TFilterValue) => TFilterValue;
56
+
57
+ /**
58
+ * Calculate how many active filters are applied
59
+ * Used for the count indicator in filter navigation
60
+ * Can also be used to check if filter is active (count > 0)
61
+ */
62
+ getActiveFiltersCount: (value: TFilterValue) => number;
63
+
64
+ /**
65
+ * Reset filter to initial state
66
+ */
67
+ reset: () => TFilterValue;
68
+
69
+ /**
70
+ * Render the filter UI component
71
+ * Receives current value and onChange callback
72
+ */
73
+ renderFilter: (value: TFilterValue, onChange: (value: TFilterValue) => void) => ReactNode;
74
+ }
75
+
76
+ /**
77
+ * Filter navigation item used to render filter tabs in the UI
78
+ */
79
+ export interface FilterNavItem {
80
+ /**
81
+ * Unique identifier for the filter (matches FilterAdapter.id)
82
+ */
83
+ id: string;
84
+ /**
85
+ * Display label for the filter tab
86
+ */
87
+ label: string;
88
+ /**
89
+ * Optional count badge to display active filters
90
+ */
91
+ count?: number;
92
+ /**
93
+ * Whether this filter is disabled
94
+ */
95
+ disabled?: boolean;
96
+ }
97
+
98
+ /**
99
+ * Filter state managed by useTableFilters hook
100
+ */
101
+ export interface FilterState<TValue> {
102
+ [filterId: string]: TValue;
103
+ }
104
+
105
+ /**
106
+ * Column filter for TanStack Table
107
+ */
108
+ export interface ColumnFilterState<TValue> {
109
+ id: string;
110
+ value: TValue;
111
+ }
@@ -0,0 +1,107 @@
1
+ import React from 'react';
2
+ import {
3
+ type DragEndEvent,
4
+ type UniqueIdentifier,
5
+ closestCenter,
6
+ DndContext,
7
+ KeyboardSensor,
8
+ MouseSensor,
9
+ TouchSensor,
10
+ useSensor,
11
+ useSensors,
12
+ } from '@dnd-kit/core';
13
+ import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
14
+ import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
15
+
16
+ export interface UseDragAndDropResult {
17
+ DragWrapper: React.ComponentType<React.PropsWithChildren>;
18
+ SortableWrapper: React.ComponentType<React.PropsWithChildren>;
19
+ }
20
+
21
+ export interface UseDragAndDropOptions<T> {
22
+ isDragEnabled: boolean;
23
+ onOrderChange?: (newData: T[]) => void;
24
+ items: T[];
25
+ }
26
+
27
+ type DragWrapperProps = React.PropsWithChildren<{
28
+ onDragEnd: (event: DragEndEvent) => void;
29
+ sensors: ReturnType<typeof useSensors>;
30
+ }>;
31
+
32
+ const DragWrapper = ({ children, onDragEnd, sensors }: DragWrapperProps) => (
33
+ <DndContext
34
+ collisionDetection={closestCenter}
35
+ modifiers={[restrictToVerticalAxis]}
36
+ onDragEnd={onDragEnd}
37
+ sensors={sensors}
38
+ >
39
+ {children}
40
+ </DndContext>
41
+ );
42
+
43
+ type SortableWrapperProps = React.PropsWithChildren<{
44
+ items: UniqueIdentifier[];
45
+ }>;
46
+
47
+ const SortableWrapper = ({ children, items }: SortableWrapperProps) => (
48
+ <SortableContext items={items} strategy={verticalListSortingStrategy}>
49
+ {children}
50
+ </SortableContext>
51
+ );
52
+
53
+ export function useDragAndDrop<T extends { id: string }>({
54
+ isDragEnabled,
55
+ onOrderChange,
56
+ items,
57
+ }: UseDragAndDropOptions<T>): UseDragAndDropResult {
58
+ const dataIds = React.useMemo(() => items.map((row) => row.id), [items]);
59
+
60
+ const sensors = useSensors(
61
+ useSensor(MouseSensor, {}),
62
+ useSensor(TouchSensor, {}),
63
+ useSensor(KeyboardSensor, {}),
64
+ );
65
+
66
+ const handleDragEnd = React.useCallback(
67
+ (event: DragEndEvent) => {
68
+ const { active, over } = event;
69
+ if (active.id !== over?.id) {
70
+ const oldIndex = items.findIndex((row) => row.id === active.id);
71
+ const newIndex = items.findIndex((row) => row.id === over?.id);
72
+ if (oldIndex !== -1 && newIndex !== -1) {
73
+ const newData = arrayMove(items, oldIndex, newIndex);
74
+ onOrderChange?.(newData);
75
+ }
76
+ }
77
+ },
78
+ [items, onOrderChange],
79
+ );
80
+
81
+ const DragWrapperComponent = React.useMemo(
82
+ () =>
83
+ isDragEnabled
84
+ ? ({ children }: React.PropsWithChildren) => (
85
+ <DragWrapper onDragEnd={handleDragEnd} sensors={sensors}>
86
+ {children}
87
+ </DragWrapper>
88
+ )
89
+ : React.Fragment,
90
+ [isDragEnabled, handleDragEnd, sensors],
91
+ );
92
+
93
+ const SortableWrapperComponent = React.useMemo(
94
+ () =>
95
+ isDragEnabled
96
+ ? ({ children }: React.PropsWithChildren) => (
97
+ <SortableWrapper items={dataIds}>{children}</SortableWrapper>
98
+ )
99
+ : React.Fragment,
100
+ [isDragEnabled, dataIds],
101
+ );
102
+
103
+ return {
104
+ DragWrapper: DragWrapperComponent,
105
+ SortableWrapper: SortableWrapperComponent,
106
+ };
107
+ }
@@ -0,0 +1,4 @@
1
+ export type { SecondaryRowAction, RowAction } from './components/ds-table-cell';
2
+
3
+ export { default as DsTable } from './ds-table';
4
+ export * from './ds-table.types';
@@ -0,0 +1,17 @@
1
+ $border-radius: 4px;
2
+ $border-width: 1px;
3
+ $virtualized-height: 600px;
4
+ $expand-column-width: 40px;
5
+ $select-column-width: 40px;
6
+ $reorder-column-width: 60px;
7
+ $container-margin-vertical: 1rem;
8
+ $filter-input-gap: 0.5rem;
9
+ $filter-input-max-width: 24rem;
10
+ $header-sort-gap: 0.5rem;
11
+ $empty-state-height: 6rem;
12
+
13
+ $hover-bg-color: var(--color-background-secondary);
14
+ $hover-border-color: var(--color-border);
15
+ $expanded-row-bg-color: var(--color-background-secondary);
16
+ $expand-toggle-hover-bg-color: var(--color-background-action-hover-weak);
17
+ $expand-toggle-icon-color: var(--color-icon-information-main);
@@ -0,0 +1,16 @@
1
+ @use '../../../../styles/utilities' as utils;
2
+ @use '../variables' as vars;
3
+
4
+ .checkboxContainer {
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ width: fit-content;
9
+ margin: auto;
10
+ height: 100%;
11
+ }
12
+
13
+ .pageButtonIcon {
14
+ color: vars.$expand-toggle-icon-color;
15
+ @extend .rotate;
16
+ }
@@ -0,0 +1,30 @@
1
+ import type { CSSProperties } from 'react';
2
+ import { defaultColumnSizing } from '@tanstack/react-table';
3
+
4
+ /**
5
+ * Generates the appropriate style object for a table column/cell based on its size and virtualization state
6
+ *
7
+ * @param columnSize - The size of the column
8
+ * @param virtualized - Whether the table is virtualized
9
+ * @param isLastColumn - Whether this is the last column in the row
10
+ * @returns Style object or undefined if no custom styling is needed
11
+ */
12
+ export const getColumnStyle = (
13
+ columnSize: number,
14
+ virtualized?: boolean,
15
+ isLastColumn: boolean = false,
16
+ ): CSSProperties | undefined => {
17
+ const hasCustomSize = columnSize !== defaultColumnSizing.size;
18
+ if (!hasCustomSize) {
19
+ return undefined;
20
+ }
21
+
22
+ if (virtualized) {
23
+ return {
24
+ flexBasis: `${String(columnSize)}px`,
25
+ flexGrow: isLastColumn ? 1 : 0,
26
+ };
27
+ }
28
+
29
+ return { width: columnSize };
30
+ };
@@ -0,0 +1,23 @@
1
+ import type { ColumnDef } from '@tanstack/react-table';
2
+
3
+ type Args = {
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ columns: ColumnDef<any, any>[];
6
+ selectable?: boolean;
7
+ };
8
+
9
+ // In virtualized tables, the rows should all be positioned at the top with a `translateY` offset.
10
+ // We're using a Grid for this rather than `position: absolute` to ensure that each cell aligns with
11
+ // other cells in the same column (mimicking the native table behavior).
12
+ export function createColumnsGridTemplate({ columns, selectable }: Args): string {
13
+ return columns
14
+ .reduce<string[]>(
15
+ (acc, col) => {
16
+ acc.push(col.size ? `${String(col.size)}px` : 'auto');
17
+
18
+ return acc;
19
+ },
20
+ selectable ? ['max-content'] : [],
21
+ )
22
+ .join(' ');
23
+ }
@@ -0,0 +1,15 @@
1
+ import type { Ref, RefCallback } from 'react';
2
+
3
+ export const mergeRefs = <T>(...refs: Array<Ref<T> | undefined>): RefCallback<T> => {
4
+ return (value) => {
5
+ refs
6
+ .filter((r) => !!r)
7
+ .forEach((ref) => {
8
+ if (typeof ref === 'function') {
9
+ ref(value);
10
+ } else {
11
+ ref.current = value;
12
+ }
13
+ });
14
+ };
15
+ };
@@ -0,0 +1,91 @@
1
+ import type { SortingState } from '@tanstack/react-table';
2
+ import type { Status } from '../ds-table.stories';
3
+
4
+ export type Person = {
5
+ id: string;
6
+ firstName: string;
7
+ lastName: string;
8
+ age: number;
9
+ visits: number;
10
+ status: Status;
11
+ progress: number;
12
+ };
13
+
14
+ export interface GeneratedDataResult {
15
+ data: Person[];
16
+ meta: {
17
+ totalRowCount: number;
18
+ };
19
+ }
20
+
21
+ /**
22
+ * Generates mock data for table stories with pagination support
23
+ */
24
+ export const generatePersonData = (
25
+ start: number,
26
+ size: number,
27
+ sorting: SortingState,
28
+ totalRows: number = 10_000,
29
+ ): GeneratedDataResult => {
30
+ const firstNames = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack'];
31
+ const lastNames = [
32
+ 'Smith',
33
+ 'Johnson',
34
+ 'Williams',
35
+ 'Brown',
36
+ 'Jones',
37
+ 'Garcia',
38
+ 'Miller',
39
+ 'Davis',
40
+ 'Rodriguez',
41
+ 'Martinez',
42
+ ];
43
+ const statuses: Status[] = ['single', 'relationship', 'complicated'];
44
+
45
+ // Generate all data first for sorting
46
+ const allData: Person[] = Array.from({ length: totalRows }).map((_, index) => {
47
+ const i = index + 1;
48
+ return {
49
+ id: i.toString(),
50
+ firstName: firstNames[i % firstNames.length],
51
+ lastName: lastNames[i % lastNames.length],
52
+ age: 20 + (i % 50),
53
+ visits: Math.floor(Math.random() * 500) + 1,
54
+ status: statuses[i % statuses.length],
55
+ progress: Math.floor(Math.random() * 100) + 1,
56
+ };
57
+ });
58
+
59
+ // Apply sorting if provided
60
+ const sortedData = [...allData];
61
+ if (sorting.length) {
62
+ const sort = sorting[0];
63
+ const { id, desc } = sort;
64
+ sortedData.sort((a, b) => {
65
+ const aValue = a[id as keyof Person];
66
+ const bValue = b[id as keyof Person];
67
+ if (desc) {
68
+ return aValue < bValue ? 1 : -1;
69
+ }
70
+ return aValue > bValue ? 1 : -1;
71
+ });
72
+ }
73
+
74
+ // Return paginated slice
75
+ return {
76
+ data: sortedData.slice(start, start + size),
77
+ meta: {
78
+ totalRowCount: sortedData.length,
79
+ },
80
+ };
81
+ };
82
+
83
+ /**
84
+ * Simulates an API call with network delay
85
+ */
86
+ export const simulateApiCall = async <T>(dataGenerator: () => T, delayMs: number = 200): Promise<T> => {
87
+ await delay(delayMs);
88
+ return dataGenerator();
89
+ };
90
+
91
+ const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));