pict-section-recordset 1.7.0 → 1.8.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pict-section-recordset",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "Pict dynamic record set management views",
5
5
  "main": "source/Pict-Section-RecordSet.js",
6
6
  "files": [
@@ -37,9 +37,9 @@
37
37
  "browser-env": "^3.3.0",
38
38
  "eslint": "^9.28.0",
39
39
  "jquery": "^3.7.1",
40
- "pict": "^1.0.372",
40
+ "pict": "^1.0.373",
41
41
  "pict-application": "^1.0.34",
42
- "pict-docuserve": "^1.4.4",
42
+ "pict-docuserve": "^1.4.19",
43
43
  "pict-service-commandlineutility": "^1.0.19",
44
44
  "quackage": "^1.3.0",
45
45
  "typescript": "^5.9.3"
@@ -48,7 +48,7 @@
48
48
  "fable-serviceproviderbase": "^3.0.19",
49
49
  "pict-provider": "^1.0.13",
50
50
  "pict-router": "^1.0.10",
51
- "pict-section-form": "^1.0.196",
51
+ "pict-section-form": "^1.1.9",
52
52
  "pict-template": "^1.0.15",
53
53
  "pict-view": "^1.0.68",
54
54
  "sinon": "^21.0.1"
@@ -262,7 +262,7 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
262
262
  }
263
263
  }
264
264
  }
265
- this.pict.EntityProvider.cacheConnectedEntityRecords(recordsReturn, IDFields, ['User', 'User'], false, () =>
265
+ this.pict.EntityProvider.cacheConnectedEntityRecords(recordsReturn, IDFields, ['User', 'User'], false, () =>
266
266
  {
267
267
  resolve({ Records: recordsReturn, Facets: { } });
268
268
  });
@@ -304,6 +304,17 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
304
304
  return new Promise((resolve, reject) =>
305
305
  {
306
306
  const [ tmpClauses, tmpExperience ] = this._prepareFilterState(tmpEntity, pOptions, 'Count');
307
+ // The record count is identical for every page of the same filter, yet the list re-counts on each
308
+ // pagination click. A COUNT is expensive on some database engines, so cache it keyed by the compiled
309
+ // filter signature: same filter -> serve the cached count (no request); changed filter -> re-count.
310
+ // Mutations through this provider clear the cache (see createRecord / updateRecord / deleteRecord).
311
+ const tmpCountCacheKey = `${ tmpEntity }::${ JSON.stringify(tmpClauses) }`;
312
+ if (this._RecordSetCountCache && this._RecordSetCountCache.Key === tmpCountCacheKey)
313
+ {
314
+ // Publish the cached count to the result address so downstream consumers behave identically to a fetch.
315
+ this.fable.manifest.setValueByHash(this.pict, tmpExperience.ResultDestinationAddress, this._RecordSetCountCache.Count);
316
+ return resolve({ Count: this._RecordSetCountCache.Count });
317
+ }
307
318
  if (this.options.FilterEndpointOverride)
308
319
  {
309
320
  // Call the filtering endpoint with the clauses and experience.
@@ -316,7 +327,9 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
316
327
  {
317
328
  return reject(error);
318
329
  }
319
- this.fable.manifest.setValueByHash(this.pict, tmpExperience.ResultDestinationAddress, result?.Count || 0);
330
+ const tmpCount = result?.Count || 0;
331
+ this.fable.manifest.setValueByHash(this.pict, tmpExperience.ResultDestinationAddress, tmpCount);
332
+ this._RecordSetCountCache = { Key: tmpCountCacheKey, Count: tmpCount };
320
333
  resolve(result);
321
334
  });
322
335
  return;
@@ -327,7 +340,9 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
327
340
  {
328
341
  return reject(pError);
329
342
  }
330
- resolve({ Count: this.pict.resolveStateFromAddress(tmpExperience.ResultDestinationAddress) });
343
+ const tmpCount = this.pict.resolveStateFromAddress(tmpExperience.ResultDestinationAddress);
344
+ this._RecordSetCountCache = { Key: tmpCountCacheKey, Count: tmpCount };
345
+ resolve({ Count: tmpCount });
331
346
  });
332
347
  });
333
348
  }
@@ -354,6 +369,8 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
354
369
  {
355
370
  return reject(error);
356
371
  }
372
+ // A new record changes the total; drop the cached count so the next render re-counts.
373
+ this._RecordSetCountCache = null;
357
374
  resolve(result);
358
375
  });
359
376
  });
