@jmruthers/pace-core 0.5.105 → 0.5.107

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 (159) hide show
  1. package/dist/{DataTable-BE0OXZKQ.d.ts → DataTable-D5cBRca8.d.ts} +1 -1
  2. package/dist/{DataTable-LWHFLTEW.js → DataTable-H2WIR2DN.js} +3 -3
  3. package/dist/{chunk-QPCAGLUS.js → chunk-4OX5PXHX.js} +5 -2
  4. package/dist/chunk-4OX5PXHX.js.map +1 -0
  5. package/dist/{chunk-75G3NZWN.js → chunk-5JJCXTVE.js} +293 -37
  6. package/dist/chunk-5JJCXTVE.js.map +1 -0
  7. package/dist/{chunk-HBGPLSA5.js → chunk-DMNMZKWS.js} +70 -24
  8. package/dist/chunk-DMNMZKWS.js.map +1 -0
  9. package/dist/{chunk-AZFPGDCJ.js → chunk-EWKCROSF.js} +133 -49
  10. package/dist/chunk-EWKCROSF.js.map +1 -0
  11. package/dist/{chunk-4BWGRQBG.js → chunk-NFPV7MRN.js} +22 -2
  12. package/dist/chunk-NFPV7MRN.js.map +1 -0
  13. package/dist/{chunk-DWYMGSGU.js → chunk-VJ7MPS2K.js} +2 -2
  14. package/dist/components.d.ts +3 -3
  15. package/dist/components.js +4 -4
  16. package/dist/{formatting-BfDeV-ja.d.ts → formatting-BiEv5oEk.d.ts} +32 -2
  17. package/dist/hooks.d.ts +2 -2
  18. package/dist/hooks.js +3 -3
  19. package/dist/index.d.ts +5 -5
  20. package/dist/index.js +6 -6
  21. package/dist/{types-BDg1mAGG.d.ts → types-D4TVpDa1.d.ts} +24 -1
  22. package/dist/{useToast-Bm6TnSK-.d.ts → useToast-DRah6K-g.d.ts} +5 -2
  23. package/dist/utils.d.ts +3 -3
  24. package/dist/utils.js +2 -2
  25. package/docs/api/classes/ColumnFactory.md +1 -1
  26. package/docs/api/classes/ErrorBoundary.md +1 -1
  27. package/docs/api/classes/InvalidScopeError.md +1 -1
  28. package/docs/api/classes/MissingUserContextError.md +1 -1
  29. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  30. package/docs/api/classes/PermissionDeniedError.md +1 -1
  31. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  32. package/docs/api/classes/RBACAuditManager.md +1 -1
  33. package/docs/api/classes/RBACCache.md +1 -1
  34. package/docs/api/classes/RBACEngine.md +1 -1
  35. package/docs/api/classes/RBACError.md +1 -1
  36. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  37. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  38. package/docs/api/classes/StorageUtils.md +1 -1
  39. package/docs/api/enums/FileCategory.md +1 -1
  40. package/docs/api/interfaces/AggregateConfig.md +4 -4
  41. package/docs/api/interfaces/ButtonProps.md +1 -1
  42. package/docs/api/interfaces/CardProps.md +1 -1
  43. package/docs/api/interfaces/ColorPalette.md +1 -1
  44. package/docs/api/interfaces/ColorShade.md +1 -1
  45. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  46. package/docs/api/interfaces/DataRecord.md +1 -1
  47. package/docs/api/interfaces/DataTableAction.md +18 -18
  48. package/docs/api/interfaces/DataTableColumn.md +115 -10
  49. package/docs/api/interfaces/DataTableProps.md +38 -38
  50. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  51. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  52. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  53. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  54. package/docs/api/interfaces/FileMetadata.md +1 -1
  55. package/docs/api/interfaces/FileReference.md +1 -1
  56. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  57. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  58. package/docs/api/interfaces/FileUploadProps.md +1 -1
  59. package/docs/api/interfaces/FooterProps.md +1 -1
  60. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  61. package/docs/api/interfaces/InputProps.md +1 -1
  62. package/docs/api/interfaces/LabelProps.md +1 -1
  63. package/docs/api/interfaces/LoginFormProps.md +1 -1
  64. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  65. package/docs/api/interfaces/NavigationContextType.md +1 -1
  66. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  67. package/docs/api/interfaces/NavigationItem.md +1 -1
  68. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  69. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  70. package/docs/api/interfaces/Organisation.md +1 -1
  71. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  72. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  73. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  74. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  75. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  76. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  77. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  78. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  79. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  80. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  81. package/docs/api/interfaces/PaletteData.md +1 -1
  82. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  83. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  84. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  85. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  86. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  87. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  88. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  89. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  90. package/docs/api/interfaces/RBACConfig.md +1 -1
  91. package/docs/api/interfaces/RBACLogger.md +1 -1
  92. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  93. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  94. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  95. package/docs/api/interfaces/RouteConfig.md +1 -1
  96. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  97. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  98. package/docs/api/interfaces/StorageConfig.md +1 -1
  99. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  100. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  101. package/docs/api/interfaces/StorageListOptions.md +1 -1
  102. package/docs/api/interfaces/StorageListResult.md +1 -1
  103. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  104. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  105. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  106. package/docs/api/interfaces/StyleImport.md +1 -1
  107. package/docs/api/interfaces/SwitchProps.md +1 -1
  108. package/docs/api/interfaces/ToastActionElement.md +1 -1
  109. package/docs/api/interfaces/ToastProps.md +1 -1
  110. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  111. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  112. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  113. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  114. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  115. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  116. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  117. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  118. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  119. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  120. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  121. package/docs/api/interfaces/UserEventAccess.md +1 -1
  122. package/docs/api/interfaces/UserMenuProps.md +1 -1
  123. package/docs/api/interfaces/UserProfile.md +1 -1
  124. package/docs/api/modules.md +39 -18
  125. package/docs/api-reference/utilities.md +26 -3
  126. package/docs/implementation-guides/data-tables.md +390 -0
  127. package/package.json +1 -1
  128. package/src/components/DataTable/DataTable.tsx +4 -0
  129. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +25 -10
  130. package/src/components/DataTable/components/EditableRow.tsx +174 -16
  131. package/src/components/DataTable/components/UnifiedTableBody.tsx +205 -35
  132. package/src/components/DataTable/types.ts +34 -4
  133. package/src/components/FileDisplay/FileDisplay.test.tsx +184 -201
  134. package/src/components/FileDisplay/FileDisplay.tsx +40 -39
  135. package/src/components/NavigationMenu/NavigationMenu.test.tsx +189 -13
  136. package/src/components/NavigationMenu/NavigationMenu.tsx +142 -35
  137. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +4 -4
  138. package/src/components/Toast/Toast.tsx +1 -1
  139. package/src/hooks/public/usePublicFileDisplay.ts +25 -15
  140. package/src/hooks/useEventTheme.test.ts +11 -0
  141. package/src/hooks/useFileDisplay.ts +11 -0
  142. package/src/hooks/useSecureDataAccess.test.ts +22 -5
  143. package/src/hooks/useToast.ts +11 -2
  144. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +67 -3
  145. package/src/providers/__tests__/ProviderLifecycle.test.tsx +72 -4
  146. package/src/services/__tests__/OrganisationService.pagination.test.ts +10 -2
  147. package/src/styles/core.css +11 -0
  148. package/src/utils/__tests__/formatting.unit.test.ts +33 -0
  149. package/src/utils/file-reference.test.ts +44 -5
  150. package/src/utils/file-reference.ts +49 -26
  151. package/src/utils/formatting.ts +57 -2
  152. package/src/validation/__tests__/passwordSchema.unit.test.ts +3 -3
  153. package/dist/chunk-4BWGRQBG.js.map +0 -1
  154. package/dist/chunk-75G3NZWN.js.map +0 -1
  155. package/dist/chunk-AZFPGDCJ.js.map +0 -1
  156. package/dist/chunk-HBGPLSA5.js.map +0 -1
  157. package/dist/chunk-QPCAGLUS.js.map +0 -1
  158. /package/dist/{DataTable-LWHFLTEW.js.map → DataTable-H2WIR2DN.js.map} +0 -0
  159. /package/dist/{chunk-DWYMGSGU.js.map → chunk-VJ7MPS2K.js.map} +0 -0
