@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.
- package/dist/{DataTable-BE0OXZKQ.d.ts → DataTable-D5cBRca8.d.ts} +1 -1
- package/dist/{DataTable-LWHFLTEW.js → DataTable-H2WIR2DN.js} +3 -3
- package/dist/{chunk-QPCAGLUS.js → chunk-4OX5PXHX.js} +5 -2
- package/dist/chunk-4OX5PXHX.js.map +1 -0
- package/dist/{chunk-75G3NZWN.js → chunk-5JJCXTVE.js} +293 -37
- package/dist/chunk-5JJCXTVE.js.map +1 -0
- package/dist/{chunk-HBGPLSA5.js → chunk-DMNMZKWS.js} +70 -24
- package/dist/chunk-DMNMZKWS.js.map +1 -0
- package/dist/{chunk-AZFPGDCJ.js → chunk-EWKCROSF.js} +133 -49
- package/dist/chunk-EWKCROSF.js.map +1 -0
- package/dist/{chunk-4BWGRQBG.js → chunk-NFPV7MRN.js} +22 -2
- package/dist/chunk-NFPV7MRN.js.map +1 -0
- package/dist/{chunk-DWYMGSGU.js → chunk-VJ7MPS2K.js} +2 -2
- package/dist/components.d.ts +3 -3
- package/dist/components.js +4 -4
- package/dist/{formatting-BfDeV-ja.d.ts → formatting-BiEv5oEk.d.ts} +32 -2
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +3 -3
- package/dist/index.d.ts +5 -5
- package/dist/index.js +6 -6
- package/dist/{types-BDg1mAGG.d.ts → types-D4TVpDa1.d.ts} +24 -1
- package/dist/{useToast-Bm6TnSK-.d.ts → useToast-DRah6K-g.d.ts} +5 -2
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +2 -2
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +4 -4
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +18 -18
- package/docs/api/interfaces/DataTableColumn.md +115 -10
- package/docs/api/interfaces/DataTableProps.md +38 -38
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +39 -18
- package/docs/api-reference/utilities.md +26 -3
- package/docs/implementation-guides/data-tables.md +390 -0
- package/package.json +1 -1
- package/src/components/DataTable/DataTable.tsx +4 -0
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +25 -10
- package/src/components/DataTable/components/EditableRow.tsx +174 -16
- package/src/components/DataTable/components/UnifiedTableBody.tsx +205 -35
- package/src/components/DataTable/types.ts +34 -4
- package/src/components/FileDisplay/FileDisplay.test.tsx +184 -201
- package/src/components/FileDisplay/FileDisplay.tsx +40 -39
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +189 -13
- package/src/components/NavigationMenu/NavigationMenu.tsx +142 -35
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +4 -4
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/hooks/public/usePublicFileDisplay.ts +25 -15
- package/src/hooks/useEventTheme.test.ts +11 -0
- package/src/hooks/useFileDisplay.ts +11 -0
- package/src/hooks/useSecureDataAccess.test.ts +22 -5
- package/src/hooks/useToast.ts +11 -2
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +67 -3
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +72 -4
- package/src/services/__tests__/OrganisationService.pagination.test.ts +10 -2
- package/src/styles/core.css +11 -0
- package/src/utils/__tests__/formatting.unit.test.ts +33 -0
- package/src/utils/file-reference.test.ts +44 -5
- package/src/utils/file-reference.ts +49 -26
- package/src/utils/formatting.ts +57 -2
- package/src/validation/__tests__/passwordSchema.unit.test.ts +3 -3
- package/dist/chunk-4BWGRQBG.js.map +0 -1
- package/dist/chunk-75G3NZWN.js.map +0 -1
- package/dist/chunk-AZFPGDCJ.js.map +0 -1
- package/dist/chunk-HBGPLSA5.js.map +0 -1
- package/dist/chunk-QPCAGLUS.js.map +0 -1
- /package/dist/{DataTable-LWHFLTEW.js.map → DataTable-H2WIR2DN.js.map} +0 -0
- /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
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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=
|
|
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'
|
|
713
|
-
?.map((header) =>
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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<
|
|
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<
|
|
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
|
/**
|