@fgv/ts-res-ui-components 5.0.0-19 → 5.0.0-20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +138 -1
  2. package/config/jest.setup.js +3 -0
  3. package/dist/ts-res-ui-components.d.ts +116 -156
  4. package/lib/components/common/QualifierContextControl.js +4 -2
  5. package/lib/components/common/ResolutionContextOptionsControl.d.ts +8 -0
  6. package/lib/components/common/ResolutionContextOptionsControl.js +9 -1
  7. package/lib/components/orchestrator/ResourceOrchestrator.js +11 -0
  8. package/lib/components/views/ResolutionView/EditableJsonView.d.ts +1 -1
  9. package/lib/components/views/ResolutionView/EditableJsonView.js +1 -1
  10. package/lib/components/views/ResolutionView/NewResourceModal.d.ts +21 -0
  11. package/lib/components/views/ResolutionView/NewResourceModal.js +60 -0
  12. package/lib/components/views/ResolutionView/UnifiedChangeControls.d.ts +53 -0
  13. package/lib/components/views/ResolutionView/UnifiedChangeControls.js +71 -0
  14. package/lib/components/views/ResolutionView/index.js +180 -34
  15. package/lib/hooks/useResolutionState.js +510 -60
  16. package/lib/index.js +1 -1
  17. package/lib/namespaces/ResolutionTools.d.ts +1 -1
  18. package/lib/namespaces/ResolutionTools.js +1 -1
  19. package/lib/test/unit/components/ResolutionView.unified.test.d.ts +2 -0
  20. package/lib/test/unit/hooks/useResolutionState.context.test.d.ts +2 -0
  21. package/lib/types/index.d.ts +54 -2
  22. package/lib/utils/resolutionEditing.d.ts +17 -0
  23. package/lib/utils/resolutionEditing.js +87 -71
  24. package/lib-commonjs/components/common/QualifierContextControl.js +4 -2
  25. package/lib-commonjs/components/common/ResolutionContextOptionsControl.js +9 -1
  26. package/lib-commonjs/components/orchestrator/ResourceOrchestrator.js +11 -0
  27. package/lib-commonjs/components/views/ResolutionView/EditableJsonView.js +1 -1
  28. package/lib-commonjs/components/views/ResolutionView/NewResourceModal.js +65 -0
  29. package/lib-commonjs/components/views/ResolutionView/UnifiedChangeControls.js +76 -0
  30. package/lib-commonjs/components/views/ResolutionView/index.js +178 -32
  31. package/lib-commonjs/hooks/useResolutionState.js +509 -59
  32. package/lib-commonjs/index.js +1 -1
  33. package/lib-commonjs/namespaces/ResolutionTools.js +3 -3
  34. package/lib-commonjs/utils/resolutionEditing.js +89 -71
  35. package/package.json +11 -7
  36. package/src/components/common/QualifierContextControl.tsx +19 -1
  37. package/src/components/common/ResolutionContextOptionsControl.tsx +42 -1
  38. package/src/components/orchestrator/ResourceOrchestrator.tsx +13 -0
  39. package/src/components/views/ResolutionView/EditableJsonView.tsx +1 -1
  40. package/src/components/views/ResolutionView/NewResourceModal.tsx +158 -0
  41. package/src/components/views/ResolutionView/UnifiedChangeControls.tsx +163 -0
  42. package/src/components/views/ResolutionView/index.tsx +244 -39
  43. package/src/hooks/useResolutionState.ts +627 -89
  44. package/src/index.ts +1 -1
  45. package/src/namespaces/ResolutionTools.ts +1 -1
  46. package/src/types/index.ts +58 -2
  47. package/src/utils/resolutionEditing.ts +107 -80
  48. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts +0 -152
  49. package/lib/components/views/ResolutionView/ResolutionEditControls.js +0 -210
  50. package/lib-commonjs/components/views/ResolutionView/ResolutionEditControls.js +0 -215
  51. package/src/components/views/ResolutionView/ResolutionEditControls.tsx +0 -303
package/README.md CHANGED
@@ -14,9 +14,11 @@ This packlet is largely AI written, and it shows.
14
14
  - **🔄 Resource Management**: Import, process, and manage ts-res configurations and bundles
15
15
  - **🔍 Advanced Filtering**: Filter resources by context with qualifier reduction
16
16
  - **🎯 Resource Resolution**: Test resource resolution with dynamic context values