@@ -21,7 +21,7 @@ import { ActionButtons } from './ActionButtons';
21
21
  import { EditableRow } from './EditableRow';
22
22
  import { getTableCellClasses, getTableHeadClasses, getTableRowClasses } from '../styles';
23
23
  import { Input } from '../../Input/Input';
24
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../Select/Select';
24
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectGroup, SelectLabel, SelectSeparator } from '../../Select/Select';
25
25
  import type {
26
26
  AggregateConfig,
27
27
  DataRecord,
@@ -120,6 +120,165 @@ interface UnifiedTableBodyProps<TData extends DataRecord> {
120
120
  };
121
121
  }
122
122
 
123
+ // Component for select fields with searchable and creatable support
124
+ function SelectEditField<TData extends DataRecord>({
125
+ columnDef,
126
+ accessorKey,
127
+ currentValue,
128
+ placeholder,
129
+ onChange,
130
+ }: {
131
+ columnDef: EditableColumnDef<TData>;
132
+ accessorKey: string;
133
+ currentValue: CellValue;
134
+ placeholder?: string;
135
+ onChange: (value: CellValue) => void;
136
+ }) {
137
+ const isSearchable = columnDef.selectSearchable !== false; // Default to true for better UX
138
+ const isCreatable = columnDef.creatable === true;
139
+ const selectRef = React.useRef<HTMLFormElement>(null);
140
+ const [searchTerm, setSearchTerm] = React.useState('');
141
+ const [isOpen, setIsOpen] = React.useState(false);
142
+ const [showCreateOption, setShowCreateOption] = React.useState(false);
143
+
144
+ // Monitor search input value via DOM events to detect when user types
145
+ React.useEffect(() => {
146
+ if (!isOpen || !isSearchable || !isCreatable || !selectRef.current) return;
147
+
148
+ const searchInput = selectRef.current.querySelector<HTMLInputElement>('[data-testid="select-search-input"]');
149
+ if (!searchInput) return;
150
+
151
+ const handleInput = (e: Event) => {
152
+ const target = e.target as HTMLInputElement;
153
+ const currentSearch = target.value;
154
+ setSearchTerm(currentSearch);
155
+
156
+ // Check if search doesn't match any option (including items in groups)
157
+ if (currentSearch.trim()) {
158
+ const searchLower = currentSearch.toLowerCase().trim();
159
+
160
+ // Helper to check if an option matches
161
+ // Use explicit union type instead of typeof to avoid Babel parsing issues
162
+ type FieldOption =
163
+ | { value: string | number; label: string }
164
+ | { type: 'group'; label: string; items: Array<{ value: string | number; label: string }> }
165
+ | { type: 'separator' };
166
+
167
+ const checkMatch = (opt: FieldOption): boolean => {
168
+ // Simple option
169
+ if ('value' in opt && !('type' in opt)) {
170
+ return opt.label.toLowerCase().includes(searchLower);
171
+ }
172
+ // Group - check items within the group
173
+ if ('type' in opt && opt.type === 'group') {
174
+ return (opt as { type: 'group'; label: string; items: Array<{ value: string | number; label: string }> }).items.some((item: { value: string | number; label: string }) => item.label.toLowerCase().includes(searchLower));
175
+ }
176
+ // Separator - doesn't match
177
+ return false;
178
+ };
179
+
180
+ const hasMatch = (columnDef.fieldOptions || []).some(checkMatch);
181
+ setShowCreateOption(!hasMatch);
182
+ } else {
183
+ setShowCreateOption(false);
184
+ }
185
+ };
186
+
187
+ searchInput.addEventListener('input', handleInput);
188
+
189
+ return () => {
190
+ searchInput.removeEventListener('input', handleInput);
191
+ };
192
+ }, [isOpen, isSearchable, isCreatable, columnDef.fieldOptions]);
193
+
194
+ const handleCreateNew = React.useCallback(async () => {
195
+ if (!isCreatable || !columnDef.onCreateNew || !searchTerm.trim()) return;
196
+
197
+ try {
198
+ const newValue = await columnDef.onCreateNew(searchTerm.trim());
199
+ onChange(newValue);
200
+ setSearchTerm('');
201
+ setShowCreateOption(false);
202
+ } catch (error) {
203
+ console.error('Error creating new item:', error);
204
+ }
205
+ }, [isCreatable, columnDef.onCreateNew, searchTerm, onChange]);
206
+
207
+ return (
208
+ <Select
209
+ ref={selectRef}
210
+ value={String(currentValue)}
211
+ onValueChange={(newValue) => {
212
+ if (newValue.startsWith('__create_new__')) {
213
+ handleCreateNew();
214
+ } else {
215
+ onChange(newValue as CellValue);
216
+ }
217
+ }}
218
+ onOpenChange={(open) => {
219
+ setIsOpen(open);
220
+ if (!open) {
221
+ setSearchTerm('');
222
+ setShowCreateOption(false);
223
+ }
224
+ }}
225
+ >
226
+ <SelectTrigger className="h-8">
227
+ <SelectValue placeholder={placeholder || `Select ${columnDef.header || 'option'}...`} />
228
+ </SelectTrigger>
229
+ <SelectContent
230
+ searchable={isSearchable}
231
+ searchPlaceholder={`Search ${columnDef.header || 'options'}...`}
232
+ maxHeight={columnDef.selectMaxHeight}
233
+ className={columnDef.selectContentClassName}
234
+ style={columnDef.selectContentStyle}
235
+ >
236
+ {columnDef.fieldOptions?.map((option, index) => {
237
+ // Simple option item
238
+ if ('value' in option && !('type' in option)) {
239
+ return (
240
+ <SelectItem key={`${option.value}-${index}`} value={String(option.value)}>
241
+ {option.label}
242
+ </SelectItem>
243
+ );
244
+ }
245
+
246
+ // Separator
247
+ if ('type' in option && option.type === 'separator') {
248
+ return <SelectSeparator key={`separator-${index}`} />;
249
+ }
250
+
251
+ // Group with label
252
+ if ('type' in option && option.type === 'group') {
253
+ const groupOption = option as { type: 'group'; label: string; items: Array<{ value: string | number; label: string }> };
254
+ return (
255
+ <SelectGroup key={`group-${groupOption.label}-${index}`}>
256
+ <SelectLabel>{groupOption.label}</SelectLabel>
257
+ {groupOption.items.map((item: { value: string | number; label: string }) => (
258
+ <SelectItem key={`${item.value}-${index}`} value={String(item.value)}>
259
+ {item.label}
260
+ </SelectItem>
261
+ ))}
262
+ </SelectGroup>
263
+ );
264
+ }
265
+
266
+ return null;
267
+ })}
268
+ {showCreateOption && isCreatable && searchTerm.trim() && (
269
+ <SelectItem
270
+ key="__create_new__"
271
+ value={`__create_new__${searchTerm}`}
272
+ className="bg-main-100 font-medium border-t border-main-200"
273
+ >
274
+ Create "{searchTerm}"
275
+ </SelectItem>
276
+ )}
277
+ </SelectContent>
278
+ </Select>
279
+ );
280
+ }
281
+
123
282
  // Helper function to render the appropriate input type based on column configuration
