@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.
- package/README.md +401 -155
- package/config/jest.setup.js +10 -0
- package/dist/ts-res-ui-components.d.ts +1657 -76
- package/lib/components/common/QualifierContextControl.js +4 -1
- package/lib/components/common/ResourceTreeView.js +4 -1
- package/lib/components/forms/GenericQualifierTypeEditForm.d.ts +26 -0
- package/lib/components/forms/GenericQualifierTypeEditForm.js +166 -0
- package/lib/components/forms/QualifierEditForm.d.ts +1 -1
- package/lib/components/forms/index.d.ts +2 -0
- package/lib/components/forms/index.js +1 -0
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts +3 -0
- package/lib/components/orchestrator/ResourceOrchestrator.js +118 -51
- package/lib/components/pickers/ResourcePicker/ResourcePickerTree.js +32 -10
- package/lib/components/pickers/ResourcePicker/index.js +4 -2
- package/lib/components/views/CompiledView/index.js +75 -16
- package/lib/components/views/ConfigurationView/index.js +94 -35
- package/lib/components/views/FilterView/index.js +7 -4
- package/lib/components/views/GridView/EditableGridCell.d.ts +76 -0
- package/lib/components/views/GridView/EditableGridCell.js +224 -0
- package/lib/components/views/GridView/GridSelector.d.ts +43 -0
- package/lib/components/views/GridView/GridSelector.js +89 -0
- package/lib/components/views/GridView/MultiGridView.d.ts +85 -0
- package/lib/components/views/GridView/MultiGridView.js +196 -0
- package/lib/components/views/GridView/ResourceGrid.d.ts +38 -0
- package/lib/components/views/GridView/ResourceGrid.js +232 -0
- package/lib/components/views/GridView/SharedContextControls.d.ts +47 -0
- package/lib/components/views/GridView/SharedContextControls.js +95 -0
- package/lib/components/views/GridView/cells/BooleanCell.d.ts +44 -0
- package/lib/components/views/GridView/cells/BooleanCell.js +49 -0
- package/lib/components/views/GridView/cells/DropdownCell.d.ts +58 -0
- package/lib/components/views/GridView/cells/DropdownCell.js +182 -0
- package/lib/components/views/GridView/cells/StringCell.d.ts +57 -0
- package/lib/components/views/GridView/cells/StringCell.js +106 -0
- package/lib/components/views/GridView/cells/TriStateCell.d.ts +54 -0
- package/lib/components/views/GridView/cells/TriStateCell.js +112 -0
- package/lib/components/views/GridView/cells/index.d.ts +15 -0
- package/lib/components/views/GridView/cells/index.js +11 -0
- package/lib/components/views/GridView/index.d.ts +53 -0
- package/lib/components/views/GridView/index.js +212 -0
- package/lib/components/views/ImportView/index.js +22 -19
- package/lib/components/views/MessagesWindow/index.js +4 -1
- package/lib/components/views/ResolutionView/index.js +8 -5
- package/lib/contexts/ObservabilityContext.d.ts +85 -0
- package/lib/contexts/ObservabilityContext.js +98 -0
- package/lib/contexts/index.d.ts +2 -0
- package/lib/contexts/index.js +24 -0
- package/lib/hooks/useConfigurationState.d.ts +3 -3
- package/lib/hooks/useResolutionState.js +850 -246
- package/lib/hooks/useResourceData.d.ts +7 -4
- package/lib/hooks/useResourceData.js +185 -184
- package/lib/index.d.ts +5 -1
- package/lib/index.js +8 -1
- package/lib/namespaces/GridTools.d.ts +136 -0
- package/lib/namespaces/GridTools.js +138 -0
- package/lib/namespaces/ObservabilityTools.d.ts +3 -0
- package/lib/namespaces/ObservabilityTools.js +23 -0
- package/lib/namespaces/ResolutionTools.d.ts +2 -1
- package/lib/namespaces/ResolutionTools.js +2 -0
- package/lib/namespaces/index.d.ts +2 -0
- package/lib/namespaces/index.js +2 -0
- package/lib/test/integration/observability.integration.test.d.ts +2 -0
- package/lib/test/unit/hooks/useResourceData.test.d.ts +2 -0
- package/lib/test/unit/utils/downloadHelper.test.d.ts +2 -0
- package/lib/test/unit/workflows/resolutionWorkflows.test.d.ts +2 -0
- package/lib/test/unit/workflows/resourceCreation.test.d.ts +2 -0
- package/lib/test/unit/workflows/resultPatternExtensions.test.d.ts +2 -0
- package/lib/test/unit/workflows/validation.test.d.ts +2 -0
- package/lib/types/index.d.ts +387 -20
- package/lib/types/index.js +2 -1
- package/lib/utils/cellValidation.d.ts +113 -0
- package/lib/utils/cellValidation.js +248 -0
- package/lib/utils/downloadHelper.d.ts +66 -0
- package/lib/utils/downloadHelper.js +195 -0
- package/lib/utils/observability/factories.d.ts +29 -0
- package/lib/utils/observability/factories.js +58 -0
- package/lib/utils/observability/implementations.d.ts +61 -0
- package/lib/utils/observability/implementations.js +103 -0
- package/lib/utils/observability/index.d.ts +4 -0
- package/lib/utils/observability/index.js +26 -0
- package/lib/utils/observability/interfaces.d.ts +30 -0
- package/lib/utils/observability/interfaces.js +23 -0
- package/lib/utils/resolutionEditing.js +2 -1
- package/lib/utils/resourceSelector.d.ts +97 -0
- package/lib/utils/resourceSelector.js +195 -0
- package/lib/utils/resourceSelectors.d.ts +146 -0
- package/lib/utils/resourceSelectors.js +233 -0
- package/lib/utils/tsResIntegration.d.ts +6 -41
- package/lib/utils/tsResIntegration.js +20 -16
- package/lib/utils/zipLoader/zipProcessingHelpers.d.ts +3 -2
- package/lib/utils/zipLoader/zipProcessingHelpers.js +6 -5
- package/lib-commonjs/components/common/QualifierContextControl.js +4 -1
- package/lib-commonjs/components/common/ResourceTreeView.js +4 -1
- package/lib-commonjs/components/forms/GenericQualifierTypeEditForm.js +171 -0
- package/lib-commonjs/components/forms/index.js +3 -1
- package/lib-commonjs/components/orchestrator/ResourceOrchestrator.js +118 -51
- package/lib-commonjs/components/pickers/ResourcePicker/ResourcePickerTree.js +32 -10
- package/lib-commonjs/components/pickers/ResourcePicker/index.js +4 -2
- package/lib-commonjs/components/views/CompiledView/index.js +75 -16
- package/lib-commonjs/components/views/ConfigurationView/index.js +93 -34
- package/lib-commonjs/components/views/FilterView/index.js +7 -4
- package/lib-commonjs/components/views/GridView/EditableGridCell.js +232 -0
- package/lib-commonjs/components/views/GridView/GridSelector.js +94 -0
- package/lib-commonjs/components/views/GridView/MultiGridView.js +201 -0
- package/lib-commonjs/components/views/GridView/ResourceGrid.js +237 -0
- package/lib-commonjs/components/views/GridView/SharedContextControls.js +100 -0
- package/lib-commonjs/components/views/GridView/cells/BooleanCell.js +54 -0
- package/lib-commonjs/components/views/GridView/cells/DropdownCell.js +187 -0
- package/lib-commonjs/components/views/GridView/cells/StringCell.js +111 -0
- package/lib-commonjs/components/views/GridView/cells/TriStateCell.js +117 -0
- package/lib-commonjs/components/views/GridView/cells/index.js +18 -0
- package/lib-commonjs/components/views/GridView/index.js +217 -0
- package/lib-commonjs/components/views/ImportView/index.js +22 -19
- package/lib-commonjs/components/views/MessagesWindow/index.js +4 -1
- package/lib-commonjs/components/views/ResolutionView/index.js +8 -5
- package/lib-commonjs/contexts/ObservabilityContext.js +104 -0
- package/lib-commonjs/contexts/index.js +30 -0
- package/lib-commonjs/hooks/useResolutionState.js +849 -245
- package/lib-commonjs/hooks/useResourceData.js +184 -215
- package/lib-commonjs/index.js +15 -1
- package/lib-commonjs/namespaces/GridTools.js +161 -0
- package/lib-commonjs/namespaces/ObservabilityTools.js +33 -0
- package/lib-commonjs/namespaces/ResolutionTools.js +10 -1
- package/lib-commonjs/namespaces/index.js +3 -1
- package/lib-commonjs/types/index.js +10 -0
- package/lib-commonjs/utils/cellValidation.js +253 -0
- package/lib-commonjs/utils/downloadHelper.js +198 -0
- package/lib-commonjs/utils/observability/factories.js +63 -0
- package/lib-commonjs/utils/observability/implementations.js +109 -0
- package/lib-commonjs/utils/observability/index.js +36 -0
- package/lib-commonjs/utils/observability/interfaces.js +24 -0
- package/lib-commonjs/utils/resolutionEditing.js +2 -1
- package/lib-commonjs/utils/resourceSelector.js +200 -0
- package/lib-commonjs/utils/resourceSelectors.js +242 -0
- package/lib-commonjs/utils/tsResIntegration.js +21 -16
- package/lib-commonjs/utils/zipLoader/zipProcessingHelpers.js +7 -5
- package/package.json +7 -7
- package/src/components/common/QualifierContextControl.tsx +0 -338
- package/src/components/common/ResolutionContextOptionsControl.tsx +0 -450
- package/src/components/common/ResolutionResults/index.tsx +0 -481
- package/src/components/common/ResourceListView.tsx +0 -167
- package/src/components/common/ResourcePickerOptionsControl.tsx +0 -351
- package/src/components/common/ResourceTreeView.tsx +0 -417
- package/src/components/common/SourceResourceDetail/index.tsx +0 -493
- package/src/components/forms/HierarchyEditor.tsx +0 -285
- package/src/components/forms/QualifierEditForm.tsx +0 -487
- package/src/components/forms/QualifierTypeEditForm.tsx +0 -458
- package/src/components/forms/ResourceTypeEditForm.tsx +0 -437
- package/src/components/forms/index.ts +0 -11
- package/src/components/orchestrator/ResourceOrchestrator.tsx +0 -444
- package/src/components/pickers/ResourcePicker/README.md +0 -570
- package/src/components/pickers/ResourcePicker/ResourceItem.tsx +0 -127
- package/src/components/pickers/ResourcePicker/ResourcePickerList.tsx +0 -114
- package/src/components/pickers/ResourcePicker/ResourcePickerTree.tsx +0 -461
- package/src/components/pickers/ResourcePicker/index.tsx +0 -234
- package/src/components/pickers/ResourcePicker/types.ts +0 -301
- package/src/components/pickers/ResourcePicker/utils/treeNavigation.ts +0 -210
- package/src/components/views/CompiledView/index.tsx +0 -1342
- package/src/components/views/ConfigurationView/index.tsx +0 -848
- package/src/components/views/FilterView/index.tsx +0 -681
- package/src/components/views/ImportView/index.tsx +0 -789
- package/src/components/views/MessagesWindow/index.tsx +0 -325
- package/src/components/views/ResolutionView/EditableJsonView.tsx +0 -386
- package/src/components/views/ResolutionView/NewResourceModal.tsx +0 -158
- package/src/components/views/ResolutionView/UnifiedChangeControls.tsx +0 -163
- package/src/components/views/ResolutionView/index.tsx +0 -751
- package/src/components/views/SourceView/index.tsx +0 -291
- package/src/hooks/useConfigurationState.ts +0 -436
- package/src/hooks/useFilterState.ts +0 -150
- package/src/hooks/useResolutionState.ts +0 -893
- package/src/hooks/useResourceData.ts +0 -596
- package/src/hooks/useViewState.ts +0 -97
- package/src/index.ts +0 -68
- package/src/namespaces/ConfigurationTools.ts +0 -59
- package/src/namespaces/FilterTools.ts +0 -47
- package/src/namespaces/ImportTools.ts +0 -42
- package/src/namespaces/PickerTools.ts +0 -104
- package/src/namespaces/ResolutionTools.ts +0 -68
- package/src/namespaces/ResourceTools.ts +0 -106
- package/src/namespaces/TsResTools.ts +0 -49
- package/src/namespaces/ViewStateTools.ts +0 -91
- package/src/namespaces/ZipTools.ts +0 -49
- package/src/namespaces/index.ts +0 -49
- package/src/types/index.ts +0 -1273
- package/src/utils/configurationUtils.ts +0 -339
- package/src/utils/fileProcessing.ts +0 -164
- package/src/utils/filterResources.ts +0 -356
- package/src/utils/resolutionEditing.ts +0 -346
- package/src/utils/resolutionUtils.ts +0 -740
- package/src/utils/tsResIntegration.ts +0 -475
- package/src/utils/zipLoader/index.ts +0 -5
- package/src/utils/zipLoader/zipProcessingHelpers.ts +0 -46
- 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;
|