@fgv/ts-res-ui-components 5.0.0-21 → 5.0.0-23

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 (192) hide show
  1. package/README.md +401 -155
  2. package/config/jest.setup.js +10 -0
  3. package/dist/ts-res-ui-components.d.ts +1657 -76
  4. package/lib/components/common/QualifierContextControl.js +4 -1
  5. package/lib/components/common/ResourceTreeView.js +4 -1
  6. package/lib/components/forms/GenericQualifierTypeEditForm.d.ts +26 -0
  7. package/lib/components/forms/GenericQualifierTypeEditForm.js +166 -0
  8. package/lib/components/forms/QualifierEditForm.d.ts +1 -1
  9. package/lib/components/forms/index.d.ts +2 -0
  10. package/lib/components/forms/index.js +1 -0
  11. package/lib/components/orchestrator/ResourceOrchestrator.d.ts +3 -0
  12. package/lib/components/orchestrator/ResourceOrchestrator.js +118 -51
  13. package/lib/components/pickers/ResourcePicker/ResourcePickerTree.js +32 -10
  14. package/lib/components/pickers/ResourcePicker/index.js +4 -2
  15. package/lib/components/views/CompiledView/index.js +75 -16
  16. package/lib/components/views/ConfigurationView/index.js +94 -35
  17. package/lib/components/views/FilterView/index.js +7 -4
  18. package/lib/components/views/GridView/EditableGridCell.d.ts +76 -0
  19. package/lib/components/views/GridView/EditableGridCell.js +224 -0
  20. package/lib/components/views/GridView/GridSelector.d.ts +43 -0
  21. package/lib/components/views/GridView/GridSelector.js +89 -0
  22. package/lib/components/views/GridView/MultiGridView.d.ts +85 -0
  23. package/lib/components/views/GridView/MultiGridView.js +196 -0
  24. package/lib/components/views/GridView/ResourceGrid.d.ts +38 -0
  25. package/lib/components/views/GridView/ResourceGrid.js +232 -0
  26. package/lib/components/views/GridView/SharedContextControls.d.ts +47 -0
  27. package/lib/components/views/GridView/SharedContextControls.js +95 -0
  28. package/lib/components/views/GridView/cells/BooleanCell.d.ts +44 -0
  29. package/lib/components/views/GridView/cells/BooleanCell.js +49 -0
  30. package/lib/components/views/GridView/cells/DropdownCell.d.ts +58 -0
  31. package/lib/components/views/GridView/cells/DropdownCell.js +182 -0
  32. package/lib/components/views/GridView/cells/StringCell.d.ts +57 -0
  33. package/lib/components/views/GridView/cells/StringCell.js +106 -0
  34. package/lib/components/views/GridView/cells/TriStateCell.d.ts +54 -0
  35. package/lib/components/views/GridView/cells/TriStateCell.js +112 -0
  36. package/lib/components/views/GridView/cells/index.d.ts +15 -0
  37. package/lib/components/views/GridView/cells/index.js +11 -0
  38. package/lib/components/views/GridView/index.d.ts +53 -0
  39. package/lib/components/views/GridView/index.js +212 -0
  40. package/lib/components/views/ImportView/index.js +22 -19
  41. package/lib/components/views/MessagesWindow/index.js +4 -1
  42. package/lib/components/views/ResolutionView/index.js +8 -5
  43. package/lib/contexts/ObservabilityContext.d.ts +85 -0
  44. package/lib/contexts/ObservabilityContext.js +98 -0
  45. package/lib/contexts/index.d.ts +2 -0
  46. package/lib/contexts/index.js +24 -0
  47. package/lib/hooks/useConfigurationState.d.ts +3 -3
  48. package/lib/hooks/useResolutionState.js +850 -246
  49. package/lib/hooks/useResourceData.d.ts +7 -4
  50. package/lib/hooks/useResourceData.js +185 -184
  51. package/lib/index.d.ts +5 -1
  52. package/lib/index.js +8 -1
  53. package/lib/namespaces/GridTools.d.ts +136 -0
  54. package/lib/namespaces/GridTools.js +138 -0
  55. package/lib/namespaces/ObservabilityTools.d.ts +3 -0
  56. package/lib/namespaces/ObservabilityTools.js +23 -0
  57. package/lib/namespaces/ResolutionTools.d.ts +2 -1
  58. package/lib/namespaces/ResolutionTools.js +2 -0
  59. package/lib/namespaces/index.d.ts +2 -0
  60. package/lib/namespaces/index.js +2 -0
  61. package/lib/test/integration/observability.integration.test.d.ts +2 -0
  62. package/lib/test/unit/hooks/useResourceData.test.d.ts +2 -0
  63. package/lib/test/unit/utils/downloadHelper.test.d.ts +2 -0
  64. package/lib/test/unit/workflows/resolutionWorkflows.test.d.ts +2 -0
  65. package/lib/test/unit/workflows/resourceCreation.test.d.ts +2 -0
  66. package/lib/test/unit/workflows/resultPatternExtensions.test.d.ts +2 -0
  67. package/lib/test/unit/workflows/validation.test.d.ts +2 -0
  68. package/lib/types/index.d.ts +387 -20
  69. package/lib/types/index.js +2 -1
  70. package/lib/utils/cellValidation.d.ts +113 -0
  71. package/lib/utils/cellValidation.js +248 -0
  72. package/lib/utils/downloadHelper.d.ts +66 -0
  73. package/lib/utils/downloadHelper.js +195 -0
  74. package/lib/utils/observability/factories.d.ts +29 -0
  75. package/lib/utils/observability/factories.js +58 -0
  76. package/lib/utils/observability/implementations.d.ts +61 -0
  77. package/lib/utils/observability/implementations.js +103 -0
  78. package/lib/utils/observability/index.d.ts +4 -0
  79. package/lib/utils/observability/index.js +26 -0
  80. package/lib/utils/observability/interfaces.d.ts +30 -0
  81. package/lib/utils/observability/interfaces.js +23 -0
  82. package/lib/utils/resolutionEditing.js +2 -1
  83. package/lib/utils/resourceSelector.d.ts +97 -0
  84. package/lib/utils/resourceSelector.js +195 -0
  85. package/lib/utils/resourceSelectors.d.ts +146 -0
  86. package/lib/utils/resourceSelectors.js +233 -0
  87. package/lib/utils/tsResIntegration.d.ts +6 -41
  88. package/lib/utils/tsResIntegration.js +20 -16
  89. package/lib/utils/zipLoader/zipProcessingHelpers.d.ts +3 -2
  90. package/lib/utils/zipLoader/zipProcessingHelpers.js +6 -5
  91. package/lib-commonjs/components/common/QualifierContextControl.js +4 -1
  92. package/lib-commonjs/components/common/ResourceTreeView.js +4 -1
  93. package/lib-commonjs/components/forms/GenericQualifierTypeEditForm.js +171 -0
  94. package/lib-commonjs/components/forms/index.js +3 -1
  95. package/lib-commonjs/components/orchestrator/ResourceOrchestrator.js +118 -51
  96. package/lib-commonjs/components/pickers/ResourcePicker/ResourcePickerTree.js +32 -10
  97. package/lib-commonjs/components/pickers/ResourcePicker/index.js +4 -2
  98. package/lib-commonjs/components/views/CompiledView/index.js +75 -16
  99. package/lib-commonjs/components/views/ConfigurationView/index.js +93 -34
  100. package/lib-commonjs/components/views/FilterView/index.js +7 -4
  101. package/lib-commonjs/components/views/GridView/EditableGridCell.js +232 -0
  102. package/lib-commonjs/components/views/GridView/GridSelector.js +94 -0
  103. package/lib-commonjs/components/views/GridView/MultiGridView.js +201 -0
  104. package/lib-commonjs/components/views/GridView/ResourceGrid.js +237 -0
  105. package/lib-commonjs/components/views/GridView/SharedContextControls.js +100 -0
  106. package/lib-commonjs/components/views/GridView/cells/BooleanCell.js +54 -0
  107. package/lib-commonjs/components/views/GridView/cells/DropdownCell.js +187 -0
  108. package/lib-commonjs/components/views/GridView/cells/StringCell.js +111 -0
  109. package/lib-commonjs/components/views/GridView/cells/TriStateCell.js +117 -0
  110. package/lib-commonjs/components/views/GridView/cells/index.js +18 -0
  111. package/lib-commonjs/components/views/GridView/index.js +217 -0
  112. package/lib-commonjs/components/views/ImportView/index.js +22 -19
  113. package/lib-commonjs/components/views/MessagesWindow/index.js +4 -1
  114. package/lib-commonjs/components/views/ResolutionView/index.js +8 -5
  115. package/lib-commonjs/contexts/ObservabilityContext.js +104 -0
  116. package/lib-commonjs/contexts/index.js +30 -0
  117. package/lib-commonjs/hooks/useResolutionState.js +849 -245
  118. package/lib-commonjs/hooks/useResourceData.js +184 -215
  119. package/lib-commonjs/index.js +15 -1
  120. package/lib-commonjs/namespaces/GridTools.js +161 -0
  121. package/lib-commonjs/namespaces/ObservabilityTools.js +33 -0
  122. package/lib-commonjs/namespaces/ResolutionTools.js +10 -1
  123. package/lib-commonjs/namespaces/index.js +3 -1
  124. package/lib-commonjs/types/index.js +10 -0
  125. package/lib-commonjs/utils/cellValidation.js +253 -0
  126. package/lib-commonjs/utils/downloadHelper.js +198 -0
  127. package/lib-commonjs/utils/observability/factories.js +63 -0
  128. package/lib-commonjs/utils/observability/implementations.js +109 -0
  129. package/lib-commonjs/utils/observability/index.js +36 -0
  130. package/lib-commonjs/utils/observability/interfaces.js +24 -0
  131. package/lib-commonjs/utils/resolutionEditing.js +2 -1
  132. package/lib-commonjs/utils/resourceSelector.js +200 -0
  133. package/lib-commonjs/utils/resourceSelectors.js +242 -0
  134. package/lib-commonjs/utils/tsResIntegration.js +21 -16
  135. package/lib-commonjs/utils/zipLoader/zipProcessingHelpers.js +7 -5
  136. package/package.json +7 -7
  137. package/src/components/common/QualifierContextControl.tsx +0 -338
  138. package/src/components/common/ResolutionContextOptionsControl.tsx +0 -450
  139. package/src/components/common/ResolutionResults/index.tsx +0 -481
  140. package/src/components/common/ResourceListView.tsx +0 -167
  141. package/src/components/common/ResourcePickerOptionsControl.tsx +0 -351
  142. package/src/components/common/ResourceTreeView.tsx +0 -417
  143. package/src/components/common/SourceResourceDetail/index.tsx +0 -493
  144. package/src/components/forms/HierarchyEditor.tsx +0 -285
  145. package/src/components/forms/QualifierEditForm.tsx +0 -487
  146. package/src/components/forms/QualifierTypeEditForm.tsx +0 -458
  147. package/src/components/forms/ResourceTypeEditForm.tsx +0 -437
  148. package/src/components/forms/index.ts +0 -11
  149. package/src/components/orchestrator/ResourceOrchestrator.tsx +0 -444
  150. package/src/components/pickers/ResourcePicker/README.md +0 -570
  151. package/src/components/pickers/ResourcePicker/ResourceItem.tsx +0 -127
  152. package/src/components/pickers/ResourcePicker/ResourcePickerList.tsx +0 -114
  153. package/src/components/pickers/ResourcePicker/ResourcePickerTree.tsx +0 -461
  154. package/src/components/pickers/ResourcePicker/index.tsx +0 -234
  155. package/src/components/pickers/ResourcePicker/types.ts +0 -301
  156. package/src/components/pickers/ResourcePicker/utils/treeNavigation.ts +0 -210
  157. package/src/components/views/CompiledView/index.tsx +0 -1342
  158. package/src/components/views/ConfigurationView/index.tsx +0 -848
  159. package/src/components/views/FilterView/index.tsx +0 -681
  160. package/src/components/views/ImportView/index.tsx +0 -789
  161. package/src/components/views/MessagesWindow/index.tsx +0 -325
  162. package/src/components/views/ResolutionView/EditableJsonView.tsx +0 -386
  163. package/src/components/views/ResolutionView/NewResourceModal.tsx +0 -158
  164. package/src/components/views/ResolutionView/UnifiedChangeControls.tsx +0 -163
  165. package/src/components/views/ResolutionView/index.tsx +0 -751
  166. package/src/components/views/SourceView/index.tsx +0 -291
  167. package/src/hooks/useConfigurationState.ts +0 -436
  168. package/src/hooks/useFilterState.ts +0 -150
  169. package/src/hooks/useResolutionState.ts +0 -893
  170. package/src/hooks/useResourceData.ts +0 -596
  171. package/src/hooks/useViewState.ts +0 -97
  172. package/src/index.ts +0 -68
  173. package/src/namespaces/ConfigurationTools.ts +0 -59
  174. package/src/namespaces/FilterTools.ts +0 -47
  175. package/src/namespaces/ImportTools.ts +0 -42
  176. package/src/namespaces/PickerTools.ts +0 -104
  177. package/src/namespaces/ResolutionTools.ts +0 -68
  178. package/src/namespaces/ResourceTools.ts +0 -106
  179. package/src/namespaces/TsResTools.ts +0 -49
  180. package/src/namespaces/ViewStateTools.ts +0 -91
  181. package/src/namespaces/ZipTools.ts +0 -49
  182. package/src/namespaces/index.ts +0 -49
  183. package/src/types/index.ts +0 -1273
  184. package/src/utils/configurationUtils.ts +0 -339
  185. package/src/utils/fileProcessing.ts +0 -164
  186. package/src/utils/filterResources.ts +0 -356
  187. package/src/utils/resolutionEditing.ts +0 -346
  188. package/src/utils/resolutionUtils.ts +0 -740
  189. package/src/utils/tsResIntegration.ts +0 -475
  190. package/src/utils/zipLoader/index.ts +0 -5
  191. package/src/utils/zipLoader/zipProcessingHelpers.ts +0 -46
  192. package/src/utils/zipLoader/zipUtils.ts +0 -7
