@fgv/ts-res-ui-components 5.0.0-10 → 5.0.0-11
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/.rush/temp/{03c8b056281d9db0a97d8a6e25eea798a160d393.tar.log → 96dcc283522e3a0fcf1e18730f9bb0a1d0fe9ad2.tar.log} +2 -2
- package/.rush/temp/chunked-rush-logs/ts-res-ui-components.build.chunks.jsonl +2 -2
- package/.rush/temp/operation/build/all.log +2 -2
- package/.rush/temp/operation/build/log-chunks.jsonl +2 -2
- package/.rush/temp/operation/build/state.json +1 -1
- package/README.md +506 -12
- package/lib/components/forms/QualifierEditForm.d.ts.map +1 -1
- package/lib/components/forms/QualifierEditForm.js +34 -9
- package/lib/components/forms/QualifierEditForm.js.map +1 -1
- package/lib/components/forms/QualifierTypeEditForm.js.map +1 -1
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts +48 -0
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts.map +1 -1
- package/lib/components/orchestrator/ResourceOrchestrator.js +39 -0
- package/lib/components/orchestrator/ResourceOrchestrator.js.map +1 -1
- package/lib/components/views/CompiledView/index.d.ts.map +1 -1
- package/lib/components/views/CompiledView/index.js +170 -133
- package/lib/components/views/CompiledView/index.js.map +1 -1
- package/lib/components/views/ConfigurationView/index.d.ts.map +1 -1
- package/lib/components/views/ConfigurationView/index.js +9 -7
- package/lib/components/views/ConfigurationView/index.js.map +1 -1
- package/lib/components/views/FilterView/index.d.ts.map +1 -1
- package/lib/components/views/FilterView/index.js.map +1 -1
- package/lib/components/views/SourceView/index.d.ts.map +1 -1
- package/lib/components/views/SourceView/index.js.map +1 -1
- package/lib/hooks/useResourceData.d.ts +54 -0
- package/lib/hooks/useResourceData.d.ts.map +1 -1
- package/lib/hooks/useResourceData.js +78 -7
- package/lib/hooks/useResourceData.js.map +1 -1
- package/lib/types/index.d.ts +166 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js.map +1 -1
- package/package.json +6 -6
- package/rush-logs/ts-res-ui-components.build.cache.log +1 -1
- package/rush-logs/ts-res-ui-components.build.log +2 -2
- package/src/components/forms/QualifierEditForm.tsx +52 -17
- package/src/components/forms/QualifierTypeEditForm.tsx +9 -9
- package/src/components/orchestrator/ResourceOrchestrator.tsx +48 -0
- package/src/components/views/CompiledView/index.tsx +200 -157
- package/src/components/views/ConfigurationView/index.tsx +8 -6
- package/src/components/views/FilterView/index.tsx +4 -1
- package/src/components/views/SourceView/index.tsx +3 -1
- package/src/hooks/useResourceData.ts +110 -7
- package/src/types/index.ts +167 -10
- package/temp/build/typescript/ts_gZid87Hu.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
Start time:
|
|
2
|
-
Invoking "/usr/bin/tar -c -f /home/runner/work/fgv/fgv/common/temp/build-cache/
|
|
1
|
+
Start time: Sun Aug 10 2025 04:16:47 GMT+0000 (Coordinated Universal Time)
|
|
2
|
+
Invoking "/usr/bin/tar -c -f /home/runner/work/fgv/fgv/common/temp/build-cache/96dcc283522e3a0fcf1e18730f9bb0a1d0fe9ad2-1d929c2480b91670.temp -z --files-from=-"
|
|
3
3
|
|
|
4
4
|
======= BEGIN PROCESS INPUT ======
|
|
5
5
|
.rush/temp/operation/build/all.log
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
{"kind":"O","text":"[build:sass] No SCSS files to process.\n"}
|
|
6
6
|
{"kind":"O","text":"[build:typescript] Using TypeScript version 5.8.3\n"}
|
|
7
7
|
{"kind":"O","text":"[build:webpack] No Webpack configuration found\n"}
|
|
8
|
-
{"kind":"O","text":" ---- build finished (9.
|
|
9
|
-
{"kind":"O","text":"-------------------- Finished (9.
|
|
8
|
+
{"kind":"O","text":" ---- build finished (9.270s) ---- \n"}
|
|
9
|
+
{"kind":"O","text":"-------------------- Finished (9.275s) --------------------\n"}
|
|
@@ -5,5 +5,5 @@ Invoking: heft build --clean
|
|
|
5
5
|
[build:sass] No SCSS files to process.
|
|
6
6
|
[build:typescript] Using TypeScript version 5.8.3
|
|
7
7
|
[build:webpack] No Webpack configuration found
|
|
8
|
-
---- build finished (9.
|
|
9
|
-
-------------------- Finished (9.
|
|
8
|
+
---- build finished (9.270s) ----
|
|
9
|
+
-------------------- Finished (9.275s) --------------------
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
{"kind":"O","text":"[build:sass] No SCSS files to process.\n"}
|
|
6
6
|
{"kind":"O","text":"[build:typescript] Using TypeScript version 5.8.3\n"}
|
|
7
7
|
{"kind":"O","text":"[build:webpack] No Webpack configuration found\n"}
|
|
8
|
-
{"kind":"O","text":" ---- build finished (9.
|
|
9
|
-
{"kind":"O","text":"-------------------- Finished (9.
|
|
8
|
+
{"kind":"O","text":" ---- build finished (9.270s) ---- \n"}
|
|
9
|
+
{"kind":"O","text":"-------------------- Finished (9.275s) --------------------\n"}
|
package/README.md
CHANGED
|
@@ -1,18 +1,512 @@
|
|
|
1
|
-
#
|
|
2
|
-
### @fgv/ts-res-ui-components
|
|
1
|
+
# @fgv/ts-res-ui-components
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
Reusable React components for ts-res resource visualization and management
|
|
3
|
+
React components for building user interfaces that work with the [ts-res](https://github.com/fgv/ts-res) multidimensional resource management library.
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
## Overview
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
This library provides a complete set of React components, hooks, and utilities for creating applications that visualize, manage, and interact with ts-res resource systems. It supports the full workflow from importing configurations to resolving resources with dynamic context.
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
## Warning
|
|
10
|
+
This packlet is largely AI written, and it shows.
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
`npm test`
|
|
12
|
+
## Features
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
- **🔄 Resource Management**: Import, process, and manage ts-res configurations and bundles
|
|
15
|
+
- **🔍 Advanced Filtering**: Filter resources by context with qualifier reduction
|
|
16
|
+
- **🎯 Resource Resolution**: Test resource resolution with dynamic context values
|
|
17
|
+
- **📊 Visualization**: Multiple views for exploring resource structures and compiled output
|
|
18
|
+
- **⚙️ Configuration**: Visual configuration management for qualifier types, qualifiers, and resource types
|
|
19
|
+
- **📁 File Handling**: Support for directory imports, ZIP files, and bundle loading
|
|
20
|
+
- **🎨 Modern UI**: Built with Tailwind CSS and Heroicons for a clean, responsive interface
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @fgv/ts-res-ui-components
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Peer Dependencies
|
|
29
|
+
|
|
30
|
+
This library requires the following peer dependencies:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"@fgv/ts-res": "^5.0.0",
|
|
35
|
+
"@fgv/ts-utils": "^5.0.0",
|
|
36
|
+
"@fgv/ts-json-base": "^5.0.0",
|
|
37
|
+
"react": "^18.0.0",
|
|
38
|
+
"react-dom": "^18.0.0"
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
### Basic Usage with ResourceOrchestrator
|
|
45
|
+
|
|
46
|
+
The `ResourceOrchestrator` component provides centralized state management for all ts-res UI functionality:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import React from 'react';
|
|
50
|
+
import { ResourceOrchestrator, ImportView, SourceView } from '@fgv/ts-res-ui-components';
|
|
51
|
+
|
|
52
|
+
function App() {
|
|
53
|
+
return (
|
|
54
|
+
<ResourceOrchestrator>
|
|
55
|
+
{({ state, actions }) => (
|
|
56
|
+
<div className="min-h-screen bg-gray-50">
|
|
57
|
+
<div className="container mx-auto px-4 py-8">
|
|
58
|
+
<h1 className="text-3xl font-bold mb-8">Resource Manager</h1>
|
|
59
|
+
|
|
60
|
+
{!state.processedResources ? (
|
|
61
|
+
<ImportView
|
|
62
|
+
onImport={actions.importDirectory}
|
|
63
|
+
onBundleImport={actions.importBundle}
|
|
64
|
+
onZipImport={actions.importZipWithConfig}
|
|
65
|
+
/>
|
|
66
|
+
) : (
|
|
67
|
+
<SourceView
|
|
68
|
+
resources={state.processedResources}
|
|
69
|
+
onExport={actions.exportData}
|
|
70
|
+
/>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
</ResourceOrchestrator>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default App;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Using Individual Hooks
|
|
83
|
+
|
|
84
|
+
For more granular control, you can use individual hooks:
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import React from 'react';
|
|
88
|
+
import { useResourceData, SourceView } from '@fgv/ts-res-ui-components';
|
|
89
|
+
|
|
90
|
+
function MyResourceViewer() {
|
|
91
|
+
const { state, actions } = useResourceData();
|
|
92
|
+
|
|
93
|
+
const handleFileImport = async (files: File[]) => {
|
|
94
|
+
const importedFiles = await processFiles(files); // Your file processing logic
|
|
95
|
+
await actions.processFiles(importedFiles);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<div>
|
|
100
|
+
{state.isProcessing && <div>Processing...</div>}
|
|
101
|
+
{state.error && <div className="error">{state.error}</div>}
|
|
102
|
+
{state.processedResources && (
|
|
103
|
+
<SourceView
|
|
104
|
+
resources={state.processedResources}
|
|
105
|
+
onExport={(data) => console.log('Export:', data)}
|
|
106
|
+
/>
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Architecture
|
|
114
|
+
|
|
115
|
+
### Component Hierarchy
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
ResourceOrchestrator (state management)
|
|
119
|
+
├── ImportView (file/bundle import)
|
|
120
|
+
├── SourceView (resource collection display)
|
|
121
|
+
├── FilterView (context filtering)
|
|
122
|
+
├── CompiledView (compiled resource structure)
|
|
123
|
+
├── ResolutionView (resource resolution testing)
|
|
124
|
+
├── ConfigurationView (system configuration)
|
|
125
|
+
└── ZipLoaderView (ZIP file handling)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Data Flow
|
|
129
|
+
|
|
130
|
+
1. **Import Phase**: Resources are imported via `ImportView` or programmatically
|
|
131
|
+
2. **Processing Phase**: Raw files are processed into a `ProcessedResources` object containing:
|
|
132
|
+
- `ResourceManagerBuilder` for build-time operations
|
|
133
|
+
- `CompiledResourceCollection` for runtime efficiency
|
|
134
|
+
- `ResourceResolver` for resource resolution
|
|
135
|
+
3. **Interaction Phase**: Users can filter, explore, and test resource resolution
|
|
136
|
+
4. **Export Phase**: Processed resources can be exported as JSON or compiled bundles
|
|
137
|
+
|
|
138
|
+
### Key Concepts
|
|
139
|
+
|
|
140
|
+
- **ProcessedResources**: The main data structure containing both build-time and runtime representations
|
|
141
|
+
- **ResourceManagerBuilder**: Build-time resource manager for constructing and modifying resources
|
|
142
|
+
- **CompiledResourceCollection**: Runtime-optimized representation for efficient resolution
|
|
143
|
+
- **Filter Context**: Dynamic context values used to filter and resolve resources
|
|
144
|
+
- **Resolution State**: Testing environment for resource resolution with different contexts
|
|
145
|
+
|
|
146
|
+
## Core Components
|
|
147
|
+
|
|
148
|
+
### ResourceOrchestrator
|
|
149
|
+
|
|
150
|
+
The main orchestration component that manages all state and provides actions via render props.
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
<ResourceOrchestrator
|
|
154
|
+
initialConfiguration={myConfig}
|
|
155
|
+
onStateChange={(state) => console.log('State changed:', state)}
|
|
156
|
+
>
|
|
157
|
+
{({ state, actions }) => (
|
|
158
|
+
// Your UI components
|
|
159
|
+
)}
|
|
160
|
+
</ResourceOrchestrator>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### ImportView
|
|
164
|
+
|
|
165
|
+
Handles importing resource files, directories, bundles, and ZIP files.
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
<ImportView
|
|
169
|
+
onImport={actions.importDirectory}
|
|
170
|
+
onBundleImport={actions.importBundle}
|
|
171
|
+
onZipImport={actions.importZipWithConfig}
|
|
172
|
+
acceptedFileTypes={['.json', '.ts', '.js']}
|
|
173
|
+
onMessage={(type, message) => console.log(type, message)}
|
|
174
|
+
/>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### SourceView
|
|
178
|
+
|
|
179
|
+
Displays the source resource collection with search and navigation capabilities.
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
<SourceView
|
|
183
|
+
resources={state.processedResources}
|
|
184
|
+
selectedResourceId={selectedId}
|
|
185
|
+
onResourceSelect={setSelectedId}
|
|
186
|
+
onExport={actions.exportData}
|
|
187
|
+
/>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### FilterView
|
|
191
|
+
|
|
192
|
+
Provides filtering capabilities with context value specification.
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
<FilterView
|
|
196
|
+
resources={state.processedResources}
|
|
197
|
+
filterState={filterState}
|
|
198
|
+
filterActions={filterActions}
|
|
199
|
+
filterResult={filterResult}
|
|
200
|
+
onFilterResult={setFilterResult}
|
|
201
|
+
/>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### CompiledView
|
|
205
|
+
|
|
206
|
+
Shows the compiled resource structure with detailed candidate information.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
<CompiledView
|
|
210
|
+
resources={state.processedResources}
|
|
211
|
+
filterResult={filterResult}
|
|
212
|
+
useNormalization={true}
|
|
213
|
+
onExport={(data, type) => exportData(data, type)}
|
|
214
|
+
/>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### ResolutionView
|
|
218
|
+
|
|
219
|
+
Interactive resource resolution testing with context management.
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
<ResolutionView
|
|
223
|
+
resources={state.processedResources}
|
|
224
|
+
resolutionState={resolutionState}
|
|
225
|
+
resolutionActions={resolutionActions}
|
|
226
|
+
availableQualifiers={availableQualifiers}
|
|
227
|
+
/>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### ConfigurationView
|
|
231
|
+
|
|
232
|
+
Visual configuration management for the ts-res system.
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
<ConfigurationView
|
|
236
|
+
configuration={state.activeConfiguration}
|
|
237
|
+
onConfigurationChange={actions.applyConfiguration}
|
|
238
|
+
onMessage={(type, msg) => showMessage(type, msg)}
|
|
239
|
+
/>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Hooks API
|
|
243
|
+
|
|
244
|
+
### useResourceData
|
|
245
|
+
|
|
246
|
+
Main hook for resource processing and management.
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
const { state, actions } = useResourceData();
|
|
250
|
+
|
|
251
|
+
// Process files
|
|
252
|
+
await actions.processFiles(importedFiles);
|
|
253
|
+
|
|
254
|
+
// Resolve a resource
|
|
255
|
+
const result = await actions.resolveResource('my.resource', {
|
|
256
|
+
language: 'en-US',
|
|
257
|
+
environment: 'production'
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Apply configuration
|
|
261
|
+
actions.applyConfiguration(newConfig);
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### useFilterState
|
|
265
|
+
|
|
266
|
+
Manages resource filtering state and actions.
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
const { filterState, filterActions } = useFilterState();
|
|
270
|
+
|
|
271
|
+
// Update filter values
|
|
272
|
+
filterActions.updateFilterValues({
|
|
273
|
+
language: 'en-US',
|
|
274
|
+
environment: 'prod'
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Apply filters
|
|
278
|
+
filterActions.applyFilterValues();
|
|
279
|
+
|
|
280
|
+
// Check if there are pending changes
|
|
281
|
+
if (filterState.hasPendingChanges) {
|
|
282
|
+
// Show save prompt
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### useResolutionState
|
|
287
|
+
|
|
288
|
+
Manages resource resolution testing state.
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
const { state, actions } = useResolutionState(processedResources);
|
|
292
|
+
|
|
293
|
+
// Set context for resolution testing
|
|
294
|
+
actions.setContextValues({ language: 'en-US' });
|
|
295
|
+
|
|
296
|
+
// Test resource resolution
|
|
297
|
+
const result = await actions.resolveResource('test.resource');
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### useConfigurationState
|
|
301
|
+
|
|
302
|
+
Manages system configuration state.
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
const { configuration, updateConfiguration, resetConfiguration } = useConfigurationState();
|
|
306
|
+
|
|
307
|
+
// Update qualifier types
|
|
308
|
+
updateConfiguration({
|
|
309
|
+
...configuration,
|
|
310
|
+
qualifierTypes: [...newQualifierTypes]
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Styling
|
|
315
|
+
|
|
316
|
+
This library uses Tailwind CSS for styling. Make sure to include Tailwind CSS in your project:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
npm install -D tailwindcss
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Tailwind Configuration
|
|
323
|
+
|
|
324
|
+
Add the library's source files to your Tailwind content configuration:
|
|
325
|
+
|
|
326
|
+
```js
|
|
327
|
+
// tailwind.config.js
|
|
328
|
+
module.exports = {
|
|
329
|
+
content: [
|
|
330
|
+
'./src/**/*.{js,jsx,ts,tsx}',
|
|
331
|
+
'./node_modules/@fgv/ts-res-ui-components/**/*.{js,jsx,ts,tsx}'
|
|
332
|
+
],
|
|
333
|
+
theme: {
|
|
334
|
+
extend: {},
|
|
335
|
+
},
|
|
336
|
+
plugins: [],
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Custom Styling
|
|
341
|
+
|
|
342
|
+
All components accept a `className` prop for custom styling:
|
|
343
|
+
|
|
344
|
+
```tsx
|
|
345
|
+
<SourceView
|
|
346
|
+
className="my-custom-class"
|
|
347
|
+
resources={resources}
|
|
348
|
+
/>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Advanced Usage
|
|
352
|
+
|
|
353
|
+
### Custom Resource Processing
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
import { processImportedDirectory, createTsResSystemFromConfig } from '@fgv/ts-res-ui-components';
|
|
357
|
+
|
|
358
|
+
// Custom processing pipeline
|
|
359
|
+
const customProcessor = async (files: ImportedFile[]) => {
|
|
360
|
+
// Pre-process files
|
|
361
|
+
const processedFiles = files.map(transformFile);
|
|
362
|
+
|
|
363
|
+
// Create configuration
|
|
364
|
+
const config = await createConfigFromFiles(processedFiles);
|
|
365
|
+
|
|
366
|
+
// Create ts-res system
|
|
367
|
+
const system = await createTsResSystemFromConfig(config);
|
|
368
|
+
|
|
369
|
+
return system;
|
|
370
|
+
};
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Resource Resolution with Custom Context
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
const { state, actions } = useResourceData();
|
|
377
|
+
|
|
378
|
+
// Complex context resolution
|
|
379
|
+
const resolveWithComplexContext = async (resourceId: string) => {
|
|
380
|
+
const context = {
|
|
381
|
+
language: getUserLanguage(),
|
|
382
|
+
region: getUserRegion(),
|
|
383
|
+
theme: getThemePreference(),
|
|
384
|
+
featureFlags: await getFeatureFlags()
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
return await actions.resolveResource(resourceId, context);
|
|
388
|
+
};
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Bundle Creation and Export
|
|
392
|
+
|
|
393
|
+
```tsx
|
|
394
|
+
const { state, actions } = useResourceData();
|
|
395
|
+
|
|
396
|
+
// Create and export bundle
|
|
397
|
+
const exportBundle = async () => {
|
|
398
|
+
if (state.processedResources) {
|
|
399
|
+
const bundleData = {
|
|
400
|
+
...state.processedResources.compiledCollection,
|
|
401
|
+
metadata: {
|
|
402
|
+
version: '1.0.0',
|
|
403
|
+
created: new Date().toISOString(),
|
|
404
|
+
description: 'My resource bundle'
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
actions.exportData(bundleData, 'bundle');
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
## Error Handling
|
|
414
|
+
|
|
415
|
+
The library provides comprehensive error handling through the state management system:
|
|
416
|
+
|
|
417
|
+
```tsx
|
|
418
|
+
<ResourceOrchestrator>
|
|
419
|
+
{({ state, actions }) => (
|
|
420
|
+
<div>
|
|
421
|
+
{state.error && (
|
|
422
|
+
<div className="bg-red-50 border border-red-200 rounded p-4 mb-4">
|
|
423
|
+
<h3 className="text-red-800 font-medium">Error</h3>
|
|
424
|
+
<p className="text-red-600">{state.error}</p>
|
|
425
|
+
<button
|
|
426
|
+
onClick={actions.clearError}
|
|
427
|
+
className="mt-2 px-3 py-1 bg-red-600 text-white rounded text-sm"
|
|
428
|
+
>
|
|
429
|
+
Dismiss
|
|
430
|
+
</button>
|
|
431
|
+
</div>
|
|
432
|
+
)}
|
|
433
|
+
|
|
434
|
+
{/* Rest of your UI */}
|
|
435
|
+
</div>
|
|
436
|
+
)}
|
|
437
|
+
</ResourceOrchestrator>
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## TypeScript Support
|
|
441
|
+
|
|
442
|
+
This library is written in TypeScript and provides comprehensive type definitions:
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
import type {
|
|
446
|
+
ProcessedResources,
|
|
447
|
+
FilterState,
|
|
448
|
+
ResolutionResult,
|
|
449
|
+
Message,
|
|
450
|
+
ImportedFile
|
|
451
|
+
} from '@fgv/ts-res-ui-components';
|
|
452
|
+
|
|
453
|
+
// Type-safe component props
|
|
454
|
+
interface MyComponentProps {
|
|
455
|
+
resources: ProcessedResources;
|
|
456
|
+
onMessage: (type: Message['type'], message: string) => void;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const MyComponent: React.FC<MyComponentProps> = ({ resources, onMessage }) => {
|
|
460
|
+
// Component implementation
|
|
461
|
+
};
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
## Performance Considerations
|
|
465
|
+
|
|
466
|
+
- **Lazy Loading**: Components are designed for lazy loading and code splitting
|
|
467
|
+
- **Memoization**: Internal state updates are optimized with React.memo and useMemo
|
|
468
|
+
- **Virtual Scrolling**: Large resource lists use virtual scrolling for performance
|
|
469
|
+
- **Compiled Resources**: Runtime resolution uses pre-compiled structures for efficiency
|
|
470
|
+
|
|
471
|
+
## Browser Support
|
|
472
|
+
|
|
473
|
+
- Chrome/Edge: >= 88
|
|
474
|
+
- Firefox: >= 85
|
|
475
|
+
- Safari: >= 14
|
|
476
|
+
- Modern browsers with ES2020 support
|
|
477
|
+
|
|
478
|
+
## Development
|
|
479
|
+
|
|
480
|
+
### Building
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
npm run build
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Testing
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
npm test
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Linting
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
npm run lint
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
## License
|
|
499
|
+
|
|
500
|
+
MIT License - see [LICENSE](./LICENSE) file for details.
|
|
501
|
+
|
|
502
|
+
## Contributing
|
|
503
|
+
|
|
504
|
+
Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
|
|
505
|
+
|
|
506
|
+
## Support
|
|
507
|
+
|
|
508
|
+
For questions and support, please:
|
|
509
|
+
|
|
510
|
+
1. Check the [documentation](https://docs.ts-res.dev)
|
|
511
|
+
2. Search [existing issues](https://github.com/fgv/ts-res/issues)
|
|
512
|
+
3. Create a [new issue](https://github.com/fgv/ts-res/issues/new)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QualifierEditForm.d.ts","sourceRoot":"","sources":["../../../src/components/forms/QualifierEditForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"QualifierEditForm.d.ts","sourceRoot":"","sources":["../../../src/components/forms/QualifierEditForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGzD,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,UAAU,CAAC,cAAc,CAAC;IACtC,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,0BAA0B,EAAE,CAAC;IACnE,MAAM,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,cAAc,KAAK,IAAI,CAAC;IACvD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAWD,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CA+W9D,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState, useCallback } from 'react';
|
|
2
2
|
import { XMarkIcon, InformationCircleIcon } from '@heroicons/react/24/outline';
|
|
3
|
+
import { Converters } from '@fgv/ts-utils';
|
|
3
4
|
export const QualifierEditForm = ({ qualifier, qualifierTypes, onSave, onCancel, existingNames = [] }) => {
|
|
4
5
|
const [formData, setFormData] = useState(() => {
|
|
5
6
|
if (qualifier) {
|
|
@@ -24,7 +25,13 @@ export const QualifierEditForm = ({ qualifier, qualifierTypes, onSave, onCancel,
|
|
|
24
25
|
const [errors, setErrors] = useState({});
|
|
25
26
|
// Get the selected qualifier type for context
|
|
26
27
|
const selectedQualifierType = qualifierTypes.find((qt) => qt.name === formData.typeName);
|
|
27
|
-
|
|
28
|
+
// Type-safe extraction of allowContextList property
|
|
29
|
+
const allowsContextList = (() => {
|
|
30
|
+
if (!selectedQualifierType?.configuration)
|
|
31
|
+
return false;
|
|
32
|
+
const result = Converters.boolean.convert(selectedQualifierType.configuration.allowContextList);
|
|
33
|
+
return result.isSuccess() ? result.value : false;
|
|
34
|
+
})();
|
|
28
35
|
// Validation
|
|
29
36
|
const validateForm = useCallback(() => {
|
|
30
37
|
const newErrors = {};
|
|
@@ -64,7 +71,9 @@ export const QualifierEditForm = ({ qualifier, qualifierTypes, onSave, onCancel,
|
|
|
64
71
|
const updated = { ...prev, [field]: value };
|
|
65
72
|
// Auto-generate token from name if no custom token is set
|
|
66
73
|
if (field === 'name' && !prev.token) {
|
|
67
|
-
updated.token = value
|
|
74
|
+
updated.token = String(value)
|
|
75
|
+
.toLowerCase()
|
|
76
|
+
.replace(/[^a-zA-Z0-9]/g, '');
|
|
68
77
|
}
|
|
69
78
|
// Clear tokenIsOptional if token is cleared
|
|
70
79
|
if (field === 'token' && !value) {
|
|
@@ -85,7 +94,13 @@ export const QualifierEditForm = ({ qualifier, qualifierTypes, onSave, onCancel,
|
|
|
85
94
|
case 'territory':
|
|
86
95
|
return allowsContextList ? 'e.g., US or US,CA' : 'e.g., US';
|
|
87
96
|
case 'literal':
|
|
88
|
-
|
|
97
|
+
// Type-safe extraction of enumeratedValues
|
|
98
|
+
const enumValues = (() => {
|
|
99
|
+
if (!selectedQualifierType.configuration)
|
|
100
|
+
return undefined;
|
|
101
|
+
const result = Converters.arrayOf(Converters.string).convert(selectedQualifierType.configuration.enumeratedValues);
|
|
102
|
+
return result.isSuccess() ? result.value : undefined;
|
|
103
|
+
})();
|
|
89
104
|
if (enumValues && enumValues.length > 0) {
|
|
90
105
|
return allowsContextList
|
|
91
106
|
? `e.g., ${enumValues[0]} or ${enumValues.slice(0, 2).join(',')}`
|
|
@@ -157,23 +172,33 @@ export const QualifierEditForm = ({ qualifier, qualifierTypes, onSave, onCancel,
|
|
|
157
172
|
' ',
|
|
158
173
|
allowsContextList ? 'Yes' : 'No'),
|
|
159
174
|
selectedQualifierType.systemType === 'literal' && selectedQualifierType.configuration && (React.createElement(React.Fragment, null,
|
|
160
|
-
selectedQualifierType.configuration.caseSensitive !==
|
|
175
|
+
selectedQualifierType.configuration.caseSensitive !==
|
|
176
|
+
undefined && (React.createElement("p", null,
|
|
161
177
|
React.createElement("span", { className: "font-medium" }, "Case Sensitive:"),
|
|
162
178
|
' ',
|
|
163
|
-
selectedQualifierType.configuration
|
|
179
|
+
selectedQualifierType.configuration
|
|
180
|
+
.caseSensitive
|
|
181
|
+
? 'Yes'
|
|
182
|
+
: 'No')),
|
|
164
183
|
selectedQualifierType.configuration.enumeratedValues && (React.createElement("p", null,
|
|
165
184
|
React.createElement("span", { className: "font-medium" }, "Allowed Values:"),
|
|
166
185
|
' ',
|
|
167
|
-
selectedQualifierType.configuration
|
|
186
|
+
selectedQualifierType.configuration
|
|
187
|
+
.enumeratedValues.join(', '))))),
|
|
168
188
|
selectedQualifierType.systemType === 'territory' && selectedQualifierType.configuration && (React.createElement(React.Fragment, null,
|
|
169
|
-
selectedQualifierType.configuration.acceptLowercase !==
|
|
189
|
+
selectedQualifierType.configuration.acceptLowercase !==
|
|
190
|
+
undefined && (React.createElement("p", null,
|
|
170
191
|
React.createElement("span", { className: "font-medium" }, "Accept Lowercase:"),
|
|
171
192
|
' ',
|
|
172
|
-
selectedQualifierType.configuration
|
|
193
|
+
selectedQualifierType.configuration
|
|
194
|
+
.acceptLowercase
|
|
195
|
+
? 'Yes'
|
|
196
|
+
: 'No')),
|
|
173
197
|
selectedQualifierType.configuration.allowedTerritories && (React.createElement("p", null,
|
|
174
198
|
React.createElement("span", { className: "font-medium" }, "Allowed Territories:"),
|
|
175
199
|
' ',
|
|
176
|
-
selectedQualifierType.configuration
|
|
200
|
+
selectedQualifierType.configuration
|
|
201
|
+
.allowedTerritories.join(', '))))))))),
|
|
177
202
|
React.createElement("div", { className: "flex justify-end space-x-3 px-6 py-4 border-t bg-gray-50 flex-shrink-0" },
|
|
178
203
|
React.createElement("button", { onClick: onCancel, className: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" }, "Cancel"),
|
|
179
204
|
React.createElement("button", { onClick: handleSave, className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" }, qualifier ? 'Save Changes' : 'Add Qualifier')))));
|