17
+ - **➕ Resource Creation**: Create new resources with pending/apply workflow and template support
17
18
  - **🔒 View Mode Locking**: Lock to single view mode for simplified interfaces
18
19
  - **📊 Visualization**: Multiple views for exploring resource structures and compiled output
19
20
  - **⚙️ Configuration**: Visual configuration management for qualifier types, qualifiers, and resource types
21
+ - **🎛️ Host Control**: Programmatic control of qualifier values and resource types
20
22
  - **📁 File Handling**: Support for directory imports, ZIP files via ts-res zip-archive packlet, and bundle loading
21
23
  - **🎨 Modern UI**: Built with Tailwind CSS and Heroicons for a clean, responsive interface
22
24
 
@@ -364,7 +366,7 @@ Shows the compiled resource structure with detailed candidate information using
364
366
 
365
367
  ### ResolutionView
366
368
 
367
- Interactive resource resolution testing with context management and support for custom resource editors via the ResourceEditorFactory pattern. Supports locking to a single view mode to simplify the interface for specific use cases.
369
+ Interactive resource resolution testing with context management and support for custom resource editors via the ResourceEditorFactory pattern. Supports locking to a single view mode to simplify the interface for specific use cases. Now includes host-controlled resolution support for programmatic qualifier value management and the ability to create new resources with a pending/apply workflow.
368
370
 
369
371
  > 📚 **[See ResolutionView documentation →](./docs/ts-res-ui-components.resolutionview.md)**
370
372
 
@@ -381,9 +383,144 @@ Interactive resource resolution testing with context management and support for
381
383
  enableSearch: true,
382
384
  searchPlaceholder: "Search resources for resolution testing..."
383
385
  }}
386
+ // Optional: Host-controlled resolution
387
+ contextOptions={{
388
+ hostManagedValues: {
389
+ language: 'en-US',
390
+ platform: 'web',
391
+ market: 'eastern-europe'
392
+ },
393
+ showContextControls: true // Can hide controls for host-only resolution
394
+ }}
395
+ // Optional: Resource creation with pending/apply workflow
396
+ allowResourceCreation={true}
397
+ defaultResourceType="json" // Optional: Host-controlled resource type
398
+ showPendingResourcesInList={true} // Show pending resources in the picker (default: true)
399
+ onPendingResourcesApplied={(added, deleted) => {
400
+ console.log(`Applied ${added.length} new resources, ${deleted.length} deletions`);
401
+ }}
384
402
  />