124
283
  const renderEditField = <TData extends DataRecord>(
125
284
  column: Column<TData, unknown>,
@@ -143,33 +302,26 @@ const renderEditField = <TData extends DataRecord>(
143
302
  const currentValue = editingData[accessorKey] ?? value ?? '';
144
303
 
145
304
  return (
146
- <Select
147
- value={String(currentValue)}
148
- onValueChange={(newValue) => onChange({ [accessorKey]: newValue as CellValue })}
149
- >
150
- <SelectTrigger className="h-8">
151
- <SelectValue placeholder={placeholder || `Select ${columnDef.header || column.id}...`} />
152
- </SelectTrigger>
153
- <SelectContent>
154
- {columnDef.fieldOptions.map(option => (
155
- <SelectItem key={option.value} value={String(option.value)}>
156
- {option.label}
157
- </SelectItem>
158
- ))}
159
- </SelectContent>
160
- </Select>
305
+ <SelectEditField
306
+ columnDef={columnDef}
307
+ accessorKey={accessorKey}
308
+ currentValue={currentValue}
309
+ placeholder={placeholder}
310
+ onChange={(newValue) => onChange({ [accessorKey]: newValue })}
311
+ />
161
312
  );
162
313
  }
163
314
 
164
315
  // Check for number type
165
316
  if (columnDef.fieldType === 'number') {
317
+ const hideSpinners = columnDef.hideNumberSpinners !== false; // Default to true
166
318
  return (
167
319
  <Input
168
320
  type="number"
169
321
  value={String(value ?? '')}
170
322
  onChange={(e) => onChange(e.target.value as unknown as CellValue)}
171
323
  placeholder={placeholder || `Enter ${columnDef.header || column.id}...`}
172
- className="h-8"
324
+ className={`h-8 ${hideSpinners ? 'datatable-number-no-spinners' : ''}`}
173
325
  />
174
326
  );
175
327
  }
@@ -709,24 +861,42 @@ export function UnifiedTableBody<TData extends Record<string, any>>({
709
861
  ? header.column.getIsVisible()
710
862
  : true;
711
863
  })
712
- ?.filter(header => header.column.id !== 'actions' && header.column.id !== 'select')
713
- ?.map((header) => (
714
- <td
715
- key={header.column.id}
716
- className={getTableCellClasses({
717
- isCompact: true,
718
- className: "px-3 py-2"
719
- })}
720
- >
721
- {renderEditField(header.column, creationData[header.column.id], (value) => {
722
- if (typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
723
- onCreationDataChange({ ...creationData, ...(value as Record<string, CellValue>) });
724
- } else {
725
- onCreationDataChange({ ...creationData, [header.column.id]: value as CellValue });
726
- }
727
- }, creationData)}
728
- </td>
729
- ))}
864
+ ?.filter(header => header.column.id !== 'actions') // Only exclude actions column
865
+ ?.map((header) => {
866
+ // Handle select column separately - render empty checkbox cell for alignment
867
+ if (header.column.id === 'select') {
868
+ return (
869
+ <td
870
+ key={header.column.id}
871
+ className={getTableCellClasses({
872
+ isCompact: true,
873
+ className: "px-3 py-2"
874
+ })}
875
+ >
876
+ {/* Empty cell for selection checkbox to maintain alignment */}
877
+ </td>
878
+ );
879
+ }
880
+
881
+ // Render edit fields for data columns
882
+ return (
883
+ <td
884
+ key={header.column.id}
885
+ className={getTableCellClasses({
886
+ isCompact: true,
887
+ className: "px-3 py-2"
888
+ })}
889
+ >
890
+ {renderEditField(header.column, creationData[header.column.id], (value) => {
891
+ if (typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
892
+ onCreationDataChange({ ...creationData, ...(value as Record<string, CellValue>) });
893
+ } else {
894
+ onCreationDataChange({ ...creationData, [header.column.id]: value as CellValue });
895
+ }
896
+ }, creationData)}
897
+ </td>
898
+ );
899
+ })}
730
900
  <td