@@ -1,570 +0,0 @@
1
- # ResourcePicker Component
2
-
3
- A comprehensive resource selection component that provides search, list/tree views, branch isolation, and edit support. The component focuses on resource selection and leaves action handling to the host application.
4
-
5
- ## Features
6
-
7
- - 🔍 **Search and filtering** across resources
8
- - 📋 **Dual view modes** - List and tree views with seamless switching
9
- - 🌳 **Branch isolation** - Display specific subtrees of the resource hierarchy
10
- - ✏️ **Edit support** - Display pending resources (new, modified, deleted)
11
- - 🏷️ **Flexible annotations** - Host-provided badges, indicators, and metadata
12
- - 🎯 **Single selection** with callback notification
13
- - ⚡ **Performance optimized** with virtual tree abstraction
14
-
15
- ## Quick Start
16
-
17
- ```typescript
18
- import { ResourcePicker, ResourceSelection } from '@fgv/ts-res-ui-components';
19
-
20
- function MyComponent() {
21
- const [selectedId, setSelectedId] = useState<string | null>(null);
22
-
23
- const handleResourceSelect = (selection: ResourceSelection) => {
24
- setSelectedId(selection.resourceId);
25
-
26
- // Access resource data directly
27
- if (selection.resourceData) {
28
- console.log('Resource data:', selection.resourceData);
29
- }
30
- };
31
-
32
- return (
33
- <ResourcePicker
34
- resources={processedResources}
35
- selectedResourceId={selectedId}
36
- onResourceSelect={handleResourceSelect}
37
- />
38
- );
39
- }
40
- ```
41
-
42
- ## Props API
43
-
44
- ### Core Props
45
-
46
- | Prop | Type | Required | Description |
47
- |------|------|----------|-------------|
48
- | `resources` | `ProcessedResources \| ExtendedProcessedResources \| null` | ✅ | The resource data to display |
49
- | `selectedResourceId` | `string \| null` | ✅ | Currently selected resource ID |
50
- | `onResourceSelect` | `(selection: ResourceSelection<T>) => void` | ✅ | Enhanced callback with resource data when selection changes |
51
-
52
- ### View Configuration
53
-
54
- | Prop | Type | Default | Description |
55
- |------|------|---------|-------------|
56
- | `defaultView` | `'list' \| 'tree'` | `'list'` | Initial view mode |
57
- | `showViewToggle` | `boolean` | `true` | Show list/tree toggle buttons |
58
- | `height` | `string \| number` | `'600px'` | Container height |
59
- | `className` | `string` | `''` | Additional CSS classes |
60
- | `emptyMessage` | `string` | `'No resources available'` | Message when no resources |
61
-
62
- ### Search Configuration
63
-
64
- | Prop | Type | Default | Description |
65
- |------|------|---------|-------------|
66
- | `enableSearch` | `boolean` | `true` | Enable search functionality |
67
- | `searchPlaceholder` | `string` | Auto-generated | Custom search placeholder |
68
- | `searchScope` | `'all' \| 'current-branch'` | `'current-branch'` | Search within branch or all resources |
69
-
70
- ### Branch Isolation
71
-
72
- | Prop | Type | Default | Description |
73
- |------|------|---------|-------------|
74
- | `rootPath` | `string` | `undefined` | Root path for branch isolation (e.g., `"strings"`) |
75
- | `hideRootNode` | `boolean` | `false` | Hide the root node, show only children |
76
-
77
- ### Annotations and Edit Support
78
-
79
- | Prop | Type | Default | Description |
80
- |------|------|---------|-------------|
81
- | `resourceAnnotations` | `ResourceAnnotations` | `{}` | Host-provided resource decorations |
82
- | `pendingResources` | `PendingResource[]` | `[]` | Resources with pending changes |
83
-
84
- ### Events
85
-
86
- | Prop | Type | Description |
87
- |------|------|-------------|
88
- | `onMessage` | `(type: 'info' \| 'warning' \| 'error' \| 'success', message: string) => void` | Optional message callback |
89
-
90
- ## Enhanced Resource Selection
91
-
92
- The `onResourceSelect` callback now returns a `ResourceSelection<T>` object that provides comprehensive information about the selected resource:
93
-
94
- ```typescript
95
- interface ResourceSelection<T = unknown> {
96
- resourceId: string | null; // The resource ID (same as before)
97
- resourceData?: T; // The actual resource data if available
98
- isPending?: boolean; // Whether this is a pending resource
99
- pendingType?: 'new' | 'modified' | 'deleted'; // Type of pending operation
100
- }
101
- ```
102
-
103
- ### Benefits of Enhanced Callback
104
-
105
- 1. **Immediate Access to Resource Data**: No need to separately fetch resource data by ID
106
- 2. **Pending Resource Information**: Easily distinguish between existing and pending resources
107
- 3. **Type Safety**: Generic `T` parameter allows type-safe resource data handling
108
- 4. **Reduced Bookkeeping**: Consumer applications need less state management logic
109
-
110
- ### Example Usage
111
-
112
- ```typescript
113
- const handleResourceSelect = (selection: ResourceSelection<MyResourceType>) => {
114
- if (selection.resourceId) {
115
- // Resource selected
116
- if (selection.isPending) {
117
- console.log(`Pending ${selection.pendingType} resource:`, selection.resourceData);
118
- } else {
119
- console.log('Existing resource selected:', selection.resourceId);
120
- }
121
-
122
- // Use the resource data directly
123
- if (selection.resourceData) {
124
- processResourceData(selection.resourceData);
125
- }
126
- } else {
127
- // No resource selected
128
- console.log('Resource deselected');
129
- }
130
- };
131
- ```
132
-
133
- ## Type Definitions
134
-
135
- ### ResourceAnnotations
136
-
137
- ```typescript
138
- interface ResourceAnnotations {
139
- [resourceId: string]: ResourceAnnotation;
140
- }
141
-
142
- interface ResourceAnnotation {
143
- badge?: {
144
- text: string;
145
- variant: 'info' | 'warning' | 'success' | 'error' | 'edited' | 'new';
146
- };
147
- indicator?: {
148
- type: 'dot' | 'icon' | 'text';
149
- value: string | React.ReactNode;
150
- tooltip?: string;
151
- };
152
- suffix?: React.ReactNode; // e.g., "(3 candidates)"
153
- className?: string; // Additional styling
154
- }
155
- ```
156
-
157
- ### PendingResource
158
-
159
- ```typescript
160
- interface PendingResource<T = unknown> {
161
- id: string;
162
- type: 'new' | 'modified' | 'deleted';
163
- resourceType?: string;
164
- displayName?: string; // Custom display name
165
- resourceData?: T; // The actual resource data
166
- }
167
- ```
168
-
169
- **Enhanced Features:**
170
- - **Generic Type Support**: Use `PendingResource<MyType>` for type-safe resource data
171
- - **Resource Data**: Include actual resource content for immediate access
172
- - **Backward Compatible**: All existing code continues to work unchanged
173
-
174
- ## Usage Examples
175
-
176
- ### Basic Resource Selection
177
-
178
- ```typescript
179
- import { ResourcePicker } from '@fgv/ts-res-ui-components';
180
-
181
- function ResourceSelector({ resources }) {
182
- const [selected, setSelected] = useState<string | null>(null);
183
-
184
- return (
185
- <div className="w-full h-96">
186
- <ResourcePicker
187
- resources={resources}
188
- selectedResourceId={selected}
189
- onResourceSelect={setSelected}
190
- defaultView="tree"
191
- enableSearch={true}
192
- />
193
- </div>
194
- );
195
- }
196
- ```
197
-
198
- ### Branch Isolation
199
-
200
- Perfect for focusing on specific parts of the resource tree:
201
-
202
- ```typescript
203
- // Show only the "strings" branch
204
- <ResourcePicker
205
- resources={resources}
206
- selectedResourceId={selectedId}
207
- onResourceSelect={setSelectedId}
208
- rootPath="strings"
209
- hideRootNode={true} // Show children of "strings", not "strings" itself
210
- searchPlaceholder="Search strings..."
211
- />
212
-
213
- // Multiple isolated views
214
- <div className="grid grid-cols-3 gap-4">
215
- <ResourcePicker
216
- resources={resources}
217
- rootPath="strings"
218
- onResourceSelect={(id) => handleSelect('strings', id)}
219
- />
220
- <ResourcePicker
221
- resources={resources}
222
- rootPath="images"
223
- onResourceSelect={(id) => handleSelect('images', id)}
224
- />
225
- <ResourcePicker
226
- resources={resources}
227
- rootPath="app.ui"
228
- onResourceSelect={(id) => handleSelect('ui', id)}
229
- />
230
- </div>
231
- ```
232
-
233
- ### With Annotations
234
-
235
- Add visual indicators and metadata to resources:
236
-
237
- ```typescript
238
- const annotations: ResourceAnnotations = {
239
- 'strings.errors.validation': {
240
- badge: { text: 'EDITED', variant: 'edited' },
241
- suffix: '(3 candidates)'
242
- },
243
- 'strings.common.ok': {
244
- indicator: {
245
- type: 'dot',
246
- value: '●',
247
- tooltip: 'Has conflicts'
248
- }
249
- },
250
- 'app.ui.buttons': {
251
- badge: { text: 'NEW', variant: 'new' }
252
- }
253
- };
254
-
255
- <ResourcePicker
256
- resources={resources}
257
- selectedResourceId={selectedId}
258
- onResourceSelect={setSelectedId}
259
- resourceAnnotations={annotations}
260
- />
261
- ```
262
-
263
- ### Editor with Pending Resources
264
-
265
- For resource editors that show unsaved changes:
266
-
267
- ```typescript
268
- const pendingResources: PendingResource[] = [
269
- {
270
- id: 'strings.new-feature',
271
- type: 'new',
272
- displayName: 'new-feature (unsaved)'
273
- },
274
- {
275
- id: 'strings.common.updated',
276
- type: 'modified',
277
- displayName: 'updated (pending)'
278
- },
279
- {
280
- id: 'strings.old-feature',
281
- type: 'deleted'
282
- }
283
- ];
284
-
285
- const annotations: ResourceAnnotations = {
286
- 'strings.new-feature': {
287
- badge: { text: 'NEW', variant: 'new' }
288
- },
289
- 'strings.common.updated': {
290
- badge: { text: 'MODIFIED', variant: 'edited' }
291
- }
292
- };
293
-
294
- <ResourcePicker
295
- resources={resources}
296
- selectedResourceId={selectedId}
297
- onResourceSelect={setSelectedId}
298
- pendingResources={pendingResources}
299
- resourceAnnotations={annotations}
300
- rootPath="strings"
301
- />
302
- ```
303
-
304
- ### Custom Styling
305
-
306
- ```typescript
307
- <ResourcePicker
308
- resources={resources}
309
- selectedResourceId={selectedId}
310
- onResourceSelect={setSelectedId}
311
- className="border border-gray-300 rounded-lg shadow-sm"
312
- height={400}
313
- emptyMessage="No resources found. Import some resources to get started."
314
- />
315
- ```
316
-
317
- ## Branch Isolation Details
318
-
319
- Branch isolation allows you to show only a specific subtree of the resource hierarchy. This is useful for:
320
-
321
- - **Multi-panel interfaces** - Show different resource categories in separate panels
322
- - **Context-focused editing** - Limit scope to relevant resources
323
- - **Simplified navigation** - Reduce cognitive load by hiding irrelevant resources
324
-
325
- ### Behavior Examples
326
-
327
- Given resources: `["string.common", "strings.errors", "strings.errors.validation", "app.dialog", "app.ui"]`
328
-
329
- **No isolation:**
330
- - Shows: All resources in full hierarchy
331
-
332
- **`rootPath="strings"`:**
333
- - Shows: `["strings.common", "strings.errors", "strings.errors.validation"]`
334
- - List view displays: `["common", "errors", "errors.validation"]` (prefix truncated)
335
-
336
- **`rootPath="strings"` + `hideRootNode=true`:**
337
- - Shows: `["strings.errors", "strings.errors.validation", "strings.common"]`
338
- - List view displays: `["errors", "errors.validation", "common"]` (prefix truncated)
339
-
340
- ## Pending Resources
341
-
342
- Pending resources represent changes that haven't been applied yet. They integrate seamlessly with the existing resource tree:
343
-
344
- ### Types
345
- - **`new`** - Resources being created (shown in emerald)
346
- - **`deleted`** - Resources marked for deletion (hidden from display)
347
-
348
- ### Integration
349
- - Pending resources are merged into the virtual tree structure
350
- - They follow the same filtering and search rules as real resources
351
- - Selection works identically for pending and real resources
352
- - Visual styling distinguishes them from existing resources
353
-
354
- ## Performance Considerations
355
-
356
- - **Virtual tree abstraction** efficiently merges real and pending resources
357
- - **Memoized filtering** prevents unnecessary recalculations
358
- - **Optimized search** works on filtered result sets
359
- - **Lazy expansion** in tree view for large hierarchies
360
-
361
- ## Accessibility
362
-
363
- - **Keyboard navigation** supported in both list and tree views
364
- - **Screen reader friendly** with proper ARIA labels
365
- - **Focus management** maintains logical tab order
366
- - **High contrast** support with semantic color variants
367
-
368
- ## Migration from Existing Components
369
-
370
- ### From ResourceListView
371
-
372
- ```typescript
373
- // Before
374
- <ResourceListView
375
- resourceIds={resourceIds}
376
- selectedResourceId={selectedId}
377
- onResourceSelect={setSelectedId}
378
- />
379
-
380
- // After
381
- <ResourcePicker
382
- resources={processedResources}
383
- selectedResourceId={selectedId}
384
- onResourceSelect={setSelectedId}
385
- defaultView="list"
386
- showViewToggle={false}
387
- />
388
- ```
389
-
390
- ### From ResourceTreeView
391
-
392
- ```typescript
393
- // Before
394
- <ResourceTreeView
395
- resources={processedResources}
396
- selectedResourceId={selectedId}
397
- onResourceSelect={setSelectedId}
398
- />
399
-
400
- // After
401
- <ResourcePicker
402
- resources={processedResources}
403
- selectedResourceId={selectedId}
404
- onResourceSelect={setSelectedId}
405
- defaultView="tree"
406
- showViewToggle={false}
407
- />
408
- ```
409
-
410
- ## Best Practices
411
-
412
- ### Do's
413
- - ✅ Use branch isolation to focus user attention
414
- - ✅ Provide meaningful annotations for resource status
415
- - ✅ Handle the `onResourceSelect` callback appropriately
416
- - ✅ Use pending resources to show unsaved changes
417
- - ✅ Provide clear empty states with helpful messages
418
-
419
- ### Don'ts
420
- - ❌ Don't put action buttons inside the picker (use external controls)
421
- - ❌ Don't modify resources directly (use pending resources for changes)
422
- - ❌ Don't forget to handle null selection (when nothing is selected)
423
- - ❌ Don't use overly deep branch isolation (can confuse users)
424
-
425
- ## Troubleshooting
426
-
427
- ### Common Issues
428
-
429
- **Resources not appearing:**
430
- - Ensure `resources` prop is not null and contains `summary.resourceIds`
431
- - Check branch isolation settings (`rootPath`, `hideRootNode`)
432
- - Verify search term isn't filtering out resources
433
-
434
- **Selection not working:**
435
- - Ensure `onResourceSelect` callback is provided
436
- - Check that `selectedResourceId` is being updated in parent state
437
- - Verify resource IDs match exactly (including pending resources)
438
-
439
- **Pending resources not showing:**
440
- - Ensure `pendingResources` array contains valid resource objects
441
- - Check that pending resource IDs don't conflict with existing resources
442
- - Verify pending resources aren't being filtered out by branch isolation
443
-
444
- **Styling issues:**
445
- - Check that required CSS is imported
446
- - Verify Tailwind CSS classes are available
447
- - Use browser dev tools to inspect component structure
448
-
449
- ### Debug Tips
450
-
451
- Enable debug logging to understand resource processing:
452
-
453
- ```typescript
454
- // Add this to see what resources are being processed
455
- React.useEffect(() => {
456
- if (resources) {
457
- console.log('ResourcePicker resources:', {
458
- total: resources.summary?.resourceIds?.length || 0,
459
- resourceIds: resources.summary?.resourceIds?.slice(0, 10),
460
- pendingCount: pendingResources?.length || 0
461
- });
462
- }
463
- }, [resources, pendingResources]);
464
- ```
465
-
466
- ## ResourcePickerOptionsControl
467
-
468
- A debugging and design tool for interactively configuring ResourcePicker behavior. This component is hidden by default for production use but can be enabled during development to easily adjust picker settings.
469
-
470
- ### Basic Usage
471
-
472
- ```typescript
473
- import { PickerTools } from '@fgv/ts-res-ui-components';
474
-
475
- function MyComponent() {
476
- const [pickerOptions, setPickerOptions] = useState({
477
- defaultView: 'list',
478
- enableSearch: true,
479
- height: '400px'
480
- });
481
-
482
- return (
483
- <div>
484
- {/* Enable the options control for debugging */}
485
- <PickerTools.ResourcePickerOptionsControl
486
- options={pickerOptions}
487
- onOptionsChange={setPickerOptions}
488
- presentation="collapsible"
489
- title="Picker Settings"
490
- showAdvanced={true}
491
- />
492
-
493
- {/* ResourcePicker uses the options */}
494
- <PickerTools.ResourcePicker
495
- resources={resources}
496
- selectedResourceId={selectedId}
497
- onResourceSelect={setSelectedId}
498
- options={pickerOptions}
499
- />
500
- </div>
501
- );
502
- }
503
- ```
504
-
505
- ### Presentation Modes
506
-
507
- | Mode | Description | Use Case |
508
- |------|-------------|----------|
509
- | `'hidden'` | Not displayed (default) | Production builds |
510
- | `'inline'` | Always visible with expanded controls | Development with lots of space |
511
- | `'collapsible'` | Expandable/collapsible section | Development with limited space |
512
- | `'popup'` | Full modal dialog overlay | Quick access without space constraints |
513
- | `'popover'` | Small dropdown overlay | Minimal space usage |
514
-
515
- ### Integration with Views
516
-
517
- All view components support the `pickerOptionsPresentation` prop:
518
-
519
- ```typescript
520
- // Enable in SourceView for debugging
521
- <SourceView
522
- resources={processedResources}
523
- pickerOptionsPresentation="collapsible"
524
- onMessage={(type, message) => console.log(`${type}: ${message}`)}
525
- />
526
-
527
- // Quick popup access in FilterView
528
- <FilterView
529
- resources={processedResources}
530
- filterState={filterState}
531
- filterActions={filterActions}
532
- pickerOptionsPresentation="popup"
533
- />
534
- ```
535
-
536
- ### Features
537
-
538
- The options control provides interactive configuration for:
539
-
540
- - **View Settings**: Toggle between list/tree views, show/hide view switcher
541
- - **Search Configuration**: Enable/disable search, set placeholder text, choose search scope
542
- - **Branch Isolation**: Set root path, hide root node, enable branch isolation
543
- - **Display Options**: Configure height, empty message, visual settings
544
- - **Quick Paths**: One-click branch isolation for common paths (strings, app, images, etc.)
545
-
546
- ### Development Workflow
547
-
548
- ```typescript
549
- // During development
550
- const isDevelopment = process.env.NODE_ENV === 'development';
551
-
552
- <SourceView
553
- resources={resources}
554
- pickerOptionsPresentation={isDevelopment ? 'collapsible' : 'hidden'}
555
- onMessage={handleMessage}
556
- />
557
- ```
558
-
559
- ## Related Components
560
-
561
- - **ResourceItem** - Individual resource display component
562
- - **ResourceTreeView** - Standalone tree view (legacy)
563
- - **ResourceListView** - Standalone list view (legacy)
564
-
565
- ## Version History
566
-
567
- - **v1.0.0** - Initial release with basic selection
568
- - **v1.1.0** - Added branch isolation support
569
- - **v1.2.0** - Added pending resources and edit support
570
- - **v1.3.0** - Enhanced list view with prefix truncation
@@ -1,127 +0,0 @@
1
- import React from 'react';
2
- import { DocumentTextIcon, PlusCircleIcon } from '@heroicons/react/24/outline';
3
- import { ResourceItemProps, ResourceSelection } from './types';
4
-
5
- /**
6
- * Individual resource item with annotation support
7
- */
8
- export const ResourceItem = <T = unknown,>({
9
- resourceId,
10
- displayName,
11
- isSelected,
12
- isPending = false,
13
- annotation,
14
- onClick,
15
- searchTerm = '',
16
- className = '',
17
- resourceData,
18
- pendingType
19
- }: ResourceItemProps<T>) => {
20
- const name = displayName || resourceId;
21
-
22
- // Highlight search term in the name
23
- const highlightedName = React.useMemo(() => {
24
- if (!searchTerm) {
25
- return <span>{name}</span>;
26
- }
27
-
28
- const regex = new RegExp(`(${searchTerm})`, 'gi');
29
- const parts = name.split(regex);
30
-
31
- return (
32
- <span>
33
- {parts.map((part, index) =>
34
- regex.test(part) ? (
35
- <mark key={index} className="bg-yellow-200">
36
- {part}
37
- </mark>
38
- ) : (
39
- <span key={index}>{part}</span>
40
- )
41
- )}
42
- </span>
43
- );
44
- }, [name, searchTerm]);
45
-
46
- // Get badge styling based on variant
47
- const getBadgeClasses = (variant: string) => {
48
- const baseClasses = 'px-1.5 py-0.5 text-xs font-medium rounded';
49
- const variantClasses = {
50
- info: 'bg-blue-100 text-blue-800',
51
- warning: 'bg-yellow-100 text-yellow-800',
52
- success: 'bg-green-100 text-green-800',
53
- error: 'bg-red-100 text-red-800',
54
- edited: 'bg-purple-100 text-purple-800',
55
- new: 'bg-emerald-100 text-emerald-800'
56
- };
57
- return `${baseClasses} ${variantClasses[variant as keyof typeof variantClasses] || variantClasses.info}`;
58
- };
59
-
60
- return (
61
- <div
62
- className={`
63
- flex items-center px-3 py-2 cursor-pointer hover:bg-gray-100
64
- border-b border-gray-100 last:border-b-0
65
- ${isSelected ? 'bg-purple-50 border-l-2 border-purple-500' : ''}
66
- ${isPending ? 'opacity-70 italic' : ''}
67
- ${searchTerm && name.toLowerCase().includes(searchTerm.toLowerCase()) ? 'bg-yellow-50' : ''}
68
- ${annotation?.className || ''}
69
- ${className}
70
- `}
71
- onClick={() =>
72
- onClick({
73
- resourceId,
74
- resourceData,
75
- isPending,
76
- pendingType
77
- })
78
- }
79
- title={resourceId}
80
- >
81
- {/* Icon */}
82
- <div className="flex-shrink-0 mr-2">
83
- {isPending ? (
84
- <PlusCircleIcon className="w-4 h-4 text-emerald-500" />
85
- ) : (
86
- <DocumentTextIcon className="w-4 h-4 text-green-500" />
87
- )}
88
- </div>
89
-
90
- {/* Resource name */}
91
- <span
92
- className={`
93
- text-sm truncate flex-1
94
- ${isSelected ? 'font-medium text-purple-900' : 'text-gray-700'}
95
- `}
96
- >
97
- {highlightedName}
98
- </span>
99
-
100
- {/* Annotations */}
101
- {annotation && (
102
- <div className="flex items-center gap-2 ml-2">
103
- {/* Indicator */}
104
- {annotation.indicator && (
105
- <span className="text-xs" title={annotation.indicator.tooltip}>
106
- {annotation.indicator.type === 'dot' ? (
107
- <span className="text-orange-500">●</span>
108
- ) : (
109
- annotation.indicator.value
110
- )}
111
- </span>
112
- )}
113
-
114
- {/* Badge */}
115
- {annotation.badge && (
116
- <span className={getBadgeClasses(annotation.badge.variant)}>{annotation.badge.text}</span>
117
- )}
118
-
119
- {/* Suffix */}
120
- {annotation.suffix && <span className="text-xs text-gray-500">{annotation.suffix}</span>}
121
- </div>
122
- )}
123
- </div>
124
- );
125
- };
126
-
127
- export default ResourceItem;