@fgv/ts-res-ui-components 5.0.0-10

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 (231) hide show
  1. package/.rush/temp/03c8b056281d9db0a97d8a6e25eea798a160d393.tar.log +271 -0
  2. package/.rush/temp/chunked-rush-logs/ts-res-ui-components.build.chunks.jsonl +9 -0
  3. package/.rush/temp/operation/build/all.log +9 -0
  4. package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
  5. package/.rush/temp/operation/build/state.json +3 -0
  6. package/.rush/temp/shrinkwrap-deps.json +1111 -0
  7. package/README.md +18 -0
  8. package/REFACTORING_PLAN.md +171 -0
  9. package/config/jest.config.json +16 -0
  10. package/config/jest.setup.js +64 -0
  11. package/config/rig.json +16 -0
  12. package/lib/components/common/QualifierContextControl.d.ts +14 -0
  13. package/lib/components/common/QualifierContextControl.d.ts.map +1 -0
  14. package/lib/components/common/QualifierContextControl.js +78 -0
  15. package/lib/components/common/QualifierContextControl.js.map +1 -0
  16. package/lib/components/common/ResourceListView.d.ts +11 -0
  17. package/lib/components/common/ResourceListView.d.ts.map +1 -0
  18. package/lib/components/common/ResourceListView.js +20 -0
  19. package/lib/components/common/ResourceListView.js.map +1 -0
  20. package/lib/components/common/ResourceTreeView.d.ts +12 -0
  21. package/lib/components/common/ResourceTreeView.d.ts.map +1 -0
  22. package/lib/components/common/ResourceTreeView.js +162 -0
  23. package/lib/components/common/ResourceTreeView.js.map +1 -0
  24. package/lib/components/forms/HierarchyEditor.d.ts +10 -0
  25. package/lib/components/forms/HierarchyEditor.d.ts.map +1 -0
  26. package/lib/components/forms/HierarchyEditor.js +106 -0
  27. package/lib/components/forms/HierarchyEditor.js.map +1 -0
  28. package/lib/components/forms/QualifierEditForm.d.ts +11 -0
  29. package/lib/components/forms/QualifierEditForm.d.ts.map +1 -0
  30. package/lib/components/forms/QualifierEditForm.js +181 -0
  31. package/lib/components/forms/QualifierEditForm.js.map +1 -0
  32. package/lib/components/forms/QualifierTypeEditForm.d.ts +10 -0
  33. package/lib/components/forms/QualifierTypeEditForm.d.ts.map +1 -0
  34. package/lib/components/forms/QualifierTypeEditForm.js +172 -0
  35. package/lib/components/forms/QualifierTypeEditForm.js.map +1 -0
  36. package/lib/components/forms/ResourceTypeEditForm.d.ts +10 -0
  37. package/lib/components/forms/ResourceTypeEditForm.d.ts.map +1 -0
  38. package/lib/components/forms/ResourceTypeEditForm.js +188 -0
  39. package/lib/components/forms/ResourceTypeEditForm.js.map +1 -0
  40. package/lib/components/forms/index.d.ts +9 -0
  41. package/lib/components/forms/index.d.ts.map +1 -0
  42. package/lib/components/forms/index.js +5 -0
  43. package/lib/components/forms/index.js.map +1 -0
  44. package/lib/components/orchestrator/ResourceOrchestrator.d.ts +14 -0
  45. package/lib/components/orchestrator/ResourceOrchestrator.d.ts.map +1 -0
  46. package/lib/components/orchestrator/ResourceOrchestrator.js +278 -0
  47. package/lib/components/orchestrator/ResourceOrchestrator.js.map +1 -0
  48. package/lib/components/views/CompiledView/index.d.ts +5 -0
  49. package/lib/components/views/CompiledView/index.d.ts.map +1 -0
  50. package/lib/components/views/CompiledView/index.js +595 -0
  51. package/lib/components/views/CompiledView/index.js.map +1 -0
  52. package/lib/components/views/ConfigurationView/index.d.ts +5 -0
  53. package/lib/components/views/ConfigurationView/index.d.ts.map +1 -0
  54. package/lib/components/views/ConfigurationView/index.js +363 -0
  55. package/lib/components/views/ConfigurationView/index.js.map +1 -0
  56. package/lib/components/views/FilterView/index.d.ts +5 -0
  57. package/lib/components/views/FilterView/index.d.ts.map +1 -0
  58. package/lib/components/views/FilterView/index.js +463 -0
  59. package/lib/components/views/FilterView/index.js.map +1 -0
  60. package/lib/components/views/ImportView/index.d.ts +5 -0
  61. package/lib/components/views/ImportView/index.d.ts.map +1 -0
  62. package/lib/components/views/ImportView/index.js +514 -0
  63. package/lib/components/views/ImportView/index.js.map +1 -0
  64. package/lib/components/views/ResolutionView/EditableJsonView.d.ts +21 -0
  65. package/lib/components/views/ResolutionView/EditableJsonView.d.ts.map +1 -0
  66. package/lib/components/views/ResolutionView/EditableJsonView.js +109 -0
  67. package/lib/components/views/ResolutionView/EditableJsonView.js.map +1 -0
  68. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts +19 -0
  69. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts.map +1 -0
  70. package/lib/components/views/ResolutionView/ResolutionEditControls.js +82 -0
  71. package/lib/components/views/ResolutionView/ResolutionEditControls.js.map +1 -0
  72. package/lib/components/views/ResolutionView/index.d.ts +5 -0
  73. package/lib/components/views/ResolutionView/index.d.ts.map +1 -0
  74. package/lib/components/views/ResolutionView/index.js +255 -0
  75. package/lib/components/views/ResolutionView/index.js.map +1 -0
  76. package/lib/components/views/SourceView/index.d.ts +5 -0
  77. package/lib/components/views/SourceView/index.d.ts.map +1 -0
  78. package/lib/components/views/SourceView/index.js +316 -0
  79. package/lib/components/views/SourceView/index.js.map +1 -0
  80. package/lib/components/views/ZipLoaderView/index.d.ts +5 -0
  81. package/lib/components/views/ZipLoaderView/index.d.ts.map +1 -0
  82. package/lib/components/views/ZipLoaderView/index.js +313 -0
  83. package/lib/components/views/ZipLoaderView/index.js.map +1 -0
  84. package/lib/hooks/useConfigurationState.d.ts +46 -0
  85. package/lib/hooks/useConfigurationState.d.ts.map +1 -0
  86. package/lib/hooks/useConfigurationState.js +239 -0
  87. package/lib/hooks/useConfigurationState.js.map +1 -0
  88. package/lib/hooks/useFilterState.d.ts +7 -0
  89. package/lib/hooks/useFilterState.d.ts.map +1 -0
  90. package/lib/hooks/useFilterState.js +80 -0
  91. package/lib/hooks/useFilterState.js.map +1 -0
  92. package/lib/hooks/useResolutionState.d.ts +8 -0
  93. package/lib/hooks/useResolutionState.d.ts.map +1 -0
  94. package/lib/hooks/useResolutionState.js +253 -0
  95. package/lib/hooks/useResolutionState.js.map +1 -0
  96. package/lib/hooks/useResourceData.d.ts +19 -0
  97. package/lib/hooks/useResourceData.d.ts.map +1 -0
  98. package/lib/hooks/useResourceData.js +368 -0
  99. package/lib/hooks/useResourceData.js.map +1 -0
  100. package/lib/hooks/useViewState.d.ts +10 -0
  101. package/lib/hooks/useViewState.d.ts.map +1 -0
  102. package/lib/hooks/useViewState.js +29 -0
  103. package/lib/hooks/useViewState.js.map +1 -0
  104. package/lib/index.d.ts +27 -0
  105. package/lib/index.d.ts.map +1 -0
  106. package/lib/index.js +34 -0
  107. package/lib/index.js.map +1 -0
  108. package/lib/test/helpers/testDataLoader.d.ts +37 -0
  109. package/lib/test/helpers/testDataLoader.d.ts.map +1 -0
  110. package/lib/test/helpers/testDataLoader.js +171 -0
  111. package/lib/test/helpers/testDataLoader.js.map +1 -0
  112. package/lib/test/unit/utils/configurationUtils.test.d.ts +2 -0
  113. package/lib/test/unit/utils/configurationUtils.test.d.ts.map +1 -0
  114. package/lib/test/unit/utils/configurationUtils.test.js +497 -0
  115. package/lib/test/unit/utils/configurationUtils.test.js.map +1 -0
  116. package/lib/test/unit/utils/fileProcessing.test.d.ts +2 -0
  117. package/lib/test/unit/utils/fileProcessing.test.d.ts.map +1 -0
  118. package/lib/test/unit/utils/fileProcessing.test.js +321 -0
  119. package/lib/test/unit/utils/fileProcessing.test.js.map +1 -0
  120. package/lib/test/unit/utils/filterResources.test.d.ts +2 -0
  121. package/lib/test/unit/utils/filterResources.test.d.ts.map +1 -0
  122. package/lib/test/unit/utils/filterResources.test.js +403 -0
  123. package/lib/test/unit/utils/filterResources.test.js.map +1 -0
  124. package/lib/test/unit/utils/resolutionEditing.test.d.ts +2 -0
  125. package/lib/test/unit/utils/resolutionEditing.test.d.ts.map +1 -0
  126. package/lib/test/unit/utils/resolutionEditing.test.js +439 -0
  127. package/lib/test/unit/utils/resolutionEditing.test.js.map +1 -0
  128. package/lib/test/unit/utils/resolutionUtils.test.d.ts +2 -0
  129. package/lib/test/unit/utils/resolutionUtils.test.d.ts.map +1 -0
  130. package/lib/test/unit/utils/resolutionUtils.test.js +397 -0
  131. package/lib/test/unit/utils/resolutionUtils.test.js.map +1 -0
  132. package/lib/test/unit/utils/tsResIntegration.test.d.ts +2 -0
  133. package/lib/test/unit/utils/tsResIntegration.test.d.ts.map +1 -0
  134. package/lib/test/unit/utils/tsResIntegration.test.js +376 -0
  135. package/lib/test/unit/utils/tsResIntegration.test.js.map +1 -0
  136. package/lib/types/index.d.ts +251 -0
  137. package/lib/types/index.d.ts.map +1 -0
  138. package/lib/types/index.js +2 -0
  139. package/lib/types/index.js.map +1 -0
  140. package/lib/utils/configurationUtils.d.ts +74 -0
  141. package/lib/utils/configurationUtils.d.ts.map +1 -0
  142. package/lib/utils/configurationUtils.js +359 -0
  143. package/lib/utils/configurationUtils.js.map +1 -0
  144. package/lib/utils/fileProcessing.d.ts +18 -0
  145. package/lib/utils/fileProcessing.d.ts.map +1 -0
  146. package/lib/utils/fileProcessing.js +142 -0
  147. package/lib/utils/fileProcessing.js.map +1 -0
  148. package/lib/utils/filterResources.d.ts +38 -0
  149. package/lib/utils/filterResources.d.ts.map +1 -0
  150. package/lib/utils/filterResources.js +153 -0
  151. package/lib/utils/filterResources.js.map +1 -0
  152. package/lib/utils/resolutionEditing.d.ts +58 -0
  153. package/lib/utils/resolutionEditing.d.ts.map +1 -0
  154. package/lib/utils/resolutionEditing.js +246 -0
  155. package/lib/utils/resolutionEditing.js.map +1 -0
  156. package/lib/utils/resolutionUtils.d.ts +28 -0
  157. package/lib/utils/resolutionUtils.d.ts.map +1 -0
  158. package/lib/utils/resolutionUtils.js +216 -0
  159. package/lib/utils/resolutionUtils.js.map +1 -0
  160. package/lib/utils/tsResIntegration.d.ts +71 -0
  161. package/lib/utils/tsResIntegration.d.ts.map +1 -0
  162. package/lib/utils/tsResIntegration.js +294 -0
  163. package/lib/utils/tsResIntegration.js.map +1 -0
  164. package/lib/utils/zipLoader/browserZipLoader.d.ts +48 -0
  165. package/lib/utils/zipLoader/browserZipLoader.d.ts.map +1 -0
  166. package/lib/utils/zipLoader/browserZipLoader.js +247 -0
  167. package/lib/utils/zipLoader/browserZipLoader.js.map +1 -0
  168. package/lib/utils/zipLoader/index.d.ts +8 -0
  169. package/lib/utils/zipLoader/index.d.ts.map +1 -0
  170. package/lib/utils/zipLoader/index.js +13 -0
  171. package/lib/utils/zipLoader/index.js.map +1 -0
  172. package/lib/utils/zipLoader/nodeZipBuilder.d.ts +55 -0
  173. package/lib/utils/zipLoader/nodeZipBuilder.d.ts.map +1 -0
  174. package/lib/utils/zipLoader/nodeZipBuilder.js +98 -0
  175. package/lib/utils/zipLoader/nodeZipBuilder.js.map +1 -0
  176. package/lib/utils/zipLoader/types.d.ts +139 -0
  177. package/lib/utils/zipLoader/types.d.ts.map +1 -0
  178. package/lib/utils/zipLoader/types.js +2 -0
  179. package/lib/utils/zipLoader/types.js.map +1 -0
  180. package/lib/utils/zipLoader/zipUtils.d.ts +53 -0
  181. package/lib/utils/zipLoader/zipUtils.d.ts.map +1 -0
  182. package/lib/utils/zipLoader/zipUtils.js +229 -0
  183. package/lib/utils/zipLoader/zipUtils.js.map +1 -0
  184. package/package.json +69 -0
  185. package/rush-logs/ts-res-ui-components.build.cache.log +3 -0
  186. package/rush-logs/ts-res-ui-components.build.log +9 -0
  187. package/src/components/common/QualifierContextControl.tsx +151 -0
  188. package/src/components/common/ResourceListView.tsx +63 -0
  189. package/src/components/common/ResourceTreeView.tsx +271 -0
  190. package/src/components/forms/HierarchyEditor.tsx +204 -0
  191. package/src/components/forms/QualifierEditForm.tsx +355 -0
  192. package/src/components/forms/QualifierTypeEditForm.tsx +347 -0
  193. package/src/components/forms/ResourceTypeEditForm.tsx +331 -0
  194. package/src/components/forms/index.ts +11 -0
  195. package/src/components/orchestrator/ResourceOrchestrator.tsx +372 -0
  196. package/src/components/views/CompiledView/index.tsx +922 -0
  197. package/src/components/views/ConfigurationView/index.tsx +800 -0
  198. package/src/components/views/FilterView/index.tsx +825 -0
  199. package/src/components/views/ImportView/index.tsx +717 -0
  200. package/src/components/views/ResolutionView/EditableJsonView.tsx +214 -0
  201. package/src/components/views/ResolutionView/ResolutionEditControls.tsx +170 -0
  202. package/src/components/views/ResolutionView/index.tsx +591 -0
  203. package/src/components/views/SourceView/index.tsx +536 -0
  204. package/src/components/views/ZipLoaderView/index.tsx +485 -0
  205. package/src/hooks/useConfigurationState.ts +374 -0
  206. package/src/hooks/useFilterState.ts +97 -0
  207. package/src/hooks/useResolutionState.ts +355 -0
  208. package/src/hooks/useResourceData.ts +467 -0
  209. package/src/hooks/useViewState.ts +44 -0
  210. package/src/index.ts +45 -0
  211. package/src/test/helpers/testDataLoader.ts +195 -0
  212. package/src/test/unit/utils/configurationUtils.test.ts +630 -0
  213. package/src/test/unit/utils/fileProcessing.test.ts +391 -0
  214. package/src/test/unit/utils/filterResources.test.ts +574 -0
  215. package/src/test/unit/utils/resolutionEditing.test.ts +556 -0
  216. package/src/test/unit/utils/resolutionUtils.test.ts +521 -0
  217. package/src/test/unit/utils/tsResIntegration.test.ts +433 -0
  218. package/src/types/index.ts +322 -0
  219. package/src/utils/configurationUtils.ts +424 -0
  220. package/src/utils/fileProcessing.ts +160 -0
  221. package/src/utils/filterResources.ts +206 -0
  222. package/src/utils/resolutionEditing.ts +319 -0
  223. package/src/utils/resolutionUtils.ts +289 -0
  224. package/src/utils/tsResIntegration.ts +440 -0
  225. package/src/utils/zipLoader/browserZipLoader.ts +319 -0
  226. package/src/utils/zipLoader/index.ts +26 -0
  227. package/src/utils/zipLoader/nodeZipBuilder.ts +153 -0
  228. package/src/utils/zipLoader/types.ts +175 -0
  229. package/src/utils/zipLoader/zipUtils.ts +266 -0
  230. package/temp/build/typescript/ts_gZid87Hu.json +1 -0
  231. package/tsconfig.json +15 -0
