@fgv/ts-res-ui-components 5.0.0-20 → 5.0.0-21
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/README.md +50 -1
- package/dist/ts-res-ui-components.d.ts +6 -8
- package/lib/components/orchestrator/ResourceOrchestrator.js +1 -1
- package/lib/components/views/ResolutionView/EditableJsonView.d.ts +4 -3
- package/lib/components/views/ResolutionView/EditableJsonView.js +4 -3
- package/lib/hooks/useResolutionState.js +11 -61
- package/lib/types/index.d.ts +2 -5
- package/lib-commonjs/components/orchestrator/ResourceOrchestrator.js +1 -1
- package/lib-commonjs/components/views/ResolutionView/EditableJsonView.js +4 -3
- package/lib-commonjs/hooks/useResolutionState.js +10 -60
- package/package.json +7 -7
- package/src/components/orchestrator/ResourceOrchestrator.tsx +1 -1
- package/src/components/views/ResolutionView/EditableJsonView.tsx +4 -3
- package/src/hooks/useResolutionState.ts +10 -81
- package/src/types/index.ts +3 -5
package/README.md
CHANGED
|
@@ -55,6 +55,51 @@ This library requires the following peer dependencies:
|
|
|
55
55
|
|
|
56
56
|
## Quick Start
|
|
57
57
|
|
|
58
|
+
### Minimal Editing App (Unified Apply)
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import React from 'react';
|
|
62
|
+
import { ResourceOrchestrator, ResolutionView } from '@fgv/ts-res-ui-components';
|
|
63
|
+
|
|
64
|
+
export default function App() {
|
|
65
|
+
return (
|
|
66
|
+
<ResourceOrchestrator>
|
|
67
|
+
{({ state, actions }) => (
|
|
68
|
+
<ResolutionView
|
|
69
|
+
resources={state.resources}
|
|
70
|
+
resolutionState={state.resolutionState}
|
|
71
|
+
resolutionActions={{
|
|
72
|
+
updateContextValue: actions.updateResolutionContext,
|
|
73
|
+
applyContext: actions.applyResolutionContext,
|
|
74
|
+
selectResource: actions.selectResourceForResolution,
|
|
75
|
+
setViewMode: actions.setResolutionViewMode,
|
|
76
|
+
resetCache: actions.resetResolutionCache,
|
|
77
|
+
saveEdit: actions.saveResourceEdit,
|
|
78
|
+
getEditedValue: actions.getEditedValue,
|
|
79
|
+
hasEdit: actions.hasResourceEdit,
|
|
80
|
+
clearEdits: actions.clearResourceEdits,
|
|
81
|
+
discardEdits: actions.discardResourceEdits,
|
|
82
|
+
startNewResource: actions.startNewResource,
|
|
83
|
+
updateNewResourceId: actions.updateNewResourceId,
|
|
84
|
+
selectResourceType: actions.selectResourceType,
|
|
85
|
+
saveNewResourceAsPending: actions.saveNewResourceAsPending,
|
|
86
|
+
cancelNewResource: actions.cancelNewResource,
|
|
87
|
+
removePendingResource: actions.removePendingResource,
|
|
88
|
+
markResourceForDeletion: actions.markResourceForDeletion,
|
|
89
|
+
applyPendingResources: actions.applyPendingResources,
|
|
90
|
+
discardPendingResources: actions.discardPendingResources
|
|
91
|
+
}}
|
|
92
|
+
allowResourceCreation
|
|
93
|
+
defaultResourceType="json"
|
|
94
|
+
showPendingResourcesInList
|
|
95
|
+
onMessage={actions.addMessage}
|
|
96
|
+
/>
|
|
97
|
+
)}
|
|
98
|
+
</ResourceOrchestrator>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
58
103
|
### Basic Usage with ResourceOrchestrator
|
|
59
104
|
|
|
60
105
|
The `ResourceOrchestrator` component provides centralized state management for all ts-res UI functionality:
|
|
@@ -75,7 +120,11 @@ function App() {
|
|
|
75
120
|
<ImportView
|
|
76
121
|
onImport={actions.importDirectory}
|
|
77
122
|
onBundleImport={actions.importBundle}
|
|
78
|
-
onZipImport={
|
|
123
|
+
onZipImport={(zipData, config) => {
|
|
124
|
+
if (config) actions.applyConfiguration(config);
|
|
125
|
+
if (zipData.directory) actions.importDirectory(zipData.directory);
|
|
126
|
+
else if (zipData.files?.length) actions.importFiles(zipData.files);
|
|
127
|
+
}}
|
|
79
128
|
/>
|
|
80
129
|
) : (
|
|
81
130
|
<SourceView
|
|
@@ -680,9 +680,10 @@ declare function createTsResSystemFromConfig(systemConfig?: Config.Model.ISystem
|
|
|
680
680
|
* <ResolutionTools.UnifiedChangeControls
|
|
681
681
|
* editCount={state.editedResources.size}
|
|
682
682
|
* isApplying={state.isApplyingEdits}
|
|
683
|
-
*
|
|
684
|
-
*
|
|
685
|
-
*
|
|
683
|
+
* addCount={state.pendingResources.size}
|
|
684
|
+
* deleteCount={state.pendingResourceDeletions.size}
|
|
685
|
+
* onApplyAll={actions.applyPendingResources}
|
|
686
|
+
* onDiscardAll={() => { actions.clearEdits(); actions.discardPendingResources(); }}
|
|
686
687
|
* />
|
|
687
688
|
* </div>
|
|
688
689
|
* )}
|
|
@@ -2073,8 +2074,8 @@ declare interface MessagesWindowProps {
|
|
|
2073
2074
|
* const newValue = { text: 'Updated welcome message' };
|
|
2074
2075
|
* actions.saveResourceEdit('user.welcome', newValue);
|
|
2075
2076
|
*
|
|
2076
|
-
* // Apply all edits
|
|
2077
|
-
* await actions.
|
|
2077
|
+
* // Apply all pending changes (edits + new resources)
|
|
2078
|
+
* await actions.applyPendingResources();
|
|
2078
2079
|
* ```
|
|
2079
2080
|
*
|
|
2080
2081
|
* @example
|
|
@@ -2133,7 +2134,6 @@ export declare interface OrchestratorActions {
|
|
|
2133
2134
|
getEditedValue: (resourceId: string) => JsonValue | undefined;
|
|
2134
2135
|
hasResourceEdit: (resourceId: string) => boolean;
|
|
2135
2136
|
clearResourceEdits: () => void;
|
|
2136
|
-
applyResourceEdits: () => Promise<void>;
|
|
2137
2137
|
discardResourceEdits: () => void;
|
|
2138
2138
|
startNewResource: (defaultTypeName?: string) => void;
|
|
2139
2139
|
updateNewResourceId: (id: string) => void;
|
|
@@ -2825,8 +2825,6 @@ declare interface ResolutionActions {
|
|
|
2825
2825
|
hasEdit: (resourceId: string) => boolean;
|
|
2826
2826
|
/** Clear all pending edits */
|
|
2827
2827
|
clearEdits: () => void;
|
|
2828
|
-
/** Apply all edits to the resource system */
|
|
2829
|
-
applyEdits: () => Promise<void>;
|
|
2830
2828
|
/** Discard all pending edits */
|
|
2831
2829
|
discardEdits: () => void;
|
|
2832
2830
|
/** Start creating a new resource */
|
|
@@ -309,7 +309,7 @@ export const ResourceOrchestrator = ({ children, initialConfiguration, qualifier
|
|
|
309
309
|
getEditedValue: resolutionData.actions.getEditedValue,
|
|
310
310
|
hasResourceEdit: resolutionData.actions.hasEdit,
|
|
311
311
|
clearResourceEdits: resolutionData.actions.clearEdits,
|
|
312
|
-
|
|
312
|
+
// Edits applied through unified applyPendingResources
|
|
313
313
|
discardResourceEdits: resolutionData.actions.discardEdits,
|
|
314
314
|
// Resource creation actions
|
|
315
315
|
startNewResource: resolutionData.actions.startNewResource,
|
|
@@ -175,9 +175,10 @@ export interface EditableJsonViewProps {
|
|
|
175
175
|
* <ResolutionTools.UnifiedChangeControls
|
|
176
176
|
* editCount={state.editedResources.size}
|
|
177
177
|
* isApplying={state.isApplyingEdits}
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
178
|
+
* addCount={state.pendingResources.size}
|
|
179
|
+
* deleteCount={state.pendingResourceDeletions.size}
|
|
180
|
+
* onApplyAll={actions.applyPendingResources}
|
|
181
|
+
* onDiscardAll={() => { actions.clearEdits(); actions.discardPendingResources(); }}
|
|
181
182
|
* />
|
|
182
183
|
* </div>
|
|
183
184
|
* )}
|
|
@@ -155,9 +155,10 @@ import { validateEditedResource } from '../../../utils/resolutionEditing';
|
|
|
155
155
|
* <ResolutionTools.UnifiedChangeControls
|
|
156
156
|
* editCount={state.editedResources.size}
|
|
157
157
|
* isApplying={state.isApplyingEdits}
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
158
|
+
* addCount={state.pendingResources.size}
|
|
159
|
+
* deleteCount={state.pendingResourceDeletions.size}
|
|
160
|
+
* onApplyAll={actions.applyPendingResources}
|
|
161
|
+
* onDiscardAll={() => { actions.clearEdits(); actions.discardPendingResources(); }}
|
|
161
162
|
* />
|
|
162
163
|
* </div>
|
|
163
164
|
* )}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useCallback, useMemo } from 'react';
|
|
2
2
|
import { Runtime } from '@fgv/ts-res';
|
|
3
3
|
import { createResolverWithContext, resolveResourceDetailed, getAvailableQualifiers, hasPendingContextChanges } from '../utils/resolutionUtils';
|
|
4
|
-
import { validateEditedResource, computeResourceDelta,
|
|
4
|
+
import { validateEditedResource, computeResourceDelta, rebuildSystemWithChanges, extractResolutionContext } from '../utils/resolutionEditing';
|
|
5
5
|
/**
|
|
6
6
|
* Hook for managing resource resolution state and editing operations.
|
|
7
7
|
*
|
|
@@ -447,59 +447,7 @@ export function useResolutionState(processedResources, onMessage, onSystemUpdate
|
|
|
447
447
|
onMessage?.('info', 'All unsaved edits discarded');
|
|
448
448
|
}
|
|
449
449
|
}, [hasUnsavedEdits, onMessage]);
|
|
450
|
-
|
|
451
|
-
if (!processedResources || editedResources.size === 0) {
|
|
452
|
-
onMessage?.('warning', 'No edits to apply');
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
if (!onSystemUpdate) {
|
|
456
|
-
onMessage?.('error', 'System update callback not provided');
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
setIsApplyingEdits(true);
|
|
460
|
-
try {
|
|
461
|
-
// Extract current resolution context (filter out undefined values)
|
|
462
|
-
const cleanedContextValues = {};
|
|
463
|
-
Object.entries(effectiveContext).forEach(([key, value]) => {
|
|
464
|
-
if (value !== undefined) {
|
|
465
|
-
cleanedContextValues[key] = value;
|
|
466
|
-
}
|
|
467
|
-
});
|
|
468
|
-
const currentContext = extractResolutionContext(currentResolver, cleanedContextValues);
|
|
469
|
-
// Check for potential conflicts
|
|
470
|
-
const conflictCheck = checkEditConflicts(processedResources.system.resourceManager, editedResourcesInternal, currentContext);
|
|
471
|
-
// Show warnings about potential conflicts
|
|
472
|
-
conflictCheck.warnings.forEach((warning) => onMessage?.('warning', warning));
|
|
473
|
-
if (conflictCheck.conflicts.length > 0) {
|
|
474
|
-
onMessage?.('error', `Conflicts detected: ${conflictCheck.conflicts.join(', ')}`);
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
// Rebuild the system with edits
|
|
478
|
-
const rebuildResult = await rebuildSystemWithEdits(processedResources.system, editedResourcesInternal, currentContext);
|
|
479
|
-
if (rebuildResult.isFailure()) {
|
|
480
|
-
onMessage?.('error', `Failed to apply edits: ${rebuildResult.message}`);
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
// Update the system through the callback
|
|
484
|
-
onSystemUpdate(rebuildResult.value);
|
|
485
|
-
// Clear edits after successful application
|
|
486
|
-
setEditedResourcesInternal(new Map());
|
|
487
|
-
onMessage?.('success', `Successfully applied ${editedResourcesInternal.size} edit(s)`);
|
|
488
|
-
}
|
|
489
|
-
catch (error) {
|
|
490
|
-
onMessage?.('error', `Error applying edits: ${error instanceof Error ? error.message : String(error)}`);
|
|
491
|
-
}
|
|
492
|
-
finally {
|
|
493
|
-
setIsApplyingEdits(false);
|
|
494
|
-
}
|
|
495
|
-
}, [
|
|
496
|
-
processedResources,
|
|
497
|
-
editedResourcesInternal,
|
|
498
|
-
onSystemUpdate,
|
|
499
|
-
currentResolver,
|
|
500
|
-
onMessage,
|
|
501
|
-
effectiveContext
|
|
502
|
-
]);
|
|
450
|
+
// Removed applyEdits in favor of unified applyPendingResources
|
|
503
451
|
// Resource creation actions
|
|
504
452
|
const startNewResource = useCallback((defaultTypeName) => {
|
|
505
453
|
const defaultType = defaultTypeName
|
|
@@ -616,9 +564,10 @@ export function useResolutionState(processedResources, onMessage, onSystemUpdate
|
|
|
616
564
|
onMessage?.('info', `Marked resource ${resourceId} for deletion`);
|
|
617
565
|
}, [onMessage]);
|
|
618
566
|
const applyPendingResources = useCallback(async () => {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
567
|
+
const hasAnyChanges = editedResourcesInternal.size > 0 || pendingResources.size > 0 || pendingResourceDeletions.size > 0;
|
|
568
|
+
if (!hasAnyChanges || !processedResources || !onSystemUpdate) {
|
|
569
|
+
if (!hasAnyChanges) {
|
|
570
|
+
onMessage?.('warning', 'No pending changes to apply');
|
|
622
571
|
}
|
|
623
572
|
else if (!processedResources) {
|
|
624
573
|
onMessage?.('error', 'No resource system available');
|
|
@@ -629,6 +578,7 @@ export function useResolutionState(processedResources, onMessage, onSystemUpdate
|
|
|
629
578
|
return;
|
|
630
579
|
}
|
|
631
580
|
try {
|
|
581
|
+
setIsApplyingEdits(true);
|
|
632
582
|
// Extract current resolution context (filter out undefined values)
|
|
633
583
|
const cleanedContextValues = {};
|
|
634
584
|
Object.entries(effectiveContext).forEach(([key, value]) => {
|
|
@@ -663,7 +613,7 @@ export function useResolutionState(processedResources, onMessage, onSystemUpdate
|
|
|
663
613
|
return;
|
|
664
614
|
}
|
|
665
615
|
onSystemUpdate(rebuildResult.value);
|
|
666
|
-
onMessage?.('success', `Applied ${newResourcesArray.length} additions and ${pendingResourceDeletions.size} deletions`);
|
|
616
|
+
onMessage?.('success', `Applied ${editedResourcesInternal.size} edits, ${newResourcesArray.length} additions, and ${pendingResourceDeletions.size} deletions`);
|
|
667
617
|
// Clear pending additions after successful application (deletions still deferred)
|
|
668
618
|
setPendingResources(new Map());
|
|
669
619
|
setPendingResourceDeletions(new Set());
|
|
@@ -675,8 +625,10 @@ export function useResolutionState(processedResources, onMessage, onSystemUpdate
|
|
|
675
625
|
catch (error) {
|
|
676
626
|
onMessage?.('error', `Failed to apply pending resources: ${error instanceof Error ? error.message : String(error)}`);
|
|
677
627
|
}
|
|
628
|
+
finally {
|
|
629
|
+
setIsApplyingEdits(false);
|
|
630
|
+
}
|
|
678
631
|
}, [
|
|
679
|
-
hasPendingResourceChanges,
|
|
680
632
|
pendingResources,
|
|
681
633
|
pendingResourceDeletions,
|
|
682
634
|
processedResources,
|
|
@@ -723,7 +675,6 @@ export function useResolutionState(processedResources, onMessage, onSystemUpdate
|
|
|
723
675
|
getEditedValue,
|
|
724
676
|
hasEdit,
|
|
725
677
|
clearEdits,
|
|
726
|
-
applyEdits,
|
|
727
678
|
discardEdits,
|
|
728
679
|
// Resource creation actions
|
|
729
680
|
startNewResource,
|
|
@@ -745,7 +696,6 @@ export function useResolutionState(processedResources, onMessage, onSystemUpdate
|
|
|
745
696
|
getEditedValue,
|
|
746
697
|
hasEdit,
|
|
747
698
|
clearEdits,
|
|
748
|
-
applyEdits,
|
|
749
699
|
discardEdits,
|
|
750
700
|
startNewResource,
|
|
751
701
|
updateNewResourceId,
|
package/lib/types/index.d.ts
CHANGED
|
@@ -436,8 +436,6 @@ export interface ResolutionActions {
|
|
|
436
436
|
hasEdit: (resourceId: string) => boolean;
|
|
437
437
|
/** Clear all pending edits */
|
|
438
438
|
clearEdits: () => void;
|
|
439
|
-
/** Apply all edits to the resource system */
|
|
440
|
-
applyEdits: () => Promise<void>;
|
|
441
439
|
/** Discard all pending edits */
|
|
442
440
|
discardEdits: () => void;
|
|
443
441
|
/** Start creating a new resource */
|
|
@@ -1117,8 +1115,8 @@ export interface OrchestratorState {
|
|
|
1117
1115
|
* const newValue = { text: 'Updated welcome message' };
|
|
1118
1116
|
* actions.saveResourceEdit('user.welcome', newValue);
|
|
1119
1117
|
*
|
|
1120
|
-
* // Apply all edits
|
|
1121
|
-
* await actions.
|
|
1118
|
+
* // Apply all pending changes (edits + new resources)
|
|
1119
|
+
* await actions.applyPendingResources();
|
|
1122
1120
|
* ```
|
|
1123
1121
|
*
|
|
1124
1122
|
* @example
|
|
@@ -1177,7 +1175,6 @@ export interface OrchestratorActions {
|
|
|
1177
1175
|
getEditedValue: (resourceId: string) => JsonValue | undefined;
|
|
1178
1176
|
hasResourceEdit: (resourceId: string) => boolean;
|
|
1179
1177
|
clearResourceEdits: () => void;
|
|
1180
|
-
applyResourceEdits: () => Promise<void>;
|
|
1181
1178
|
discardResourceEdits: () => void;
|
|
1182
1179
|
startNewResource: (defaultTypeName?: string) => void;
|
|
1183
1180
|
updateNewResourceId: (id: string) => void;
|
|
@@ -313,7 +313,7 @@ const ResourceOrchestrator = ({ children, initialConfiguration, qualifierTypeFac
|
|
|
313
313
|
getEditedValue: resolutionData.actions.getEditedValue,
|
|
314
314
|
hasResourceEdit: resolutionData.actions.hasEdit,
|
|
315
315
|
clearResourceEdits: resolutionData.actions.clearEdits,
|
|
316
|
-
|
|
316
|
+
// Edits applied through unified applyPendingResources
|
|
317
317
|
discardResourceEdits: resolutionData.actions.discardEdits,
|
|
318
318
|
// Resource creation actions
|
|
319
319
|
startNewResource: resolutionData.actions.startNewResource,
|
|
@@ -159,9 +159,10 @@ const resolutionEditing_1 = require("../../../utils/resolutionEditing");
|
|
|
159
159
|
* <ResolutionTools.UnifiedChangeControls
|
|
160
160
|
* editCount={state.editedResources.size}
|
|
161
161
|
* isApplying={state.isApplyingEdits}
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
162
|
+
* addCount={state.pendingResources.size}
|
|
163
|
+
* deleteCount={state.pendingResourceDeletions.size}
|
|
164
|
+
* onApplyAll={actions.applyPendingResources}
|
|
165
|
+
* onDiscardAll={() => { actions.clearEdits(); actions.discardPendingResources(); }}
|
|
165
166
|
* />
|
|
166
167
|
* </div>
|
|
167
168
|
* )}
|
|
@@ -451,59 +451,7 @@ function useResolutionState(processedResources, onMessage, onSystemUpdate) {
|
|
|
451
451
|
onMessage?.('info', 'All unsaved edits discarded');
|
|
452
452
|
}
|
|
453
453
|
}, [hasUnsavedEdits, onMessage]);
|
|
454
|
-
|
|
455
|
-
if (!processedResources || editedResources.size === 0) {
|
|
456
|
-
onMessage?.('warning', 'No edits to apply');
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
if (!onSystemUpdate) {
|
|
460
|
-
onMessage?.('error', 'System update callback not provided');
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
setIsApplyingEdits(true);
|
|
464
|
-
try {
|
|
465
|
-
// Extract current resolution context (filter out undefined values)
|
|
466
|
-
const cleanedContextValues = {};
|
|
467
|
-
Object.entries(effectiveContext).forEach(([key, value]) => {
|
|
468
|
-
if (value !== undefined) {
|
|
469
|
-
cleanedContextValues[key] = value;
|
|
470
|
-
}
|
|
471
|
-
});
|
|
472
|
-
const currentContext = (0, resolutionEditing_1.extractResolutionContext)(currentResolver, cleanedContextValues);
|
|
473
|
-
// Check for potential conflicts
|
|
474
|
-
const conflictCheck = (0, resolutionEditing_1.checkEditConflicts)(processedResources.system.resourceManager, editedResourcesInternal, currentContext);
|
|
475
|
-
// Show warnings about potential conflicts
|
|
476
|
-
conflictCheck.warnings.forEach((warning) => onMessage?.('warning', warning));
|
|
477
|
-
if (conflictCheck.conflicts.length > 0) {
|
|
478
|
-
onMessage?.('error', `Conflicts detected: ${conflictCheck.conflicts.join(', ')}`);
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
// Rebuild the system with edits
|
|
482
|
-
const rebuildResult = await (0, resolutionEditing_1.rebuildSystemWithEdits)(processedResources.system, editedResourcesInternal, currentContext);
|
|
483
|
-
if (rebuildResult.isFailure()) {
|
|
484
|
-
onMessage?.('error', `Failed to apply edits: ${rebuildResult.message}`);
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
// Update the system through the callback
|
|
488
|
-
onSystemUpdate(rebuildResult.value);
|
|
489
|
-
// Clear edits after successful application
|
|
490
|
-
setEditedResourcesInternal(new Map());
|
|
491
|
-
onMessage?.('success', `Successfully applied ${editedResourcesInternal.size} edit(s)`);
|
|
492
|
-
}
|
|
493
|
-
catch (error) {
|
|
494
|
-
onMessage?.('error', `Error applying edits: ${error instanceof Error ? error.message : String(error)}`);
|
|
495
|
-
}
|
|
496
|
-
finally {
|
|
497
|
-
setIsApplyingEdits(false);
|
|
498
|
-
}
|
|
499
|
-
}, [
|
|
500
|
-
processedResources,
|
|
501
|
-
editedResourcesInternal,
|
|
502
|
-
onSystemUpdate,
|
|
503
|
-
currentResolver,
|
|
504
|
-
onMessage,
|
|
505
|
-
effectiveContext
|
|
506
|
-
]);
|
|
454
|
+
// Removed applyEdits in favor of unified applyPendingResources
|
|
507
455
|
// Resource creation actions
|
|
508
456
|
const startNewResource = (0, react_1.useCallback)((defaultTypeName) => {
|
|
509
457
|
const defaultType = defaultTypeName
|
|
@@ -620,9 +568,10 @@ function useResolutionState(processedResources, onMessage, onSystemUpdate) {
|
|
|
620
568
|
onMessage?.('info', `Marked resource ${resourceId} for deletion`);
|
|
621
569
|
}, [onMessage]);
|
|
622
570
|
const applyPendingResources = (0, react_1.useCallback)(async () => {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
571
|
+
const hasAnyChanges = editedResourcesInternal.size > 0 || pendingResources.size > 0 || pendingResourceDeletions.size > 0;
|
|
572
|
+
if (!hasAnyChanges || !processedResources || !onSystemUpdate) {
|
|
573
|
+
if (!hasAnyChanges) {
|
|
574
|
+
onMessage?.('warning', 'No pending changes to apply');
|
|
626
575
|
}
|
|
627
576
|
else if (!processedResources) {
|
|
628
577
|
onMessage?.('error', 'No resource system available');
|
|
@@ -633,6 +582,7 @@ function useResolutionState(processedResources, onMessage, onSystemUpdate) {
|
|
|
633
582
|
return;
|
|
634
583
|
}
|
|
635
584
|
try {
|
|
585
|
+
setIsApplyingEdits(true);
|
|
636
586
|
// Extract current resolution context (filter out undefined values)
|
|
637
587
|
const cleanedContextValues = {};
|
|
638
588
|
Object.entries(effectiveContext).forEach(([key, value]) => {
|
|
@@ -667,7 +617,7 @@ function useResolutionState(processedResources, onMessage, onSystemUpdate) {
|
|
|
667
617
|
return;
|
|
668
618
|
}
|
|
669
619
|
onSystemUpdate(rebuildResult.value);
|
|
670
|
-
onMessage?.('success', `Applied ${newResourcesArray.length} additions and ${pendingResourceDeletions.size} deletions`);
|
|
620
|
+
onMessage?.('success', `Applied ${editedResourcesInternal.size} edits, ${newResourcesArray.length} additions, and ${pendingResourceDeletions.size} deletions`);
|
|
671
621
|
// Clear pending additions after successful application (deletions still deferred)
|
|
672
622
|
setPendingResources(new Map());
|
|
673
623
|
setPendingResourceDeletions(new Set());
|
|
@@ -679,8 +629,10 @@ function useResolutionState(processedResources, onMessage, onSystemUpdate) {
|
|
|
679
629
|
catch (error) {
|
|
680
630
|
onMessage?.('error', `Failed to apply pending resources: ${error instanceof Error ? error.message : String(error)}`);
|
|
681
631
|
}
|
|
632
|
+
finally {
|
|
633
|
+
setIsApplyingEdits(false);
|
|
634
|
+
}
|
|
682
635
|
}, [
|
|
683
|
-
hasPendingResourceChanges,
|
|
684
636
|
pendingResources,
|
|
685
637
|
pendingResourceDeletions,
|
|
686
638
|
processedResources,
|
|
@@ -727,7 +679,6 @@ function useResolutionState(processedResources, onMessage, onSystemUpdate) {
|
|
|
727
679
|
getEditedValue,
|
|
728
680
|
hasEdit,
|
|
729
681
|
clearEdits,
|
|
730
|
-
applyEdits,
|
|
731
682
|
discardEdits,
|
|
732
683
|
// Resource creation actions
|
|
733
684
|
startNewResource,
|
|
@@ -749,7 +700,6 @@ function useResolutionState(processedResources, onMessage, onSystemUpdate) {
|
|
|
749
700
|
getEditedValue,
|
|
750
701
|
hasEdit,
|
|
751
702
|
clearEdits,
|
|
752
|
-
applyEdits,
|
|
753
703
|
discardEdits,
|
|
754
704
|
startNewResource,
|
|
755
705
|
updateNewResourceId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fgv/ts-res-ui-components",
|
|
3
|
-
"version": "5.0.0-
|
|
3
|
+
"version": "5.0.0-21",
|
|
4
4
|
"description": "Reusable React components for ts-res resource visualization and management",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"@heroicons/react": "~2.2.0",
|
|
24
24
|
"tslib": "^2.8.1",
|
|
25
25
|
"json-edit-react": "~1.28.2",
|
|
26
|
-
"@fgv/ts-res": "5.0.0-
|
|
27
|
-
"@fgv/ts-utils": "5.0.0-
|
|
28
|
-
"@fgv/ts-json": "5.0.0-
|
|
29
|
-
"@fgv/ts-
|
|
30
|
-
"@fgv/ts-
|
|
26
|
+
"@fgv/ts-res": "5.0.0-21",
|
|
27
|
+
"@fgv/ts-utils": "5.0.0-21",
|
|
28
|
+
"@fgv/ts-json": "5.0.0-21",
|
|
29
|
+
"@fgv/ts-extras": "5.0.0-21",
|
|
30
|
+
"@fgv/ts-json-base": "5.0.0-21"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"react": ">=18.0.0",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@microsoft/api-documenter": "^7.26.31",
|
|
63
63
|
"@microsoft/api-extractor": "^7.52.10",
|
|
64
64
|
"@testing-library/dom": "^10.4.0",
|
|
65
|
-
"@fgv/ts-utils-jest": "5.0.0-
|
|
65
|
+
"@fgv/ts-utils-jest": "5.0.0-21"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "heft build --clean",
|
|
@@ -411,7 +411,7 @@ export const ResourceOrchestrator: React.FC<ResourceOrchestratorProps> = ({
|
|
|
411
411
|
getEditedValue: resolutionData.actions.getEditedValue,
|
|
412
412
|
hasResourceEdit: resolutionData.actions.hasEdit,
|
|
413
413
|
clearResourceEdits: resolutionData.actions.clearEdits,
|
|
414
|
-
|
|
414
|
+
// Edits applied through unified applyPendingResources
|
|
415
415
|
discardResourceEdits: resolutionData.actions.discardEdits,
|
|
416
416
|
|
|
417
417
|
// Resource creation actions
|
|
@@ -180,9 +180,10 @@ export interface EditableJsonViewProps {
|
|
|
180
180
|
* <ResolutionTools.UnifiedChangeControls
|
|
181
181
|
* editCount={state.editedResources.size}
|
|
182
182
|
* isApplying={state.isApplyingEdits}
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
183
|
+
* addCount={state.pendingResources.size}
|
|
184
|
+
* deleteCount={state.pendingResourceDeletions.size}
|
|
185
|
+
* onApplyAll={actions.applyPendingResources}
|
|
186
|
+
* onDiscardAll={() => { actions.clearEdits(); actions.discardPendingResources(); }}
|
|
186
187
|
* />
|
|
187
188
|
* </div>
|
|
188
189
|
* )}
|
|
@@ -568,80 +568,7 @@ export function useResolutionState(
|
|
|
568
568
|
}
|
|
569
569
|
}, [hasUnsavedEdits, onMessage]);
|
|
570
570
|
|
|
571
|
-
|
|
572
|
-
if (!processedResources || editedResources.size === 0) {
|
|
573
|
-
onMessage?.('warning', 'No edits to apply');
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
if (!onSystemUpdate) {
|
|
578
|
-
onMessage?.('error', 'System update callback not provided');
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
setIsApplyingEdits(true);
|
|
583
|
-
|
|
584
|
-
try {
|
|
585
|
-
// Extract current resolution context (filter out undefined values)
|
|
586
|
-
const cleanedContextValues: Record<string, string> = {};
|
|
587
|
-
Object.entries(effectiveContext).forEach(([key, value]) => {
|
|
588
|
-
if (value !== undefined) {
|
|
589
|
-
cleanedContextValues[key] = value;
|
|
590
|
-
}
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
const currentContext = extractResolutionContext(
|
|
594
|
-
currentResolver as Runtime.ResourceResolver,
|
|
595
|
-
cleanedContextValues
|
|
596
|
-
);
|
|
597
|
-
|
|
598
|
-
// Check for potential conflicts
|
|
599
|
-
const conflictCheck = checkEditConflicts(
|
|
600
|
-
processedResources.system.resourceManager,
|
|
601
|
-
editedResourcesInternal,
|
|
602
|
-
currentContext
|
|
603
|
-
);
|
|
604
|
-
|
|
605
|
-
// Show warnings about potential conflicts
|
|
606
|
-
conflictCheck.warnings.forEach((warning) => onMessage?.('warning', warning));
|
|
607
|
-
|
|
608
|
-
if (conflictCheck.conflicts.length > 0) {
|
|
609
|
-
onMessage?.('error', `Conflicts detected: ${conflictCheck.conflicts.join(', ')}`);
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// Rebuild the system with edits
|
|
614
|
-
const rebuildResult = await rebuildSystemWithEdits(
|
|
615
|
-
processedResources.system,
|
|
616
|
-
editedResourcesInternal,
|
|
617
|
-
currentContext
|
|
618
|
-
);
|
|
619
|
-
|
|
620
|
-
if (rebuildResult.isFailure()) {
|
|
621
|
-
onMessage?.('error', `Failed to apply edits: ${rebuildResult.message}`);
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
// Update the system through the callback
|
|
626
|
-
onSystemUpdate(rebuildResult.value);
|
|
627
|
-
|
|
628
|
-
// Clear edits after successful application
|
|
629
|
-
setEditedResourcesInternal(new Map());
|
|
630
|
-
|
|
631
|
-
onMessage?.('success', `Successfully applied ${editedResourcesInternal.size} edit(s)`);
|
|
632
|
-
} catch (error) {
|
|
633
|
-
onMessage?.('error', `Error applying edits: ${error instanceof Error ? error.message : String(error)}`);
|
|
634
|
-
} finally {
|
|
635
|
-
setIsApplyingEdits(false);
|
|
636
|
-
}
|
|
637
|
-
}, [
|
|
638
|
-
processedResources,
|
|
639
|
-
editedResourcesInternal,
|
|
640
|
-
onSystemUpdate,
|
|
641
|
-
currentResolver,
|
|
642
|
-
onMessage,
|
|
643
|
-
effectiveContext
|
|
644
|
-
]);
|
|
571
|
+
// Removed applyEdits in favor of unified applyPendingResources
|
|
645
572
|
|
|
646
573
|
// Resource creation actions
|
|
647
574
|
const startNewResource = useCallback(
|
|
@@ -792,9 +719,11 @@ export function useResolutionState(
|
|
|
792
719
|
);
|
|
793
720
|
|
|
794
721
|
const applyPendingResources = useCallback(async () => {
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
722
|
+
const hasAnyChanges =
|
|
723
|
+
editedResourcesInternal.size > 0 || pendingResources.size > 0 || pendingResourceDeletions.size > 0;
|
|
724
|
+
if (!hasAnyChanges || !processedResources || !onSystemUpdate) {
|
|
725
|
+
if (!hasAnyChanges) {
|
|
726
|
+
onMessage?.('warning', 'No pending changes to apply');
|
|
798
727
|
} else if (!processedResources) {
|
|
799
728
|
onMessage?.('error', 'No resource system available');
|
|
800
729
|
} else if (!onSystemUpdate) {
|
|
@@ -804,6 +733,7 @@ export function useResolutionState(
|
|
|
804
733
|
}
|
|
805
734
|
|
|
806
735
|
try {
|
|
736
|
+
setIsApplyingEdits(true);
|
|
807
737
|
// Extract current resolution context (filter out undefined values)
|
|
808
738
|
const cleanedContextValues: Record<string, string> = {};
|
|
809
739
|
Object.entries(effectiveContext).forEach(([key, value]) => {
|
|
@@ -851,7 +781,7 @@ export function useResolutionState(
|
|
|
851
781
|
|
|
852
782
|
onMessage?.(
|
|
853
783
|
'success',
|
|
854
|
-
`Applied ${newResourcesArray.length} additions and ${pendingResourceDeletions.size} deletions`
|
|
784
|
+
`Applied ${editedResourcesInternal.size} edits, ${newResourcesArray.length} additions, and ${pendingResourceDeletions.size} deletions`
|
|
855
785
|
);
|
|
856
786
|
|
|
857
787
|
// Clear pending additions after successful application (deletions still deferred)
|
|
@@ -866,9 +796,10 @@ export function useResolutionState(
|
|
|
866
796
|
'error',
|
|
867
797
|
`Failed to apply pending resources: ${error instanceof Error ? error.message : String(error)}`
|
|
868
798
|
);
|
|
799
|
+
} finally {
|
|
800
|
+
setIsApplyingEdits(false);
|
|
869
801
|
}
|
|
870
802
|
}, [
|
|
871
|
-
hasPendingResourceChanges,
|
|
872
803
|
pendingResources,
|
|
873
804
|
pendingResourceDeletions,
|
|
874
805
|
processedResources,
|
|
@@ -919,7 +850,6 @@ export function useResolutionState(
|
|
|
919
850
|
getEditedValue,
|
|
920
851
|
hasEdit,
|
|
921
852
|
clearEdits,
|
|
922
|
-
applyEdits,
|
|
923
853
|
discardEdits,
|
|
924
854
|
// Resource creation actions
|
|
925
855
|
startNewResource,
|
|
@@ -942,7 +872,6 @@ export function useResolutionState(
|
|
|
942
872
|
getEditedValue,
|
|
943
873
|
hasEdit,
|
|
944
874
|
clearEdits,
|
|
945
|
-
applyEdits,
|
|
946
875
|
discardEdits,
|
|
947
876
|
startNewResource,
|
|
948
877
|
updateNewResourceId,
|
package/src/types/index.ts
CHANGED
|
@@ -475,8 +475,6 @@ export interface ResolutionActions {
|
|
|
475
475
|
hasEdit: (resourceId: string) => boolean;
|
|
476
476
|
/** Clear all pending edits */
|
|
477
477
|
clearEdits: () => void;
|
|
478
|
-
/** Apply all edits to the resource system */
|
|
479
|
-
applyEdits: () => Promise<void>;
|
|
480
478
|
/** Discard all pending edits */
|
|
481
479
|
discardEdits: () => void;
|
|
482
480
|
/** Start creating a new resource */
|
|
@@ -1173,8 +1171,8 @@ export interface OrchestratorState {
|
|
|
1173
1171
|
* const newValue = { text: 'Updated welcome message' };
|
|
1174
1172
|
* actions.saveResourceEdit('user.welcome', newValue);
|
|
1175
1173
|
*
|
|
1176
|
-
* // Apply all edits
|
|
1177
|
-
* await actions.
|
|
1174
|
+
* // Apply all pending changes (edits + new resources)
|
|
1175
|
+
* await actions.applyPendingResources();
|
|
1178
1176
|
* ```
|
|
1179
1177
|
*
|
|
1180
1178
|
* @example
|
|
@@ -1245,7 +1243,7 @@ export interface OrchestratorActions {
|
|
|
1245
1243
|
getEditedValue: (resourceId: string) => JsonValue | undefined;
|
|
1246
1244
|
hasResourceEdit: (resourceId: string) => boolean;
|
|
1247
1245
|
clearResourceEdits: () => void;
|
|
1248
|
-
|
|
1246
|
+
// Removed: unified apply via applyPendingResources
|
|
1249
1247
|
discardResourceEdits: () => void;
|
|
1250
1248
|
|
|
1251
1249
|
// Resource creation actions
|