@drivenets/design-system 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/index.cjs +1893 -824
  3. package/dist/index.d.cts +363 -247
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +363 -247
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1896 -829
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +25 -25
  11. package/src/components/ds-alert-banner/ds-alert-banner-global.figma.ts +49 -0
  12. package/src/components/ds-alert-banner/ds-alert-banner.figma.ts +51 -0
  13. package/src/components/ds-alert-banner/ds-alert-banner.tsx +7 -6
  14. package/src/components/ds-autocomplete/ds-autocomplete.figma.ts +2 -2
  15. package/src/components/ds-autocomplete/ds-autocomplete.module.scss +15 -4
  16. package/src/components/ds-autocomplete/ds-autocomplete.tsx +10 -3
  17. package/src/components/ds-autocomplete/ds-autocomplete.types.ts +8 -0
  18. package/src/components/ds-avatar/ds-avatar.figma.ts +2 -2
  19. package/src/components/ds-avatar-group/ds-avatar-group.figma.ts +2 -2
  20. package/src/components/ds-breadcrumb/ds-breadcrumb.figma.ts +2 -2
  21. package/src/components/ds-button-v3/ds-button-v3.figma.ts +7 -5
  22. package/src/components/ds-button-v3/ds-button-v3.module.scss +84 -17
  23. package/src/components/ds-button-v3/ds-button-v3.tsx +2 -0
  24. package/src/components/ds-button-v3/ds-button-v3.types.ts +11 -2
  25. package/src/components/ds-button-v3/ds-button-v4.figma.ts +121 -0
  26. package/src/components/ds-card/figma/ds-card-content.figma.ts +2 -2
  27. package/src/components/ds-card/figma/ds-card-footer.figma.ts +2 -2
  28. package/src/components/ds-card/figma/ds-card-header.figma.ts +2 -2
  29. package/src/components/ds-card/figma/ds-card.figma.ts +2 -2
  30. package/src/components/ds-catalog-layout/ds-catalog-layout.figma.ts +51 -0
  31. package/src/components/ds-catalog-layout/ds-catalog-layout.tsx +9 -8
  32. package/src/components/ds-checkbox/ds-checkbox-level-2.figma.ts +2 -2
  33. package/src/components/ds-checkbox/ds-checkbox.figma.ts +2 -2
  34. package/src/components/ds-checkbox-group/ds-checkbox-group.figma.ts +2 -2
  35. package/src/components/ds-comment-bubble/ds-comment-bubble.figma.ts +52 -0
  36. package/src/components/ds-comment-card/ds-comment-card.figma.ts +36 -0
  37. package/src/components/ds-comment-indicator/ds-comment-indicator.figma.ts +32 -0
  38. package/src/components/ds-comments-drawer/components/comments-filter-modal/index.ts +0 -1
  39. package/src/components/ds-comments-drawer/ds-comments-drawer.figma.ts +19 -0
  40. package/src/components/ds-confirmation/ds-confirmation.tsx +13 -11
  41. package/src/components/ds-date-picker/ds-date-picker.figma.ts +2 -2
  42. package/src/components/ds-divider/ds-divider.figma.ts +2 -2
  43. package/src/components/ds-drawer/ds-drawer.tsx +11 -10
  44. package/src/components/ds-drawer/figma/ds-drawer-content.figma.ts +2 -2
  45. package/src/components/ds-drawer/figma/ds-drawer-footer.figma.ts +2 -2
  46. package/src/components/ds-drawer/figma/ds-drawer-header.figma.ts +2 -2
  47. package/src/components/ds-drawer/figma/ds-drawer.figma.ts +2 -2
  48. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-footer.figma.ts +2 -2
  49. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-group.figma.ts +2 -2
  50. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-item.figma.ts +2 -2
  51. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-separator.figma.ts +2 -2
  52. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu.figma.ts +2 -2
  53. package/src/components/ds-expandable-text-input/ds-expandable-text-input.figma.ts +2 -2
  54. package/src/components/ds-file-upload/components/file-upload-item/index.ts +0 -1
  55. package/src/components/ds-file-upload/ds-file-upload.figma.ts +2 -2
  56. package/src/components/ds-filter-status-icon/ds-filter-status-icon.figma.ts +2 -2
  57. package/src/components/ds-form-control/ds-form-control.figma.ts +2 -2
  58. package/src/components/ds-form-control/ds-form-control.tsx +15 -13
  59. package/src/components/ds-icon/figma/ds-icon-material.figma.batch.ts +1 -1
  60. package/src/components/ds-icon/figma/ds-icon-special.figma.batch.ts +1 -1
  61. package/src/components/ds-key-value-pair/ds-key-value-pair.figma.ts +2 -2
  62. package/src/components/ds-loader/ds-loader.figma.ts +2 -2
  63. package/src/components/ds-modal/ds-modal.tsx +10 -9
  64. package/src/components/ds-modal/figma/ds-modal-content.figma.ts +2 -2
  65. package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +2 -2
  66. package/src/components/ds-modal/figma/ds-modal-header.figma.ts +2 -2
  67. package/src/components/ds-modal/figma/ds-modal.figma.ts +2 -2
  68. package/src/components/ds-number-input/ds-number-input.tsx +1 -0
  69. package/src/components/ds-panel/ds-panel.figma.ts +2 -2
  70. package/src/components/ds-popover/figma/ds-popover-content-item.figma.ts +2 -2
  71. package/src/components/ds-popover/figma/ds-popover-content.figma.ts +2 -2
  72. package/src/components/ds-popover/figma/ds-popover-footer.figma.ts +2 -2
  73. package/src/components/ds-popover/figma/ds-popover-header.figma.ts +2 -2
  74. package/src/components/ds-popover/figma/ds-popover.figma.ts +2 -2
  75. package/src/components/ds-progress-arc/ds-progress-arc.figma.ts +2 -2
  76. package/src/components/ds-progress-donut/ds-progress-donut.figma.ts +2 -2
  77. package/src/components/ds-progress-linear/ds-progress-linear.figma.ts +2 -2
  78. package/src/components/ds-progress-task-bar/ds-progress-task-bar.figma.ts +2 -2
  79. package/src/components/ds-radio-group/figma/ds-radio-group-item.figma.ts +2 -2
  80. package/src/components/ds-radio-group/figma/ds-radio-group.figma.ts +2 -2
  81. package/src/components/ds-segment-group/figma/ds-segment-group-item.figma.ts +2 -2
  82. package/src/components/ds-segment-group/figma/ds-segment-group.figma.ts +2 -2
  83. package/src/components/ds-select/ds-select.figma.ts +4 -3
  84. package/src/components/ds-select/ds-select.tsx +6 -1
  85. package/src/components/ds-select/ds-select.types.ts +2 -1
  86. package/src/components/ds-site-node/ds-site-node.figma.ts +2 -2
  87. package/src/components/ds-slider/ds-slider.figma.ts +29 -0
  88. package/src/components/ds-slider/ds-slider.module.scss +104 -0
  89. package/src/components/ds-slider/ds-slider.tsx +118 -0
  90. package/src/components/ds-slider/ds-slider.types.ts +102 -0
  91. package/src/components/ds-slider/ds-slider.utils.ts +14 -0
  92. package/src/components/ds-slider/index.ts +11 -0
  93. package/src/components/ds-smart-tabs/ds-smart-tabs.tsx +5 -4
  94. package/src/components/ds-smart-tabs/figma/ds-smart-tabs-tab.figma.ts +83 -0
  95. package/src/components/ds-smart-tabs/figma/ds-smart-tabs.figma.ts +23 -0
  96. package/src/components/ds-spinner/ds-spinner.figma.ts +2 -2
  97. package/src/components/ds-split-button/ds-split-button.figma.ts +2 -2
  98. package/src/components/ds-status-badge-v2/ds-status-badge-v2.figma.ts +2 -2
  99. package/src/components/ds-stepper/ds-stepper.figma.ts +2 -2
  100. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +5 -1
  101. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +15 -3
  102. package/src/components/ds-table/components/ds-table-group-header-cell/index.ts +0 -1
  103. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +17 -3
  104. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +30 -9
  105. package/src/components/ds-table/components/ds-table-resize-handle/ds-table-resize-handle.module.scss +23 -0
  106. package/src/components/ds-table/components/ds-table-resize-handle/ds-table-resize-handle.tsx +218 -0
  107. package/src/components/ds-table/components/ds-table-resize-handle/ds-table-resize-handle.types.ts +14 -0
  108. package/src/components/ds-table/components/ds-table-resize-handle/index.ts +1 -0
  109. package/src/components/ds-table/components/ds-table-resize-overlay/ds-table-resize-overlay.module.scss +18 -0
  110. package/src/components/ds-table/components/ds-table-resize-overlay/ds-table-resize-overlay.tsx +16 -0
  111. package/src/components/ds-table/components/ds-table-resize-overlay/ds-table-resize-overlay.types.ts +13 -0
  112. package/src/components/ds-table/components/ds-table-resize-overlay/index.ts +1 -0
  113. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +1 -1
  114. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +9 -2
  115. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +2 -1
  116. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +9 -2
  117. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-checkbox.tsx +2 -2
  118. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-date.tsx +2 -1
  119. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-number.tsx +2 -1
  120. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-select.tsx +2 -1
  121. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-text.tsx +2 -1
  122. package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.tsx +12 -3
  123. package/src/components/ds-table/context/ds-table-context.tsx +208 -0
  124. package/src/components/ds-table/ds-table-defaults.ts +42 -0
  125. package/src/components/ds-table/ds-table.module.scss +19 -1
  126. package/src/components/ds-table/ds-table.tsx +163 -235
  127. package/src/components/ds-table/ds-table.types.ts +59 -47
  128. package/src/components/ds-table/figma/ds-table-expandable.figma.ts +2 -2
  129. package/src/components/ds-table/figma/ds-table.figma.ts +2 -2
  130. package/src/components/ds-table/grouping/utils/column-group.ts +7 -5
  131. package/src/components/ds-table/hooks/use-cell-editor.ts +5 -1
  132. package/src/components/ds-table/hooks/use-column-resize.ts +385 -0
  133. package/src/components/ds-table/hooks/use-editing-state.ts +190 -0
  134. package/src/components/ds-table/index.ts +1 -0
  135. package/src/components/ds-table/styles/_variables.scss +2 -1
  136. package/src/components/ds-table/utils/column-size-meta.augmentation.ts +16 -0
  137. package/src/components/ds-table/utils/column-size.ts +213 -10
  138. package/src/components/ds-table/utils/column-size.unit.test.ts +213 -0
  139. package/src/components/ds-table/utils/constants.ts +28 -0
  140. package/src/components/ds-table/utils/frozen-body.ts +33 -0
  141. package/src/components/ds-table/utils/frozen-body.unit.test.ts +44 -0
  142. package/src/components/ds-table/utils/table-api.ts +42 -0
  143. package/src/components/ds-table/utils/table-api.unit.test.ts +82 -0
  144. package/src/components/ds-table/utils/table-columns.tsx +119 -0
  145. package/src/components/ds-table/utils/table-columns.unit.test.ts +199 -0
  146. package/src/components/ds-tabs/figma/ds-tabs-tab-vertical.figma.ts +2 -2
  147. package/src/components/ds-tabs/figma/ds-tabs-tab.figma.ts +2 -2
  148. package/src/components/ds-tabs/figma/ds-tabs-vertical.figma.ts +2 -2
  149. package/src/components/ds-tabs/figma/ds-tabs.figma.ts +2 -2
  150. package/src/components/ds-tag/ds-tag.figma.ts +51 -0
  151. package/src/components/ds-tag-filter/ds-tag-filter.figma.ts +16 -0
  152. package/src/components/ds-text-input/ds-input-field-v02.figma.ts +31 -6
  153. package/src/components/ds-text-input/ds-text-input.figma.ts +5 -4
  154. package/src/components/ds-text-input/ds-text-input.tsx +1 -0
  155. package/src/components/ds-text-input/ds-text-input.types.ts +1 -1
  156. package/src/components/ds-time-picker/ds-time-picker.figma.ts +2 -2
  157. package/src/components/ds-toast/ds-toast.figma.ts +3 -3
  158. package/src/components/ds-toggle/ds-toggle.figma.ts +2 -2
  159. package/src/components/ds-tooltip/ds-tooltip.figma.ts +2 -2
  160. package/src/components/ds-tree/ds-tree.figma.ts +54 -0
  161. package/src/components/ds-tree/ds-tree.module.scss +2 -2
  162. package/src/components/ds-tree/ds-tree.tsx +6 -0
  163. package/src/components/ds-tree/ds-tree.types.ts +16 -0
  164. package/src/components/ds-typography/ds-typography.figma.ts +2 -2
  165. package/src/components/ds-user-card/ds-user-card.figma.ts +2 -2
  166. package/src/components/ds-vertical-tabs/ds-vertical-tabs.tsx +13 -6
  167. package/src/components/ds-workspace-layout/ds-workspace-layout.figma.ts +99 -0
  168. package/src/components/ds-workspace-layout/ds-workspace-layout.module.scss +1 -8
  169. package/src/components/ds-workspace-layout/ds-workspace-layout.tsx +11 -10
  170. package/src/index.ts +1 -0
  171. package/src/styles/_gradients.scss +19 -0
  172. package/src/styles/_root.scss +39 -15
  173. package/src/styles/_typography.scss +1 -0
  174. package/src/styles/shared/_input.scss +10 -0
  175. package/src/styles/styles.scss +1 -0
  176. package/src/components/ds-table/context/ds-table-context.ts +0 -112
