pict-section-recordset 1.0.43 → 1.0.45

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 (39) hide show
  1. package/example_applications/simple_entity/Simple-RecordSet-Application.js +199 -3
  2. package/example_applications/simple_entity/html/index.html +6 -0
  3. package/package.json +2 -2
  4. package/source/providers/Filter-Data-Provider.js +371 -0
  5. package/source/providers/RecordSet-Link-Manager.js +9 -1
  6. package/source/providers/RecordSet-RecordProvider-Base.js +1 -1
  7. package/source/providers/RecordSet-RecordProvider-MeadowEndpoints.js +76 -43
  8. package/source/services/RecordsSet-MetaController.js +48 -1
  9. package/source/templates/Pict-Template-FilterInstanceViews.js +11 -7
  10. package/source/views/RecordSet-Filters.js +78 -45
  11. package/source/views/filters/RecordSet-Filter-Base.js +19 -1
  12. package/source/views/filters/RecordSet-Filter-ExternalJoinSelectedValue.js +270 -0
  13. package/source/views/filters/RecordSet-Filter-ExternalJoinSelectedValueList.js +277 -0
  14. package/source/views/filters/RecordSet-Filter-InternalJoinSelectedValue.js +270 -0
  15. package/source/views/filters/RecordSet-Filter-InternalJoinSelectedValueList.js +277 -0
  16. package/source/views/filters/index.js +4 -0
  17. package/types/providers/Filter-Data-Provider.d.ts +120 -0
  18. package/types/providers/Filter-Data-Provider.d.ts.map +1 -0
  19. package/types/providers/RecordSet-Link-Manager.d.ts +10 -5
  20. package/types/providers/RecordSet-Link-Manager.d.ts.map +1 -1
  21. package/types/providers/RecordSet-RecordProvider-MeadowEndpoints.d.ts +1 -1
  22. package/types/providers/RecordSet-RecordProvider-MeadowEndpoints.d.ts.map +1 -1
  23. package/types/services/RecordsSet-MetaController.d.ts +36 -7
  24. package/types/services/RecordsSet-MetaController.d.ts.map +1 -1
  25. package/types/templates/Pict-Template-FilterInstanceViews.d.ts +1 -1
  26. package/types/templates/Pict-Template-FilterInstanceViews.d.ts.map +1 -1
  27. package/types/views/RecordSet-Filters.d.ts +10 -0
  28. package/types/views/RecordSet-Filters.d.ts.map +1 -1
  29. package/types/views/filters/RecordSet-Filter-Base.d.ts +10 -0
  30. package/types/views/filters/RecordSet-Filter-Base.d.ts.map +1 -1
  31. package/types/views/filters/RecordSet-Filter-ExternalJoinSelectedValue.d.ts +24 -0
  32. package/types/views/filters/RecordSet-Filter-ExternalJoinSelectedValue.d.ts.map +1 -0
  33. package/types/views/filters/RecordSet-Filter-ExternalJoinSelectedValueList.d.ts +24 -0
  34. package/types/views/filters/RecordSet-Filter-ExternalJoinSelectedValueList.d.ts.map +1 -0
  35. package/types/views/filters/RecordSet-Filter-InternalJoinSelectedValue.d.ts +24 -0
  36. package/types/views/filters/RecordSet-Filter-InternalJoinSelectedValue.d.ts.map +1 -0
  37. package/types/views/filters/RecordSet-Filter-InternalJoinSelectedValueList.d.ts +24 -0
  38. package/types/views/filters/RecordSet-Filter-InternalJoinSelectedValueList.d.ts.map +1 -0
  39. package/types/views/filters/index.d.ts +4 -0
@@ -381,45 +381,76 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
381
381
  }
382
382
  tmpFieldFilterClauses = [];
383
383
  const tmpFieldHumanName = this._getHumanReadableFieldName(pSchemaField);
