@jmruthers/pace-core 0.5.136 → 0.5.139
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-CYOHOX3O.js → DataTable-JXFCA2BJ.js} +10 -9
- package/dist/{EventLogo-801uofbR.d.ts → EventLogo-rFL_kRjk.d.ts} +73 -1
- package/dist/{UnifiedAuthProvider-5E5TUNMS.js → UnifiedAuthProvider-XIQQ7LVU.js} +4 -5
- package/dist/{chunk-YLKIDTUK.js → chunk-22WKWKRX.js} +4 -4
- package/dist/{chunk-TVYPTYOY.js → chunk-4C7EXCAR.js} +60 -24
- package/dist/chunk-4C7EXCAR.js.map +1 -0
- package/dist/{chunk-NOHEVYVX.js → chunk-5JMOHWDI.js} +417 -319
- package/dist/chunk-5JMOHWDI.js.map +1 -0
- package/dist/{chunk-FHWWBIHA.js → chunk-6DXZ6V5Q.js} +5 -5
- package/dist/{chunk-2TWNJ46Y.js → chunk-6LAAY47Q.js} +2 -2
- package/dist/{chunk-444EZN6N.js → chunk-7QCC6MCP.js} +88 -1
- package/dist/chunk-7QCC6MCP.js.map +1 -0
- package/dist/chunk-BJPBT3CU.js +21 -0
- package/dist/chunk-BJPBT3CU.js.map +1 -0
- package/dist/{chunk-L6PGMCMD.js → chunk-BOOI7GK2.js} +38 -12
- package/dist/chunk-BOOI7GK2.js.map +1 -0
- package/dist/{chunk-XARJS7CD.js → chunk-INQLMHPF.js} +2 -2
- package/dist/chunk-JISYG63F.js +70 -0
- package/dist/chunk-JISYG63F.js.map +1 -0
- package/dist/{chunk-SL2YQDR6.js → chunk-MA6EPSGZ.js} +2 -2
- package/dist/{chunk-5DPZ5EAT.js → chunk-OWAG3GSU.js} +1 -3
- package/dist/{chunk-LTV3XIJJ.js → chunk-T6JN6LH6.js} +4 -4
- package/dist/{chunk-HJGGOMQ6.js → chunk-TLT2ZR3L.js} +147 -103
- package/dist/chunk-TLT2ZR3L.js.map +1 -0
- package/dist/{chunk-4MT5BGGL.js → chunk-YCWDTTUK.js} +4 -6
- package/dist/{chunk-4MT5BGGL.js.map → chunk-YCWDTTUK.js.map} +1 -1
- package/dist/components.d.ts +1 -1
- package/dist/components.js +12 -11
- package/dist/components.js.map +1 -1
- package/dist/hooks.js +8 -9
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +15 -14
- package/dist/index.js.map +1 -1
- package/dist/providers.js +3 -4
- package/dist/rbac/index.js +8 -9
- package/dist/schema-DTDZQe2u.d.ts +28 -0
- package/dist/types.d.ts +152 -3
- package/dist/types.js +51 -16
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +89 -4
- package/dist/utils.js +214 -96
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +1 -343
- package/dist/validation.js +3 -100
- 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 +1 -1
- package/docs/api/interfaces/BadgeProps.md +27 -0
- 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 +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.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/GrantEventAppRoleParams.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/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.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/SessionRestorationLoaderProps.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 +84 -15
- package/docs/architecture/README.md +0 -1
- package/docs/styles/README.md +0 -2
- package/examples/RBAC/CompleteRBACExample.tsx +324 -0
- package/examples/RBAC/EventBasedApp.tsx +239 -0
- package/examples/RBAC/PermissionExample.tsx +151 -0
- package/examples/RBAC/index.ts +13 -0
- package/examples/public-pages/CorrectPublicPageImplementation.tsx +301 -0
- package/examples/public-pages/PublicEventPage.tsx +274 -0
- package/examples/public-pages/PublicPageApp.tsx +308 -0
- package/examples/public-pages/PublicPageUsageExample.tsx +216 -0
- package/examples/public-pages/index.ts +14 -0
- package/package.json +1 -10
- package/src/__tests__/TEST_STANDARD.md +92 -0
- package/src/components/Badge/Badge.test.tsx +314 -0
- package/src/components/Badge/Badge.tsx +304 -0
- package/src/components/Badge/index.ts +3 -0
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
- package/src/components/DataTable/__tests__/styles.test.ts +1 -1
- package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
- package/src/components/DataTable/components/DataTableBody.tsx +461 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
- package/src/components/DataTable/components/FilterRow.tsx +9 -3
- package/src/components/DataTable/components/PaginationControls.tsx +1 -0
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
- package/src/components/DataTable/core/ActionManager.ts +235 -0
- package/src/components/DataTable/core/ColumnManager.ts +205 -0
- package/src/components/DataTable/core/DataManager.ts +188 -0
- package/src/components/DataTable/core/DataTableContext.tsx +181 -0
- package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
- package/src/components/DataTable/core/PluginRegistry.ts +229 -0
- package/src/components/DataTable/core/StateManager.ts +311 -0
- package/src/components/DataTable/core/interfaces.ts +338 -0
- package/src/components/DataTable/styles.ts +27 -6
- package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
- package/src/components/DataTable/utils/columnUtils.ts +40 -0
- package/src/components/DataTable/utils/debugTools.ts +609 -0
- package/src/components/DataTable/utils/index.ts +1 -0
- package/src/components/Dialog/README.md +804 -0
- package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
- package/src/components/Dialog/utils/safeHtml.ts +185 -0
- package/src/components/Footer/Footer.test.tsx +1 -1
- package/src/components/Form/Form.test.tsx +1 -1
- package/src/components/Form/FormErrorSummary.tsx +113 -0
- package/src/components/Form/FormFieldset.tsx +127 -0
- package/src/components/Form/FormLiveRegion.tsx +198 -0
- package/src/components/LoginForm/LoginForm.test.tsx +1 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
- package/src/components/PasswordReset/PasswordResetForm.test.tsx +597 -0
- package/src/components/PasswordReset/PasswordResetForm.tsx +201 -0
- package/src/components/PublicLayout/PublicPageDebugger.tsx +104 -0
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +162 -0
- package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
- package/src/components/Select/Select.test.tsx +1 -1
- package/src/components/Select/Select.tsx +20 -8
- package/src/components/Table/__tests__/Table.test.tsx +1 -1
- package/src/components/index.ts +3 -0
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
- package/src/index.ts +4 -0
- package/src/rbac/hooks/useCan.test.ts +24 -0
- package/src/rbac/hooks/usePermissions.ts +49 -12
- package/src/styles/core.css +3 -0
- package/src/utils/appConfig.ts +47 -0
- package/src/utils/appIdResolver.test.ts +499 -0
- package/src/utils/appIdResolver.ts +130 -0
- package/src/utils/appNameResolver.simple.test.ts +212 -0
- package/src/utils/appNameResolver.test.ts +121 -0
- package/src/utils/appNameResolver.ts +191 -0
- package/src/utils/audit.ts +127 -0
- package/src/utils/auth-utils.ts +96 -0
- package/src/utils/bundleAnalysis.ts +129 -0
- package/src/utils/cn.ts +7 -0
- package/src/utils/debugLogger.ts +67 -0
- package/src/utils/deviceFingerprint.ts +215 -0
- package/src/utils/dynamicUtils.ts +105 -0
- package/src/utils/file-reference.test.ts +788 -0
- package/src/utils/file-reference.ts +519 -0
- package/src/utils/formatDate.test.ts +237 -0
- package/src/utils/formatting.ts +133 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/lazyLoad.tsx +44 -0
- package/src/utils/logger.ts +179 -0
- package/src/utils/organisationContext.test.ts +322 -0
- package/src/utils/organisationContext.ts +153 -0
- package/src/utils/performanceBenchmark.ts +64 -0
- package/src/utils/performanceBudgets.ts +110 -0
- package/src/utils/permissionTypes.ts +37 -0
- package/src/utils/permissionUtils.test.ts +393 -0
- package/src/utils/permissionUtils.ts +34 -0
- package/src/utils/sanitization.ts +264 -0
- package/src/utils/schemaUtils.ts +37 -0
- package/src/utils/secureDataAccess.test.ts +711 -0
- package/src/utils/secureDataAccess.ts +377 -0
- package/src/utils/secureErrors.ts +79 -0
- package/src/utils/secureStorage.ts +244 -0
- package/src/utils/security.ts +156 -0
- package/src/utils/securityMonitor.ts +45 -0
- package/src/utils/sessionTracking.ts +126 -0
- package/src/utils/validation.ts +111 -0
- package/src/utils/validationUtils.ts +120 -0
- package/src/validation/index.ts +2 -2
- package/dist/chunk-444EZN6N.js.map +0 -1
- package/dist/chunk-APIBCTL2.js +0 -670
- package/dist/chunk-APIBCTL2.js.map +0 -1
- package/dist/chunk-HJGGOMQ6.js.map +0 -1
- package/dist/chunk-K2WWTH7O.js +0 -94
- package/dist/chunk-K2WWTH7O.js.map +0 -1
- package/dist/chunk-L6PGMCMD.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-NOHEVYVX.js.map +0 -1
- package/dist/chunk-TVYPTYOY.js.map +0 -1
- package/dist/validation-8npbysjg.d.ts +0 -177
- /package/dist/{DataTable-CYOHOX3O.js.map → DataTable-JXFCA2BJ.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-5E5TUNMS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
- /package/dist/{chunk-YLKIDTUK.js.map → chunk-22WKWKRX.js.map} +0 -0
- /package/dist/{chunk-FHWWBIHA.js.map → chunk-6DXZ6V5Q.js.map} +0 -0
- /package/dist/{chunk-2TWNJ46Y.js.map → chunk-6LAAY47Q.js.map} +0 -0
- /package/dist/{chunk-XARJS7CD.js.map → chunk-INQLMHPF.js.map} +0 -0
- /package/dist/{chunk-SL2YQDR6.js.map → chunk-MA6EPSGZ.js.map} +0 -0
- /package/dist/{chunk-5DPZ5EAT.js.map → chunk-OWAG3GSU.js.map} +0 -0
- /package/dist/{chunk-LTV3XIJJ.js.map → chunk-T6JN6LH6.js.map} +0 -0
- /package/examples/{components → components 2}/DataTable/HierarchicalActionsExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/HierarchicalExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/InitialPageSizeExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/PerformanceExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/index.ts +0 -0
- /package/examples/{components → components 2}/Dialog/BasicHtmlTest.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/DebugHtmlExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/HtmlDialogExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/ScrollableDialogExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/SimpleHtmlTest.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/SmartDialogExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/index.ts +0 -0
- /package/examples/{components → components 2}/index.ts +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file DataTable Context
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/DataTable/Architecture
|
|
5
|
+
* @since 0.3.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { createContext, useContext, useMemo, useCallback, useRef, useEffect } from 'react';
|
|
9
|
+
import { useReactTable, getCoreRowModel, getFilteredRowModel, getSortedRowModel, getPaginationRowModel } from '@tanstack/react-table';
|
|
10
|
+
import { ChevronDown, ChevronUp, ChevronsUpDown, Filter, MoreHorizontal, Plus, Search, Settings, Trash2, Upload, Download, Eye, Edit, Copy } from 'lucide-react';
|
|
11
|
+
import { Button } from '../../Button/Button';
|
|
12
|
+
import { Input } from '../../Input/Input';
|
|
13
|
+
import { Checkbox } from '../../Checkbox/Checkbox';
|
|
14
|
+
// DropdownMenu components have been merged into Select components
|
|
15
|
+
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '../../Dialog/Dialog';
|
|
16
|
+
import type { DataTableContext as IDataTableContext, DataTableConfig, DataTableUtils } from './interfaces';
|
|
17
|
+
import type { DataRecord } from '../types';
|
|
18
|
+
import { DataManager } from './DataManager';
|
|
19
|
+
import { ColumnManagerImpl } from './ColumnManager';
|
|
20
|
+
import { ActionManagerImpl } from './ActionManager';
|
|
21
|
+
import { StateManagerImpl } from './StateManager';
|
|
22
|
+
import { PluginRegistryImpl } from './PluginRegistry';
|
|
23
|
+
import { LocalDataAdapter } from './LocalDataAdapter';
|
|
24
|
+
|
|
25
|
+
// Create the context
|
|
26
|
+
const DataTableContext = createContext<IDataTableContext<any> | null>(null);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* DataTable provider component
|
|
30
|
+
*/
|
|
31
|
+
export interface DataTableProviderProps<TData extends DataRecord> {
|
|
32
|
+
children: React.ReactNode;
|
|
33
|
+
config: DataTableConfig<TData>;
|
|
34
|
+
data?: TData[];
|
|
35
|
+
columns?: any[];
|
|
36
|
+
actions?: any[];
|
|
37
|
+
pageSize?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function DataTableProvider<TData extends DataRecord>({
|
|
41
|
+
children,
|
|
42
|
+
config,
|
|
43
|
+
data = [],
|
|
44
|
+
columns = [],
|
|
45
|
+
actions = [],
|
|
46
|
+
pageSize = 10,
|
|
47
|
+
}: DataTableProviderProps<TData>) {
|
|
48
|
+
// Create managers
|
|
49
|
+
const adapter = useMemo(() => new LocalDataAdapter<TData>(data), [data]);
|
|
50
|
+
const dataManager = useMemo(() => new DataManager<TData>(adapter), [adapter]);
|
|
51
|
+
const columnManager = useMemo(() => new ColumnManagerImpl<TData>(columns), [columns]);
|
|
52
|
+
const actionManager = useMemo(() => new ActionManagerImpl<TData>(actions), [actions]);
|
|
53
|
+
const stateManager = useMemo(() => new StateManagerImpl<TData>({
|
|
54
|
+
ui: {
|
|
55
|
+
globalFilter: '',
|
|
56
|
+
columnFilters: [],
|
|
57
|
+
sorting: [],
|
|
58
|
+
grouping: [],
|
|
59
|
+
expanded: {},
|
|
60
|
+
pagination: {
|
|
61
|
+
pageIndex: 0,
|
|
62
|
+
pageSize: pageSize,
|
|
63
|
+
},
|
|
64
|
+
rowSelection: {},
|
|
65
|
+
editing: {
|
|
66
|
+
rowId: null,
|
|
67
|
+
data: {},
|
|
68
|
+
isCreating: false,
|
|
69
|
+
creationData: {},
|
|
70
|
+
},
|
|
71
|
+
modals: {
|
|
72
|
+
import: false,
|
|
73
|
+
export: false,
|
|
74
|
+
view: false,
|
|
75
|
+
viewData: null,
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
}), [pageSize]);
|
|
79
|
+
const pluginRegistry = useMemo(() => new PluginRegistryImpl<TData>(), []);
|
|
80
|
+
|
|
81
|
+
// Create utilities
|
|
82
|
+
const utils: DataTableUtils<TData> = useMemo(() => ({
|
|
83
|
+
getRowId: (row: TData, index: number) => (row as any).id || String(index),
|
|
84
|
+
formatValue: (value: any, column: any) => String(value),
|
|
85
|
+
validateData: (data: Partial<TData>) => null,
|
|
86
|
+
debounce: <T extends (...args: any[]) => any>(func: T, delay: number) => {
|
|
87
|
+
let timeoutId: NodeJS.Timeout;
|
|
88
|
+
return ((...args: any[]) => {
|
|
89
|
+
clearTimeout(timeoutId);
|
|
90
|
+
timeoutId = setTimeout(() => func(...args), delay);
|
|
91
|
+
}) as T;
|
|
92
|
+
},
|
|
93
|
+
}), []);
|
|
94
|
+
|
|
95
|
+
// Create context value
|
|
96
|
+
const contextValue: IDataTableContext<TData> = useMemo(() => ({
|
|
97
|
+
dataManager,
|
|
98
|
+
columnManager,
|
|
99
|
+
actionManager,
|
|
100
|
+
stateManager,
|
|
101
|
+
pluginRegistry,
|
|
102
|
+
adapter,
|
|
103
|
+
observable: stateManager,
|
|
104
|
+
config,
|
|
105
|
+
utils,
|
|
106
|
+
}), [dataManager, columnManager, actionManager, stateManager, pluginRegistry, adapter, config, utils]);
|
|
107
|
+
|
|
108
|
+
// Initialize data
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
// Update the adapter with new data
|
|
111
|
+
adapter.setData(data);
|
|
112
|
+
|
|
113
|
+
if (data.length > 0) {
|
|
114
|
+
stateManager.updateData(data);
|
|
115
|
+
} else {
|
|
116
|
+
// Handle empty data case
|
|
117
|
+
stateManager.updateData([]);
|
|
118
|
+
}
|
|
119
|
+
}, [data, dataManager, stateManager, adapter]);
|
|
120
|
+
|
|
121
|
+
// Initialize columns
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (columns.length > 0) {
|
|
124
|
+
columnManager.setColumns(columns);
|
|
125
|
+
stateManager.updateColumns(columns);
|
|
126
|
+
}
|
|
127
|
+
}, [columns, columnManager, stateManager]);
|
|
128
|
+
|
|
129
|
+
// Initialize actions
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (actions.length > 0) {
|
|
132
|
+
actionManager.setActions(actions);
|
|
133
|
+
stateManager.updateActions(actions);
|
|
134
|
+
}
|
|
135
|
+
}, [actions, actionManager, stateManager]);
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<DataTableContext.Provider value={contextValue}>
|
|
139
|
+
{children}
|
|
140
|
+
</DataTableContext.Provider>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Hook to use DataTable context
|
|
146
|
+
*/
|
|
147
|
+
export function useDataTableContext<TData extends DataRecord = DataRecord>(): IDataTableContext<TData> {
|
|
148
|
+
const context = useContext(DataTableContext);
|
|
149
|
+
if (!context) {
|
|
150
|
+
throw new Error('useDataTableContext must be used within a DataTableProvider');
|
|
151
|
+
}
|
|
152
|
+
return context as IDataTableContext<TData>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Hook to use specific managers
|
|
157
|
+
*/
|
|
158
|
+
export function useDataManager<TData extends DataRecord = DataRecord>() {
|
|
159
|
+
const context = useDataTableContext<TData>();
|
|
160
|
+
return context.dataManager;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function useColumnManager<TData extends DataRecord = DataRecord>() {
|
|
164
|
+
const context = useDataTableContext<TData>();
|
|
165
|
+
return context.columnManager;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function useActionManager<TData extends DataRecord = DataRecord>() {
|
|
169
|
+
const context = useDataTableContext<TData>();
|
|
170
|
+
return context.actionManager;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function useStateManager<TData extends DataRecord = DataRecord>() {
|
|
174
|
+
const context = useDataTableContext<TData>();
|
|
175
|
+
return context.stateManager;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function usePluginRegistry<TData extends DataRecord = DataRecord>() {
|
|
179
|
+
const context = useDataTableContext<TData>();
|
|
180
|
+
return context.pluginRegistry;
|
|
181
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Local Data Adapter
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/DataTable/Architecture/Adapters
|
|
5
|
+
* @since 0.3.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { DataAdapter, FetchOptions, ExportFormat, ExportOptions } from './interfaces';
|
|
9
|
+
import type { DataRecord } from '../types';
|
|
10
|
+
import { generateCSVContent } from '../utils/exportUtils';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Local data adapter for in-memory data operations
|
|
14
|
+
* Implements DataAdapter interface for local data management
|
|
15
|
+
*/
|
|
16
|
+
export class LocalDataAdapter<TData extends DataRecord = DataRecord> implements DataAdapter<TData> {
|
|
17
|
+
public name = 'local';
|
|
18
|
+
private data: TData[] = [];
|
|
19
|
+
private error: Error | null = null;
|
|
20
|
+
|
|
21
|
+
constructor(initialData: TData[] = []) {
|
|
22
|
+
this.data = [...initialData];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Fetch data with filtering, sorting, and pagination
|
|
27
|
+
*/
|
|
28
|
+
async fetchData(options: FetchOptions = {}): Promise<TData[]> {
|
|
29
|
+
try {
|
|
30
|
+
this.error = null;
|
|
31
|
+
let result = [...this.data];
|
|
32
|
+
|
|
33
|
+
// Apply search filter
|
|
34
|
+
if (options.search) {
|
|
35
|
+
const searchTerm = options.search.toLowerCase();
|
|
36
|
+
result = result.filter(item =>
|
|
37
|
+
Object.values(item).some(value =>
|
|
38
|
+
String(value).toLowerCase().includes(searchTerm)
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Apply column filters
|
|
44
|
+
if (options.filters) {
|
|
45
|
+
result = result.filter(item => {
|
|
46
|
+
return Object.entries(options.filters!).every(([key, value]) => {
|
|
47
|
+
const itemValue = item[key];
|
|
48
|
+
if (value === null || value === undefined) return true;
|
|
49
|
+
if (typeof value === 'string') {
|
|
50
|
+
return String(itemValue).toLowerCase().includes(value.toLowerCase());
|
|
51
|
+
}
|
|
52
|
+
return itemValue === value;
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Apply sorting
|
|
58
|
+
if (options.sortBy) {
|
|
59
|
+
result.sort((a, b) => {
|
|
60
|
+
const aValue = a[options.sortBy!];
|
|
61
|
+
const bValue = b[options.sortBy!];
|
|
62
|
+
|
|
63
|
+
// Handle null/undefined values
|
|
64
|
+
if (aValue == null && bValue == null) return 0;
|
|
65
|
+
if (aValue == null) return 1;
|
|
66
|
+
if (bValue == null) return -1;
|
|
67
|
+
|
|
68
|
+
// Compare values - convert to comparable types
|
|
69
|
+
const aComparable = typeof aValue === 'string' ? aValue.toLowerCase() : aValue;
|
|
70
|
+
const bComparable = typeof bValue === 'string' ? bValue.toLowerCase() : bValue;
|
|
71
|
+
|
|
72
|
+
if (aComparable < bComparable) return options.sortDirection === 'desc' ? 1 : -1;
|
|
73
|
+
if (aComparable > bComparable) return options.sortDirection === 'desc' ? -1 : 1;
|
|
74
|
+
return 0;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Apply pagination
|
|
79
|
+
if (options.page !== undefined && options.pageSize) {
|
|
80
|
+
const start = options.page * options.pageSize;
|
|
81
|
+
const end = start + options.pageSize;
|
|
82
|
+
result = result.slice(start, end);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return result;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
this.error = error as Error;
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Update a data item
|
|
94
|
+
*/
|
|
95
|
+
async updateData(id: string, data: Partial<TData>): Promise<void> {
|
|
96
|
+
try {
|
|
97
|
+
this.error = null;
|
|
98
|
+
const index = this.data.findIndex(item => this.getRowId(item) === id);
|
|
99
|
+
|
|
100
|
+
if (index === -1) {
|
|
101
|
+
throw new Error(`Item with ID "${id}" not found`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.data[index] = { ...this.data[index], ...data };
|
|
105
|
+
} catch (error) {
|
|
106
|
+
this.error = error as Error;
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Delete a data item
|
|
113
|
+
*/
|
|
114
|
+
async deleteData(id: string): Promise<void> {
|
|
115
|
+
try {
|
|
116
|
+
this.error = null;
|
|
117
|
+
const index = this.data.findIndex(item => this.getRowId(item) === id);
|
|
118
|
+
|
|
119
|
+
if (index === -1) {
|
|
120
|
+
throw new Error(`Item with ID "${id}" not found`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.data.splice(index, 1);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
this.error = error as Error;
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Create a new data item
|
|
132
|
+
*/
|
|
133
|
+
async createData(data: Partial<TData>): Promise<TData> {
|
|
134
|
+
try {
|
|
135
|
+
this.error = null;
|
|
136
|
+
const newItem = {
|
|
137
|
+
id: this.generateId(),
|
|
138
|
+
...data,
|
|
139
|
+
} as unknown as TData;
|
|
140
|
+
|
|
141
|
+
this.data.push(newItem);
|
|
142
|
+
return newItem;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
this.error = error as Error;
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Export data in specified format
|
|
151
|
+
*/
|
|
152
|
+
async exportData(format: ExportFormat, options: ExportOptions = {}): Promise<string> {
|
|
153
|
+
try {
|
|
154
|
+
this.error = null;
|
|
155
|
+
|
|
156
|
+
switch (format) {
|
|
157
|
+
case 'csv':
|
|
158
|
+
return this.exportToCSV(options);
|
|
159
|
+
case 'json':
|
|
160
|
+
return this.exportToJSON(options);
|
|
161
|
+
case 'xlsx':
|
|
162
|
+
return this.exportToXLSX(options);
|
|
163
|
+
default:
|
|
164
|
+
throw new Error(`Unsupported export format: ${format}`);
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
this.error = error as Error;
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Import data
|
|
174
|
+
*/
|
|
175
|
+
async importData(data: TData[]): Promise<void> {
|
|
176
|
+
try {
|
|
177
|
+
this.error = null;
|
|
178
|
+
this.data = [...data];
|
|
179
|
+
} catch (error) {
|
|
180
|
+
this.error = error as Error;
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Check if adapter is connected
|
|
187
|
+
*/
|
|
188
|
+
isConnected(): boolean {
|
|
189
|
+
return true; // Local adapter is always connected
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get current error
|
|
194
|
+
*/
|
|
195
|
+
getError(): Error | null {
|
|
196
|
+
return this.error;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Set data directly
|
|
201
|
+
*/
|
|
202
|
+
setData(data: TData[]): void {
|
|
203
|
+
this.data = [...data];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get all data without filtering
|
|
208
|
+
*/
|
|
209
|
+
getAllData(): TData[] {
|
|
210
|
+
return [...this.data];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Get data count
|
|
215
|
+
*/
|
|
216
|
+
getDataCount(): number {
|
|
217
|
+
return this.data.length;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Clear all data
|
|
222
|
+
*/
|
|
223
|
+
clearData(): void {
|
|
224
|
+
this.data = [];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get row ID from data item
|
|
229
|
+
*/
|
|
230
|
+
private getRowId(item: TData): string {
|
|
231
|
+
return (item as any).id || (item as any).key || JSON.stringify(item);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Generate unique ID
|
|
236
|
+
*/
|
|
237
|
+
private generateId(): string {
|
|
238
|
+
return `id-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Export to CSV format
|
|
243
|
+
*/
|
|
244
|
+
private exportToCSV(options: ExportOptions): string {
|
|
245
|
+
if (this.data.length === 0) return '';
|
|
246
|
+
|
|
247
|
+
// Create column definitions from data keys
|
|
248
|
+
const columns = Object.keys(this.data[0]).map(key => ({
|
|
249
|
+
accessorKey: key,
|
|
250
|
+
header: key
|
|
251
|
+
}));
|
|
252
|
+
|
|
253
|
+
return generateCSVContent(this.data, columns, {
|
|
254
|
+
includeHeaders: options.includeHeaders
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Export to JSON format
|
|
260
|
+
*/
|
|
261
|
+
private exportToJSON(options: ExportOptions): string {
|
|
262
|
+
return JSON.stringify(this.data, null, 2);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Export to XLSX format (simplified - returns JSON for now)
|
|
267
|
+
*/
|
|
268
|
+
private exportToXLSX(options: ExportOptions): string {
|
|
269
|
+
// In a real implementation, you would use a library like xlsx
|
|
270
|
+
// For now, return JSON format
|
|
271
|
+
return this.exportToJSON(options);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file DataTable Plugin Registry
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/DataTable/Architecture
|
|
5
|
+
* @since 0.3.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { PluginRegistry, DataTablePlugin, DataTableContext } from './interfaces';
|
|
9
|
+
import type { DataRecord } from '../types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Plugin registry implementation
|
|
13
|
+
* Manages plugin registration, dependency resolution, and lifecycle
|
|
14
|
+
*/
|
|
15
|
+
export class PluginRegistryImpl<TData extends DataRecord = DataRecord> implements PluginRegistry<TData> {
|
|
16
|
+
private plugins = new Map<string, DataTablePlugin<TData>>();
|
|
17
|
+
private initializedPlugins = new Set<string>();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Register a plugin
|
|
21
|
+
*/
|
|
22
|
+
register(plugin: DataTablePlugin<TData>): void {
|
|
23
|
+
if (this.plugins.has(plugin.name)) {
|
|
24
|
+
throw new Error(`Plugin "${plugin.name}" is already registered`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
this.plugins.set(plugin.name, plugin);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Unregister a plugin
|
|
32
|
+
*/
|
|
33
|
+
unregister(name: string): void {
|
|
34
|
+
const plugin = this.plugins.get(name);
|
|
35
|
+
if (!plugin) {
|
|
36
|
+
throw new Error(`Plugin "${name}" is not registered`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Cleanup if initialized
|
|
40
|
+
if (this.initializedPlugins.has(name)) {
|
|
41
|
+
plugin.cleanup().catch(error => {
|
|
42
|
+
console.error(`Error cleaning up plugin "${name}":`, error);
|
|
43
|
+
});
|
|
44
|
+
this.initializedPlugins.delete(name);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.plugins.delete(name);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get a plugin by name
|
|
52
|
+
*/
|
|
53
|
+
getPlugin(name: string): DataTablePlugin<TData> | undefined {
|
|
54
|
+
return this.plugins.get(name);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get all enabled plugins
|
|
59
|
+
*/
|
|
60
|
+
getEnabledPlugins(): DataTablePlugin<TData>[] {
|
|
61
|
+
return Array.from(this.plugins.values());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get plugins in dependency order
|
|
66
|
+
*/
|
|
67
|
+
getDependencyOrder(): DataTablePlugin<TData>[] {
|
|
68
|
+
const sorted: DataTablePlugin<TData>[] = [];
|
|
69
|
+
const visited = new Set<string>();
|
|
70
|
+
const visiting = new Set<string>();
|
|
71
|
+
|
|
72
|
+
const visit = (pluginName: string) => {
|
|
73
|
+
if (visiting.has(pluginName)) {
|
|
74
|
+
throw new Error(`Circular dependency detected: ${pluginName}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (visited.has(pluginName)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const plugin = this.plugins.get(pluginName);
|
|
82
|
+
if (!plugin) {
|
|
83
|
+
throw new Error(`Plugin "${pluginName}" not found`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
visiting.add(pluginName);
|
|
87
|
+
|
|
88
|
+
// Visit dependencies first
|
|
89
|
+
if (plugin.dependencies) {
|
|
90
|
+
for (const dependency of plugin.dependencies) {
|
|
91
|
+
visit(dependency);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
visiting.delete(pluginName);
|
|
96
|
+
visited.add(pluginName);
|
|
97
|
+
sorted.push(plugin);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Visit all plugins
|
|
101
|
+
for (const pluginName of this.plugins.keys()) {
|
|
102
|
+
if (!visited.has(pluginName)) {
|
|
103
|
+
visit(pluginName);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return sorted;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Initialize all plugins
|
|
112
|
+
*/
|
|
113
|
+
async initializePlugins(context: DataTableContext<TData>): Promise<void> {
|
|
114
|
+
const plugins = this.getDependencyOrder();
|
|
115
|
+
|
|
116
|
+
for (const plugin of plugins) {
|
|
117
|
+
try {
|
|
118
|
+
await plugin.initialize(context);
|
|
119
|
+
this.initializedPlugins.add(plugin.name);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(`Failed to initialize plugin "${plugin.name}":`, error);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Cleanup all plugins
|
|
129
|
+
*/
|
|
130
|
+
async cleanupPlugins(): Promise<void> {
|
|
131
|
+
const plugins = this.getDependencyOrder().reverse(); // Reverse for cleanup order
|
|
132
|
+
|
|
133
|
+
for (const plugin of plugins) {
|
|
134
|
+
if (this.initializedPlugins.has(plugin.name)) {
|
|
135
|
+
try {
|
|
136
|
+
await plugin.cleanup();
|
|
137
|
+
this.initializedPlugins.delete(plugin.name);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error(`Error cleaning up plugin "${plugin.name}":`, error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Render all plugins
|
|
147
|
+
*/
|
|
148
|
+
renderPlugins(): React.ReactNode[] {
|
|
149
|
+
return this.getEnabledPlugins()
|
|
150
|
+
.filter(plugin => this.initializedPlugins.has(plugin.name))
|
|
151
|
+
.map(plugin => plugin.render());
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Check if plugin is initialized
|
|
156
|
+
*/
|
|
157
|
+
isPluginInitialized(name: string): boolean {
|
|
158
|
+
return this.initializedPlugins.has(name);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get plugin count
|
|
163
|
+
*/
|
|
164
|
+
getPluginCount(): number {
|
|
165
|
+
return this.plugins.size;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get initialized plugin count
|
|
170
|
+
*/
|
|
171
|
+
getInitializedPluginCount(): number {
|
|
172
|
+
return this.initializedPlugins.size;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Check if plugin has dependencies
|
|
177
|
+
*/
|
|
178
|
+
hasDependencies(name: string): boolean {
|
|
179
|
+
const plugin = this.plugins.get(name);
|
|
180
|
+
return plugin ? !!(plugin.dependencies && plugin.dependencies.length > 0) : false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get plugin dependencies
|
|
185
|
+
*/
|
|
186
|
+
getPluginDependencies(name: string): string[] {
|
|
187
|
+
const plugin = this.plugins.get(name);
|
|
188
|
+
return plugin?.dependencies || [];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Check if all dependencies are satisfied
|
|
193
|
+
*/
|
|
194
|
+
areDependenciesSatisfied(name: string): boolean {
|
|
195
|
+
const dependencies = this.getPluginDependencies(name);
|
|
196
|
+
return dependencies.every(dep => this.plugins.has(dep));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get plugins that depend on a specific plugin
|
|
201
|
+
*/
|
|
202
|
+
getDependents(name: string): DataTablePlugin<TData>[] {
|
|
203
|
+
return Array.from(this.plugins.values()).filter(plugin =>
|
|
204
|
+
plugin.dependencies?.includes(name)
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Validate plugin dependencies
|
|
210
|
+
*/
|
|
211
|
+
validateDependencies(): { valid: boolean; errors: string[] } {
|
|
212
|
+
const errors: string[] = [];
|
|
213
|
+
|
|
214
|
+
for (const [name, plugin] of this.plugins) {
|
|
215
|
+
if (plugin.dependencies) {
|
|
216
|
+
for (const dependency of plugin.dependencies) {
|
|
217
|
+
if (!this.plugins.has(dependency)) {
|
|
218
|
+
errors.push(`Plugin "${name}" depends on "${dependency}" which is not registered`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
valid: errors.length === 0,
|
|
226
|
+
errors
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|