@@ -0,0 +1,218 @@
1
+ import {
2
+ useEffect,
3
+ useRef,
4
+ type KeyboardEvent,
5
+ type MouseEvent,
6
+ type PointerEvent,
7
+ type TouchEvent,
8
+ } from 'react';
9
+ import { useDsTableContext } from '../../context/ds-table-context';
10
+ import { defaultDsTableLocale } from '../../ds-table.types';
11
+ import {
12
+ RESIZE_KEYBOARD_STEP,
13
+ RESIZE_KEYBOARD_STEP_LARGE,
14
+ RESIZE_MIN_COLUMN_WIDTH,
15
+ } from '../../utils/constants';
16
+ import type { DsTableResizeHandleProps } from './ds-table-resize-handle.types';
17
+ import styles from './ds-table-resize-handle.module.scss';
18
+
19
+ const clientPointFromEvent = (event: Event): { x: number; y: number } | null => {
20
+ if (event instanceof MouseEvent) {
21
+ return { x: event.clientX, y: event.clientY };
22
+ }
23
+
24
+ if (typeof TouchEvent !== 'undefined' && event instanceof TouchEvent) {
25
+ const touch = event.changedTouches[0];
26
+ return touch ? { x: touch.clientX, y: touch.clientY } : null;
27
+ }
28
+
29
+ return null;
30
+ };
31
+
32
+ /**
33
+ * Handle straddling the right edge of a resizable header cell.
34
+ *
35
+ * Drag is TanStack v8 `header.getResizeHandler()`. Hover drives the full-height
36
+ * overlay; double-click restores snapshot origin widths for this header's
37
+ * leaves. As a focusable `separator`, Arrow keys nudge the width (Shift for a
38
+ * larger step) for keyboard operability (WCAG 2.1.1).
39
+ */
40
+ export const DsTableResizeHandle = <TData, TValue>({ header }: DsTableResizeHandleProps<TData, TValue>) => {
41
+ const {
42
+ resizeContainerRef,
43
+ onResizeHover,
44
+ onResizeDragStart,
45
+ onResizeReset,
46
+ onResizeKeyboardNudge,
47
+ locale,
48
+ } = useDsTableContext<TData, TValue>();
49
+ const handleRef = useRef<HTMLDivElement>(null);
50
+ const isPointerOverRef = useRef(false);
51
+ const detachDragEndRef = useRef<(() => void) | null>(null);
52
+
53
+ useEffect(
54
+ () => () => {
55
+ detachDragEndRef.current?.();
56
+ },
57
+ [],
58
+ );
59
+
60
+ const label = { ...defaultDsTableLocale, ...locale }.resizeColumn;
61
+
62
+ const measureCellRightOffset = (): number | null => {
63
+ const containerEl = resizeContainerRef?.current;
64
+ const cellEl = handleRef.current?.closest<HTMLElement>('th');
65
+ if (!containerEl || !cellEl) {
66
+ return null;
67
+ }
68
+ // The overlay is absolutely positioned inside the horizontally scrollable
69
+ // container, so its offset must be in the container's scrolled content
70
+ // space — add `scrollLeft` to the viewport-relative distance.
71
+ return (
72
+ cellEl.getBoundingClientRect().right - containerEl.getBoundingClientRect().left + containerEl.scrollLeft
73
+ );
74
+ };
75
+
76
+ const reportHover = () => {
77
+ const offset = measureCellRightOffset();
78
+ if (offset === null) {
79
+ return;
80
+ }
81
+ onResizeHover?.(header.column.id, offset);
82
+ };
83
+
84
+ const clearHover = () => {
85
+ isPointerOverRef.current = false;
86
+ onResizeHover?.(null, null);
87
+ };
88
+
89
+ const syncHoverAtPoint = (clientX: number, clientY: number) => {
90
+ const handleEl = handleRef.current;
91
+ const doc = handleEl?.ownerDocument ?? document;
92
+ const hit = doc.elementFromPoint(clientX, clientY);
93
+ if (handleEl && hit && (hit === handleEl || handleEl.contains(hit))) {
94
+ isPointerOverRef.current = true;
95
+ reportHover();
96
+ return;
97
+ }
98
+
99
+ clearHover();
100
+ };
101
+
102
+ const attachDragEndListeners = () => {
103
+ detachDragEndRef.current?.();
104
+
105
+ const doc = handleRef.current?.ownerDocument ?? document;
106
+ const onDragEnd = (event: Event) => {
107
+ detachDragEndRef.current?.();
108
+ detachDragEndRef.current = null;
109
+
110
+ const point = clientPointFromEvent(event);
111
+ clearHover();
112
+
113
+ const frame = handleRef.current?.ownerDocument.defaultView ?? window;
114
+ frame.requestAnimationFrame(() => {
115
+ if (!point) {
116
+ return;
117
+ }
118
+
119
+ syncHoverAtPoint(point.x, point.y);
120
+ });
121
+ };
122
+
123
+ doc.addEventListener('mouseup', onDragEnd);
124
+ doc.addEventListener('touchend', onDragEnd);
125
+ detachDragEndRef.current = () => {
126
+ doc.removeEventListener('mouseup', onDragEnd);
127
+ doc.removeEventListener('touchend', onDragEnd);
128
+ };
129
+ };
130
+
131
+ const handlePointerEnter = () => {
132
+ isPointerOverRef.current = true;
133
+ reportHover();
134
+ };
135
+
136
+ const handlePointerLeave = () => {
137
+ clearHover();
138
+ };
139
+
140
+ const startDrag = () => {
141
+ const offset = measureCellRightOffset();
142
+ if (offset !== null) {
143
+ onResizeDragStart?.(offset);
144
+ }
145
+ };
146
+
147
+ const handlePointerDown = (event: PointerEvent<HTMLDivElement>) => {
148
+ if (event.pointerType === 'touch') {
149
+ return;
150
+ }
151
+
152
+ event.stopPropagation();
153
+ startDrag();
154
+ header.getResizeHandler(handleRef.current?.ownerDocument ?? document)(event);
155
+ attachDragEndListeners();
156
+ };
157
+
158
+ const handleTouchStart = (event: TouchEvent<HTMLDivElement>) => {
159
+ event.stopPropagation();
160
+ startDrag();
161
+ header.getResizeHandler(handleRef.current?.ownerDocument ?? document)(event);
162
+ attachDragEndListeners();
163
+ };
164
+
165
+ const handleReset = (event: MouseEvent<HTMLDivElement>) => {
166
+ event.preventDefault();
167
+ event.stopPropagation();
168
+ const leafIds = header.column.getLeafColumns().map((column) => column.id);
169
+ onResizeReset?.(leafIds);
170
+ if (isPointerOverRef.current) {
171
+ reportHover();
172
+ } else {
173
+ clearHover();
174
+ }
175
+ };
176
+
177
+ const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
178
+ if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') {
179
+ return;
180
+ }
181
+
182
+ event.preventDefault();
183
+ event.stopPropagation();
184
+ const step = event.shiftKey ? RESIZE_KEYBOARD_STEP_LARGE : RESIZE_KEYBOARD_STEP;
185
+ const delta = event.key === 'ArrowRight' ? step : -step;
186
+ onResizeKeyboardNudge?.(header.column.id, delta);
187
+ };
188
+
189
+ // Only leaves expose a meaningful min; a group boundary's min/max would be
190
+ // misleading, so announce the current size but omit bounds for groups.
191
+ const isLeaf = header.subHeaders.length === 0;
192
+
193
+ // A focusable role="separator" with aria-valuenow/valuemin and arrow-key
194
+ // handling is the WAI-ARIA window-splitter pattern, which is interactive.
195
+ // jsx-a11y's role taxonomy does not model focusable separators, so its
196
+ // non-interactive rules misfire on this element.
197
+ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/no-noninteractive-tabindex */
198
+ return (
199
+ <div
200
+ ref={handleRef}
201
+ role="separator"
202
+ aria-orientation="vertical"
203
+ aria-label={label}
204
+ tabIndex={0}
205
+ aria-valuenow={Math.round(header.getSize())}
206
+ aria-valuemin={isLeaf ? (header.column.columnDef.minSize ?? RESIZE_MIN_COLUMN_WIDTH) : undefined}
207
+ className={styles.resizeHandle}
208
+ onPointerDown={handlePointerDown}
209
+ onPointerEnter={handlePointerEnter}
210
+ onPointerLeave={handlePointerLeave}
211
+ onTouchStart={handleTouchStart}
212
+ onClick={(event) => event.stopPropagation()}
213
+ onDoubleClick={handleReset}
214
+ onKeyDown={handleKeyDown}
215
+ />
216
+ );
217
+ /* eslint-enable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/no-noninteractive-tabindex */
218
+ };
@@ -0,0 +1,14 @@
1
+ import type { Header } from '@tanstack/react-table';
2
+
3
+ /**
4
+ * Props for the column resize handle rendered on the right edge of a resizable
5
+ * header cell.
6
+ */
7
+ export interface DsTableResizeHandleProps<TData, TValue> {
8
+ /**
9
+ * The header (leaf or group) whose right edge this handle resizes. A group
10
+ * handle uses TanStack's resize handler, which scales that group's leaf
11
+ * columns.
12
+ */
13
+ header: Header<TData, TValue>;
14
+ }
@@ -0,0 +1 @@
1
+ export { DsTableResizeHandle } from './ds-table-resize-handle';
@@ -0,0 +1,18 @@
1
+ @use '../../styles/variables' as vars;
2
+
3
+ .resizeOverlay {
4
+ position: absolute;
5
+ top: 0;
6
+ bottom: 0;
7
+ width: vars.$resize-divider-width;
8
+ background: var(--border-light);
9
+ // `left` is the cell's right edge; nudge the divider right by 1px so its 2px
10
+ // band aligns flush with the 2px group border drawn from that edge rightward.
11
+ transform: translateX(calc(-50% + #{vars.$border-width}));
12
+ pointer-events: none;
13
+ z-index: 2;
14
+
15
+ &[data-resize-phase='dragging'] {
16
+ background: var(--border-action-primary);
17
+ }
18
+ }
@@ -0,0 +1,16 @@
1
+ import type { DsTableResizeOverlayProps } from './ds-table-resize-overlay.types';
2
+ import styles from './ds-table-resize-overlay.module.scss';
3
+
4
+ /**
5
+ * Full-height divider that tracks the active column's right edge during hover
6
+ * and drag resize.
7
+ */
8
+ export const DsTableResizeOverlay = ({ offset, phase }: DsTableResizeOverlayProps) => (
9
+ <div
10
+ className={styles.resizeOverlay}
11
+ style={{ left: offset }}
12
+ data-resize-overlay
13
+ data-resize-phase={phase}
14
+ aria-hidden
15
+ />
16
+ );
@@ -0,0 +1,13 @@
1
+ export type DsTableResizeOverlayPhase = 'hover' | 'dragging';
2
+
3
+ /**
4
+ * Props for the full-height column resize divider overlay.
5
+ */
6
+ export interface DsTableResizeOverlayProps {
7
+ /**
8
+ * Distance from the table container's left edge to the column's right edge,
9
+ * in pixels.
10
+ */
11
+ offset: number;
12
+ phase: DsTableResizeOverlayPhase;
13
+ }
@@ -0,0 +1 @@
1
+ export { DsTableResizeOverlay } from './ds-table-resize-overlay';
@@ -85,7 +85,7 @@
85
85
  justify-content: center;
86
86
  }
87
87
 
88
- .groupStart {
88
+ .groupStart:not(:first-child) {
89
89
  border-left: vars.$group-border-width solid var(--border);
90
90
  }
91
91
 
@@ -9,7 +9,7 @@ import type { DsTableRowProps } from './ds-table-row.types';
9
9
  import styles from './ds-table-row.module.scss';
10
10
  import { useDsTableContext } from '../../context/ds-table-context';
11
11
  import { mergeRefs } from '../../../../utils/merge-refs';
12
- import { getColumnSizeStyle } from '../../utils/column-size';
12
+ import { getBodyCellSizeStyle, isExplicitColumnWidth } from '../../utils/column-size';
13
13
  import { isFirstLeafColumnOfGroup } from '../../grouping';
14
14
  import { EXPANDER_COLUMN_ID, REORDER_COLUMN_ID, SELECT_COLUMN_ID } from '../../utils/constants';
15
15
 
@@ -52,6 +52,7 @@ const DsTableRow = <TData,>({ ref, row, isSelected }: DsTableRowProps<TData>) =>
52
52
  secondaryRowActions,
53
53
  activeRowId,
54
54
  loading,
55
+ resizeSizingReady,
55
56
  } = useDsTableContext<TData, unknown>();
56
57
  const isExpanded = row.getIsExpanded();
57
58
  const isActive = activeRowId === row.id;
@@ -98,7 +99,13 @@ const DsTableRow = <TData,>({ ref, row, isSelected }: DsTableRowProps<TData>) =>
98
99
  onDoubleClick={() => onRowDoubleClick?.(row.original)}
99
100
  >
100
101
  {row.getVisibleCells().map((cell, idx) => {
101
- const cellStyle = getColumnSizeStyle(cell.column.getSize());
102
+ const columnSizing = cell.getContext().table.getState().columnSizing;
103
+ const cellStyle = getBodyCellSizeStyle(
104
+ cell.column.id,
105
+ cell.column.getSize(),
106
+ isExplicitColumnWidth(cell.column, resizeSizingReady ? columnSizing : {}),
107
+ !!resizeSizingReady,
108
+ );
102
109
 
103
110
  if (cell.column.id === REORDER_COLUMN_ID) {
104
111
  return (
@@ -64,9 +64,10 @@
64
64
  .selectableCell {
65
65
  padding: 0;
66
66
  justify-content: center;
67
+ text-align: center;
67
68
  }
68
69
 
69
- .groupStart {
70
+ .groupStart:not(:first-child) {
70
71
  border-left: vars.$group-border-width solid var(--border);
71
72
  }
72
73
 
@@ -1,7 +1,7 @@
1
1
  import classnames from 'classnames';
2
2
  import styles from './ds-table-row-virtualized.module.scss';
3
3
  import { useDsTableContext } from '../../context/ds-table-context';
4
- import { getColumnSizeStyle } from '../../utils/column-size';
4
+ import { getBodyCellSizeStyle, isExplicitColumnWidth } from '../../utils/column-size';
5
5
  import { isFirstLeafColumnOfGroup } from '../../grouping';
6
6
  import { TableCell, TableRow } from '../core-table';
7
7
  import type { DsTableRowVirtualizedProps } from './ds-table-row-virtualized.types';
@@ -27,6 +27,7 @@ export const DsTableRowVirtualized = <TData,>({
27
27
  onRowClick,
28
28
  onRowDoubleClick,
29
29
  loading,
30
+ resizeSizingReady,
30
31
  } = useDsTableContext<TData, unknown>();
31
32
  const isExpanded = row.getIsExpanded();
32
33
  const isActive = activeRowId === row.id;
@@ -70,7 +71,13 @@ export const DsTableRowVirtualized = <TData,>({
70
71
  <>
71
72
  {row.getVisibleCells().map((cell, idx) => {
72
73
  const isLastColumn = idx === row.getVisibleCells().length - 1;
73
- const cellStyle = getColumnSizeStyle(cell.column.getSize());
74
+ const columnSizing = cell.getContext().table.getState().columnSizing;
75
+ const cellStyle = getBodyCellSizeStyle(
76
+ cell.column.id,
77
+ cell.column.getSize(),
78
+ isExplicitColumnWidth(cell.column, resizeSizingReady ? columnSizing : {}),
79
+ !!resizeSizingReady,
80
+ );
74
81
 
75
82
  return (
76
83
  <TableCell
@@ -13,14 +13,14 @@ export const DsTableEditCellCheckbox = <TData extends RowData>({
13
13
  cellContext,
14
14
  disabled,
15
15
  }: DsTableEditCellCheckboxProps<TData>) => {
16
- const { value, setValue } = useCellEditor<TData, boolean>({
16
+ const { value, setValue, isPending } = useCellEditor<TData, boolean>({
17
17
  cellContext,
18
18
  });
19
19
 
20
20
  return (
21
21
  <DsCheckbox
22
22
  checked={value}
23
- disabled={disabled}
23
+ disabled={disabled || isPending}
24
24
  onCheckedChange={(checked) => setValue(checked === true)}
25
25
  />
26
26
  );
@@ -22,7 +22,7 @@ export const DsTableEditCellDate = <TData extends RowData>({
22
22
  max,
23
23
  placeholder,
24
24
  }: DsTableEditCellDateProps<TData>) => {
25
- const { value, setValue, error } = useCellEditor<TData, DsTableEditCellDateValue>({
25
+ const { value, setValue, error, isPending } = useCellEditor<TData, DsTableEditCellDateValue>({
26
26
  cellContext,
27
27
  });
28
28
 
@@ -34,6 +34,7 @@ export const DsTableEditCellDate = <TData extends RowData>({
34
34
  min={min}
35
35
  max={max}
36
36
  placeholder={placeholder}
37
+ disabled={isPending}
37
38
  onChange={setValue}
38
39
  slotProps={
39
40
  error
@@ -20,7 +20,7 @@ export const DsTableEditCellNumber = <TData extends RowData>({
20
20
  max,
21
21
  step,
22
22
  }: DsTableEditCellNumberProps<TData>) => {
23
- const { value, setValue, error } = useCellEditor<TData, number>({
23
+ const { value, setValue, error, isPending } = useCellEditor<TData, number>({
24
24
  cellContext,
25
25
  });
26
26
 
@@ -44,6 +44,7 @@ export const DsTableEditCellNumber = <TData extends RowData>({
44
44
  min={min}
45
45
  max={max}
46
46
  step={step}
47
+ disabled={isPending}
47
48
  onValueChange={setValue}
48
49
  />
49
50
  </TableEditFormControl>
@@ -18,7 +18,7 @@ export const DsTableEditCellSelect = <TData extends RowData>({
18
18
  options,
19
19
  placeholder,
20
20
  }: DsTableEditCellSelectProps<TData>) => {
21
- const { value, setValue, error } = useCellEditor<TData, string>({
21
+ const { value, setValue, error, isPending } = useCellEditor<TData, string>({
22
22
  cellContext,
23
23
  });
24
24
 
@@ -29,6 +29,7 @@ export const DsTableEditCellSelect = <TData extends RowData>({
29
29
  value={value}
30
30
  options={options}
31
31
  placeholder={placeholder}
32
+ disabled={isPending}
32
33
  onValueChange={setValue}
33
34
  />
34
35
  </TableEditFormControl>
@@ -19,7 +19,7 @@ export const DsTableEditCellText = <TData extends RowData>({
19
19
  placeholder,
20
20
  maxLength,
21
21
  }: DsTableEditCellTextProps<TData>) => {
22
- const { value, setValue, error } = useCellEditor<TData, string>({
22
+ const { value, setValue, error, isPending } = useCellEditor<TData, string>({
23
23
  cellContext,
24
24
  });
25
25
 
@@ -42,6 +42,7 @@ export const DsTableEditCellText = <TData extends RowData>({
42
42
  value={value}
43
43
  placeholder={placeholder}
44
44
  maxLength={maxLength}
45
+ disabled={isPending}
45
46
  onValueChange={setValue}
46
47
  slots={error ? { endAdornment: <DsIcon icon="error" size="tiny" /> } : undefined}
47
48
  />
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef, type FormEvent } from 'react';
1
+ import { useEffect, useRef, type SubmitEvent } from 'react';
2
2
  import { flexRender } from '@tanstack/react-table';
3
3
  import { DsButtonV3 } from '../../../../ds-button-v3';
4
4
  import { DsStack } from '../../../../ds-stack';
@@ -7,13 +7,20 @@ import styles from './ds-table-editing-cell.module.scss';
7
7
  import type { DsTableEditingCellProps } from './ds-table-editing-cell.types';
8
8
 
9
9
  export const DsTableEditingCell = <TData, TValue>({ cell }: DsTableEditingCellProps<TData, TValue>) => {
10
- const { commit, cancel } = useDsTableContext<TData, TValue>();
10
+ const { commit, cancel, editing } = useDsTableContext<TData, TValue>();
11
11
  const columnDef = cell.column.columnDef;
12
12
  const editAnchorRef = useRef<HTMLDivElement>(null);
13
13
 
14
- const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
14
+ const isActiveCell = editing?.cell.id === cell.id;
15
+ const isPending = isActiveCell && editing.pending;
16
+ const hasError = isActiveCell && editing.error !== null;
17
+
18
+ const handleSubmit = (event: SubmitEvent<HTMLFormElement>) => {
15
19
  event.preventDefault();
16
20
  event.stopPropagation();
21
+ if (isPending || hasError) {
22
+ return;
23
+ }
17
24
  commit();
18
25
  };
19
26
 
@@ -62,6 +69,8 @@ export const DsTableEditingCell = <TData, TValue>({ cell }: DsTableEditingCellPr
62
69
  variant="tertiary"
63
70
  size="small"
64
71
  icon="check"
72
+ loading={isPending}
73
+ disabled={hasError}
65
74
  aria-label="Confirm edit"
66
75
  onClick={(event) => {
67
76
  event.stopPropagation();