384
- switch (tmpFieldType)
384
+ const isUserAuditField = ['CreatingIDUser', 'DeletingIDUser', 'UpdatingIDUser'].includes(pSchemaField);
385
+ const customFilterClauses = this.options.Filters?.[pSchemaField];
386
+ if (pSchemaField.startsWith('ID') || isUserAuditField || customFilterClauses)
385
387
  {
386
- case 'string':
387
- case 'autoguid':
388
- tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Exact`, DisplayName: `${tmpFieldHumanName} Exact Match`, Type: 'StringMatch', FilterByColumn: pSchemaField, ExactMatch: true, Ordinal: tmpFieldFilterClauses.length + 1 });
389
- tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Fuzzy`, DisplayName: `${tmpFieldHumanName} Partial Match`, Type: 'StringMatch', FilterByColumn: pSchemaField, ExactMatch: false , Ordinal: tmpFieldFilterClauses.length + 1 });
390
- tmpRangeClause = { FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Range`, DisplayName: `${tmpFieldHumanName} in Range`, Type: 'StringRange', FilterByColumn: pSchemaField , Ordinal: tmpFieldFilterClauses.length + 1 };
391
- tmpRangeClause.MinimumLabel = `Minimum ${tmpFieldHumanName}`;
392
- tmpRangeClause.MaximumLabel = `Maximum ${tmpFieldHumanName}`;
393
- tmpFieldFilterClauses.push(tmpRangeClause);
394
- break;
395
- case 'date':
396
- case 'datetime':
397
- case 'createdate':
398
- case 'updatedate':
399
- tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Exact`, DisplayName: `${tmpFieldHumanName} Exact Match`, Type: 'DateMatch', FilterByColumn: pSchemaField, ExactMatch: true , Ordinal: tmpFieldFilterClauses.length + 1 });
400
- tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Fuzzy`, DisplayName: `${tmpFieldHumanName} Partial Match`, Type: 'DateMatch', FilterByColumn: pSchemaField, ExactMatch: false , Ordinal: tmpFieldFilterClauses.length + 1 });
401
- tmpRangeClause = { FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Range`, DisplayName: `${tmpFieldHumanName} in Range`, Type: 'DateRange', FilterByColumn: pSchemaField , Ordinal: tmpFieldFilterClauses.length + 1 };
402
- tmpRangeClause.MinimumLabel = `Minimum ${tmpFieldHumanName}`;
403
- tmpRangeClause.MaximumLabel = `Maximum ${tmpFieldHumanName}`;
404
- tmpFieldFilterClauses.push(tmpRangeClause);
405
- break;
406
- case 'boolean': //TODO: we didn't add filters for this - they are just numeric but it's weird for the user, maybe we should add views for this that account for the difference
407
- case 'deleted':
408
- case 'integer':
409
- case 'decimal':
410
- case 'autoidentity':
411
- case 'createiduser':
412
- case 'updateiduser':
413
- case 'deleteiduser':
414
- tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Exact`, DisplayName: `${tmpFieldHumanName} Exact Match`, Type: 'NumericMatch', FilterByColumn: pSchemaField, ExactMatch: true , Ordinal: tmpFieldFilterClauses.length + 1 });
415
- tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Fuzzy`, DisplayName: `${tmpFieldHumanName} Partial Match`, Type: 'NumericMatch', FilterByColumn: pSchemaField, ExactMatch: false , Ordinal: tmpFieldFilterClauses.length + 1 });
416
- tmpRangeClause = { FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Range`, DisplayName: `${tmpFieldHumanName} in Range`, Type: 'NumericRange', FilterByColumn: pSchemaField , Ordinal: tmpFieldFilterClauses.length + 1 };
417
- tmpRangeClause.MinimumLabel = `Minimum ${tmpFieldHumanName}`;
418
- tmpRangeClause.MaximumLabel = `Maximum ${tmpFieldHumanName}`;
419
- tmpFieldFilterClauses.push(tmpRangeClause);
420
- break;
421
- default:
422
- this.pict.log.warn(`Unsupported field type ${pColumn.type} for field ${pSchemaField}`, { Schema: pColumn });
388
+ for (const customField of Array.isArray(customFilterClauses) ? customFilterClauses : [customFilterClauses])
389
+ {
390
+ const remoteTableName = customField?.RemoteTable || pSchemaField.split('ID')[1];
391
+ const fieldName = isUserAuditField ? this._getHumanReadableFieldName(pSchemaField) : this._getHumanReadableEntityName(remoteTableName);
392
+ tmpFieldFilterClauses.push(Object.assign(
393
+ {
394
+ "Label": `${ fieldName }`,
395
+ "Type": "InternalJoinSelectedValueList",
396
+ "ExternalFilterByColumns": remoteTableName === 'User' ? [ 'NameFirst', 'NameLast' ] : [ 'Name' ],
397
+
398
+ "ExternalRecordDisplayTemplate": remoteTableName === 'User' ? '{~D:Record.Data.NameFirst~} {~D:Record.Data.NameLast~}' : '{~D:Record.Name~}',
399
+
400
+ "CoreConnectionColumn": pSchemaField,
401
+
402
+ "RemoteTable": `${ remoteTableName }`,
403
+ "JoinExternalConnectionColumn": `ID${ remoteTableName }`,
404
+ "JoinInternalConnectionColumn": `ID${ remoteTableName }`,
405
+ 'DisplayName': `Selected Records`,
406
+ 'Ordinal': tmpFieldFilterClauses.length + 1,
407
+ 'FilterKey': pSchemaField,
408
+ 'ClauseKey': `${pSchemaField}_Selected`
409
+ }, customField));
410
+ }
411
+ }
412
+ else
413
+ {
414
+ switch (tmpFieldType)
415
+ {
416
+ case 'string':
417
+ case 'autoguid':
418
+ tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Exact`, DisplayName: `Exact Match`, Type: 'StringMatch', FilterByColumn: pSchemaField, ExactMatch: true, Ordinal: tmpFieldFilterClauses.length + 1 });
419
+ tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Fuzzy`, DisplayName: `Partial Match`, Type: 'StringMatch', FilterByColumn: pSchemaField, ExactMatch: false , Ordinal: tmpFieldFilterClauses.length + 1 });
420
+ tmpRangeClause = { FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Range`, DisplayName: `In Range`, Type: 'StringRange', FilterByColumn: pSchemaField , Ordinal: tmpFieldFilterClauses.length + 1 };
421
+ tmpRangeClause.MinimumLabel = `Minimum ${tmpFieldHumanName}`;
422
+ tmpRangeClause.MaximumLabel = `Maximum ${tmpFieldHumanName}`;
423
+ tmpFieldFilterClauses.push(tmpRangeClause);
424
+ break;
425
+ case 'date':
426
+ case 'datetime':
427
+ case 'createdate':
428
+ case 'updatedate':
429
+ tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Exact`, DisplayName: `Exact Match`, Type: 'DateMatch', FilterByColumn: pSchemaField, ExactMatch: true , Ordinal: tmpFieldFilterClauses.length + 1 });
430
+ tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Fuzzy`, DisplayName: `Partial Match`, Type: 'DateMatch', FilterByColumn: pSchemaField, ExactMatch: false , Ordinal: tmpFieldFilterClauses.length + 1 });
431
+ tmpRangeClause = { FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Range`, DisplayName: `In Range`, Type: 'DateRange', FilterByColumn: pSchemaField , Ordinal: tmpFieldFilterClauses.length + 1 };
432
+ tmpRangeClause.MinimumLabel = `Minimum ${tmpFieldHumanName}`;
433
+ tmpRangeClause.MaximumLabel = `Maximum ${tmpFieldHumanName}`;
434
+ tmpFieldFilterClauses.push(tmpRangeClause);
435
+ break;
436
+ case 'boolean': //TODO: we didn't add filters for this - they are just numeric but it's weird for the user, maybe we should add views for this that account for the difference
437
+ case 'deleted':
438
+ case 'integer':
439
+ case 'decimal':
440
+ case 'autoidentity':
441
+ case 'createiduser':
442
+ case 'updateiduser':
443
+ case 'deleteiduser':
444
+ tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Exact`, DisplayName: `Exact Match`, Type: 'NumericMatch', FilterByColumn: pSchemaField, ExactMatch: true , Ordinal: tmpFieldFilterClauses.length + 1 });
445
+ tmpFieldFilterClauses.push({ FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Match_Fuzzy`, DisplayName: `Partial Match`, Type: 'NumericMatch', FilterByColumn: pSchemaField, ExactMatch: false , Ordinal: tmpFieldFilterClauses.length + 1 });
446
+ tmpRangeClause = { FilterKey: pSchemaField, ClauseKey: `${pSchemaField}_Range`, DisplayName: `In Range`, Type: 'NumericRange', FilterByColumn: pSchemaField , Ordinal: tmpFieldFilterClauses.length + 1 };
447
+ tmpRangeClause.MinimumLabel = `Minimum ${tmpFieldHumanName}`;
448
+ tmpRangeClause.MaximumLabel = `Maximum ${tmpFieldHumanName}`;
449
+ tmpFieldFilterClauses.push(tmpRangeClause);
450
+ break;
451
+ default:
452
+ this.pict.log.warn(`Unsupported field type ${pColumn.type} for field ${pSchemaField}`, { Schema: pColumn });
453
+ }
423
454
  }
424
455
  }
425
456
  return tmpFieldFilterClauses;
@@ -432,6 +463,8 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
432
463
  {
433
464
  super.onInitializeAsync((pError) =>
434
465
  {
466
+ //FIXME: load the most recent experience from local storage using the other provider
467
+ // how do we control the initialization order of the providers?
435
468
  if (this.options.FilterExperiences && typeof this.options.FilterExperiences === 'object' && !Array.isArray(this.options.FilterExperiences))
436
469
  {
437
470
  this.pict.Bundle._ActiveFilterState = this.pict.Bundle._ActiveFilterState || {};
@@ -521,7 +554,7 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
521
554
  * @param {string} pEntity - The schema field name.
522
555
  * @return {string} - The human-readable name for the entity.
523
556
  */
524
- _getHumanReadbleEntityName(pEntity)
557
+ _getHumanReadableEntityName(pEntity)
525
558
  {
526
559
  return pEntity.replace(/([a-z])([A-Z])/g, '$1 $2'); // Add space before capital letters
527
560
  }
@@ -538,11 +571,11 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
538
571
  }
539
572
  if (pSchemaField === `ID${this.options.Entity}`)
540
573
  {
541
- return `${this._getHumanReadbleEntityName(this.options.Entity)} Unique Database ID`;
574
+ return `${this._getHumanReadableEntityName(this.options.Entity)} Unique Database ID`;
542
575
  }
543
576
  if (pSchemaField === `GUID${this.options.Entity}`)
544
577
  {
545
- return `${this._getHumanReadbleEntityName(this.options.Entity)} Unique Identifier`;
578
+ return `${this._getHumanReadableEntityName(this.options.Entity)} Unique Identifier`;
546
579
  }
547
580
  if (pSchemaField === 'CreatingIDUser')
548
581
  {
@@ -637,7 +670,7 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
637
670
  }
638
671
  if (!tmpFieldFilterSchema.HelpText)
639
672
  {
640
- tmpFieldFilterSchema.HelpText = `Filter by ${tmpFieldFilterSchema.DisplayName} for the ${this._getHumanReadbleEntityName(this.options.Entity)} entity.`;
673
+ tmpFieldFilterSchema.HelpText = `Filter by ${tmpFieldFilterSchema.DisplayName} for the ${this._getHumanReadableEntityName(this.options.Entity)} entity.`;
641
674
  }
642
675
  if (tmpFieldFilterSchema.Ordinal == null)
643
676
  {
@@ -694,7 +727,7 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
694
727
  }
695
728
  if (!tmpFieldFilterSchema.HelpText)
696
729
  {
697
- tmpFieldFilterSchema.HelpText = tmpFilterClause.HelpText || `Filter by ${tmpFieldFilterSchema.DisplayName} for the ${this._getHumanReadbleEntityName(this.options.Entity)} entity.`;
730
+ tmpFieldFilterSchema.HelpText = tmpFilterClause.HelpText || `Filter by ${tmpFieldFilterSchema.DisplayName} for the ${this._getHumanReadableEntityName(this.options.Entity)} entity.`;
698
731
  }
699
732
  if (tmpFieldFilterSchema.Ordinal == null)
700
733
  {
@@ -96,6 +96,8 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
96
96
  */
97
97
 
98
98
  /**
99
+ * @param {string} pRecordSet - The RecordSet name to get the configuration for.
100
+ *
99
101
  * @return {Record<string, any>} - The registered configuration for the RecordSet
100
102
  */
101
103
  getRecordSetConfiguration(pRecordSet)
@@ -103,6 +105,9 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
103
105
  return this.recordSetProviderConfigurations[pRecordSet];
104
106
  }
105
107
 
108
+ /**
109
+ * @param {Record<string, any>} pRecordSetConfiguration - The RecordSet configuration to load.
110
+ */
106
111
  loadRecordSetConfiguration(pRecordSetConfiguration)
107
112
  {
108
113
  if (typeof pRecordSetConfiguration !== 'object')
@@ -208,6 +213,9 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
208
213
  return tmpProvider;
209
214
  }
210
215
 
216
+ /**
217
+ * @param {Array<Record<string, any>>} pRecordSetConfigurationArray - An array of RecordSet configurations to load.
218
+ */
211
219
  loadRecordSetConfigurationArray(pRecordSetConfigurationArray)
212
220
  {
213
221
  if (!Array.isArray(pRecordSetConfigurationArray))
@@ -229,6 +237,11 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
229
237
  }
230
238
  }
231
239
 
240
+ /**
241
+ * @param {Record<string, any> | string} pRecordSet - The RecordSet configuration or hash to load dynamically.
242
+ * @param {string} [pEntity] - (optional) The Entity type to use (defaults to the RecordSet name if not provided).
243
+ * @param {string} [pDefaultFilter] - (optional) The default filter to use.
244
+ */
232
245
  loadRecordSetDynamically(pRecordSet, pEntity, pDefaultFilter)
233
246
  {
234
247
  if (typeof(pRecordSet) === 'object')
@@ -238,6 +251,11 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
238
251
 
239
252
  return this.fable.providers[tmpRecordSetProviderHash].initializeAsync((pError) =>
240
253
  {
254
+ if (pError)
255
+ {
256
+ this.log.error(`RecordSet [${pRecordSet.RecordSet}] dynamically loaded with error: ${pError.message || pError}`, { Stack: pError.stack });
257
+ return;
258
+ }
241
259
  this.log.trace(`RecordSet [${pRecordSet.RecordSet} dynamically loaded.`);
242
260
  })
243
261
  }
@@ -264,6 +282,11 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
264
282
  return this.fable.providers[tmpRecordSetProviderHash].initializeAsync(
265
283
  (pError) =>
266
284
  {
285
+ if (pError)
286
+ {
287
+ this.log.error(`RecordSet [${tmpRecordSet}] dynamically loaded with error: ${pError.message || pError}`, { Stack: pError.stack });
288
+ return;
289
+ }
267
290
  this.log.trace(`RecordSet [${tmpRecordSetConfiguration.RecordSet} dynamically loaded.`);
268
291
  });
269
292
  }
@@ -272,6 +295,9 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
272
295
  return false;
273
296
  }
274
297
 
298
+ /**
299
+ * @param {Record<string, any>} pRoutePayload - The route payload containing the RecordSet and optional Entity and DefaultFilter.
300
+ */
275
301
  handleLoadDynamicRecordSetRoute(pRoutePayload)
276
302
  {
277
303
  if (typeof(pRoutePayload) != 'object')
@@ -287,6 +313,9 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
287
313
  return this.loadRecordSetDynamically(tmpRecordSet, tmpEntity, tmpDefaultFilter);
288
314
  }
289
315
 
316
+ /**
317
+ * @param {import('pict-router')} pPictRouter - The Pict Router to add the routes to.
318
+ */
290
319
  addRoutes(pPictRouter)
291
320
  {
292
321
  pPictRouter.addRoute('/PSRS/:RecordSet/LoadDynamic', this.handleLoadDynamicRecordSetRoute.bind(this));
@@ -295,6 +324,9 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
295
324
  return true;
296
325
  }
297
326
 
327
+ /**
328
+ * @param {string} pCapability - The capability to check for.
329
+ */
298
330
  async checkSession(pCapability)
299
331
  {
300
332
  for (const sessionProvider of this.sessionProviders)
@@ -307,6 +339,13 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
307
339
  return true;
308
340
  }
309
341
 
342
+ /**
343
+ * @param {string} pNameTemplate - The name template for the record link.
344
+ * @param {string} pURLTemplate - The URL template for the record link.
345
+ * @param {boolean} pDefault - Whether this is a default link template.
346
+ *
347
+ * @return {Record<string, any>} - The link template object that was added.
348
+ */
310
349
  addRecordLinkTemplate(pNameTemplate, pURLTemplate, pDefault)
311
350
  {
312
351
  return this.fable.providers.RecordSetLinkManager.addRecordLinkTemplate(pNameTemplate, pURLTemplate, pDefault);
@@ -344,7 +383,15 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
344
383
  {
345
384
  for (const tmpFilterKey of Object.keys(this.fable.settings.Filters))
346
385
  {
347
- this.pict.providers.FilterManager.addFilter(tmpFilterKey, this.fable.settings.Filters[tmpFilterKey]);
386
+ const filterArray = Array.isArray(this.fable.settings.Filters[tmpFilterKey]) ? this.fable.settings.Filters[tmpFilterKey] : [this.fable.settings.Filters[tmpFilterKey]];
387
+ for (let x = 0; x < filterArray.length; x++)
388
+ {
389
+ if (!filterArray[x].DisplayName)
390
+ {
391
+ filterArray[x].DisplayName = filterArray[x].Label || tmpFilterKey;
392
+ }
393
+ this.pict.providers.FilterManager.addFilter(filterArray[x].FilterKey || `${ tmpFilterKey }${ x ? `_${ x }` : '' }`, filterArray[x]);
394
+ }
348
395
  }
349
396
  }
350
397
 
@@ -30,17 +30,22 @@ class PictTemplateFilterInstanceViewInstruction extends libPictTemplate
30
30
  }
31
31
  }
32
32
 
33
- _getViewForFilterClauses(pClauses)
33
+ _getViewForFilterClause(pClause)
34
34
  {
35
- const tmpViewHash =`PRSP-FilterType-${pClauses.Type}`;
35
+ let tmpViewHash = `PRSP-FilterType-${pClause.Type}`;
36
+ const tmpCustomViewHash = `${tmpViewHash}-${pClause.CustomFilterViewHash}`;
37
+ if (tmpCustomViewHash in this.pict.views)
38
+ {
39
+ tmpViewHash = tmpCustomViewHash;
40
+ }
36
41
  /** @type {import('../views/filters/RecordSet-Filter-Base.js')} */
37
42
  let tmpView = this.pict.views[tmpViewHash];
38
43
  if (!tmpView)
39
44
  {
40
- const tmpViewPrototype = libFilterViews[pClauses.Type] || libFilterViews.Base;
45
+ const tmpViewPrototype = libFilterViews[pClause.Type] || libFilterViews.Base;
41
46
  if (!tmpViewPrototype)
42
47
  {
43
- this.pict.log.error(`Pict: Filter Instance Views Template Render: No view prototype found for filter type [${pClauses.Type}]`);
48
+ this.pict.log.error(`Pict: Filter Instance Views Template Render: No view prototype found for filter type [${pClause.Type}]`);
44
49
  return null;
45
50
  }
46
51
  //FIXME: is this safe? will this view get rendered at other times?
@@ -103,11 +108,10 @@ class PictTemplateFilterInstanceViewInstruction extends libPictTemplate
103
108
  const tmpClauses = this.pict.Bundle._ActiveFilterState?.[pRecord.RecordSet]?.FilterClauses || [];
104
109
  //FIXME: lookup by hash instead?
105
110
  for (let i = 0; i < tmpClauses.length; i++)
106
- //for (const tmpClauses of this.pict.Bundle._ActiveFilterState?.[pRecord.RecordSet]?.FilterClauses || [])
107
111
  {
108
112
  const tmpClause = tmpClauses[i];
109
113
  /** @type {import('../views/filters/RecordSet-Filter-Base.js')} */
110
- const tmpView = this._getViewForFilterClauses(tmpClause);
114
+ const tmpView = this._getViewForFilterClause(tmpClause);
111
115
  if (!tmpView)
112
116
  {
113
117
  continue;
@@ -182,7 +186,7 @@ class PictTemplateFilterInstanceViewInstruction extends libPictTemplate
182
186
  {
183
187
  const tmpClause = tmpClauses[i];
184
188
  /** @type {import('../views/filters/RecordSet-Filter-Base.js')} */
185
- const tmpView = this._getViewForFilterClauses(tmpClause);
189
+ const tmpView = this._getViewForFilterClause(tmpClause);
186
190
  if (!tmpView)
187
191
  {
188
192
  continue;
@@ -148,61 +148,61 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
148
148
  //FIXME: export this from PSF?
149
149
  const libPictViewDynamicForm = require('pict-section-form/source/views/Pict-View-DynamicForm.js');
150
150
 
151
- class ViewRecordSetSUBSETFilters extends libPictView
151
+ const DynamicInputViewSectionDefinition = (
152
152
  {
153
- constructor(pFable, pOptions, pServiceHash)
154
- {
155
- let tmpOptions = Object.assign({}, _DEFAULT_CONFIGURATION_SUBSET_Filter, pOptions);
156
- super(pFable, tmpOptions, pServiceHash);
157
- /** @type {import('fable') & import('pict') & { PictSectionRecordSet: import('../Pict-Section-RecordSet.js') }} */
158
- this.pict;
153
+ "Hash": "PSRSDynamicInputs",
154
+ "Name": "Custom Dynamic Inputs",
155
+ "ViewHash": "PSRSFilterProxyView",
159
156
 
160
- const tmpDynamicInputViewSection = (
161
- {
162
- "Hash": "PSRSDynamicInputs",
163
- "Name": "Custom Dynamic Inputs",
164
- "ViewHash": "PSRSFilterProxyView",
157
+ "AutoMarshalDataOnSolve": true,
158
+ "IncludeInMetatemplateSectionGeneration": false,
165
159
 
166
- "AutoMarshalDataOnSolve": true,
167
- "IncludeInMetatemplateSectionGeneration": false,
168
-
169
- "Manifests":
160
+ "Manifests":
161
+ {
162
+ "Section":
163
+ {
164
+ "Scope": "PSRSDynamic",
165
+ "Sections":
166
+ [
167
+ {
168
+ "Hash": "PSRSDynamicInputs",
169
+ "Name": "Dynamic Inputs"
170
+ }
171
+ ],
172
+ "Descriptors":
170
173
  {
171
- "Section":
174
+ "PSRS.DynamicInputPlaceholder":
172
175
  {
173
- "Scope": "PSRSDynamic",
174
- "Sections":
175
- [
176
- {
177
- "Hash": "PSRSDynamicInputs",
178
- "Name": "Dynamic Inputs"
179
- }
180
- ],
181
- "Descriptors":
176
+ "Name": "DynamicInputPlaceholder",
177
+ "Hash": "DynamicInputPlaceholder",
178
+ "DataType": "String",
179
+ "Macro":
182
180
  {
183
- "PSRS.DynamicInputPlaceholder":
184
- {
185
- "Name": "DynamicInputPlaceholder",
186
- "Hash": "DynamicInputPlaceholder",
187
- "DataType": "String",
188
- "Macro":
189
- {
190
- "HTMLSelector": ""
191
- },
192
- "PictForm":
193
- {
194
- "Section": "PSRSDynamicInputs"
195
- }
196
- }
181
+ "HTMLSelector": ""
182
+ },
183
+ "PictForm":
184
+ {
185
+ "Section": "PSRSDynamicInputs"
197
186
  }
198
187
  }
199
188
  }
200
- });
201
- if (!this.pict.views[tmpDynamicInputViewSection.ViewHash])
189
+ }
190
+ }
191
+ });
192
+ class ViewRecordSetSUBSETFilters extends libPictView
193
+ {
194
+ constructor(pFable, pOptions, pServiceHash)
195
+ {
196
+ let tmpOptions = Object.assign({}, _DEFAULT_CONFIGURATION_SUBSET_Filter, pOptions);
197
+ super(pFable, tmpOptions, pServiceHash);
198
+ /** @type {import('fable') & import('pict') & { PictSectionRecordSet: import('../Pict-Section-RecordSet.js') }} */
199
+ this.pict;
200
+
201
+ if (!this.pict.views[DynamicInputViewSectionDefinition.ViewHash])
202
202
  {
203
- const tmpViewConfiguration = Object.assign({}, tmpDynamicInputViewSection);
203
+ const tmpViewConfiguration = Object.assign({}, DynamicInputViewSectionDefinition);
204
204
  this.pict.addView(tmpViewConfiguration.ViewHash, tmpViewConfiguration, libPictViewDynamicForm);
205
- this.pict.views[tmpDynamicInputViewSection.ViewHash].viewMarshalDestination = 'Bundle';
205
+ this.pict.views[DynamicInputViewSectionDefinition.ViewHash].viewMarshalDestination = 'Bundle';
206
206
  }
207
207
  for (const tmpView of Object.values(require('./filters')))
208
208
  {
@@ -221,6 +221,25 @@ class ViewRecordSetSUBSETFilters extends libPictView
221
221
  }
222
222
  }
223
223
 
224
+ /**
225
+ * @return {string} - The marshalling prefix configured for filters. Usually 'Bundle.'
226
+ */
227
+ getInformaryAddressPrefix()
228
+ {
229
+ return `${this.pict.views[DynamicInputViewSectionDefinition.ViewHash].viewMarshalDestination}.`;
230
+ }
231
+
232
+ /**
233
+ * @param {string} pAddress - The address of the informary to get the value from.
234
+ *
235
+ * @return {any} - The value at the given address, using the informary marshalling prefix.
236
+ */
237
+ getInformaryScopedValue(pAddress)
238
+ {
239
+ const tmpCompositeAddress = `${this.getInformaryAddressPrefix()}${pAddress}`;
240
+ return this.pict.resolveStateFromAddress(tmpCompositeAddress);
241
+ }
242
+
224
243
  //NOTE: two methods below copied from the pict-section-form metacontroller
225
244
  //TODO: consider subclassing the dynamic view to somehow mark these as filter views so we can only operate on those views
226
245
 
@@ -315,6 +334,7 @@ class ViewRecordSetSUBSETFilters extends libPictView
315
334
  {
316
335
  tmpURL = tmpURL.replace(/\/FilteredTo\//, '');
317
336
  }
337
+ this.render(undefined, undefined, { RecordSet: pRecordSet, ViewContext: pViewContext });
318
338
  this.serializeFilterExperience(this.pict.Bundle._ActiveFilterState[pRecordSet]?.FilterClauses).then((pFilterExperienceSerialized) =>
319
339
  {
320
340
  if (pFilterExperienceSerialized)
@@ -342,6 +362,11 @@ class ViewRecordSetSUBSETFilters extends libPictView
342
362
  {
343
363
  delete tmpClause.Value;
344
364
  delete tmpClause.Values;
365
+ delete tmpClause.SearchInputValue;
366
+ delete tmpClause.SelectedValues;
367
+ delete tmpClause.SearchResults;
368
+ delete tmpClause.SearchResultsOffset;
369
+ delete tmpClause.LoadMoreEnabled;
345
370
  }
346
371
  }
347
372
  this.performSearch(pRecordSet, pViewContext);
@@ -428,7 +453,15 @@ class ViewRecordSetSUBSETFilters extends libPictView
428
453
  {
429
454
  return '';
430
455
  }
431
- return this.encode(await this.compress(JSON.stringify(pExperience)));
456
+ const tmpExperience = JSON.parse(JSON.stringify(pExperience));
457
+ for (const tmpClause of tmpExperience)
458
+ {
459
+ //FIXME: avoiding saving search results to the URL but this pattern isn't ideal
460
+ delete tmpClause.SearchResults;
461
+ delete tmpClause.SearchResultsOffset;
462
+ delete tmpClause.LoadMoreEnabled;
463
+ }
464
+ return this.encode(await this.compress(JSON.stringify(tmpExperience)));
432
465
  }
433
466
 
434
467
  /**
@@ -41,7 +41,7 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
41
41
  Hash: 'PRSP-Filter-Base-Template',
42
42
  Template: /*html*/`
43
43
  <!-- DefaultPackage pict view template: [PRSP-Filter-Base-Template] -->
44
- <div>
44
+ <div id="PRSP_Filter_Container_{~D:Record.Hash~}">
45
45
  <button type="button"
46
46
  onclick="_Pict.views['PRSP-Filters'].removeFilter(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}', '{~D:Record.Hash~}');"
47
47
  >
@@ -97,6 +97,24 @@ class ViewRecordSetSUBSETFilterBase extends libPictView
97
97
  {
98
98
  return 'PRSP-Filter-Base-Form';
99
99
  }
100
+
101
+ /**
102
+ * @return {string} - The prefix for the informary address.
103
+ */
104
+ getInformaryAddressPrefix()
105
+ {
106
+ return this.pict.views['PRSP-Filters'].getInformaryAddressPrefix();
107
+ }
108
+
109
+ /**
110
+ * @param {string} pInformaryAddress - The address of the informary to get the value from.
111
+ *
112
+ * @return {any} - The value at the informary address.
113
+ */
114
+ getInformaryScopedValue(pInformaryAddress)
115
+ {
116
+ return this.pict.views['PRSP-Filters'].getInformaryScopedValue(pInformaryAddress);
117
+ }
100
118
  }
101
119
 
102
120
  module.exports = ViewRecordSetSUBSETFilterBase;