@@ -381,6 +398,8 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
381
398
  {
382
399
  return reject(error);
383
400
  }
401
+ // An edit can move a record in or out of the active filter; drop the cached count to be safe.
402
+ this._RecordSetCountCache = null;
384
403
  resolve(result);
385
404
  });
386
405
  });
@@ -408,6 +427,8 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
408
427
  {
409
428
  return reject(error);
410
429
  }
430
+ // A delete changes the total; drop the cached count so the next render re-counts.
431
+ this._RecordSetCountCache = null;
411
432
  resolve(result);
412
433
  });
413
434
  });
@@ -477,11 +498,19 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
477
498
  const tmpFieldHumanName = this.getHumanReadableFieldName(pSchemaField);
478
499
  const isUserAuditField = ['CreatingIDUser', 'DeletingIDUser', 'UpdatingIDUser'].includes(pSchemaField);
479
500
  const customFilterClauses = this.options.Filters?.[pSchemaField];
480
- if (pSchemaField.startsWith('ID') || pSchemaField.startsWith('ParentID') || isUserAuditField || customFilterClauses)
501
+ // The entity's own identity column (AutoIdentity / AutoGUID) i.e. the primary key.
502
+ const isOwnIdentityField = pMeadowSchemaField && (pMeadowSchemaField.Type === 'AutoIdentity' || pMeadowSchemaField.Type === 'AutoGUID');
503
+ const isForeignKeyLike = pSchemaField.startsWith('ID') || pSchemaField.startsWith('ParentID') || isUserAuditField || customFilterClauses;
504
+ if (isForeignKeyLike)
481
505
  {
482
506
  for (const customField of Array.isArray(customFilterClauses) ? customFilterClauses : [customFilterClauses])
483
507
  {
484
- const remoteTableName = customField?.RemoteTable || pSchemaField.split('ID')[1];
508
+ // The table the picker pulls from: an explicit RemoteTable, else this
509
+ // recordset's declared Entity when the column is our own primary key (the PK
510
+ // references our own records), else the name peeled from the column for a
511
+ // plain foreign key. Peeling alone is the defect — a lake PK like
512
+ // `IDC182_HMA_MixDesign` peels to a table name that is not the entity name.
513
+ const remoteTableName = customField?.RemoteTable || (isOwnIdentityField ? this.options.Entity : pSchemaField.split('ID')[1]);
485
514
  const fieldName = this.getHumanReadableFieldName(pSchemaField);
486
515
  tmpFieldFilterClauses.push(Object.assign(
487
516
  {
@@ -491,6 +520,7 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
491
520
  "ExternalRecordDisplayTemplate": remoteTableName === 'User' ? '{~D:Record.Data.NameFirst~} {~D:Record.Data.NameLast~}' : '{~D:Record.Name~}',
492
521
  "CoreConnectionColumn": pSchemaField,
493
522
  "RemoteTable": `${ remoteTableName }`,
523
+ "URLPrefix": this.options.URLPrefix,
494
524
  "JoinExternalConnectionColumn": `ID${ remoteTableName }`,
495
525
  "JoinInternalConnectionColumn": pSchemaField,
496
526
  'DisplayName': `Selected Records`,
@@ -500,7 +530,10 @@ class MeadowEndpointsRecordSetProvider extends libRecordSetProviderBase
500
530
  }, customField));
501
531
  }
502
532
  }