731
901
  className={getTableCellClasses({
732
902
  isCompact: true,
@@ -292,8 +292,26 @@ export interface DataTableColumn<TData extends DataRecord = DataRecord> extends
292
292
  memoizedCell?: React.ComponentType<{ row: TData }>;
293
293
  /** Field type for editing (text, select, date, etc.) */
294
294
  fieldType?: 'text' | 'select' | 'date' | 'number' | 'boolean';
295
- /** Options for select fields */
296
- fieldOptions?: Array<{ value: string | number; label: string }>;
295
+ /** Options for select fields - can be simple items or grouped with labels and separators */
296
+ fieldOptions?: Array<
297
+ | { value: string | number; label: string } // Simple option
298
+ | { type: 'group'; label: string; items: Array<{ value: string | number; label: string }> } // Group with label
299
+ | { type: 'separator' } // Visual separator
300
+ >;
301
+ /** Enable keyboard search/filtering in select dropdowns within editable columns (default: true). When fieldType is 'select', this controls dropdown searchability, not global search. */
302
+ selectSearchable?: boolean;
303
+ /** Enable creating new items in select dropdowns (default: false) */
304
+ creatable?: boolean;
305
+ /** Callback to create a new item when user types non-matching text in select dropdown */
306
+ onCreateNew?: (inputValue: string) => Promise<string | number> | string | number;
307
+ /** Maximum height for select dropdown content (default: "20rem") */
308
+ selectMaxHeight?: string;
309
+ /** Custom className for select content dropdown */
310
+ selectContentClassName?: string;
311
+ /** Custom style for select content dropdown */
312
+ selectContentStyle?: React.CSSProperties;
313
+ /** Hide spinner arrows on number input fields (default: true for DataTable) */
314
+ hideNumberSpinners?: boolean;
297
315
  /** Filter type for column filtering (text, select, number, date) */
298
316
  filterType?: 'text' | 'select' | 'number' | 'date';
299
317
  /** Options for select filters (alternative to fieldOptions) */
@@ -320,8 +338,20 @@ export interface EditableColumnDef<TData extends DataRecord = DataRecord> extend
320
338
  editable?: boolean;
321
339
  /** Field type used to determine edit control */
322
340
  fieldType?: 'text' | 'select' | 'date' | 'number' | 'boolean';
323
- /** Options for select based editors */
324
- fieldOptions?: Array<{ value: string | number; label: string }>;
341
+ /** Options for select based editors - can be simple items or grouped with labels and separators */
342
+ fieldOptions?: Array<
343
+ | { value: string | number; label: string } // Simple option
344
+ | { type: 'group'; label: string; items: Array<{ value: string | number; label: string }> } // Group with label
345
+ | { type: 'separator' } // Visual separator
346
+ >;
347
+ /** Enable keyboard search/filtering in select dropdowns (inherits from DataTableColumn.selectSearchable) */
348
+ selectSearchable?: boolean;
349
+ /** Enable creating new items in select dropdowns */
350
+ creatable?: boolean;
351
+ /** Callback to create a new item when user types non-matching text */
352
+ onCreateNew?: (inputValue: string) => Promise<string | number> | string | number;
353
+ /** Hide spinner arrows on number input fields */
354
+ hideNumberSpinners?: boolean;
325
355
  }
326
356
 
327
357
  /**