@@ -0,0 +1,239 @@
1
+ import React, { useState, useCallback, useMemo, useRef } from 'react';
2
+ import { succeed, fail } from '@fgv/ts-utils';
3
+ import { getDefaultConfiguration, validateConfiguration, cloneConfiguration, compareConfigurations, trackConfigurationChanges, exportConfiguration, importConfiguration, getConfigurationTemplates } from '../utils/configurationUtils';
4
+ export function useConfigurationState(initialConfiguration, onConfigurationChange, onUnsavedChanges) {
5
+ const defaultConfig = useMemo(() => initialConfiguration || getDefaultConfiguration(), [initialConfiguration]);
6
+ const originalConfigRef = useRef(defaultConfig);
7
+ // State
8
+ const [currentConfiguration, setCurrentConfiguration] = useState(cloneConfiguration(defaultConfig));
9
+ const [activeTab, setActiveTab] = useState('qualifiers');
10
+ const [isJsonView, setIsJsonView] = useState(false);
11
+ const [jsonString, setJsonString] = useState('');
12
+ const [jsonError, setJsonError] = useState(null);
13
+ // Computed state
14
+ const hasUnsavedChanges = useMemo(() => {
15
+ return !compareConfigurations(originalConfigRef.current, currentConfiguration);
16
+ }, [currentConfiguration]);
17
+ const changes = useMemo(() => {
18
+ return trackConfigurationChanges(originalConfigRef.current, currentConfiguration);
19
+ }, [currentConfiguration]);
20
+ const validation = useMemo(() => {
21
+ return validateConfiguration(currentConfiguration);
22
+ }, [currentConfiguration]);
23
+ // Update JSON string when configuration changes and in JSON view
24
+ React.useEffect(() => {
25
+ if (isJsonView) {
26
+ const result = exportConfiguration(currentConfiguration, { format: 'json', pretty: true });
27
+ if (result.isSuccess()) {
28
+ setJsonString(result.value);
29
+ setJsonError(null);
30
+ }
31
+ else {
32
+ setJsonError(result.message);
33
+ }
34
+ }
35
+ }, [currentConfiguration, isJsonView]);
36
+ // Notify parent of configuration changes
37
+ const isFirstMount = useRef(true);
38
+ const lastNotifiedConfig = useRef(currentConfiguration);
39
+ React.useEffect(() => {
40
+ // Skip notification on first mount to avoid loops
41
+ if (isFirstMount.current) {
42
+ isFirstMount.current = false;
43
+ return;
44
+ }
45
+ // Only notify if configuration actually changed
46
+ if (!compareConfigurations(lastNotifiedConfig.current, currentConfiguration)) {
47
+ lastNotifiedConfig.current = currentConfiguration;
48
+ onConfigurationChange?.(currentConfiguration);
49
+ }
50
+ }, [currentConfiguration, onConfigurationChange]);
51
+ // Notify parent of unsaved changes
52
+ React.useEffect(() => {
53
+ onUnsavedChanges?.(hasUnsavedChanges);
54
+ }, [hasUnsavedChanges, onUnsavedChanges]);
55
+ // Actions
56
+ const loadConfiguration = useCallback((config) => {
57
+ const cloned = cloneConfiguration(config);
58
+ setCurrentConfiguration(cloned);
59
+ originalConfigRef.current = cloneConfiguration(config);
60
+ }, []);
61
+ const resetConfiguration = useCallback(() => {
62
+ setCurrentConfiguration(cloneConfiguration(originalConfigRef.current));
63
+ }, []);
64
+ const applyConfiguration = useCallback(() => {
65
+ originalConfigRef.current = cloneConfiguration(currentConfiguration);
66
+ }, [currentConfiguration]);
67
+ // Qualifier Types operations
68
+ const updateQualifierTypes = useCallback((qualifierTypes) => {
69
+ setCurrentConfiguration((prev) => ({
70
+ ...prev,
71
+ qualifierTypes
72
+ }));
73
+ }, []);
74
+ const addQualifierType = useCallback((qualifierType) => {
75
+ setCurrentConfiguration((prev) => ({
76
+ ...prev,
77
+ qualifierTypes: [...(prev.qualifierTypes || []), qualifierType]
78
+ }));
79
+ }, []);
80
+ const updateQualifierType = useCallback((index, qualifierType) => {
81
+ setCurrentConfiguration((prev) => ({
82
+ ...prev,
83
+ qualifierTypes: prev.qualifierTypes?.map((qt, i) => (i === index ? qualifierType : qt)) || []
84
+ }));
85
+ }, []);
86
+ const removeQualifierType = useCallback((index) => {
87
+ setCurrentConfiguration((prev) => ({
88
+ ...prev,
89
+ qualifierTypes: prev.qualifierTypes?.filter((_, i) => i !== index) || []
90
+ }));
91
+ }, []);
92
+ // Qualifiers operations
93
+ const updateQualifiers = useCallback((qualifiers) => {
94
+ setCurrentConfiguration((prev) => ({
95
+ ...prev,
96
+ qualifiers
97
+ }));
98
+ }, []);
99
+ const addQualifier = useCallback((qualifier) => {
100
+ setCurrentConfiguration((prev) => ({
101
+ ...prev,
102
+ qualifiers: [...(prev.qualifiers || []), qualifier]
103
+ }));
104
+ }, []);
105
+ const updateQualifier = useCallback((index, qualifier) => {
106
+ setCurrentConfiguration((prev) => ({
107
+ ...prev,
108
+ qualifiers: prev.qualifiers?.map((q, i) => (i === index ? qualifier : q)) || []
109
+ }));
110
+ }, []);
111
+ const removeQualifier = useCallback((index) => {
112
+ setCurrentConfiguration((prev) => ({
113
+ ...prev,
114
+ qualifiers: prev.qualifiers?.filter((_, i) => i !== index) || []
115
+ }));
116
+ }, []);
117
+ // Resource Types operations
118
+ const updateResourceTypes = useCallback((resourceTypes) => {
119
+ setCurrentConfiguration((prev) => ({
120
+ ...prev,
121
+ resourceTypes
122
+ }));
123
+ }, []);
124
+ const addResourceType = useCallback((resourceType) => {
125
+ setCurrentConfiguration((prev) => ({
126
+ ...prev,
127
+ resourceTypes: [...(prev.resourceTypes || []), resourceType]
128
+ }));
129
+ }, []);
130
+ const updateResourceType = useCallback((index, resourceType) => {
131
+ setCurrentConfiguration((prev) => ({
132
+ ...prev,
133
+ resourceTypes: prev.resourceTypes?.map((rt, i) => (i === index ? resourceType : rt)) || []
134
+ }));
135
+ }, []);
136
+ const removeResourceType = useCallback((index) => {
137
+ setCurrentConfiguration((prev) => ({
138
+ ...prev,
139
+ resourceTypes: prev.resourceTypes?.filter((_, i) => i !== index) || []
140
+ }));
141
+ }, []);
142
+ // View management
143
+ const toggleJsonView = useCallback(() => {
144
+ if (!isJsonView) {
145
+ // Switching to JSON view - export current config
146
+ const result = exportConfiguration(currentConfiguration, { format: 'json', pretty: true });
147
+ if (result.isSuccess()) {
148
+ setJsonString(result.value);
149
+ setJsonError(null);
150
+ }
151
+ else {
152
+ setJsonError(result.message);
153
+ }
154
+ }
155
+ setIsJsonView(!isJsonView);
156
+ }, [isJsonView, currentConfiguration]);
157
+ const updateJsonString = useCallback((json) => {
158
+ setJsonString(json);
159
+ setJsonError(null);
160
+ }, []);
161
+ const applyJsonChanges = useCallback(() => {
162
+ const result = importConfiguration(jsonString);
163
+ if (result.isSuccess()) {
164
+ setCurrentConfiguration(result.value);
165
+ setJsonError(null);
166
+ return succeed(undefined);
167
+ }
168
+ else {
169
+ setJsonError(result.message);
170
+ return fail(result.message);
171
+ }
172
+ }, [jsonString]);
173
+ // Import/Export
174
+ const exportToJson = useCallback((options) => {
175
+ return exportConfiguration(currentConfiguration, options);
176
+ }, [currentConfiguration]);
177
+ const importFromJson = useCallback((jsonData) => {
178
+ const result = importConfiguration(jsonData);
179
+ if (result.isSuccess()) {
180
+ loadConfiguration(result.value);
181
+ return succeed(undefined);
182
+ }
183
+ return fail(result.message);
184
+ }, [loadConfiguration]);
185
+ const loadTemplate = useCallback((templateId) => {
186
+ const templates = getConfigurationTemplates();
187
+ const template = templates.find((t) => t.id === templateId);
188
+ if (!template) {
189
+ return fail(`Template '${templateId}' not found`);
190
+ }
191
+ loadConfiguration(template.configuration);
192
+ return succeed(undefined);
193
+ }, [loadConfiguration]);
194
+ const validateCurrent = useCallback(() => {
195
+ return validateConfiguration(currentConfiguration);
196
+ }, [currentConfiguration]);
197
+ const state = {
198
+ currentConfiguration,
199
+ originalConfiguration: originalConfigRef.current,
200
+ hasUnsavedChanges,
201
+ changes,
202
+ validation,
203
+ activeTab,
204
+ isJsonView,
205
+ jsonString,
206
+ jsonError
207
+ };
208
+ const actions = {
209
+ loadConfiguration,
210
+ resetConfiguration,
211
+ applyConfiguration,
212
+ updateQualifierTypes,
213
+ updateQualifiers,
214
+ updateResourceTypes,
215
+ addQualifierType,
216
+ updateQualifierType,
217
+ removeQualifierType,
218
+ addQualifier,
219
+ updateQualifier,
220
+ removeQualifier,
221
+ addResourceType,
222
+ updateResourceType,
223
+ removeResourceType,
224
+ setActiveTab,
225
+ toggleJsonView,
226
+ updateJsonString,
227
+ applyJsonChanges,
228
+ exportToJson,
229
+ importFromJson,
230
+ loadTemplate,
231
+ validateCurrent
232
+ };
233
+ return {
234
+ state,
235
+ actions,
236
+ templates: getConfigurationTemplates()
237
+ };
238
+ }
239
+ //# sourceMappingURL=useConfigurationState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConfigurationState.js","sourceRoot":"","sources":["../../src/hooks/useConfigurationState.ts"],"names":[],"mappings":"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","sourcesContent":["import React, { useState, useCallback, useMemo, useRef } from 'react';\nimport { Config, QualifierTypes, Qualifiers, ResourceTypes } from '@fgv/ts-res';\nimport { Result, succeed, fail } from '@fgv/ts-utils';\nimport {\n getDefaultConfiguration,\n validateConfiguration,\n cloneConfiguration,\n compareConfigurations,\n trackConfigurationChanges,\n exportConfiguration,\n importConfiguration,\n getConfigurationTemplates,\n generateConfigurationFilename,\n ConfigurationChanges,\n ConfigurationValidationResult,\n ConfigurationExportOptions,\n ConfigurationTemplate\n} from '../utils/configurationUtils';\n\nexport interface ConfigurationState {\n currentConfiguration: Config.Model.ISystemConfiguration;\n originalConfiguration: Config.Model.ISystemConfiguration;\n hasUnsavedChanges: boolean;\n changes: ConfigurationChanges;\n validation: ConfigurationValidationResult;\n activeTab: 'qualifierTypes' | 'qualifiers' | 'resourceTypes' | 'json';\n isJsonView: boolean;\n jsonString: string;\n jsonError: string | null;\n}\n\nexport interface ConfigurationActions {\n // Configuration management\n loadConfiguration: (config: Config.Model.ISystemConfiguration) => void;\n resetConfiguration: () => void;\n applyConfiguration: () => void;\n\n // Editing operations\n updateQualifierTypes: (qualifierTypes: QualifierTypes.Config.ISystemQualifierTypeConfig[]) => void;\n updateQualifiers: (qualifiers: Qualifiers.IQualifierDecl[]) => void;\n updateResourceTypes: (resourceTypes: ResourceTypes.Config.IResourceTypeConfig[]) => void;\n\n // Individual item operations\n addQualifierType: (qualifierType: QualifierTypes.Config.ISystemQualifierTypeConfig) => void;\n updateQualifierType: (\n index: number,\n qualifierType: QualifierTypes.Config.ISystemQualifierTypeConfig\n ) => void;\n removeQualifierType: (index: number) => void;\n\n addQualifier: (qualifier: Qualifiers.IQualifierDecl) => void;\n updateQualifier: (index: number, qualifier: Qualifiers.IQualifierDecl) => void;\n removeQualifier: (index: number) => void;\n\n addResourceType: (resourceType: ResourceTypes.Config.IResourceTypeConfig) => void;\n updateResourceType: (index: number, resourceType: ResourceTypes.Config.IResourceTypeConfig) => void;\n removeResourceType: (index: number) => void;\n\n // View management\n setActiveTab: (tab: ConfigurationState['activeTab']) => void;\n toggleJsonView: () => void;\n updateJsonString: (json: string) => void;\n applyJsonChanges: () => Result<void>;\n\n // Import/Export\n exportToJson: (options?: ConfigurationExportOptions) => Result<string>;\n importFromJson: (jsonData: string) => Result<void>;\n loadTemplate: (templateId: string) => Result<void>;\n\n // Validation\n validateCurrent: () => ConfigurationValidationResult;\n}\n\nexport interface UseConfigurationStateReturn {\n state: ConfigurationState;\n actions: ConfigurationActions;\n templates: ConfigurationTemplate[];\n}\n\nexport function useConfigurationState(\n initialConfiguration?: Config.Model.ISystemConfiguration,\n onConfigurationChange?: (config: Config.Model.ISystemConfiguration) => void,\n onUnsavedChanges?: (hasChanges: boolean) => void\n): UseConfigurationStateReturn {\n const defaultConfig = useMemo(\n () => initialConfiguration || getDefaultConfiguration(),\n [initialConfiguration]\n );\n const originalConfigRef = useRef(defaultConfig);\n\n // State\n const [currentConfiguration, setCurrentConfiguration] = useState<Config.Model.ISystemConfiguration>(\n cloneConfiguration(defaultConfig)\n );\n const [activeTab, setActiveTab] = useState<ConfigurationState['activeTab']>('qualifiers');\n const [isJsonView, setIsJsonView] = useState(false);\n const [jsonString, setJsonString] = useState('');\n const [jsonError, setJsonError] = useState<string | null>(null);\n\n // Computed state\n const hasUnsavedChanges = useMemo(() => {\n return !compareConfigurations(originalConfigRef.current, currentConfiguration);\n }, [currentConfiguration]);\n\n const changes = useMemo(() => {\n return trackConfigurationChanges(originalConfigRef.current, currentConfiguration);\n }, [currentConfiguration]);\n\n const validation = useMemo(() => {\n return validateConfiguration(currentConfiguration);\n }, [currentConfiguration]);\n\n // Update JSON string when configuration changes and in JSON view\n React.useEffect(() => {\n if (isJsonView) {\n const result = exportConfiguration(currentConfiguration, { format: 'json', pretty: true });\n if (result.isSuccess()) {\n setJsonString(result.value);\n setJsonError(null);\n } else {\n setJsonError(result.message);\n }\n }\n }, [currentConfiguration, isJsonView]);\n\n // Notify parent of configuration changes\n const isFirstMount = useRef(true);\n const lastNotifiedConfig = useRef(currentConfiguration);\n\n React.useEffect(() => {\n // Skip notification on first mount to avoid loops\n if (isFirstMount.current) {\n isFirstMount.current = false;\n return;\n }\n\n // Only notify if configuration actually changed\n if (!compareConfigurations(lastNotifiedConfig.current, currentConfiguration)) {\n lastNotifiedConfig.current = currentConfiguration;\n onConfigurationChange?.(currentConfiguration);\n }\n }, [currentConfiguration, onConfigurationChange]);\n\n // Notify parent of unsaved changes\n React.useEffect(() => {\n onUnsavedChanges?.(hasUnsavedChanges);\n }, [hasUnsavedChanges, onUnsavedChanges]);\n\n // Actions\n const loadConfiguration = useCallback((config: Config.Model.ISystemConfiguration) => {\n const cloned = cloneConfiguration(config);\n setCurrentConfiguration(cloned);\n originalConfigRef.current = cloneConfiguration(config);\n }, []);\n\n const resetConfiguration = useCallback(() => {\n setCurrentConfiguration(cloneConfiguration(originalConfigRef.current));\n }, []);\n\n const applyConfiguration = useCallback(() => {\n originalConfigRef.current = cloneConfiguration(currentConfiguration);\n }, [currentConfiguration]);\n\n // Qualifier Types operations\n const updateQualifierTypes = useCallback(\n (qualifierTypes: QualifierTypes.Config.ISystemQualifierTypeConfig[]) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n qualifierTypes\n }));\n },\n []\n );\n\n const addQualifierType = useCallback((qualifierType: QualifierTypes.Config.ISystemQualifierTypeConfig) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n qualifierTypes: [...(prev.qualifierTypes || []), qualifierType]\n }));\n }, []);\n\n const updateQualifierType = useCallback(\n (index: number, qualifierType: QualifierTypes.Config.ISystemQualifierTypeConfig) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n qualifierTypes: prev.qualifierTypes?.map((qt, i) => (i === index ? qualifierType : qt)) || []\n }));\n },\n []\n );\n\n const removeQualifierType = useCallback((index: number) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n qualifierTypes: prev.qualifierTypes?.filter((_, i) => i !== index) || []\n }));\n }, []);\n\n // Qualifiers operations\n const updateQualifiers = useCallback((qualifiers: Qualifiers.IQualifierDecl[]) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n qualifiers\n }));\n }, []);\n\n const addQualifier = useCallback((qualifier: Qualifiers.IQualifierDecl) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n qualifiers: [...(prev.qualifiers || []), qualifier]\n }));\n }, []);\n\n const updateQualifier = useCallback((index: number, qualifier: Qualifiers.IQualifierDecl) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n qualifiers: prev.qualifiers?.map((q, i) => (i === index ? qualifier : q)) || []\n }));\n }, []);\n\n const removeQualifier = useCallback((index: number) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n qualifiers: prev.qualifiers?.filter((_, i) => i !== index) || []\n }));\n }, []);\n\n // Resource Types operations\n const updateResourceTypes = useCallback((resourceTypes: ResourceTypes.Config.IResourceTypeConfig[]) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n resourceTypes\n }));\n }, []);\n\n const addResourceType = useCallback((resourceType: ResourceTypes.Config.IResourceTypeConfig) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n resourceTypes: [...(prev.resourceTypes || []), resourceType]\n }));\n }, []);\n\n const updateResourceType = useCallback(\n (index: number, resourceType: ResourceTypes.Config.IResourceTypeConfig) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n resourceTypes: prev.resourceTypes?.map((rt, i) => (i === index ? resourceType : rt)) || []\n }));\n },\n []\n );\n\n const removeResourceType = useCallback((index: number) => {\n setCurrentConfiguration((prev) => ({\n ...prev,\n resourceTypes: prev.resourceTypes?.filter((_, i) => i !== index) || []\n }));\n }, []);\n\n // View management\n const toggleJsonView = useCallback(() => {\n if (!isJsonView) {\n // Switching to JSON view - export current config\n const result = exportConfiguration(currentConfiguration, { format: 'json', pretty: true });\n if (result.isSuccess()) {\n setJsonString(result.value);\n setJsonError(null);\n } else {\n setJsonError(result.message);\n }\n }\n setIsJsonView(!isJsonView);\n }, [isJsonView, currentConfiguration]);\n\n const updateJsonString = useCallback((json: string) => {\n setJsonString(json);\n setJsonError(null);\n }, []);\n\n const applyJsonChanges = useCallback((): Result<void> => {\n const result = importConfiguration(jsonString);\n if (result.isSuccess()) {\n setCurrentConfiguration(result.value);\n setJsonError(null);\n return succeed(undefined);\n } else {\n setJsonError(result.message);\n return fail(result.message);\n }\n }, [jsonString]);\n\n // Import/Export\n const exportToJson = useCallback(\n (options?: ConfigurationExportOptions): Result<string> => {\n return exportConfiguration(currentConfiguration, options);\n },\n [currentConfiguration]\n );\n\n const importFromJson = useCallback(\n (jsonData: string): Result<void> => {\n const result = importConfiguration(jsonData);\n if (result.isSuccess()) {\n loadConfiguration(result.value);\n return succeed(undefined);\n }\n return fail(result.message);\n },\n [loadConfiguration]\n );\n\n const loadTemplate = useCallback(\n (templateId: string): Result<void> => {\n const templates = getConfigurationTemplates();\n const template = templates.find((t) => t.id === templateId);\n\n if (!template) {\n return fail(`Template '${templateId}' not found`);\n }\n\n loadConfiguration(template.configuration);\n return succeed(undefined);\n },\n [loadConfiguration]\n );\n\n const validateCurrent = useCallback((): ConfigurationValidationResult => {\n return validateConfiguration(currentConfiguration);\n }, [currentConfiguration]);\n\n const state: ConfigurationState = {\n currentConfiguration,\n originalConfiguration: originalConfigRef.current,\n hasUnsavedChanges,\n changes,\n validation,\n activeTab,\n isJsonView,\n jsonString,\n jsonError\n };\n\n const actions: ConfigurationActions = {\n loadConfiguration,\n resetConfiguration,\n applyConfiguration,\n updateQualifierTypes,\n updateQualifiers,\n updateResourceTypes,\n addQualifierType,\n updateQualifierType,\n removeQualifierType,\n addQualifier,\n updateQualifier,\n removeQualifier,\n addResourceType,\n updateResourceType,\n removeResourceType,\n setActiveTab,\n toggleJsonView,\n updateJsonString,\n applyJsonChanges,\n exportToJson,\n importFromJson,\n loadTemplate,\n validateCurrent\n };\n\n return {\n state,\n actions,\n templates: getConfigurationTemplates()\n };\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import { FilterState, FilterActions } from '../types';
2
+ export interface UseFilterStateReturn {
3
+ state: FilterState;
4
+ actions: FilterActions;
5
+ }
6
+ export declare function useFilterState(initialState?: Partial<FilterState>): UseFilterStateReturn;
7
+ //# sourceMappingURL=useFilterState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFilterState.d.ts","sourceRoot":"","sources":["../../src/hooks/useFilterState.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;CACxB;AAqBD,wBAAgB,cAAc,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,oBAAoB,CAqExF"}
@@ -0,0 +1,80 @@
1
+ import { useState, useCallback } from 'react';
2
+ const initialFilterState = {
3
+ enabled: false,
4
+ values: {},
5
+ appliedValues: {},
6
+ hasPendingChanges: false,
7
+ reduceQualifiers: false
8
+ };
9
+ // Normalize values by filtering out undefined and empty strings for consistent comparison
10
+ const normalizeValues = (vals) => {
11
+ const normalized = {};
12
+ Object.entries(vals).forEach(([key, value]) => {
13
+ if (value !== undefined && value !== '') {
14
+ normalized[key] = value;
15
+ }
16
+ });
17
+ return normalized;
18
+ };
19
+ export function useFilterState(initialState) {
20
+ const [state, setState] = useState({
21
+ ...initialFilterState,
22
+ ...initialState
23
+ });
24
+ const updateFilterEnabled = useCallback((enabled) => {
25
+ setState((prev) => {
26
+ const normalizedValues = normalizeValues(prev.values);
27
+ const normalizedApplied = normalizeValues(prev.appliedValues);
28
+ const hasChanges = JSON.stringify(normalizedValues) !== JSON.stringify(normalizedApplied);
29
+ return {
30
+ ...prev,
31
+ enabled,
32
+ // Only consider filter values when determining pending changes, not the enabled state itself
33
+ hasPendingChanges: enabled && hasChanges
34
+ };
35
+ });
36
+ }, []);
37
+ const updateFilterValues = useCallback((values) => {
38
+ setState((prev) => {
39
+ const normalizedValues = normalizeValues(values);
40
+ const normalizedApplied = normalizeValues(prev.appliedValues);
41
+ const hasChanges = JSON.stringify(normalizedValues) !== JSON.stringify(normalizedApplied);
42
+ return {
43
+ ...prev,
44
+ values,
45
+ hasPendingChanges: prev.enabled && hasChanges
46
+ };
47
+ });
48
+ }, []);
49
+ const applyFilterValues = useCallback(() => {
50
+ setState((prev) => ({
51
+ ...prev,
52
+ appliedValues: { ...prev.values },
53
+ hasPendingChanges: false
54
+ }));
55
+ }, []);
56
+ const resetFilterValues = useCallback(() => {
57
+ setState((prev) => ({
58
+ ...prev,
59
+ values: {},
60
+ appliedValues: {},
61
+ hasPendingChanges: false,
62
+ enabled: false
63
+ }));
64
+ }, []);
65
+ const updateReduceQualifiers = useCallback((reduceQualifiers) => {
66
+ setState((prev) => ({
67
+ ...prev,
68
+ reduceQualifiers
69
+ }));
70
+ }, []);
71
+ const actions = {
72
+ updateFilterEnabled,
73
+ updateFilterValues,
74
+ applyFilterValues,
75
+ resetFilterValues,
76
+ updateReduceQualifiers
77
+ };
78
+ return { state, actions };
79
+ }
80
+ //# sourceMappingURL=useFilterState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFilterState.js","sourceRoot":"","sources":["../../src/hooks/useFilterState.ts"],"names":[],"mappings":"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","sourcesContent":["import { useState, useCallback } from 'react';\nimport { FilterState, FilterActions } from '../types';\n\nexport interface UseFilterStateReturn {\n state: FilterState;\n actions: FilterActions;\n}\n\nconst initialFilterState: FilterState = {\n enabled: false,\n values: {},\n appliedValues: {},\n hasPendingChanges: false,\n reduceQualifiers: false\n};\n\n// Normalize values by filtering out undefined and empty strings for consistent comparison\nconst normalizeValues = (vals: Record<string, string | undefined>): Record<string, string> => {\n const normalized: Record<string, string> = {};\n Object.entries(vals).forEach(([key, value]) => {\n if (value !== undefined && value !== '') {\n normalized[key] = value;\n }\n });\n return normalized;\n};\n\nexport function useFilterState(initialState?: Partial<FilterState>): UseFilterStateReturn {\n const [state, setState] = useState<FilterState>({\n ...initialFilterState,\n ...initialState\n });\n\n const updateFilterEnabled = useCallback((enabled: boolean) => {\n setState((prev) => {\n const normalizedValues = normalizeValues(prev.values);\n const normalizedApplied = normalizeValues(prev.appliedValues);\n const hasChanges = JSON.stringify(normalizedValues) !== JSON.stringify(normalizedApplied);\n\n return {\n ...prev,\n enabled,\n // Only consider filter values when determining pending changes, not the enabled state itself\n hasPendingChanges: enabled && hasChanges\n };\n });\n }, []);\n\n const updateFilterValues = useCallback((values: Record<string, string | undefined>) => {\n setState((prev) => {\n const normalizedValues = normalizeValues(values);\n const normalizedApplied = normalizeValues(prev.appliedValues);\n const hasChanges = JSON.stringify(normalizedValues) !== JSON.stringify(normalizedApplied);\n\n return {\n ...prev,\n values,\n hasPendingChanges: prev.enabled && hasChanges\n };\n });\n }, []);\n\n const applyFilterValues = useCallback(() => {\n setState((prev) => ({\n ...prev,\n appliedValues: { ...prev.values },\n hasPendingChanges: false\n }));\n }, []);\n\n const resetFilterValues = useCallback(() => {\n setState((prev) => ({\n ...prev,\n values: {},\n appliedValues: {},\n hasPendingChanges: false,\n enabled: false\n }));\n }, []);\n\n const updateReduceQualifiers = useCallback((reduceQualifiers: boolean) => {\n setState((prev) => ({\n ...prev,\n reduceQualifiers\n }));\n }, []);\n\n const actions: FilterActions = {\n updateFilterEnabled,\n updateFilterValues,\n applyFilterValues,\n resetFilterValues,\n updateReduceQualifiers\n };\n\n return { state, actions };\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import { ResolutionState, ResolutionActions, ProcessedResources } from '../types';
2
+ export interface UseResolutionStateReturn {
3
+ state: ResolutionState;
4
+ actions: ResolutionActions;
5
+ availableQualifiers: string[];
6
+ }
7
+ export declare function useResolutionState(processedResources: ProcessedResources | null, onMessage?: (type: 'info' | 'warning' | 'error' | 'success', message: string) => void, onSystemUpdate?: (updatedResources: ProcessedResources) => void): UseResolutionStateReturn;
8
+ //# sourceMappingURL=useResolutionState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResolutionState.d.ts","sourceRoot":"","sources":["../../src/hooks/useResolutionState.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,eAAe,EACf,iBAAiB,EAEjB,kBAAkB,EAEnB,MAAM,UAAU,CAAC;AAelB,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,wBAAgB,kBAAkB,CAChC,kBAAkB,EAAE,kBAAkB,GAAG,IAAI,EAC7C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,EACrF,cAAc,CAAC,EAAE,CAAC,gBAAgB,EAAE,kBAAkB,KAAK,IAAI,GAC9D,wBAAwB,CAiU1B"}
@@ -0,0 +1,253 @@
1
+ import React, { useState, useCallback, useMemo } from 'react';
2
+ import { createResolverWithContext, resolveResourceDetailed, getAvailableQualifiers, hasPendingContextChanges } from '../utils/resolutionUtils';
3
+ import { validateEditedResource, computeResourceDelta, rebuildSystemWithEdits, extractResolutionContext, checkEditConflicts } from '../utils/resolutionEditing';
4
+ export function useResolutionState(processedResources, onMessage, onSystemUpdate) {
5
+ // Get available qualifiers
6
+ const availableQualifiers = useMemo(() => {
7
+ if (!processedResources)
8
+ return [];
9
+ return getAvailableQualifiers(processedResources);
10
+ }, [processedResources]);
11
+ // Initialize context with all qualifiers undefined
12
+ const defaultContextValues = useMemo(() => {
13
+ const defaults = {};
14
+ availableQualifiers.forEach((qualifierName) => {
15
+ defaults[qualifierName] = undefined;
16
+ });
17
+ return defaults;
18
+ }, [availableQualifiers]);
19
+ // Resolution state
20
+ const [contextValues, setContextValues] = useState({});
21
+ const [pendingContextValues, setPendingContextValues] = useState({});
22
+ const [selectedResourceId, setSelectedResourceId] = useState(null);
23
+ const [currentResolver, setCurrentResolver] = useState(null);
24
+ const [resolutionResult, setResolutionResult] = useState(null);
25
+ const [viewMode, setViewMode] = useState('composed');
26
+ // Edit state - stores original, edited, and delta for each resource
27
+ const [editedResources, setEditedResources] = useState(new Map());
28
+ const [isApplyingEdits, setIsApplyingEdits] = useState(false);
29
+ // Update context state when defaults change
30
+ React.useEffect(() => {
31
+ setContextValues(defaultContextValues);
32
+ setPendingContextValues(defaultContextValues);
33
+ }, [defaultContextValues]);
34
+ // Check for pending changes
35
+ const hasPendingChanges = useMemo(() => {
36
+ return hasPendingContextChanges(contextValues, pendingContextValues);
37
+ }, [contextValues, pendingContextValues]);
38
+ // Check for unsaved edits
39
+ const hasUnsavedEdits = useMemo(() => {
40
+ return editedResources.size > 0;
41
+ }, [editedResources]);
42
+ // Update context value
43
+ const updateContextValue = useCallback((qualifierName, value) => {
44
+ setPendingContextValues((prev) => ({
45
+ ...prev,
46
+ [qualifierName]: value
47
+ }));
48
+ }, []);
49
+ // Apply context changes
50
+ const applyContext = useCallback(() => {
51
+ if (!processedResources) {
52
+ onMessage?.('error', 'No resources loaded');
53
+ return;
54
+ }
55
+ try {
56
+ // Create resolver with new context
57
+ const resolverResult = createResolverWithContext(processedResources, pendingContextValues, {
58
+ enableCaching: true,
59
+ enableDebugLogging: false
60
+ });
61
+ if (resolverResult.isFailure()) {
62
+ onMessage?.('error', `Failed to create resolver: ${resolverResult.message}`);
63
+ return;
64
+ }
65
+ // Update state
66
+ setContextValues({ ...pendingContextValues });
67
+ setCurrentResolver(resolverResult.value);
68
+ // If a resource is selected, resolve it with the new context
69
+ if (selectedResourceId) {
70
+ const resolutionResult = resolveResourceDetailed(resolverResult.value, selectedResourceId, processedResources);
71
+ if (resolutionResult.isSuccess()) {
72
+ setResolutionResult(resolutionResult.value);
73
+ }
74
+ else {
75
+ onMessage?.('error', `Failed to resolve resource: ${resolutionResult.message}`);
76
+ }
77
+ }
78
+ onMessage?.('success', 'Context applied successfully');
79
+ }
80
+ catch (error) {
81
+ onMessage?.('error', `Failed to apply context: ${error instanceof Error ? error.message : String(error)}`);
82
+ }
83
+ }, [processedResources, pendingContextValues, selectedResourceId, onMessage]);
84
+ // Select resource and resolve it
85
+ const selectResource = useCallback((resourceId) => {
86
+ setSelectedResourceId(resourceId);
87
+ setResolutionResult(null);
88
+ if (currentResolver && processedResources) {
89
+ const resolutionResult = resolveResourceDetailed(currentResolver, resourceId, processedResources);
90
+ if (resolutionResult.isSuccess()) {
91
+ setResolutionResult(resolutionResult.value);
92
+ onMessage?.('info', `Selected resource: ${resourceId}`);
93
+ }
94
+ else {
95
+ onMessage?.('error', `Failed to resolve resource: ${resolutionResult.message}`);
96
+ }
97
+ }
98
+ }, [currentResolver, processedResources, onMessage]);
99
+ // Reset cache
100
+ const resetCache = useCallback(() => {
101
+ if (currentResolver) {
102
+ currentResolver.clearConditionCache();
103
+ onMessage?.('info', 'Cache cleared');
104
+ }
105
+ }, [currentResolver, onMessage]);
106
+ // Auto-apply default context when resources are loaded
107
+ React.useEffect(() => {
108
+ if (processedResources && Object.keys(defaultContextValues).length > 0) {
109
+ applyContext();
110
+ }
111
+ }, [processedResources, defaultContextValues]);
112
+ // Edit management functions
113
+ const saveEdit = useCallback((resourceId, editedValue, originalValue) => {
114
+ try {
115
+ // Validate the edited value
116
+ const validation = validateEditedResource(editedValue);
117
+ if (!validation.isValid) {
118
+ onMessage?.('error', `Invalid edit: ${validation.errors.join(', ')}`);
119
+ return;
120
+ }
121
+ // Show warnings if any
122
+ if (validation.warnings.length > 0) {
123
+ validation.warnings.forEach((warning) => onMessage?.('warning', warning));
124
+ }
125
+ // Compute the delta between original and edited
126
+ const resolvedValue = originalValue || editedValue; // Use originalValue as the resolved/baseline
127
+ const deltaResult = computeResourceDelta(undefined, resolvedValue, editedValue);
128
+ if (deltaResult.isFailure()) {
129
+ onMessage?.('warning', `Could not compute delta, saving full value: ${deltaResult.message}`);
130
+ }
131
+ const delta = deltaResult.isSuccess() ? deltaResult.value : null;
132
+ // Save the edit with original, edited, and delta
133
+ setEditedResources((prev) => {
134
+ const newMap = new Map(prev);
135
+ newMap.set(resourceId, {
136
+ originalValue: resolvedValue,
137
+ editedValue,
138
+ delta
139
+ });
140
+ return newMap;
141
+ });
142
+ // Log info about delta
143
+ if (delta !== null && delta !== editedValue) {
144
+ const deltaSize = JSON.stringify(delta).length;
145
+ const fullSize = JSON.stringify(editedValue).length;
146
+ const reduction = Math.round((1 - deltaSize / fullSize) * 100);
147
+ onMessage?.('info', `Edit saved for ${resourceId} (delta: ${reduction}% smaller)`);
148
+ }
149
+ else {
150
+ onMessage?.('info', `Edit saved for resource ${resourceId}`);
151
+ }
152
+ }
153
+ catch (error) {
154
+ onMessage?.('error', `Failed to save edit: ${error instanceof Error ? error.message : String(error)}`);
155
+ }
156
+ }, [onMessage]);
157
+ const getEditedValue = useCallback((resourceId) => {
158
+ const edit = editedResources.get(resourceId);
159
+ return edit?.editedValue;
160
+ }, [editedResources]);
161
+ const hasEdit = useCallback((resourceId) => {
162
+ return editedResources.has(resourceId);
163
+ }, [editedResources]);
164
+ const clearEdits = useCallback(() => {
165
+ setEditedResources(new Map());
166
+ onMessage?.('info', 'All edits cleared');
167
+ }, [onMessage]);
168
+ const discardEdits = useCallback(() => {
169
+ if (hasUnsavedEdits) {
170
+ setEditedResources(new Map());
171
+ onMessage?.('info', 'All unsaved edits discarded');
172
+ }
173
+ }, [hasUnsavedEdits, onMessage]);
174
+ const applyEdits = useCallback(async () => {
175
+ if (!processedResources || editedResources.size === 0) {
176
+ onMessage?.('warning', 'No edits to apply');
177
+ return;
178
+ }
179
+ if (!onSystemUpdate) {
180
+ onMessage?.('error', 'System update callback not provided');
181
+ return;
182
+ }
183
+ setIsApplyingEdits(true);
184
+ try {
185
+ // Extract current resolution context (filter out undefined values)
186
+ const cleanedContextValues = {};
187
+ Object.entries(contextValues).forEach(([key, value]) => {
188
+ if (value !== undefined) {
189
+ cleanedContextValues[key] = value;
190
+ }
191
+ });
192
+ const currentContext = extractResolutionContext(currentResolver, cleanedContextValues);
193
+ // Check for potential conflicts
194
+ const conflictCheck = checkEditConflicts(processedResources.system.resourceManager, editedResources, currentContext);
195
+ // Show warnings about potential conflicts
196
+ conflictCheck.warnings.forEach((warning) => onMessage?.('warning', warning));
197
+ if (conflictCheck.conflicts.length > 0) {
198
+ onMessage?.('error', `Conflicts detected: ${conflictCheck.conflicts.join(', ')}`);
199
+ return;
200
+ }
201
+ // Rebuild the system with edits
202
+ const rebuildResult = await rebuildSystemWithEdits(processedResources.system, editedResources, currentContext);
203
+ if (rebuildResult.isFailure()) {
204
+ onMessage?.('error', `Failed to apply edits: ${rebuildResult.message}`);
205
+ return;
206
+ }
207
+ // Update the system through the callback
208
+ onSystemUpdate(rebuildResult.value);
209
+ // Clear edits after successful application
210
+ setEditedResources(new Map());
211
+ onMessage?.('success', `Successfully applied ${editedResources.size} edit(s)`);
212
+ }
213
+ catch (error) {
214
+ onMessage?.('error', `Error applying edits: ${error instanceof Error ? error.message : String(error)}`);
215
+ }
216
+ finally {
217
+ setIsApplyingEdits(false);
218
+ }
219
+ }, [processedResources, editedResources, onSystemUpdate, currentResolver, contextValues, onMessage]);
220
+ const state = {
221
+ contextValues,
222
+ pendingContextValues,
223
+ selectedResourceId,
224
+ currentResolver,
225
+ resolutionResult,
226
+ viewMode,
227
+ hasPendingChanges,
228
+ // Edit state
229
+ editedResources,
230
+ hasUnsavedEdits,
231
+ isApplyingEdits
232
+ };
233
+ const actions = {
234
+ updateContextValue,
235
+ applyContext,
236
+ selectResource,
237
+ setViewMode,
238
+ resetCache,
239
+ // Edit actions
240
+ saveEdit,
241
+ getEditedValue,
242
+ hasEdit,
243
+ clearEdits,
244
+ applyEdits,
245
+ discardEdits
246
+ };
247
+ return {
248
+ state,
249
+ actions,
250
+ availableQualifiers
251
+ };
252
+ }
253
+ //# sourceMappingURL=useResolutionState.js.map