503
- else
533
+ // The primary key is also a plain integer key, so it ALSO falls through to the
534
+ // Exact / In Range clauses below. A real foreign key gets only the picker; ordinary
535
+ // columns are matched by type.
536
+ if (!isForeignKeyLike || isOwnIdentityField)
504
537
  {
505
538
  switch (tmpFieldType)
506
539
  {
@@ -186,6 +186,12 @@ class RecordSetMetacontroller extends libFableServiceProviderBase
186
186
  return false;
187
187
  }
188
188
 
189
+ // Normalize the whole-row-click opt-in to a STRICT boolean. The action-cell and column-header
190
+ // templates branch on it with TIfAbs's TRUE/FALSE operators, which test `=== true` / `=== false`.
191
+ // An absent (undefined) or loose ('true') value would satisfy neither branch, leaving the actions
192
+ // column empty — so coerce it to a real true/false here, once, for every render path.
193
+ pRecordSetConfiguration.RowClickOpensRecord = (pRecordSetConfiguration.RowClickOpensRecord === true || pRecordSetConfiguration.RowClickOpensRecord === 'true');
194
+
189
195
  let tmpExtraColumnHeaderTemplateHash = `RecordSet-List-ExtraColumnHeader-${pRecordSetConfiguration.RecordSet}`;
190
196
  pRecordSetConfiguration.RecordSetListExtraColumnsHeaderTemplateHash = tmpExtraColumnHeaderTemplateHash;
191
197
  let tmpExtraColumnRowTemplateHash = `RecordSet-List-ExtraColumnRow-${pRecordSetConfiguration.RecordSet}`;
@@ -52,11 +52,15 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
52
52
  border: 1px solid var(--theme-color-border-default, #d7dce3); background: transparent; color: var(--theme-color-text-secondary, #45505f); }
53
53
  .prsp-filters-btn-text:hover { background: var(--theme-color-background-tertiary, #eceef2); }
54
54
 
55
- /* Slide-out drawer (CSS grid trick: 0fr -> 1fr animates height). */
55
+ /* Slide-out drawer (CSS grid trick: 0fr -> 1fr animates height). The inner is a *pure* clip box; all the
56
+ visible card chrome lives on .prsp-filters-drawer-card inside it. That way, on collapse the chrome shrinks
57
+ *with* the animating height instead of vanishing instantly — the old rule gated padding/border/background
58
+ on .drawer-open, so closing the drawer stripped the card in one frame while the height still animated for
59
+ 0.18s, making the quick-filters bar look like it popped out of the panel before disappearing. */
56
60
  .prsp-filters-drawer { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.18s ease; }
57
61
  .prsp-filters.drawer-open .prsp-filters-drawer { grid-template-rows: 1fr; }
58
62
  .prsp-filters-drawer-inner { overflow: hidden; min-height: 0; }
59
- .prsp-filters.drawer-open .prsp-filters-drawer-inner { margin-top: 0.6rem; padding: 0.95rem 1.1rem;
63
+ .prsp-filters-drawer-card { margin-top: 0.6rem; padding: 0.95rem 1.1rem;
60
64
  border: 1px solid var(--theme-color-border-light, #e8ebf0); border-radius: 10px; background: var(--theme-color-background-panel, #fff); }
61
65
  .prsp-filters-add { position: relative; margin: 0.4rem 0 0.2rem; }
62
66
  .prsp-addfilter-trigger { display: inline-flex; align-items: center; gap: 0.35rem; }
@@ -133,16 +137,20 @@ const _DEFAULT_CONFIGURATION_SUBSET_Filter =
133
137
  </form>
134
138
  <div class="prsp-filters-drawer" id="PRSP_Filter_Drawer">
135
139
  <div class="prsp-filters-drawer-inner">
136
- <!-- Quick Filters: a curated, one-interaction bar painted post-render into this host
137
- (empty when the record set has no quick filters CSS :empty hides it). -->
138
- <div class="prsp-quickfilters" id="PRSP_QuickFilters"></div>
139
- <div id="PRSP_Filter_Instances" class="prsp-filters-clauses" onkeydown="if (event.key === 'Enter' &amp;&amp; !event.target.closest('.pps')) { event.preventDefault(); _Pict.views['PRSP-Filters'].handleSearch(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}'); }">
140
- {~FIV:Record~}
141
- </div>
142
- {~T:PRSP-SUBSET-Filters-Template-AddFilter-Fieldset~}
143
- <div class="prsp-filters-footer">
144
- {~T:PRSP-SUBSET-Filters-Template-ManageFilters-Fieldset~}
145
- {~T:PRSP-SUBSET-Filters-Template-DrawerActions-Fieldset~}
140
+ <!-- The card carries all the panel chrome and lives *inside* the clip box, so it collapses
141
+ together with the animating height (see .prsp-filters-drawer-card in CSS). -->
142
+ <div class="prsp-filters-drawer-card">
143
+ <!-- Quick Filters: a curated, one-interaction bar painted post-render into this host
144
+ (empty when the record set has no quick filters → CSS :empty hides it). -->
145
+ <div class="prsp-quickfilters" id="PRSP_QuickFilters"></div>
146
+ <div id="PRSP_Filter_Instances" class="prsp-filters-clauses" onkeydown="if (event.key === 'Enter' &amp;&amp; !event.target.closest('.pps')) { event.preventDefault(); _Pict.views['PRSP-Filters'].handleSearch(event, '{~D:Record.RecordSet~}', '{~D:Record.ViewContext~}'); }">
147
+ {~FIV:Record~}
148
+ </div>
149
+ {~T:PRSP-SUBSET-Filters-Template-AddFilter-Fieldset~}
150
+ <div class="prsp-filters-footer">
151
+ {~T:PRSP-SUBSET-Filters-Template-ManageFilters-Fieldset~}
152
+ {~T:PRSP-SUBSET-Filters-Template-DrawerActions-Fieldset~}
153
+ </div>
146
154
  </div>
147
155
  </div>
148
156
  </div>
@@ -230,6 +230,7 @@ class ViewRecordSetSUBSETFilterEntityReferenceBase extends ViewRecordSetSUBSETFi
230
230
  Destination: pOffset > 0 ? `${this.getInformaryAddressPrefix()}${pClauseInformaryAddress}.SearchResultsAppend` : `${this.getInformaryAddressPrefix()}${pClauseInformaryAddress}.SearchResults`,
231
231
  RecordStartCursor: pOffset,
232
232
  PageSize: this.options.PageSize,
233
+ URLPrefix: tmpClause.URLPrefix,
233
234
  }
234
235
  ],
235
236
  () =>
@@ -22,7 +22,36 @@ const _DEFAULT_CONFIGURATION_List_RecordListEntry = (
22
22
  AutoSolveWithApp: false,
23
23
  AutoSolveOrdinal: 0,
24
24
 
25
- CSS: false,
25
+ // Record-list row chrome. Host apps brand it via --theme-color-* tokens; the hardcoded values
26
+ // are fallbacks.
27
+ CSS: /*css*/`
28
+ /* Vertically center every cell's content so the text lines up with the action control (it was
29
+ top-aligned, which read as off next to the centered action cell). */
30
+ #PRSP_List_Table td { vertical-align: middle; }
31
+
32
+ /* Row-as-button (opt-in via the RowClickOpensRecord record-set config flag): clicking anywhere in the
33
+ row — except the action cell — follows the record's default link. */
34
+ .prsp-list-row.prsp-row-clickable { cursor: pointer; }
35
+ .prsp-list-row.prsp-row-clickable:hover { background: var(--theme-color-background-hover, var(--theme-color-background-tertiary, #f1f3f7)); }
36
+
37
+ /* Per-row action menu: a subtle three-dot trigger, revealed on row hover, that opens a small popover
38
+ of the record's links (View, …). Clicks inside it never trigger the row navigation. */
39
+ .prsp-row-actions { white-space: nowrap; text-align: right; width: 1%; }
40
+ .prsp-row-actions-wrap { position: relative; display: inline-block; }
41
+ .prsp-row-actions-trigger { display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
42
+ border: none; background: transparent; line-height: 1; padding: 0.2rem 0.5rem; border-radius: 6px; font-size: 1.05rem;
43
+ color: var(--theme-color-text-muted, #6b7686); opacity: 0; transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease; }
44
+ .prsp-list-row:hover .prsp-row-actions-trigger, .prsp-row-actions-wrap.open .prsp-row-actions-trigger { opacity: 1; }
45
+ .prsp-row-actions-trigger:hover { background: var(--theme-color-background-tertiary, #eceef2); color: var(--theme-color-text-primary, #1f2733); }
46
+ .prsp-row-actions-menu { position: absolute; right: 0; top: calc(100% + 2px); z-index: 30; min-width: 9rem; display: none;
47
+ background: var(--theme-color-background-panel, #fff); border: 1px solid var(--theme-color-border-default, #d7dce3);
48
+ border-radius: 9px; box-shadow: 0 10px 26px rgba(17, 24, 39, 0.16); padding: 0.25rem; text-align: left; }
49
+ .prsp-row-actions-wrap:hover .prsp-row-actions-menu, .prsp-row-actions-wrap.open .prsp-row-actions-menu { display: block; }
50
+ .prsp-row-actions-menu ul { list-style: none; margin: 0; padding: 0; }
51
+ .prsp-row-actions-menu li > a { display: block; padding: 0.4rem 0.65rem; border-radius: 6px; text-decoration: none; white-space: nowrap;
52
+ color: var(--theme-color-text-primary, #1f2733); font-size: 0.9rem; }
53
+ .prsp-row-actions-menu li > a:hover { background: var(--theme-color-background-tertiary, #eceef2); }
54
+ `,
26
55
  CSSPriority: 500,
27
56
 
28
57
  Templates:
@@ -36,61 +65,84 @@ const _DEFAULT_CONFIGURATION_List_RecordListEntry = (
36
65
  },
37
66
  {
38
67
  Hash: 'PRSP-List-RecordListEntry-Template-Row',
68
+ // The whole row is a click target for the record's default link — opt-in via the
69
+ // RowClickOpensRecord record-set config flag (which adds prsp-row-clickable). The onclick
70
+ // only fires when that class is present, skips clicks inside the action cell, and follows
71
+ // the default link (data-prsp-default) only — so a host-overridden action cell with no
72
+ // default link makes the row click a harmless no-op.
39
73
  Template: /*html*/`
40
- <!-- DefaultPackage pict view template: [PRSP-List-RecordListEntry-Template] -->
41
- <tr>
42
- {~TSWP:PRSP-List-RecordListEntry-Template-Row-Cell:Record.Payload.TableCells:Record.Data~}
43
- {~T:PRSP-List-RecordListHeader-Template-Extra-Row~}
44
- {~T:PRSP-List-RecordListAction-Template-Cell~}
45
- </tr>
46
- <!-- DefaultPackage end view template: [PRSP-List-RecordListEntry-Template] -->
74
+ <!-- DefaultPackage pict view template: [PRSP-List-RecordListEntry-Template] -->
75
+ <tr class="prsp-list-row{~NE:Record.Payload.RecordSetConfiguration.RowClickOpensRecord^ prsp-row-clickable~}" onclick="if (this.classList.contains('prsp-row-clickable') &amp;&amp; !event.target.closest('.prsp-row-actions')) { var tmpDefaultLink = this.querySelector('.prsp-row-actions a[data-prsp-default]'); if (tmpDefaultLink) { tmpDefaultLink.click(); } }">
76
+ {~TSWP:PRSP-List-RecordListEntry-Template-Row-Cell:Record.Payload.TableCells:Record.Data~}
77
+ {~T:PRSP-List-RecordListHeader-Template-Extra-Row~}
78
+ {~T:PRSP-List-RecordListAction-Template-Cell~}
79
+ </tr>
80
+ <!-- DefaultPackage end view template: [PRSP-List-RecordListEntry-Template] -->
47
81
  `
48
- //
49
-
50
82
  },
51
83
  {
52
84
  Hash: 'PRSP-List-RecordListHeader-Template-Extra-Row',
53
85
  Template: /*html*/`
54
- <!-- DefaultPackage pict view template: [PRSP-List-RecordListHeader-Template-Extra-Row] -->
55
- {~TBR:Record.Payload.RecordSetConfiguration.RecordSetListExtraColumnRowTemplateHash~}
56
- <!-- DefaultPackage end view template: [PRSP-List-RecordListHeader-Extra-Row] -->
86
+ <!-- DefaultPackage pict view template: [PRSP-List-RecordListHeader-Template-Extra-Row] -->
87
+ {~TBR:Record.Payload.RecordSetConfiguration.RecordSetListExtraColumnRowTemplateHash~}
88
+ <!-- DefaultPackage end view template: [PRSP-List-RecordListHeader-Extra-Row] -->
57
89
  `
58
- // {~TBR:Record.Payload.RecordSetConfiguration.RecordSetListExtraColumnRowTemplateHash~}
59
-
60
90
  },
61
91
  {
62
92
  Hash: 'PRSP-List-RecordListEntry-Template-Row-Cell',
63
93
  Template: /*html*/`
64
- <td style="border-bottom: 1px solid var(--theme-color-border-default, #ccc); padding: 5px;">
65
- {~TBDA:Record.Data.PictDashboard.ValueTemplate~}
66
- </td>
67
- `
94
+ <td style="border-bottom: 1px solid var(--theme-color-border-default, #ccc); padding: 5px;">
95
+ {~TBDA:Record.Data.PictDashboard.ValueTemplate~}
96
+ </td>
97
+ `
68
98
  },
69
99
  {
70
100
  Hash: 'PRSP-List-RecordListEntry-Template-Entry-Cell-Datum',
71
101
  Template: /*html*/`
72
- <!-- DefaultPackage pict view template: [PRSP-List-RecordListEntry-Template-Entry-Cell-Datum] -->
73
- `
102
+ <!-- DefaultPackage pict view template: [PRSP-List-RecordListEntry-Template-Entry-Cell-Datum] -->
103
+ `
74
104
  },
75
105
  {
106
+ // The action cell renders the three-dot hover menu when RowClickOpensRecord is set, else the
107
+ // original always-visible link list. (Hosts such as LIMS override this whole template; those
108
+ // lists keep their custom actions and — without the flag — their original row behavior.)
76
109
  Hash: 'PRSP-List-RecordListAction-Template-Cell',
77
110
  // FIXME: Needs a better way of getting the appropriate link templates in (likely requiring piping the RecordSet to the link manager)
78
111
  Template: /*html*/`
79
- <td style="border-bottom: 1px solid var(--theme-color-border-default, #ccc); padding: 5px;">
80
- <ul>
81
- {~TSWP:PRSP-List-RecordListAction-Template-Cell-Datum:Pict.providers.RecordSetLinkManager.linkTemplates:Record~}
82
- </ul>
83
- </td>
84
- `
112
+ <td class="prsp-row-actions" style="border-bottom: 1px solid var(--theme-color-border-default, #ccc); padding: 5px;">
113
+ {~TIfAbs:PRSP-List-RecordListAction-Menu:Record:Record.Payload.RecordSetConfiguration.RowClickOpensRecord^TRUE^~}
114
+ {~TIfAbs:PRSP-List-RecordListAction-List:Record:Record.Payload.RecordSetConfiguration.RowClickOpensRecord^FALSE^~}
115
+ </td>
116
+ `
117
+ },
118
+ {
119
+ Hash: 'PRSP-List-RecordListAction-Menu',
120
+ Template: /*html*/`
121
+ <div class="prsp-row-actions-wrap">
122
+ <button type="button" class="prsp-row-actions-trigger" aria-label="Actions" title="Actions" onclick="event.stopPropagation(); this.parentNode.classList.toggle('open');">{~I:More~}</button>
123
+ <div class="prsp-row-actions-menu">
124
+ <ul>
125
+ {~TSWP:PRSP-List-RecordListAction-Template-Cell-Datum:Pict.providers.RecordSetLinkManager.linkTemplates:Record~}
126
+ </ul>
127
+ </div>
128
+ </div>
129
+ `
130
+ },
131
+ {
132
+ Hash: 'PRSP-List-RecordListAction-List',
133
+ Template: /*html*/`
134
+ <ul>
135
+ {~TSWP:PRSP-List-RecordListAction-Template-Cell-Datum:Pict.providers.RecordSetLinkManager.linkTemplates:Record~}
136
+ </ul>
137
+ `
85
138
  },
86
139
  {
87
140
  Hash: 'PRSP-List-RecordListAction-Template-Cell-Datum',
88
141
  // These use the new TemplateByReference template expression, which uses the values in these addresses to lookup the template hash then renders those template with the current Record state.
89
142
  // This is part one of refactoring to include metatemplate resolution ase a core behavior pict itself rather than a pict-section-form capability
90
143
  Template: /*html*/`
91
- <li><a href="{~TBR:Record.Data.URL~}">{~TBR:Record.Data.Name~}</a></li>
92
- `
93
-
144
+ <li><a {~NE:Record.Data.Default^data-prsp-default="1"~} href="{~TBR:Record.Data.URL~}">{~TBR:Record.Data.Name~}</a></li>
145
+ `
94
146
  },
95
147
  ],
96
148
 
@@ -119,4 +171,3 @@ class viewRecordSetListRecordListEntry extends libPictView
119
171
  module.exports = viewRecordSetListRecordListEntry;
120
172
 
121
173
  module.exports.default_configuration = _DEFAULT_CONFIGURATION_List_RecordListEntry;
122
-
@@ -61,11 +61,11 @@ const _DEFAULT_CONFIGURATION_List_RecordListHeader = (
61
61
  },
62
62
  {
63
63
  Hash: 'PRSP-List-RecordListActions-Template-Header',
64
+ // Unlabeled, shrink-to-fit actions column: width:1% + nowrap collapses it to the width of the
65
+ // row's action control (the hover "⋯" trigger), and the other columns absorb the slack.
64
66
  Template: /*html*/`
65
67
  <!-- DefaultPackage pict view template: [PRSP-List-RecordListActions-Template-Header] -->
66
- <th style="border-bottom: 1px solid var(--theme-color-border-default, #ccc); padding: 5px; background-color: var(--theme-color-background-tertiary, #f2f2f2); color: var(--theme-color-text-primary, #333);">
67
- Actions
68
- </th>
68
+ <th style="border-bottom: 1px solid var(--theme-color-border-default, #ccc); padding: 5px; width: 1%; white-space: nowrap; background-color: var(--theme-color-background-tertiary, #f2f2f2);"></th>
69
69
  <!-- DefaultPackage end view template: [PRSP-List-RecordListActions-Template-Header] -->
70
70
  `
71
71
  },