pict-section-recordset 1.19.0 → 1.21.0
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
|
@@ -395,6 +395,32 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
395
395
|
* @param {string|number} pThisID
|
|
396
396
|
* @return {Promise<Array<any>>}
|
|
397
397
|
*/
|
|
398
|
+
/**
|
|
399
|
+
* Does this join row reference a real other-side record? An unset key (undefined / null / '') is
|
|
400
|
+
* not an association; nor is 0 / '0' on an id-keyed side, where 0 is never a real auto-increment id
|
|
401
|
+
* (Meadow ids start at 1). Some hosts park sentinel rows in the join table — e.g. a "default set"
|
|
402
|
+
* keyed by IDProject = 0 that a SynthesizeDefaults hook reads — and those must not surface as broken
|
|
403
|
+
* "#0" rows in the editor. The 0-guard is gated to id-keyed sides (JoinField === IDField) so a
|
|
404
|
+
* name-keyed side, where '0' could pathologically be a real key, is never affected.
|
|
405
|
+
*
|
|
406
|
+
* @param {Record<string, any>} pJoin - a join row.
|
|
407
|
+
* @param {Record<string, any>} pOtherSide - the resolved other side.
|
|
408
|
+
* @return {Boolean}
|
|
409
|
+
*/
|
|
410
|
+
_joinReferencesRealRecord(pJoin, pOtherSide)
|
|
411
|
+
{
|
|
412
|
+
const tmpValue = pJoin[pOtherSide.JoinField];
|
|
413
|
+
if ((tmpValue === undefined) || (tmpValue === null) || (tmpValue === ''))
|
|
414
|
+
{
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
if ((pOtherSide.JoinField === pOtherSide.IDField) && ((tmpValue === 0) || (tmpValue === '0')))
|
|
418
|
+
{
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
|
|
398
424
|
async listAssociatedIDs(pAssociationHash, pThisRecordSetName, pThisID)
|
|
399
425
|
{
|
|
400
426
|
const tmpSides = this.resolveSides(pAssociationHash, pThisRecordSetName);
|
|
@@ -404,8 +430,8 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
404
430
|
}
|
|
405
431
|
const tmpJoins = await this.listJoinRecords(pAssociationHash, pThisRecordSetName, pThisID);
|
|
406
432
|
return tmpJoins
|
|
407
|
-
.
|
|
408
|
-
.
|
|
433
|
+
.filter((pJoin) => this._joinReferencesRealRecord(pJoin, tmpSides.otherSide))
|
|
434
|
+
.map((pJoin) => pJoin[tmpSides.otherSide.JoinField]);
|
|
409
435
|
}
|
|
410
436
|
|
|
411
437
|
/**
|
|
@@ -428,9 +454,10 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
428
454
|
}
|
|
429
455
|
const tmpJoinIDField = this.getJoinIDField(tmpSides.association);
|
|
430
456
|
const tmpJoins = await this.listJoinRecords(pAssociationHash, pThisRecordSetName, pThisID);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
457
|
+
// Drop sentinel / unset rows (e.g. a "default set" keyed by other-side id 0) so they don't
|
|
458
|
+
// surface as broken "#0" rows; everything downstream works off the real-association set.
|
|
459
|
+
const tmpRealJoins = tmpJoins.filter((pJoin) => this._joinReferencesRealRecord(pJoin, tmpSides.otherSide));
|
|
460
|
+
const tmpOtherIDs = tmpRealJoins.map((pJoin) => pJoin[tmpSides.otherSide.JoinField]);
|
|
434
461
|
|
|
435
462
|
let tmpByID = {};
|
|
436
463
|
if (tmpOtherIDs.length > 0)
|
|
@@ -455,8 +482,7 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
455
482
|
}
|
|
456
483
|
}
|
|
457
484
|
|
|
458
|
-
return
|
|
459
|
-
.filter((pJoin) => (pJoin[tmpSides.otherSide.JoinField] !== undefined && pJoin[tmpSides.otherSide.JoinField] !== null && pJoin[tmpSides.otherSide.JoinField] !== ''))
|
|
485
|
+
return tmpRealJoins
|
|
460
486
|
.map((pJoin) =>
|
|
461
487
|
{
|
|
462
488
|
const tmpOtherID = pJoin[tmpSides.otherSide.JoinField];
|
|
@@ -558,7 +584,11 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
558
584
|
{
|
|
559
585
|
return Promise.reject(new Error(`AssociationManager: cannot update join for [${pAssociationHash}].`));
|
|
560
586
|
}
|
|
561
|
-
|
|
587
|
+
// Minimal update — the join id + only the changed columns. The read-decorated join row can carry
|
|
588
|
+
// derived, non-column fields (e.g. a joined ModuleConfig) that the server rejects on write; Meadow
|
|
589
|
+
// updates only the fields provided, so a minimal record is both correct and safe.
|
|
590
|
+
const tmpJoinIDField = this.getJoinIDField(tmpAssociation);
|
|
591
|
+
const tmpRecord = Object.assign({ [tmpJoinIDField]: pJoinRecord[tmpJoinIDField] }, pValues || {});
|
|
562
592
|
const tmpEntityProvider = this._entityProvider(tmpAssociation.JoinURLPrefix);
|
|
563
593
|
return new Promise((resolve, reject) =>
|
|
564
594
|
{
|
|
@@ -568,6 +598,11 @@ class PictRecordSetAssociationManager extends libPictProvider
|
|
|
568
598
|
{
|
|
569
599
|
return reject(pError);
|
|
570
600
|
}
|
|
601
|
+
// Meadow returns a non-2xx error in the body (not the callback) — surface it as a failure.
|
|
602
|
+
if (pBody && pBody.ErrorCode)
|
|
603
|
+
{
|
|
604
|
+
return reject(new Error(`AssociationManager: join update rejected (ErrorCode ${pBody.ErrorCode}).`));
|
|
605
|
+
}
|
|
571
606
|
this._clearAssociationCache(tmpEntityProvider);
|
|
572
607
|
return resolve(pBody);
|
|
573
608
|
});
|
|
@@ -31,6 +31,12 @@ const _DEFAULT_CONFIGURATION_AssociationEditor = (
|
|
|
31
31
|
DefaultDestinationAddress: '#PRSP_AssociationEditor_Container',
|
|
32
32
|
DefaultTemplateRecordAddress: false,
|
|
33
33
|
|
|
34
|
+
// Show the "Add defaults" affordance when the association has a defaults synthesizer. Set false
|
|
35
|
+
// on an instance where synthesis is directionally meaningless (e.g. anchored on the type side of
|
|
36
|
+
// a "seed a project with default types" synthesizer) — the synthesizer stays registered for the
|
|
37
|
+
// side it applies to; this just hides the button on the side it does not.
|
|
38
|
+
AllowSynthesize: true,
|
|
39
|
+
|
|
34
40
|
AutoInitialize: false,
|
|
35
41
|
AutoInitializeOrdinal: 0,
|
|
36
42
|
AutoRender: false,
|
|
@@ -65,6 +71,16 @@ const _DEFAULT_CONFIGURATION_AssociationEditor = (
|
|
|
65
71
|
.prsp-assoc-remove:hover { color: var(--theme-color-status-error, #b62828); background: color-mix(in srgb, var(--theme-color-status-error, #b62828) 10%, transparent); }
|
|
66
72
|
.prsp-assoc-empty { padding: 0.7rem 0.2rem; color: var(--theme-color-text-muted, #6b7686); font-size: 0.88rem; font-style: italic; }
|
|
67
73
|
.prsp-assoc-note { color: var(--theme-color-status-error, #b62828); font-size: 0.86rem; }
|
|
74
|
+
.prsp-assoc-head-right { display: flex; align-items: center; gap: 0.6rem; }
|
|
75
|
+
.prsp-assoc-synth-btn { display: inline-flex; align-items: center; gap: 0.3rem; cursor: pointer; font: inherit; font-size: 0.76rem; font-weight: 600;
|
|
76
|
+
padding: 0.2rem 0.55rem; border-radius: 6px; border: 1px solid var(--theme-color-border-default, #d7dce3);
|
|
77
|
+
background: var(--theme-color-background-tertiary, #eceef2); color: var(--theme-color-text-secondary, #45596b); }
|
|
78
|
+
.prsp-assoc-synth-btn:hover { background: var(--theme-color-background-secondary, #e2e6ec); }
|
|
79
|
+
.prsp-assoc-row-cfg { flex: 0 0 auto; display: flex; align-items: center; gap: 0.65rem; }
|
|
80
|
+
.prsp-assoc-cfg { display: inline-flex; align-items: center; gap: 0.25rem; font-size: 0.78rem; color: var(--theme-color-text-secondary, #45596b); white-space: nowrap; cursor: default; }
|
|
81
|
+
.prsp-assoc-cfg input[type="checkbox"] { cursor: pointer; }
|
|
82
|
+
.prsp-assoc-cfg-num { width: 3.4rem; font: inherit; font-size: 0.8rem; padding: 0.1rem 0.3rem; border: 1px solid var(--theme-color-border-light, #e8ebf0); border-radius: 5px; }
|
|
83
|
+
.prsp-assoc-cfg-sel { font: inherit; font-size: 0.8rem; padding: 0.05rem 0.2rem; border: 1px solid var(--theme-color-border-light, #e8ebf0); border-radius: 5px; }
|
|
68
84
|
`,
|
|
69
85
|
CSSPriority: 500,
|
|
70
86
|
|
|
@@ -86,7 +102,10 @@ const _DEFAULT_CONFIGURATION_AssociationEditor = (
|
|
|
86
102
|
<div class="prsp-assoc-list">
|
|
87
103
|
<div class="prsp-assoc-list-head">
|
|
88
104
|
<span class="prsp-assoc-list-title">{~D:Record.ListLabel~}</span>
|
|
89
|
-
<span class="prsp-assoc-
|
|
105
|
+
<span class="prsp-assoc-head-right">
|
|
106
|
+
<span class="prsp-assoc-count">{~D:Record.Count~}</span>
|
|
107
|
+
{~TS:PRSP-AssociationEditor-SynthBtn:Record.SynthesizeSlot~}
|
|
108
|
+
</span>
|
|
90
109
|
</div>
|
|
91
110
|
{~TS:PRSP-AssociationEditor-Row:Record.Items~}
|
|
92
111
|
{~TS:PRSP-AssociationEditor-Empty:Record.EmptySlot~}
|
|
@@ -105,6 +124,7 @@ const _DEFAULT_CONFIGURATION_AssociationEditor = (
|
|
|
105
124
|
<div class="prsp-assoc-row">
|
|
106
125
|
<span class="prsp-assoc-row-name">{~D:Record.Display~}</span>
|
|
107
126
|
<span class="prsp-assoc-row-chips">{~TS:PRSP-AssociationEditor-Chip:Record.Chips~}</span>
|
|
127
|
+
<span class="prsp-assoc-row-cfg">{~TS:PRSP-AssociationEditor-EditField:Record.EditFields~}</span>
|
|
108
128
|
<span class="prsp-assoc-row-id">#{~D:Record.OtherID~}</span>
|
|
109
129
|
<button type="button" class="prsp-assoc-remove" title="Remove association" onclick="_Pict.views['{~D:Record.ViewHash~}'].removeItem({~D:Record.JoinID~})">{~I:Trash~}</button>
|
|
110
130
|
</div>
|
|
@@ -113,6 +133,36 @@ const _DEFAULT_CONFIGURATION_AssociationEditor = (
|
|
|
113
133
|
{
|
|
114
134
|
Hash: 'PRSP-AssociationEditor-Chip',
|
|
115
135
|
Template: /*html*/`<span class="prsp-assoc-chip">{~D:Record.Text~}</span>`
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
Hash: 'PRSP-AssociationEditor-SynthBtn',
|
|
139
|
+
Template: /*html*/`<button type="button" class="prsp-assoc-synth-btn" title="Add the default associations" onclick="_Pict.views['{~D:Record.ViewHash~}'].synthesizeDefaults()">{~I:Download~} Add defaults</button>`
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
// One editable per-join config control (a "rich" join's settings, e.g. Journal / Spreadsheet
|
|
143
|
+
// / Ordinal). The kind flags pick the input; onchange writes through to the join row.
|
|
144
|
+
Hash: 'PRSP-AssociationEditor-EditField',
|
|
145
|
+
Template: /*html*/`{~TIfAbs:PRSP-AssociationEditor-EditCheckbox:Record:Record.IsCheckbox^TRUE^~}{~TIfAbs:PRSP-AssociationEditor-EditNumber:Record:Record.IsNumber^TRUE^~}{~TIfAbs:PRSP-AssociationEditor-EditSelect:Record:Record.IsSelect^TRUE^~}{~TIfAbs:PRSP-AssociationEditor-EditText:Record:Record.IsText^TRUE^~}`
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
Hash: 'PRSP-AssociationEditor-EditCheckbox',
|
|
149
|
+
Template: /*html*/`<label class="prsp-assoc-cfg"><input type="checkbox" {~D:Record.CheckedAttr~} onchange="_Pict.views['{~D:Record.ViewHash~}'].updateField({~D:Record.JoinID~},'{~D:Record.Field~}',this.checked)" />{~D:Record.Label~}</label>`
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
Hash: 'PRSP-AssociationEditor-EditNumber',
|
|
153
|
+
Template: /*html*/`<label class="prsp-assoc-cfg">{~D:Record.Label~}<input type="number" class="prsp-assoc-cfg-num" value="{~D:Record.Value~}" onchange="_Pict.views['{~D:Record.ViewHash~}'].updateField({~D:Record.JoinID~},'{~D:Record.Field~}',this.value)" /></label>`
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
Hash: 'PRSP-AssociationEditor-EditSelect',
|
|
157
|
+
Template: /*html*/`<label class="prsp-assoc-cfg">{~D:Record.Label~}<select class="prsp-assoc-cfg-sel" onchange="_Pict.views['{~D:Record.ViewHash~}'].updateField({~D:Record.JoinID~},'{~D:Record.Field~}',this.value)">{~TS:PRSP-AssociationEditor-EditOption:Record.Options~}</select></label>`
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
Hash: 'PRSP-AssociationEditor-EditText',
|
|
161
|
+
Template: /*html*/`<label class="prsp-assoc-cfg">{~D:Record.Label~}<input type="text" class="prsp-assoc-cfg-sel" value="{~D:Record.Value~}" onchange="_Pict.views['{~D:Record.ViewHash~}'].updateField({~D:Record.JoinID~},'{~D:Record.Field~}',this.value)" /></label>`
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
Hash: 'PRSP-AssociationEditor-EditOption',
|
|
165
|
+
Template: /*html*/`<option value="{~D:Record.Value~}" {~D:Record.SelectedAttr~}>{~D:Record.Label~}</option>`
|
|
116
166
|
}
|
|
117
167
|
],
|
|
118
168
|
|
|
@@ -141,8 +191,10 @@ class viewRecordSetAssociationEditor extends libPictView
|
|
|
141
191
|
|
|
142
192
|
// The other-side ids currently associated (the live cull set the picker reads via a closure).
|
|
143
193
|
this._otherIDs = [];
|
|
144
|
-
// The current list items (so removeItem can find the exact join record by JoinID).
|
|
194
|
+
// The current list items (so removeItem/updateField can find the exact join record by JoinID).
|
|
145
195
|
this._lastItems = [];
|
|
196
|
+
// Guard so AutoSynthesizeWhenEmpty runs at most once per anchor (no re-synthesize loop).
|
|
197
|
+
this._autoSynthesizedFor = null;
|
|
146
198
|
}
|
|
147
199
|
|
|
148
200
|
/** @return {any} The association manager provider. */
|
|
@@ -178,12 +230,28 @@ class viewRecordSetAssociationEditor extends libPictView
|
|
|
178
230
|
if (tmpThisID !== undefined && tmpThisID !== null && tmpThisID !== '')
|
|
179
231
|
{
|
|
180
232
|
tmpItems = await this.manager.listAssociatedRecords(this.options.AssociationHash, this.options.ThisRecordSet, tmpThisID);
|
|
233
|
+
|
|
234
|
+
// Opt-in: synthesize the default associations the first time an empty anchor is opened.
|
|
235
|
+
const tmpAssociation = this.manager.getAssociation(this.options.AssociationHash);
|
|
236
|
+
if (tmpAssociation && tmpAssociation.AutoSynthesizeWhenEmpty && (tmpItems.length === 0)
|
|
237
|
+
&& this.manager.hasDefaultSynthesizer(this.options.AssociationHash) && (this._autoSynthesizedFor !== `${tmpThisID}`))
|
|
238
|
+
{
|
|
239
|
+
this._autoSynthesizedFor = `${tmpThisID}`;
|
|
240
|
+
const tmpSynth = await this.manager.synthesizeDefaults(this.options.AssociationHash, this.options.ThisRecordSet, tmpThisID);
|
|
241
|
+
if (tmpSynth && (tmpSynth.created > 0))
|
|
242
|
+
{
|
|
243
|
+
tmpItems = await this.manager.listAssociatedRecords(this.options.AssociationHash, this.options.ThisRecordSet, tmpThisID);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
181
246
|
}
|
|
182
247
|
|
|
183
|
-
//
|
|
248
|
+
// Per-join editable config controls (for "rich" joins) — empty array for a plain link.
|
|
249
|
+
const tmpEditableFields = this.manager.getJoinEditableFields(this.options.AssociationHash);
|
|
250
|
+
// Stamp the view hash + the editable controls on each row.
|
|
184
251
|
for (let i = 0; i < tmpItems.length; i++)
|
|
185
252
|
{
|
|
186
253
|
tmpItems[i].ViewHash = this.Hash;
|
|
254
|
+
tmpItems[i].EditFields = this._buildEditFields(tmpEditableFields, tmpItems[i]);
|
|
187
255
|
}
|
|
188
256
|
this._lastItems = tmpItems;
|
|
189
257
|
this._otherIDs = tmpItems.map((pItem) => pItem.OtherID);
|
|
@@ -203,6 +271,10 @@ class viewRecordSetAssociationEditor extends libPictView
|
|
|
203
271
|
// One-or-zero-element slot drives the empty-state line (TS parses inner tags; NE would not).
|
|
204
272
|
EmptySlot: (tmpItems.length === 0) ? [ { EmptyText: `No ${tmpOtherLabel} associated yet — use the search above to add some.` } ] : [],
|
|
205
273
|
PickerMissing: !tmpPickerPresent,
|
|
274
|
+
// The "Add defaults" button shows only when the host registered a defaults synthesizer AND this
|
|
275
|
+
// instance opts in (AllowSynthesize, default true) — an embedding anchored on the side the
|
|
276
|
+
// synthesizer does not apply to sets AllowSynthesize:false to hide it.
|
|
277
|
+
SynthesizeSlot: ((this.options.AllowSynthesize !== false) && this.manager.hasDefaultSynthesizer(this.options.AssociationHash)) ? [ { ViewHash: this.Hash } ] : [],
|
|
206
278
|
};
|
|
207
279
|
|
|
208
280
|
return new Promise((resolve) =>
|
|
@@ -351,6 +423,98 @@ class viewRecordSetAssociationEditor extends libPictView
|
|
|
351
423
|
return fRemove();
|
|
352
424
|
}
|
|
353
425
|
|
|
426
|
+
/**
|
|
427
|
+
* Shape an association's editable join-config fields into per-row render data (current value + the
|
|
428
|
+
* kind flags the row template dispatches on).
|
|
429
|
+
* @param {Array<Record<string, any>>} pFields - the association's JoinEditableFields.
|
|
430
|
+
* @param {Record<string, any>} pItem - one decorated association row (carries its JoinRecord).
|
|
431
|
+
* @return {Array<Record<string, any>>}
|
|
432
|
+
*/
|
|
433
|
+
_buildEditFields(pFields, pItem)
|
|
434
|
+
{
|
|
435
|
+
if (!Array.isArray(pFields) || pFields.length < 1)
|
|
436
|
+
{
|
|
437
|
+
return [];
|
|
438
|
+
}
|
|
439
|
+
const tmpJoin = pItem.JoinRecord || {};
|
|
440
|
+
return pFields.map((pField) =>
|
|
441
|
+
{
|
|
442
|
+
const tmpType = pField.Type || 'text';
|
|
443
|
+
const tmpRaw = tmpJoin[pField.Field];
|
|
444
|
+
return {
|
|
445
|
+
ViewHash: this.Hash,
|
|
446
|
+
JoinID: pItem.JoinID,
|
|
447
|
+
Field: pField.Field,
|
|
448
|
+
Label: pField.Label || pField.Field,
|
|
449
|
+
IsCheckbox: (tmpType === 'checkbox'),
|
|
450
|
+
IsNumber: (tmpType === 'number'),
|
|
451
|
+
IsSelect: (tmpType === 'select'),
|
|
452
|
+
IsText: (tmpType === 'text'),
|
|
453
|
+
CheckedAttr: ((tmpType === 'checkbox') && (tmpRaw === 1 || tmpRaw === true || tmpRaw === '1')) ? 'checked' : '',
|
|
454
|
+
Value: (tmpRaw === undefined || tmpRaw === null) ? '' : tmpRaw,
|
|
455
|
+
Options: (Array.isArray(pField.Options) ? pField.Options : []).map((pOption) =>
|
|
456
|
+
{
|
|
457
|
+
const tmpValue = (pOption && typeof pOption === 'object') ? pOption.Value : pOption;
|
|
458
|
+
return { Value: tmpValue, Label: (pOption && typeof pOption === 'object') ? (pOption.Label || pOption.Value) : pOption, SelectedAttr: (`${tmpRaw}` === `${tmpValue}`) ? 'selected' : '' };
|
|
459
|
+
}),
|
|
460
|
+
};
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Persist one per-join config change (a "rich" join's editable column). Writes through to the join
|
|
466
|
+
* row in place so focus is kept; reverts to the server state on failure.
|
|
467
|
+
* @param {string|number} pJoinID @param {string} pField @param {any} pValue
|
|
468
|
+
* @return {Promise<void>}
|
|
469
|
+
*/
|
|
470
|
+
async updateField(pJoinID, pField, pValue)
|
|
471
|
+
{
|
|
472
|
+
const tmpItem = this._lastItems.find((pItem) => String(pItem.JoinID) === String(pJoinID));
|
|
473
|
+
if (!tmpItem || !tmpItem.JoinRecord)
|
|
474
|
+
{
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
// Meadow booleans are 1/0.
|
|
478
|
+
const tmpValue = (pValue === true) ? 1 : ((pValue === false) ? 0 : pValue);
|
|
479
|
+
try
|
|
480
|
+
{
|
|
481
|
+
await this.manager.updateJoin(this.options.AssociationHash, tmpItem.JoinRecord, { [pField]: tmpValue });
|
|
482
|
+
tmpItem.JoinRecord[pField] = tmpValue;
|
|
483
|
+
}
|
|
484
|
+
catch (pError)
|
|
485
|
+
{
|
|
486
|
+
this.pict.log.error(`AssociationEditor [${this.Hash}]: failed to update join field ${pField}.`, pError);
|
|
487
|
+
this._toast('Could not save the change.', 'error');
|
|
488
|
+
await this.renderEditor();
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Seed the default associations for this anchor via the association's `SynthesizeDefaults` hook (the
|
|
494
|
+
* "Add defaults" button). PSRS dedupes + creates; the host decides what the defaults are. Reloads after.
|
|
495
|
+
* @return {Promise<void>}
|
|
496
|
+
*/
|
|
497
|
+
async synthesizeDefaults()
|
|
498
|
+
{
|
|
499
|
+
if (!this.manager.hasDefaultSynthesizer(this.options.AssociationHash))
|
|
500
|
+
{
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
let tmpResult = { created: 0, skipped: 0 };
|
|
504
|
+
try
|
|
505
|
+
{
|
|
506
|
+
tmpResult = await this.manager.synthesizeDefaults(this.options.AssociationHash, this.options.ThisRecordSet, this.options.ThisID);
|
|
507
|
+
}
|
|
508
|
+
catch (pError)
|
|
509
|
+
{
|
|
510
|
+
this.pict.log.error(`AssociationEditor [${this.Hash}]: synthesize defaults failed.`, pError);
|
|
511
|
+
this._toast('Could not add the defaults.', 'error');
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
this._toast((tmpResult.created > 0) ? `Added ${tmpResult.created} default${tmpResult.created === 1 ? '' : 's'}.` : 'No new defaults to add.', (tmpResult.created > 0) ? 'success' : 'info');
|
|
515
|
+
await this.renderEditor();
|
|
516
|
+
}
|
|
517
|
+
|
|
354
518
|
/**
|
|
355
519
|
* Non-blocking notification via the host modal's toast, when available.
|
|
356
520
|
* @param {string} pMessage @param {string} pType
|