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

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 (44) hide show
  1. package/.rush/temp/{03c8b056281d9db0a97d8a6e25eea798a160d393.tar.log → 352205c76acd8e247a1680af51abc95e0524eb82.tar.log} +2 -2
  2. package/.rush/temp/chunked-rush-logs/ts-res-ui-components.build.chunks.jsonl +2 -2
  3. package/.rush/temp/operation/build/all.log +2 -2
  4. package/.rush/temp/operation/build/log-chunks.jsonl +2 -2
  5. package/.rush/temp/operation/build/state.json +1 -1
  6. package/README.md +506 -12
  7. package/lib/components/forms/QualifierEditForm.d.ts.map +1 -1
  8. package/lib/components/forms/QualifierEditForm.js +34 -9
  9. package/lib/components/forms/QualifierEditForm.js.map +1 -1
  10. package/lib/components/forms/QualifierTypeEditForm.js.map +1 -1
  11. package/lib/components/orchestrator/ResourceOrchestrator.d.ts +48 -0
  12. package/lib/components/orchestrator/ResourceOrchestrator.d.ts.map +1 -1
  13. package/lib/components/orchestrator/ResourceOrchestrator.js +39 -0
  14. package/lib/components/orchestrator/ResourceOrchestrator.js.map +1 -1
  15. package/lib/components/views/CompiledView/index.d.ts.map +1 -1
  16. package/lib/components/views/CompiledView/index.js +170 -133
  17. package/lib/components/views/CompiledView/index.js.map +1 -1
  18. package/lib/components/views/ConfigurationView/index.d.ts.map +1 -1
  19. package/lib/components/views/ConfigurationView/index.js +9 -7
  20. package/lib/components/views/ConfigurationView/index.js.map +1 -1
  21. package/lib/components/views/FilterView/index.d.ts.map +1 -1
  22. package/lib/components/views/FilterView/index.js.map +1 -1
  23. package/lib/components/views/SourceView/index.d.ts.map +1 -1
  24. package/lib/components/views/SourceView/index.js.map +1 -1
  25. package/lib/hooks/useResourceData.d.ts +54 -0
  26. package/lib/hooks/useResourceData.d.ts.map +1 -1
  27. package/lib/hooks/useResourceData.js +78 -7
  28. package/lib/hooks/useResourceData.js.map +1 -1
  29. package/lib/types/index.d.ts +166 -1
  30. package/lib/types/index.d.ts.map +1 -1
  31. package/lib/types/index.js.map +1 -1
  32. package/package.json +6 -6
  33. package/rush-logs/ts-res-ui-components.build.cache.log +1 -1
  34. package/rush-logs/ts-res-ui-components.build.log +2 -2
  35. package/src/components/forms/QualifierEditForm.tsx +52 -17
  36. package/src/components/forms/QualifierTypeEditForm.tsx +9 -9
  37. package/src/components/orchestrator/ResourceOrchestrator.tsx +48 -0
  38. package/src/components/views/CompiledView/index.tsx +200 -157
  39. package/src/components/views/ConfigurationView/index.tsx +8 -6
  40. package/src/components/views/FilterView/index.tsx +4 -1
  41. package/src/components/views/SourceView/index.tsx +3 -1
  42. package/src/hooks/useResourceData.ts +110 -7
  43. package/src/types/index.ts +167 -10
  44. package/temp/build/typescript/ts_gZid87Hu.json +1 -1
@@ -1,5 +1,5 @@
1
- Start time: Sat Aug 09 2025 19:30:22 GMT+0000 (Coordinated Universal Time)
2
- Invoking "/usr/bin/tar -c -f /home/runner/work/fgv/fgv/common/temp/build-cache/03c8b056281d9db0a97d8a6e25eea798a160d393-5dfd4ce4c834780c.temp -z --files-from=-"
1
+ Start time: Sun Aug 10 2025 14:32:18 GMT+0000 (Coordinated Universal Time)
2
+ Invoking "/usr/bin/tar -c -f /home/runner/work/fgv/fgv/common/temp/build-cache/352205c76acd8e247a1680af51abc95e0524eb82-1f631d7db013ea48.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.286s) ---- \n"}
9
- {"kind":"O","text":"-------------------- Finished (9.291s) --------------------\n"}
8
+ {"kind":"O","text":" ---- build finished (9.217s) ---- \n"}
9
+ {"kind":"O","text":"-------------------- Finished (9.221s) --------------------\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.286s) ----
9
- -------------------- Finished (9.291s) --------------------
8
+ ---- build finished (9.217s) ----
9
+ -------------------- Finished (9.221s) --------------------
@@ -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.286s) ---- \n"}
9
- {"kind":"O","text":"-------------------- Finished (9.291s) --------------------\n"}
8
+ {"kind":"O","text":" ---- build finished (9.217s) ---- \n"}
9
+ {"kind":"O","text":"-------------------- Finished (9.221s) --------------------\n"}
@@ -1,3 +1,3 @@
1
1
  {
2
- "nonCachedDurationMs": 9754.645378000001
2
+ "nonCachedDurationMs": 9703.163389999987
3
3
  }
package/README.md CHANGED
@@ -1,18 +1,512 @@
1
- # Name
2
- ### @fgv/ts-res-ui-components
1
+ # @fgv/ts-res-ui-components
3
2
 
4
- # Synopsis
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
- # Description
5
+ ## Overview
8
6
 
9
- # Example
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
- # Install:
12
- `npm install @fgv/ts-res-ui-components`
9
+ ## Warning
10
+ This packlet is largely AI written, and it shows.
13
11
 
14
- # Test:
15
- `npm test`
12
+ ## Features
16
13
 
17
- #License:
18
- MIT
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;AAEzD,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,CA6U9D,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
- const allowsContextList = selectedQualifierType?.configuration && selectedQualifierType.configuration.allowContextList;
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.toLowerCase().replace(/[^a-zA-Z0-9]/g, '');
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
- const enumValues = selectedQualifierType.configuration?.enumeratedValues;
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 !== undefined && (React.createElement("p", null,
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.caseSensitive ? 'Yes' : 'No')),
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.enumeratedValues.join(', '))))),
186
+ selectedQualifierType.configuration
187
+ .enumeratedValues.join(', '))))),
168
188
  selectedQualifierType.systemType === 'territory' && selectedQualifierType.configuration && (React.createElement(React.Fragment, null,
169
- selectedQualifierType.configuration.acceptLowercase !== undefined && (React.createElement("p", null,
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.acceptLowercase ? 'Yes' : 'No')),
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.allowedTerritories.join(', '))))))))),
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')))));