@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,116 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Row } from '@tanstack/react-table';
3
+ import type { FilterAdapter } from '../types/filter-adapter.types';
4
+ import { type CheckboxFilterItem, CheckboxFilter } from '../components/checkbox-filter';
5
+ import { createFilterAdapter } from './create-filter-adapter';
6
+
7
+ export interface CheckboxFilterAdapterConfig<TData, TValue> {
8
+ /**
9
+ * Unique identifier (should match column accessorKey)
10
+ */
11
+ id: string;
12
+
13
+ /**
14
+ * Display label for filter navigation
15
+ */
16
+ label: string;
17
+
18
+ /**
19
+ * Available items to select from
20
+ */
21
+ items: CheckboxFilterItem<TValue>[];
22
+
23
+ /**
24
+ * Optional custom renderer for each item
25
+ */
26
+ renderer?: (item: CheckboxFilterItem<TValue>) => ReactNode;
27
+
28
+ /**
29
+ * Optional custom chip label generator
30
+ * @default (item) => `${label}: ${item.label}`
31
+ */
32
+ chipLabelTemplate?: (item: CheckboxFilterItem<TValue>) => string;
33
+
34
+ /**
35
+ * Optional custom cell renderer for table column
36
+ */
37
+ cellRenderer?: (value: TValue) => ReactNode;
38
+
39
+ /**
40
+ * How to extract the value from a row for comparison
41
+ * @default (row) => row.getValue(id)
42
+ */
43
+ getRowValue?: (row: Row<TData>) => TValue;
44
+ }
45
+
46
+ /**
47
+ * Factory function to create a checkbox filter adapter
48
+ *
49
+ * Handles multi-select checkbox filtering with inclusion model:
50
+ * - Empty selection = show all data (no filter active)
51
+ * - Selected items = show only those items
52
+ */
53
+ export function createCheckboxFilterAdapter<TData, TValue = string>(
54
+ config: CheckboxFilterAdapterConfig<TData, TValue>,
55
+ ): FilterAdapter<TData, CheckboxFilterItem<TValue>[], TValue> {
56
+ const {
57
+ id,
58
+ label,
59
+ items,
60
+ renderer,
61
+ chipLabelTemplate = (item) => `${label}: ${item.label}`,
62
+ cellRenderer,
63
+ getRowValue = (row) => row.getValue(id),
64
+ } = config;
65
+
66
+ return createFilterAdapter<TData, CheckboxFilterItem<TValue>[], TValue>({
67
+ id,
68
+ label,
69
+ initialValue: [], // Start with nothing selected (show all)
70
+
71
+ filterFn: (row, columnId, filterValue) => {
72
+ // Empty selection = no filter = show all
73
+ if (filterValue.length === 0) {
74
+ return true;
75
+ }
76
+
77
+ // Otherwise, show only selected items
78
+ const rowValue = getRowValue(row);
79
+ return filterValue.some((item) => item.value === rowValue);
80
+ },
81
+
82
+ cellRenderer,
83
+
84
+ toChips: (selectedItems) => {
85
+ // Generate chips for all selected items
86
+ return selectedItems.map((item) => ({
87
+ id: `${id}_${String(item.value)}`,
88
+ label: chipLabelTemplate(item),
89
+ metadata: {
90
+ key: id,
91
+ value: item.value,
92
+ },
93
+ }));
94
+ },
95
+
96
+ fromChip: (chip, currentValue) => {
97
+ return currentValue.filter((item) => item.value !== chip.metadata?.value);
98
+ },
99
+
100
+ getActiveFiltersCount: (selectedItems) => {
101
+ // Count = number of selected items (0 means none selected)
102
+ return selectedItems.length;
103
+ },
104
+
105
+ renderFilter: (value, onChange) => {
106
+ return (
107
+ <CheckboxFilter
108
+ items={items}
109
+ renderer={renderer}
110
+ selectedItems={value}
111
+ onSelectionChange={onChange}
112
+ />
113
+ );
114
+ },
115
+ });
116
+ }
@@ -0,0 +1,171 @@
1
+ import type { Row } from '@tanstack/react-table';
2
+ import type { ChipItem } from '../../../ds-chip-group';
3
+ import type { FilterAdapter } from '../types/filter-adapter.types';
4
+ import { RangeFilter } from '../components/range-filter';
5
+ import { createFilterAdapter } from './create-filter-adapter';
6
+
7
+ export interface RangeValue {
8
+ from?: number;
9
+ to?: number;
10
+ }
11
+
12
+ export interface DualRangeFilterValue {
13
+ [fieldName: string]: RangeValue;
14
+ }
15
+
16
+ export interface DualRangeFilterAdapterConfig<TData> {
17
+ /**
18
+ * Unique identifier (should match column accessorKey)
19
+ */
20
+ id: string;
21
+
22
+ /**
23
+ * Display label for filter navigation
24
+ */
25
+ label: string;
26
+
27
+ /**
28
+ * Field configurations: { fieldKey: displayLabel }
29
+ * Example: { running: 'Running', completed: 'Completed' }
30
+ */
31
+ fields: Record<string, string>;
32
+
33
+ /**
34
+ * Optional formatter for chip display
35
+ * @default (num) => num.toLocaleString('en-US')
36
+ */
37
+ formatNumber?: (num: number) => string;
38
+
39
+ /**
40
+ * How to extract the value object from a row
41
+ * @default (row) => row.getValue(id)
42
+ */
43
+ getRowValue?: (row: Row<TData>) => Record<string, number>;
44
+ }
45
+
46
+ /**
47
+ * Factory function to create a dual-range filter adapter
48
+ * Handles filtering on multiple numeric ranges within a single column
49
+ */
50
+ export function createDualRangeFilterAdapter<TData>(
51
+ config: DualRangeFilterAdapterConfig<TData>,
52
+ ): FilterAdapter<TData, DualRangeFilterValue, Record<string, number>> {
53
+ const {
54
+ id,
55
+ label,
56
+ fields,
57
+ formatNumber = (num) => num.toLocaleString('en-US'),
58
+ getRowValue = (row) => row.getValue(id),
59
+ } = config;
60
+
61
+ const initialValue: DualRangeFilterValue = {};
62
+ Object.keys(fields).forEach((key) => {
63
+ initialValue[key] = {};
64
+ });
65
+
66
+ return createFilterAdapter<TData, DualRangeFilterValue, Record<string, number>>({
67
+ id,
68
+ label,
69
+ initialValue,
70
+
71
+ filterFn: (row, columnId, filterValue) => {
72
+ const rowValue = getRowValue(row);
73
+
74
+ // Check each field's range
75
+ for (const [fieldKey, range] of Object.entries(filterValue)) {
76
+ const hasFilter = range.from !== undefined || range.to !== undefined;
77
+
78
+ if (hasFilter) {
79
+ const fieldValue = rowValue[fieldKey];
80
+ const matchesFrom = range.from === undefined || fieldValue >= range.from;
81
+ const matchesTo = range.to === undefined || fieldValue <= range.to;
82
+
83
+ if (!matchesFrom || !matchesTo) {
84
+ return false; // AND logic: all ranges must match
85
+ }
86
+ }
87
+ }
88
+
89
+ return true;
90
+ },
91
+
92
+ cellRenderer: undefined, // Let the column definition handle rendering
93
+
94
+ toChips: (value) => {
95
+ const chips: ChipItem[] = [];
96
+
97
+ Object.entries(value).forEach(([fieldKey, range]) => {
98
+ const hasFilter = range.from !== undefined || range.to !== undefined;
99
+
100
+ if (hasFilter) {
101
+ const fieldLabel = fields[fieldKey] || fieldKey;
102
+ const fromText = range.from !== undefined ? formatNumber(range.from) : '';
103
+ const toText = range.to !== undefined ? formatNumber(range.to) : '';
104
+
105
+ chips.push({
106
+ id: `${id}_${fieldKey}`,
107
+ label: `${fieldLabel}: From ${fromText} to ${toText}`,
108
+ metadata: {
109
+ key: id,
110
+ field: fieldKey,
111
+ from: range.from,
112
+ to: range.to,
113
+ },
114
+ });
115
+ }
116
+ });
117
+
118
+ return chips;
119
+ },
120
+
121
+ fromChip: (chip, currentValue) => {
122
+ const fieldKey = chip.metadata?.field as string;
123
+ if (!fieldKey) {
124
+ return currentValue;
125
+ }
126
+
127
+ return {
128
+ ...currentValue,
129
+ [fieldKey]: {},
130
+ };
131
+ },
132
+
133
+ getActiveFiltersCount: (value) => {
134
+ let count = 0;
135
+ Object.values(value).forEach((range) => {
136
+ if (range.from !== undefined || range.to !== undefined) {
137
+ count++;
138
+ }
139
+ });
140
+ return count;
141
+ },
142
+
143
+ renderFilter: (value, onChange) => {
144
+ return (
145
+ <div>
146
+ {Object.entries(fields).map(([fieldKey, fieldLabel]) => (
147
+ <RangeFilter
148
+ key={fieldKey}
149
+ label={fieldLabel}
150
+ // This is not necessarily a redundant condition. It depends on `noUncheckedIndexedAccess`.
151
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
152
+ value={value[fieldKey] || {}}
153
+ onChange={(rangeValue) =>
154
+ onChange({
155
+ ...value,
156
+ [fieldKey]: rangeValue,
157
+ })
158
+ }
159
+ onClear={() =>
160
+ onChange({
161
+ ...value,
162
+ [fieldKey]: {},
163
+ })
164
+ }
165
+ />
166
+ ))}
167
+ </div>
168
+ );
169
+ },
170
+ });
171
+ }
@@ -0,0 +1,131 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { FilterAdapter } from '../types/filter-adapter.types';
3
+
4
+ export interface FilterAdapterConfig<TData, TFilterValue, TCellValue = unknown> {
5
+ /**
6
+ * Unique identifier (should match column accessorKey)
7
+ */
8
+ id: string;
9
+
10
+ /**
11
+ * Display label for filter navigation
12
+ */
13
+ label: string;
14
+
15
+ /**
16
+ * Initial/default value for the filter
17
+ */
18
+ initialValue: TFilterValue;
19
+
20
+ /**
21
+ * Filter function - determines if row matches filter value
22
+ */
23
+ filterFn: FilterAdapter<TData, TFilterValue, TCellValue>['columnFilterFn'];
24
+
25
+ /**
26
+ * Convert filter value to display chips
27
+ */
28
+ toChips: FilterAdapter<TData, TFilterValue, TCellValue>['toChips'];
29
+
30
+ /**
31
+ * Remove chip effect from filter value
32
+ */
33
+ fromChip: FilterAdapter<TData, TFilterValue, TCellValue>['fromChip'];
34
+
35
+ /**
36
+ * Calculate active filter count
37
+ */
38
+ getActiveFiltersCount: FilterAdapter<TData, TFilterValue, TCellValue>['getActiveFiltersCount'];
39
+
40
+ /**
41
+ * Render the filter UI
42
+ */
43
+ renderFilter: (value: TFilterValue, onChange: (value: TFilterValue) => void) => ReactNode;
44
+
45
+ /**
46
+ * Optional custom cell renderer for table cells
47
+ * Note: This receives the cell value from the table, not the filter value
48
+ */
49
+ cellRenderer?: (value: TCellValue) => ReactNode;
50
+ }
51
+
52
+ /**
53
+ * Base factory function to create a filter adapter
54
+ *
55
+ * This is the core function that all specialized filter adapters use internally.
56
+ * Use this when checkbox or dual-range adapters don't fit your requirements.
57
+ *
58
+ * ## When to use directly:
59
+ * - Complex filtering logic (e.g., editor + date range)
60
+ * - Unique UI that doesn't fit generic patterns
61
+ * - Combining multiple sub-filters
62
+ * - Special chip generation requirements
63
+ *
64
+ * ## When to use specialized helpers instead:
65
+ * - **Checkbox filters**: Use `createCheckboxFilterAdapter` for multi-select
66
+ * - **Range filters**: Use `createDualRangeFilterAdapter` for numeric ranges
67
+ *
68
+ * ## Example:
69
+ * ```typescript
70
+ * interface MyFilterValue {
71
+ * users: string[];
72
+ * dateRange: { from?: Date; to?: Date };
73
+ * }
74
+ *
75
+ * const myFilter = createFilterAdapter<DataType, MyFilterValue>({
76
+ * id: 'myColumn',
77
+ * label: 'My Filter',
78
+ * initialValue: { users: [], dateRange: {} },
79
+ *
80
+ * filterFn: (row, columnId, filterValue) => {
81
+ * // Your custom logic
82
+ * return matchesUsers && matchesDateRange;
83
+ * },
84
+ *
85
+ * toChips: (value) => {
86
+ * // Generate chips from your filter value
87
+ * return chips;
88
+ * },
89
+ *
90
+ * fromChip: (chip, currentValue) => {
91
+ * // Remove chip's effect from value
92
+ * return updatedValue;
93
+ * },
94
+ *
95
+ * getActiveFiltersCount: (value) => {
96
+ * // Count active filters (0 means none active)
97
+ * return count;
98
+ * },
99
+ *
100
+ * renderFilter: (value, onChange) => (
101
+ * <YourCustomFilterComponent
102
+ * value={value}
103
+ * onChange={onChange}
104
+ * />
105
+ * ),
106
+ * });
107
+ * ```
108
+ *
109
+ * ## Reference Implementation:
110
+ * See `workflow-filters.config.tsx` for a complete example (lastEditedFilterAdapter).
111
+ *
112
+ * ## Note:
113
+ * Most use cases are covered by `createCheckboxFilterAdapter` or `createDualRangeFilterAdapter`.
114
+ * Only use this base function directly when you need completely custom filter logic.
115
+ */
116
+ export function createFilterAdapter<TData, TFilterValue, TCellValue = unknown>(
117
+ config: FilterAdapterConfig<TData, TFilterValue, TCellValue>,
118
+ ): FilterAdapter<TData, TFilterValue, TCellValue> {
119
+ return {
120
+ id: config.id,
121
+ label: config.label,
122
+ initialValue: config.initialValue,
123
+ columnFilterFn: config.filterFn,
124
+ cellRenderer: config.cellRenderer,
125
+ toChips: config.toChips,
126
+ fromChip: config.fromChip,
127
+ getActiveFiltersCount: config.getActiveFiltersCount,
128
+ reset: () => config.initialValue,
129
+ renderFilter: config.renderFilter,
130
+ };
131
+ }
@@ -0,0 +1,12 @@
1
+ export { createFilterAdapter } from './create-filter-adapter';
2
+ export type { FilterAdapterConfig } from './create-filter-adapter';
3
+
4
+ export { createCheckboxFilterAdapter } from './create-checkbox-filter-adapter';
5
+ export type { CheckboxFilterAdapterConfig } from './create-checkbox-filter-adapter';
6
+
7
+ export { createDualRangeFilterAdapter } from './create-dual-range-filter-adapter';
8
+ export type {
9
+ RangeValue,
10
+ DualRangeFilterValue,
11
+ DualRangeFilterAdapterConfig,
12
+ } from './create-dual-range-filter-adapter';
@@ -0,0 +1,43 @@
1
+ import type { ReactNode } from 'react';
2
+ import { DsCheckbox } from '../../../../ds-checkbox';
3
+
4
+ export interface CheckboxFilterItem<TValue = string> {
5
+ value: TValue;
6
+ label: string;
7
+ metadata?: Record<string, unknown>;
8
+ }
9
+
10
+ export interface CheckboxFilterProps<TValue> {
11
+ items: CheckboxFilterItem<TValue>[];
12
+ renderer?: (item: CheckboxFilterItem<TValue>) => ReactNode;
13
+ selectedItems: CheckboxFilterItem<TValue>[];
14
+ onSelectionChange: (selectedItems: CheckboxFilterItem<TValue>[]) => void;
15
+ }
16
+
17
+ export const CheckboxFilter = <TValue,>({
18
+ items,
19
+ renderer,
20
+ selectedItems,
21
+ onSelectionChange,
22
+ }: CheckboxFilterProps<TValue>) => {
23
+ const handleCheckedChange = (selected: CheckboxFilterItem<TValue>, checked: boolean) => {
24
+ if (checked) {
25
+ onSelectionChange([...selectedItems, selected]);
26
+ } else {
27
+ onSelectionChange(selectedItems.filter((item) => item.value !== selected.value));
28
+ }
29
+ };
30
+
31
+ return items.map((item) => {
32
+ const label = renderer ? renderer(item) : item.label;
33
+ const checked = selectedItems.findIndex((selectedItem) => selectedItem.value === item.value) > -1;
34
+ return (
35
+ <DsCheckbox
36
+ key={JSON.stringify(item.value)}
37
+ label={label}
38
+ checked={checked}
39
+ onCheckedChange={(newState) => handleCheckedChange(item, newState === true)}
40
+ />
41
+ );
42
+ });
43
+ };
@@ -0,0 +1,2 @@
1
+ export { CheckboxFilter } from './checkbox-filter';
2
+ export type { CheckboxFilterProps, CheckboxFilterItem } from './checkbox-filter';
@@ -0,0 +1,2 @@
1
+ export { RangeFilter } from './range-filter';
2
+ export type { RangeFilterProps, RangeFilterValue } from './range-filter';
@@ -0,0 +1,25 @@
1
+ .container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 10px;
5
+ padding-bottom: 16px;
6
+ border-bottom: 1px solid var(--color-border);
7
+ }
8
+
9
+ .header {
10
+ display: flex;
11
+ align-items: center;
12
+ gap: 8px;
13
+ }
14
+
15
+ .inputs {
16
+ display: flex;
17
+ gap: 16px;
18
+ }
19
+
20
+ .inputGroup {
21
+ display: flex;
22
+ flex-direction: column;
23
+ flex: 1;
24
+ gap: 2px;
25
+ }
@@ -0,0 +1,79 @@
1
+ import { useState } from 'react';
2
+ import { DsTypography } from '../../../../ds-typography';
3
+ import { DsButton } from '../../../../ds-button';
4
+ import { DsTextInput } from '../../../../ds-text-input';
5
+ import styles from './range-filter.module.scss';
6
+
7
+ export interface RangeFilterValue {
8
+ from?: number;
9
+ to?: number;
10
+ }
11
+
12
+ export interface RangeFilterProps {
13
+ label: string;
14
+ value: RangeFilterValue;
15
+ onChange: (value: RangeFilterValue) => void;
16
+ onClear?: () => void;
17
+ }
18
+
19
+ export const RangeFilter = ({ label, value, onChange, onClear }: RangeFilterProps) => {
20
+ const [localFrom, setLocalFrom] = useState(value.from?.toString() || '');
21
+ const [localTo, setLocalTo] = useState(value.to?.toString() || '');
22
+
23
+ const handleFromChange = (val: string) => {
24
+ setLocalFrom(val);
25
+ const numVal = val ? parseInt(val, 10) : undefined;
26
+ onChange({ from: numVal, to: value.to });
27
+ };
28
+
29
+ const handleToChange = (val: string) => {
30
+ setLocalTo(val);
31
+ const numVal = val ? parseInt(val, 10) : undefined;
32
+ onChange({ from: value.from, to: numVal });
33
+ };
34
+
35
+ const handleClear = () => {
36
+ setLocalFrom('');
37
+ setLocalTo('');
38
+ if (onClear) {
39
+ onClear();
40
+ }
41
+ };
42
+
43
+ const hasValue = value.from !== undefined || value.to !== undefined;
44
+
45
+ return (
46
+ <div className={styles.container}>
47
+ <div className={styles.header}>
48
+ <DsTypography variant="body-md-md">{label}</DsTypography>
49
+ {hasValue && onClear && (
50
+ <DsButton design="v1.2" buttonType="tertiary" size="small" onClick={handleClear}>
51
+ Clear
52
+ </DsButton>
53
+ )}
54
+ </div>
55
+ <div className={styles.inputs}>
56
+ <div className={styles.inputGroup}>
57
+ <DsTypography variant="body-sm-reg">From</DsTypography>
58
+ <DsTextInput
59
+ type="number"
60
+ placeholder=""
61
+ value={localFrom}
62
+ onValueChange={handleFromChange}
63
+ size="default"
64
+ />
65
+ </div>
66
+ <div className={styles.inputGroup}>
67
+ <DsTypography variant="body-sm-reg">To</DsTypography>
68
+ <DsTextInput
69
+ type="number"
70
+ placeholder=""
71
+ value={localTo}
72
+ onValueChange={handleToChange}
73
+ size="default"
74
+ />
75
+ </div>
76
+ </div>
77
+ </div>
78
+ );
79
+ };
@@ -0,0 +1 @@
1
+ export type { UseTableFiltersResult } from './use-table-filters';