385
403
  ```
386
404
 
405
+ #### Host-Controlled Resolution
406
+
407
+ The ResolutionView now supports a three-layer context system that allows host applications to programmatically control qualifier values while still allowing user interaction:
408
+
409
+ 1. **Host-Managed Values**: Values controlled by the host application that are applied automatically
410
+ 2. **Applied User Values**: User values that have been explicitly applied via the "Apply Changes" button
411
+ 3. **Pending User Values**: User edits that haven't been applied yet
412
+
413
+ This is particularly useful when:
414
+ - The UI needs to be hidden but resolution still needs to work
415
+ - Certain qualifier values should be controlled by the application logic
416
+ - You want to provide default values that users can optionally override
417
+
418
+ ```tsx
419
+ // Example: Host-controlled resolution with hidden UI
420
+ <ResolutionView
421
+ resources={state.processedResources}
422
+ resolutionState={resolutionState}
423
+ resolutionActions={resolutionActions}
424
+ availableQualifiers={['language', 'platform', 'market']}
425
+ contextOptions={{
426
+ hostManagedValues: {
427
+ language: userProfile.language,
428
+ platform: detectPlatform(),
429
+ market: userProfile.region
430
+ },
431
+ showContextControls: false, // Hide the context controls
432
+ qualifierOptions: {
433
+ language: { visible: false }, // Hide specific qualifiers
434
+ platform: { visible: false }
435
+ }
436
+ }}
437
+ />
438
+
439
+ // Example: Mixed host and user control
440
+ <ResolutionView
441
+ resources={state.processedResources}
442
+ resolutionState={resolutionState}
443
+ resolutionActions={resolutionActions}
444
+ availableQualifiers={availableQualifiers}
445
+ contextOptions={{
446
+ hostManagedValues: {
447
+ platform: 'web' // Platform is always controlled by host
448
+ },
449
+ qualifierOptions: {
450
+ platform: {
451
+ visible: true,
452
+ hostValue: 'web', // Shows as read-only in UI
453
+ disabled: true
454
+ }
455
+ }
456
+ }}
457
+ />
458
+ ```
459
+
460
+ **Key features of host-controlled resolution:**
461
+ - Host values are applied automatically when they change
462
+ - User values require explicit "Apply Changes" action
463
+ - Host values override user values when both are set
464
+ - Flexible UI control - can hide all controls, specific qualifiers, or show as read-only
465
+ - Works seamlessly with the existing resolution state management
466
+
467
+ #### Resource Creation
468
+
469
+ ResolutionView now supports creating new resources directly in the UI with a pending/apply workflow similar to context management:
470
+
471
+ ```tsx
472
+ // Basic resource creation - user selects resource type
473
+ <ResolutionView
474
+ resources={state.processedResources}
475
+ resolutionState={resolutionState}
476
+ resolutionActions={resolutionActions}
477
+ allowResourceCreation={true}
478
+ onPendingResourcesApplied={(added, deleted) => {
479
+ // Rebuild resource manager with new resources
480
+ const updatedResources = rebuildWithResources(added, deleted);
481
+ setState({ processedResources: updatedResources });
482
+ }}
483
+ />
484
+
485
+ // Host-controlled resource type
486
+ <ResolutionView
487
+ resources={state.processedResources}
488
+ resolutionState={resolutionState}
489
+ resolutionActions={resolutionActions}
490
+ allowResourceCreation={true}
491
+ defaultResourceType="json" // Type selector hidden, always creates JSON resources
492
+ onPendingResourcesApplied={(added, deleted) => {
493
+ console.log(`Applied ${added.length} additions, ${deleted.length} deletions`);
494
+ }}
495
+ />
496
+
497
+ // With custom resource types
498
+ <ResolutionView
499
+ resources={state.processedResources}
500
+ resolutionState={resolutionState}
501
+ resolutionActions={resolutionActions}
502
+ allowResourceCreation={true}
503
+ resourceTypeFactory={[customType1, customType2]} // Provide custom resource types
504
+ showPendingResourcesInList={true} // Show pending resources with visual distinction
505
+ />
506
+ ```
507
+
508
+ **Unified Change Workflow (Edits, Additions, Deletions):**
509
+ 1. Click "Add Resource" in the picker header to create new resources (if enabled)
510
+ 2. Edit existing resources from the results pane using the JSON or custom editors
511
+ 3. Mark resources for deletion where supported
512
+ 4. All changes appear in a single "Pending Changes" bar with counts (edits/additions/deletions)
513
+ 5. Click "Apply Changes" to commit everything in one atomic rebuild
514
+ 6. Or click "Discard Changes" to remove all pending changes
515
+
516
+ **Key features:**
517
+ - **Template-based creation**: Resource types provide default templates for new resources
518
+ - **Pending workflow**: Resources aren't added immediately, allowing review before applying
519
+ - **Host control**: Can specify a default resource type to hide the type selector
520
+ - **Batch operations**: Create multiple resources before applying
521
+ - **Visual feedback**: Pending resources shown with distinct styling
522
+ - **Validation**: Resource IDs are validated for uniqueness
523
+
387
524
  #### Custom Resource Editors
388
525
 
389
526
  The ResolutionView supports custom editors for specific resource types through the `ResourceEditorFactory` interface:
@@ -50,6 +50,9 @@ global.URL = {
50
50
  revokeObjectURL: jest.fn()
51
51
  };
52
52
 
53
+ // Extend jest matchers (toBeInTheDocument, etc.)
54
+ require('@testing-library/jest-dom');
55
+
53
56
  // Mock document methods for file export
54
57
  global.document = {
55
58
  createElement: jest.fn(() => ({
@@ -677,7 +677,7 @@ declare function createTsResSystemFromConfig(systemConfig?: Config.Model.ISystem
677
677
  * }}
678
678
  * />
679
679
  *
680
- * <ResolutionTools.ResolutionEditControls
680
+ * <ResolutionTools.UnifiedChangeControls
681
681
  * editCount={state.editedResources.size}
682
682
  * isApplying={state.isApplyingEdits}
683
683
  * hasEdits={state.hasUnsavedEdits}
@@ -2135,6 +2135,15 @@ export declare interface OrchestratorActions {
2135
2135
  clearResourceEdits: () => void;
2136
2136
  applyResourceEdits: () => Promise<void>;
2137
2137
  discardResourceEdits: () => void;
2138
+ startNewResource: (defaultTypeName?: string) => void;
2139
+ updateNewResourceId: (id: string) => void;
2140
+ selectResourceType: (type: string) => void;
2141
+ saveNewResourceAsPending: () => void;
2142
+ cancelNewResource: () => void;
2143
+ removePendingResource: (resourceId: string) => void;
2144
+ markResourceForDeletion: (resourceId: string) => void;
2145
+ applyPendingResources: () => Promise<void>;
2146
+ discardPendingResources: () => void;
2138
2147
  selectResource: (resourceId: string | null) => void;
2139
2148
  addMessage: (type: Message['type'], message: string) => void;
2140
2149
  clearMessages: () => void;
@@ -2800,8 +2809,8 @@ declare function readFilesFromInput(files: FileList): Promise<ImportedFile[]>;
2800
2809
  declare interface ResolutionActions {
2801
2810
  /** Update a context value for resolution testing */
2802
2811
  updateContextValue: (qualifierName: string, value: string | undefined) => void;
2803
- /** Apply pending context changes to the resolver */
2804
- applyContext: () => void;
2812
+ /** Apply pending context changes to the resolver (with optional host-managed values) */
2813
+ applyContext: (hostManagedValues?: Record<string, string | undefined>) => void;
2805
2814
  /** Select a resource for detailed resolution testing */
2806
2815
  selectResource: (resourceId: string) => void;
2807
2816
  /** Change how resolution results are displayed */
@@ -2820,6 +2829,24 @@ declare interface ResolutionActions {
2820
2829
  applyEdits: () => Promise<void>;
2821
2830
  /** Discard all pending edits */
2822
2831
  discardEdits: () => void;
2832
+ /** Start creating a new resource */
2833
+ startNewResource: (defaultTypeName?: string) => void;
2834
+ /** Update the resource ID for the new resource being created */
2835
+ updateNewResourceId: (id: string) => void;
2836
+ /** Select a resource type for the new resource */
2837
+ selectResourceType: (type: string) => void;
2838
+ /** Add the new resource to pending resources (not applied yet) */
2839
+ saveNewResourceAsPending: () => void;
2840
+ /** Cancel the new resource creation */
2841
+ cancelNewResource: () => void;
2842
+ /** Remove a pending resource */
2843
+ removePendingResource: (resourceId: string) => void;
2844
+ /** Mark an existing resource for deletion */
2845
+ markResourceForDeletion: (resourceId: string) => void;
2846
+ /** Apply all pending resource additions and deletions */
2847
+ applyPendingResources: () => Promise<void>;
2848
+ /** Discard all pending resource changes */
2849
+ discardPendingResources: () => void;
2823
2850
  }
2824
2851
 
2825
2852
  /**
@@ -2934,158 +2961,14 @@ declare interface ResolutionContextOptionsControlProps {
2934
2961
  className?: string;
2935
2962
  /** Title for the control section */
2936
2963
  title?: string;
2937
- }
2938
-
2939
- /**
2940
- * Control panel for managing pending resource edits during resolution testing.
2941
- *
2942
- * The ResolutionEditControls component provides a user interface for applying or discarding
2943
- * resource edits made during resolution testing. It shows the number of pending edits,
2944
- * provides clear actions for applying or discarding them, and includes safety confirmations
2945
- * to prevent accidental data loss.
2946
- *
2947
- * @example
2948
- * ```tsx
2949
- * import { ResolutionTools } from '@fgv/ts-res-ui-components';
2950
- *
2951
- * // Basic usage with resolution state
2952
- * const ResolutionInterface = () => {
2953
- * const { state, actions } = useResolutionState(processedResources);
2954
- *
2955
- * return (
2956
- * <div>
2957
- * <ResolutionTools.ResolutionEditControls
2958
- * editCount={state.editedResources.size}
2959
- * isApplying={state.isApplyingEdits}
2960
- * hasEdits={state.hasUnsavedEdits}
2961
- * onApplyEdits={actions.applyEdits}
2962
- * onDiscardEdits={actions.discardEdits}
2963
- * />
2964
- * </div>
2965
- * );
2966
- * };
2967
- * ```
2968
- *
2969
- * @example
2970
- * ```tsx
2971
- * // Advanced usage with custom handlers and state management
2972
- * const AdvancedResolutionEditor = () => {
2973
- * const [edits, setEdits] = useState(new Map());
2974
- * const [isApplying, setIsApplying] = useState(false);
2975
- *
2976
- * const handleApplyEdits = async () => {
2977
- * setIsApplying(true);
2978
- * try {
2979
- * // Apply each edit to the resource system
2980
- * for (const [resourceId, editedValue] of edits.entries()) {
2981
- * await applyResourceEdit(resourceId, editedValue);
2982
- * }
2983
- *
2984
- * // Rebuild the resource system
2985
- * await rebuildResourceSystem();
2986
- *
2987
- * // Clear edits after successful application
2988
- * setEdits(new Map());
2989
- * showSuccess('Edits applied successfully');
2990
- * } catch (error) {
2991
- * showError(`Failed to apply edits: ${error.message}`);
2992
- * } finally {
2993
- * setIsApplying(false);
2994
- * }
2995
- * };
2996
- *
2997
- * const handleDiscardEdits = () => {
2998
- * setEdits(new Map());
2999
- * showInfo('All pending edits discarded');
3000
- * };
3001
- *
3002
- * return (
3003
- * <ResolutionTools.ResolutionEditControls
3004
- * editCount={edits.size}
3005
- * isApplying={isApplying}
3006
- * hasEdits={edits.size > 0}
3007
- * onApplyEdits={handleApplyEdits}
3008
- * onDiscardEdits={handleDiscardEdits}
3009
- * className="my-custom-controls"
3010
- * />
3011
- * );
3012
- * };
3013
- * ```
3014
- *
3015
- * @example
3016
- * ```tsx
3017
- * // Integration with resource editing workflow
3018
- * const ResourceEditingWorkflow = () => {
3019
- * const [selectedResource, setSelectedResource] = useState(null);
3020
- * const [pendingEdits, setPendingEdits] = useState({});
3021
- *
3022
- * const saveResourceEdit = (resourceId, editedValue, originalValue) => {
3023
- * setPendingEdits(prev => ({
3024
- * ...prev,
3025
- * [resourceId]: { editedValue, originalValue, timestamp: new Date() }
3026
- * }));
3027
- * };
3028
- *
3029
- * const applyAllEdits = async () => {
3030
- * const resourceManager = getResourceManager();
3031
- *
3032
- * // Apply edits as new candidates with current context
3033
- * for (const [resourceId, edit] of Object.entries(pendingEdits)) {
3034
- * await resourceManager.addCandidate(resourceId, {
3035
- * value: edit.editedValue,
3036
- * conditions: getCurrentResolutionContext(),
3037
- * metadata: { editedAt: edit.timestamp }
3038
- * });
3039
- * }
3040
- *
3041
- * // Clear pending edits
3042
- * setPendingEdits({});
3043
- * };
3044
- *
3045
- * return (
3046
- * <div className="resolution-workflow">
3047
- * <ResolutionTools.EditableJsonView
3048
- * resourceId={selectedResource}
3049
- * value={getResourceValue(selectedResource)}
3050
- * onSave={saveResourceEdit}
3051
- * />
3052
- *
3053
- * <ResolutionTools.ResolutionEditControls
3054
- * editCount={Object.keys(pendingEdits).length}
3055
- * isApplying={false}
3056
- * hasEdits={Object.keys(pendingEdits).length > 0}
3057
- * onApplyEdits={applyAllEdits}
3058
- * onDiscardEdits={() => setPendingEdits({})}
3059
- * />
3060
- * </div>
3061
- * );
3062
- * };
3063
- * ```
3064
- *
3065
- * @public
3066
- */
3067
- declare const ResolutionEditControls: React_2.FC<ResolutionEditControlsProps>;
3068
-
3069
- /**
3070
- * Props for the ResolutionEditControls component.
3071
- *
3072
- * @public
3073
- */
3074
- declare interface ResolutionEditControlsProps {
3075
- /** Number of unsaved edits */
3076
- editCount: number;
3077
- /** Whether edit application is currently in progress */
3078
- isApplying: boolean;
3079
- /** Whether any edits exist to operate on */
3080
- hasEdits: boolean;
3081
- /** Callback to apply all pending edits */
3082
- onApplyEdits?: () => Promise<void>;
3083
- /** Callback to discard all pending edits */
3084
- onDiscardEdits?: () => void;
3085
- /** Whether the controls should be disabled */
3086
- disabled?: boolean;
3087
- /** Additional CSS classes */
3088
- className?: string;
2964
+ /** Editing/creation toggle - when provided, show UI to control it */
2965
+ allowResourceCreation?: boolean;
2966
+ /** Callback for editing/creation toggle */
2967
+ onAllowResourceCreationChange?: (allow: boolean) => void;
2968
+ /** Pending resources list visibility - when provided, show UI to control it */
2969
+ showPendingResourcesInList?: boolean;
2970
+ /** Callback for pending resources list visibility */
2971
+ onShowPendingResourcesInListChange?: (show: boolean) => void;
3089
2972
  }
3090
2973
 
3091
2974
  /**
@@ -3324,13 +3207,28 @@ declare interface ResolutionState {
3324
3207
  hasUnsavedEdits: boolean;
3325
3208
  /** Whether edits are currently being applied to the system */
3326
3209
  isApplyingEdits: boolean;
3210
+ /** Resources waiting to be added to the system */
3211
+ pendingResources: Map<string, ResourceJson.Json.ILooseResourceDecl>;
3212
+ /** IDs of resources marked for deletion */
3213
+ pendingResourceDeletions: Set<string>;
3214
+ /** Draft of a new resource being created */
3215
+ newResourceDraft?: {
3216
+ resourceId: string;
3217
+ resourceType: string;
3218
+ template: ResourceJson.Json.ILooseResourceDecl;
3219
+ isValid: boolean;
3220
+ };
3221
+ /** Available resource types for creation */
3222
+ availableResourceTypes: ResourceTypes.IResourceType[];
3223
+ /** Whether there are pending resource additions or deletions */
3224
+ hasPendingResourceChanges: boolean;
3327
3225
  }
3328
3226
 
3329
3227
  export declare namespace ResolutionTools {
3330
3228
  export {
3331
3229
  ResolutionView,
3332
3230
  EditableJsonView,
3333
- ResolutionEditControls,
3231
+ UnifiedChangeControls,
3334
3232
  QualifierContextControl,
3335
3233
  ResolutionContextOptionsControl,
3336
3234
  useResolutionState,
@@ -3439,6 +3337,16 @@ declare interface ResolutionViewProps extends ViewBaseProps {
3439
3337
  /** Title for the results section (default: "Results") */
3440
3338
  results?: string;
3441
3339
  };
3340
+ /** Allow creating new resources in the UI */
3341
+ allowResourceCreation?: boolean;
3342
+ /** Default resource type for new resources (hides type selector if provided) */
3343
+ defaultResourceType?: string;
3344
+ /** Factory for creating custom resource types */
3345
+ resourceTypeFactory?: ResourceTypes.IResourceType[];
3346
+ /** Callback when pending resources are applied */
3347
+ onPendingResourcesApplied?: (added: ResourceJson.Json.ILooseResourceDecl[], deleted: string[]) => void;
3348
+ /** Show pending resources in the resource list with visual distinction */
3349
+ showPendingResourcesInList?: boolean;
3442
3350
  }
3443
3351
 
3444
3352
  /**
@@ -4619,6 +4527,58 @@ export declare namespace TsResTools {
4619
4527
  }
4620
4528
  }
4621
4529
 
4530
+ /**
4531
+ * Unified change controls for ResolutionView.
4532
+ *
4533
+ * @example
4534
+ * ```tsx
4535
+ * <UnifiedChangeControls
4536
+ * editCount={state.editedResources.size}
4537
+ * addCount={state.pendingResources.size}
4538
+ * deleteCount={state.pendingResourceDeletions.size}
4539
+ * isApplying={state.isApplyingEdits}
4540
+ * disabled={!state.currentResolver}
4541
+ * onApplyAll={actions.applyPendingResources}
4542
+ * onDiscardAll={() => {
4543
+ * actions.discardEdits();
4544
+ * actions.discardPendingResources();
4545
+ * }}
4546
+ * />
4547
+ * ```
4548
+ *
4549
+ * @public
4550
+ */
4551
+ declare const UnifiedChangeControls: React_2.FC<UnifiedChangeControlsProps>;
4552
+
4553
+ /**
4554
+ * Unified control bar that summarizes all pending changes (edits, additions, deletions)
4555
+ * and provides a single Apply/Discard action.
4556
+ *
4557
+ * Renders chips for the counts of pending edits, additions, and deletions. The Apply button
4558
+ * should commit all pending changes atomically (via a system rebuild), and Discard clears all
4559
+ * pending change buffers. This replaces older separate controls for edits and resource creation.
4560
+ *
4561
+ * @public
4562
+ */
4563
+ declare interface UnifiedChangeControlsProps {
4564
+ /** Number of pending edits to existing resources */
4565
+ editCount: number;
4566
+ /** Number of pending new resources to be added */
4567
+ addCount: number;
4568
+ /** Number of resources marked for deletion */
4569
+ deleteCount: number;
4570
+ /** Whether an apply operation is in progress (shows spinner and disables buttons) */
4571
+ isApplying?: boolean;
4572
+ /** Disable all actions (e.g., when no resolver/context is active) */
4573
+ disabled?: boolean;
4574
+ /** Optional CSS class names to attach to the container */
4575
+ className?: string;
4576
+ /** Apply all pending changes in one step (typically triggers rebuild) */
4577
+ onApplyAll: () => Promise<void> | void;
4578
+ /** Discard all pending changes (edits, additions, deletions) */
4579
+ onDiscardAll: () => void;
4580
+ }
4581
+
4622
4582
  /**
4623
4583
  * Hook for managing system configuration state including qualifiers, qualifier types, and resource types.
4624
4584
  *
@@ -192,9 +192,11 @@ export const QualifierContextControl = ({ qualifierName, value, onChange, disabl
192
192
  }
193
193
  return (React.createElement("div", { className: `bg-white rounded border border-gray-200 p-2 ${className} ${customClassName}` },
194
194
  React.createElement("div", { className: "flex items-center gap-2" },
195
- React.createElement("label", { className: "text-sm font-medium text-gray-700 min-w-0 flex-shrink-0" },
195
+ React.createElement("label", { className: "text-sm font-medium text-gray-700 min-w-0 flex-shrink-0 flex items-center gap-1" },
196
196
  qualifierName,
197
- ":"),
197
+ ":",
198
+ isHostManaged && showHostValue && (React.createElement("span", { className: "text-[10px] px-1 py-0.5 rounded bg-blue-100 text-blue-700", title: "Host-managed value" }, "HOST")),
199
+ !isHostManaged && !isEditable && (React.createElement("span", { className: "text-[10px] px-1 py-0.5 rounded bg-gray-200 text-gray-700", title: "Locked by host or configuration" }, "LOCKED"))),
198
200
  React.createElement("div", { className: "flex-1 flex items-center gap-1" },
199
201
  hasEnumeratedValues ? (
200
202
  // Dropdown for enumerated values
@@ -17,6 +17,14 @@ export interface ResolutionContextOptionsControlProps {
17
17
  className?: string;
18
18
  /** Title for the control section */
19
19
  title?: string;
20
+ /** Editing/creation toggle - when provided, show UI to control it */
21
+ allowResourceCreation?: boolean;
22
+ /** Callback for editing/creation toggle */
23
+ onAllowResourceCreationChange?: (allow: boolean) => void;
24
+ /** Pending resources list visibility - when provided, show UI to control it */
25
+ showPendingResourcesInList?: boolean;
26
+ /** Callback for pending resources list visibility */
27
+ onShowPendingResourcesInListChange?: (show: boolean) => void;
20
28
  }
21
29
  /**
22
30
  * Reusable control for configuring ResolutionView context options.
@@ -36,7 +36,7 @@ import { CogIcon, ChevronDownIcon, ChevronUpIcon, XMarkIcon } from '@heroicons/r
36
36
  *
37
37
  * @public
38
38
  */
39
- export const ResolutionContextOptionsControl = ({ options, onOptionsChange, availableQualifiers = [], presentation = 'hidden', className = '', title = 'Context Options' }) => {
39
+ export const ResolutionContextOptionsControl = ({ options, onOptionsChange, availableQualifiers = [], presentation = 'hidden', className = '', title = 'Context Options', allowResourceCreation, onAllowResourceCreationChange, showPendingResourcesInList, onShowPendingResourcesInListChange }) => {
40
40
  // Early return for hidden presentation
41
41
  if (presentation === 'hidden') {
42
42
  return null;
@@ -105,6 +105,14 @@ export const ResolutionContextOptionsControl = ({ options, onOptionsChange, avai
105
105
  "Use ",
106
106
  '{qualifierName}',
107
107
  " for dynamic qualifier names"))),
108
+ (allowResourceCreation !== undefined || showPendingResourcesInList !== undefined) && (React.createElement("div", { className: "space-y-3 pt-3 border-t border-gray-200" },
109
+ React.createElement("h4", { className: "text-sm font-medium text-gray-700" }, "Editing & Creation"),
110
+ allowResourceCreation !== undefined && (React.createElement("label", { className: "flex items-center text-xs" },
111
+ React.createElement("input", { type: "checkbox", checked: !!allowResourceCreation, onChange: (e) => onAllowResourceCreationChange?.(e.target.checked), className: "mr-2 rounded" }),
112
+ "Allow Resource Creation")),
113
+ showPendingResourcesInList !== undefined && (React.createElement("label", { className: "flex items-center text-xs" },
114
+ React.createElement("input", { type: "checkbox", checked: !!showPendingResourcesInList, onChange: (e) => onShowPendingResourcesInListChange?.(e.target.checked), className: "mr-2 rounded" }),
115
+ "Show Pending Resources In List")))),
108
116
  availableQualifiers.length > 0 && (React.createElement("div", { className: "space-y-3 pt-3 border-t border-gray-200" },
109
117
  React.createElement("h4", { className: "text-sm font-medium text-gray-700" }, "Per-Qualifier Settings"),
110
118
  React.createElement("div", { className: "space-y-3" }, availableQualifiers.map((qualifierName) => {
@@ -311,6 +311,17 @@ export const ResourceOrchestrator = ({ children, initialConfiguration, qualifier
311
311
  clearResourceEdits: resolutionData.actions.clearEdits,
312
312
  applyResourceEdits: resolutionData.actions.applyEdits,
313
313
  discardResourceEdits: resolutionData.actions.discardEdits,
314
+ // Resource creation actions
315
+ startNewResource: resolutionData.actions.startNewResource,
316
+ updateNewResourceId: resolutionData.actions.updateNewResourceId,
317
+ selectResourceType: resolutionData.actions.selectResourceType,
318
+ saveNewResourceAsPending: resolutionData.actions.saveNewResourceAsPending,
319
+ cancelNewResource: resolutionData.actions.cancelNewResource,
320
+ removePendingResource: resolutionData.actions.removePendingResource,
321
+ markResourceForDeletion: resolutionData.actions.markResourceForDeletion,
322
+ applyPendingResources: resolutionData.actions.applyPendingResources,
323
+ discardPendingResources: resolutionData.actions.discardPendingResources,
324
+ // Combined apply/discard removed; use applyPendingResources/discard* directly
314
325
  // UI state management
315
326
  selectResource: viewState.selectResource,
316
327
  addMessage: viewState.addMessage,
@@ -172,7 +172,7 @@ export interface EditableJsonViewProps {
172
172
  * }}
173
173
  * />
174
174
  *
175
- * <ResolutionTools.ResolutionEditControls
175
+ * <ResolutionTools.UnifiedChangeControls
176
176
  * editCount={state.editedResources.size}
177
177
  * isApplying={state.isApplyingEdits}
178
178
  * hasEdits={state.hasUnsavedEdits}
@@ -152,7 +152,7 @@ import { validateEditedResource } from '../../../utils/resolutionEditing';
152
152
  * }}
153
153
  * />
154
154
  *
155
- * <ResolutionTools.ResolutionEditControls
155
+ * <ResolutionTools.UnifiedChangeControls
156
156
  * editCount={state.editedResources.size}
157
157
  * isApplying={state.isApplyingEdits}
158
158
  * hasEdits={state.hasUnsavedEdits}
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { ResourceTypes } from '@fgv/ts-res';
3
+ interface NewResourceModalProps {
4
+ isOpen: boolean;
5
+ onClose: () => void;
6
+ resourceId: string;
7
+ resourceType: string;
8
+ availableResourceTypes: ResourceTypes.IResourceType[];
9
+ isValid: boolean;
10
+ defaultResourceType?: string;
11
+ onUpdateResourceId: (id: string) => void;
12
+ onSelectResourceType: (type: string) => void;
13
+ onSave: () => void;
14
+ }
15
+ /**
16
+ * Modal dialog for creating new resources with type selection and ID input.
17
+ * Supports host-controlled resource types that hide the type selector.
18
+ */
19
+ export declare const NewResourceModal: React.FC<NewResourceModalProps>;
20
+ export default NewResourceModal;
21
+ //# sourceMappingURL=NewResourceModal.d.ts.map