@datagrok/hit-triage 1.3.7 → 1.3.8
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/CHANGELOG.md +4 -0
- package/README_HD.md +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/package.json +1 -1
- package/src/app/accordeons/new-hit-design-campaign-accordeon.ts +5 -1
- package/src/app/hit-design-app.ts +27 -4
- package/src/app/hit-design-views/info-view.ts +5 -3
- package/src/app/pepti-hit-app.ts +1 -1
- package/src/app/types.ts +1 -0
- package/src/app/utils.ts +71 -1
package/package.json
CHANGED
|
@@ -10,6 +10,7 @@ import {getNewVid} from '../utils/calculate-single-cell';
|
|
|
10
10
|
type NewHitDesignCampaignRes = {
|
|
11
11
|
df: DG.DataFrame,
|
|
12
12
|
campaignProps: {[key: string]: any}
|
|
13
|
+
name: string,
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
type HitDesignCampaignAccordeon = {
|
|
@@ -50,12 +51,15 @@ export function newHitDesignCampaignAccordeon(template: HitDesignTemplate, pepti
|
|
|
50
51
|
{name: field.name, type: CampaignFieldTypes[field.type as keyof typeof CampaignFieldTypes],
|
|
51
52
|
nullable: !field.required, ...(field.semtype ? {semType: field.semtype} : {})}));
|
|
52
53
|
const campaignPropsObject: {[key: string]: any} = {};
|
|
54
|
+
const campaignNameInput = ui.input.string('Campaing Name', {tooltipText: 'New campaign name. If empty, campaign code will be used.'});
|
|
53
55
|
const campaignPropsForm = ui.input.form(campaignPropsObject, campaignProps);
|
|
56
|
+
campaignPropsForm.prepend(campaignNameInput.root);
|
|
54
57
|
campaignPropsForm.classList.remove('ui-form');
|
|
55
58
|
const form = ui.div([
|
|
56
59
|
...(template.campaignFields?.length > 0 ? []: []),
|
|
57
60
|
campaignPropsForm,
|
|
58
61
|
]);
|
|
62
|
+
|
|
59
63
|
const buttonsDiv = ui.buttonsInput([]); // div for create and cancel buttons
|
|
60
64
|
form.appendChild(buttonsDiv);
|
|
61
65
|
const okPromise = new Promise<NewHitDesignCampaignRes>((resolve) => {
|
|
@@ -69,7 +73,7 @@ export function newHitDesignCampaignAccordeon(template: HitDesignTemplate, pepti
|
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
|
-
resolve({df, campaignProps: campaignPropsObject});
|
|
76
|
+
resolve({df, campaignProps: campaignPropsObject, name: campaignNameInput.value});
|
|
73
77
|
});
|
|
74
78
|
buttonsDiv.appendChild(startCampaignButton);
|
|
75
79
|
});
|
|
@@ -10,7 +10,7 @@ import {CampaignIdKey, CampaignJsonName, CampaignTableName,
|
|
|
10
10
|
import {calculateColumns, calculateSingleCellValues, getNewVid} from './utils/calculate-single-cell';
|
|
11
11
|
import '../../css/hit-triage.css';
|
|
12
12
|
import {_package} from '../package';
|
|
13
|
-
import {addBreadCrumbsToRibbons, checkRibbonsHaveSubmit, modifyUrl, toFormatedDateString} from './utils';
|
|
13
|
+
import {addBreadCrumbsToRibbons, checkRibbonsHaveSubmit, editableTableField, modifyUrl, toFormatedDateString} from './utils';
|
|
14
14
|
import {HitDesignSubmitView} from './hit-design-views/submit-view';
|
|
15
15
|
import {getTilesViewDialog} from './hit-design-views/tiles-view';
|
|
16
16
|
import {HitAppBase} from './hit-app-base';
|
|
@@ -174,6 +174,11 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
174
174
|
|
|
175
175
|
get designViewName(): string {return this._designViewName;}
|
|
176
176
|
|
|
177
|
+
set designViewName(name: string) {
|
|
178
|
+
this._designViewName = name;
|
|
179
|
+
this._designView && (this._designView.name = name);
|
|
180
|
+
}
|
|
181
|
+
|
|
177
182
|
get molColName() {
|
|
178
183
|
return this._molColName ??= this.dataFrame?.columns.bySemType(DG.SEMTYPE.MOLECULE)?.name ?? HitDesignMolColName;
|
|
179
184
|
}
|
|
@@ -337,7 +342,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
337
342
|
getTilesViewDialog(this, () => this._designView ?? null);
|
|
338
343
|
});
|
|
339
344
|
|
|
340
|
-
const submitButton = ui.bigButton('Submit', () => {
|
|
345
|
+
const submitButton = ui.bigButton('Submit...', () => {
|
|
341
346
|
const dialogContent = this._submitView?.render();
|
|
342
347
|
if (dialogContent) {
|
|
343
348
|
const dlg = ui.dialog('Submit');
|
|
@@ -385,10 +390,11 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
385
390
|
}
|
|
386
391
|
|
|
387
392
|
protected getDesignView(): DG.TableView {
|
|
393
|
+
this._designView && this._designView.close();
|
|
388
394
|
const subs: Subscription[] = [];
|
|
389
395
|
const isNew = this.dataFrame!.col(this.molColName)?.toList().every((m) => !m && m === '');
|
|
390
396
|
const view = grok.shell.addTableView(this.dataFrame!);
|
|
391
|
-
this._designViewName = this.campaign?.name ?? this._designViewName;
|
|
397
|
+
this._designViewName = this.campaign?.friendlyName ?? this.campaign?.name ?? this._designViewName;
|
|
392
398
|
view.name = this._designViewName;
|
|
393
399
|
|
|
394
400
|
view._onAdded();
|
|
@@ -635,6 +641,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
635
641
|
newPathInput.addOptions(cancelButton);
|
|
636
642
|
newPathInput.addOptions(saveButton);
|
|
637
643
|
pathDiv.appendChild(newPathInput.root);
|
|
644
|
+
newPathInput.input.focus();
|
|
638
645
|
}, 'Edit file path');
|
|
639
646
|
editIcon.style.marginLeft = '5px';
|
|
640
647
|
const folderPath = getFolderPath();
|
|
@@ -644,7 +651,21 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
644
651
|
return pathDiv;
|
|
645
652
|
};
|
|
646
653
|
|
|
654
|
+
const campaignName = this.campaign?.friendlyName ?? this.campaign?.name ?? this.campaignId!;
|
|
655
|
+
const campaignNameField = editableTableField(ui.divText(campaignName), {
|
|
656
|
+
tooltip: 'Edit Campaign Name',
|
|
657
|
+
nullable: false,
|
|
658
|
+
onOk: async (a) => {
|
|
659
|
+
this.campaign!.friendlyName = a!;
|
|
660
|
+
await this.saveCampaign(true);
|
|
661
|
+
},
|
|
662
|
+
validator: async (a) => !!a?.trim() ? null : 'Campaign name can not be empty',
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
|
|
647
666
|
return {
|
|
667
|
+
'Name': campaignNameField,
|
|
668
|
+
'Code': this.campaignId ?? this._campaign?.name,
|
|
648
669
|
'Template': this.template?.name ?? 'Molecules',
|
|
649
670
|
'File Path': getPathEditor(),
|
|
650
671
|
...(this.campaign?.authorUserFriendlyName ? {'Author': this.campaign.authorUserFriendlyName} : {}),
|
|
@@ -657,7 +678,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
657
678
|
};
|
|
658
679
|
}
|
|
659
680
|
|
|
660
|
-
async saveCampaign(notify = true, isCreating = false): Promise<HitDesignCampaign> {
|
|
681
|
+
async saveCampaign(notify = true, isCreating = false, customProps?: Partial<HitDesignCampaign>): Promise<HitDesignCampaign> {
|
|
661
682
|
const campaignId = this.campaignId!;
|
|
662
683
|
const templateName = this.template!.name;
|
|
663
684
|
const enrichedDf = this.dataFrame!;
|
|
@@ -689,6 +710,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
689
710
|
const authorName = authorUserId ? this.campaign?.authorUserFriendlyName ?? (await grok.dapi.users.find(authorUserId))?.friendlyName : undefined;
|
|
690
711
|
const campaign: HitDesignCampaign = {
|
|
691
712
|
name: campaignName,
|
|
713
|
+
friendlyName: this.campaign?.friendlyName ?? customProps?.friendlyName ?? campaignName,
|
|
692
714
|
templateName,
|
|
693
715
|
status: this.campaign?.status ?? 'In Progress',
|
|
694
716
|
createDate: this.campaign?.createDate ?? toFormatedDateString(new Date()),
|
|
@@ -745,6 +767,7 @@ export class HitDesignApp<T extends HitDesignTemplate = HitDesignTemplate> exten
|
|
|
745
767
|
notify && grok.shell.info('Campaign saved successfully.');
|
|
746
768
|
!notify && isCreating && grok.shell.info('Campaign created successfully.');
|
|
747
769
|
this.campaign = campaign;
|
|
770
|
+
this.designViewName = campaign.friendlyName ?? campaign.name;
|
|
748
771
|
return campaign;
|
|
749
772
|
}
|
|
750
773
|
}
|
|
@@ -255,7 +255,8 @@ export class HitDesignInfoView
|
|
|
255
255
|
};
|
|
256
256
|
|
|
257
257
|
const table = ui.table(Object.values(grouppedCampaigns).flat(), (info) =>
|
|
258
|
-
([ui.link(info.name, () => this.setCampaign(info.name), 'Continue Campaign', ''),
|
|
258
|
+
([ui.link(info.friendlyName ?? info.name, () => this.setCampaign(info.name), 'Continue Campaign', ''),
|
|
259
|
+
info.name,
|
|
259
260
|
info.createDate,
|
|
260
261
|
info.authorUserFriendlyName ?? '',
|
|
261
262
|
info.lastModifiedUserName ?? '',
|
|
@@ -263,7 +264,7 @@ export class HitDesignInfoView
|
|
|
263
264
|
info.status,
|
|
264
265
|
...(deleteAndShareCampaignIcons(info)),
|
|
265
266
|
]),
|
|
266
|
-
['Name', 'Created', 'Author', 'Last Modified by', 'Molecules', 'Status', '', '']);
|
|
267
|
+
['Name', 'Code', 'Created', 'Author', 'Last Modified by', 'Molecules', 'Status', '', '']);
|
|
267
268
|
table.style.color = 'var(--grey-5)';
|
|
268
269
|
table.style.marginLeft = '24px';
|
|
269
270
|
processGroupingTable(table, grouppedCampaigns);
|
|
@@ -280,7 +281,8 @@ export class HitDesignInfoView
|
|
|
280
281
|
this.app.dataFrame = camp.df;
|
|
281
282
|
await this.app.setTemplate(template);
|
|
282
283
|
this.app.campaignProps = camp.campaignProps;
|
|
283
|
-
|
|
284
|
+
const campaignName = !!camp.name?.trim() ? camp.name?.trim() : undefined;
|
|
285
|
+
await this.app.saveCampaign(false, true, {friendlyName: campaignName});
|
|
284
286
|
if (template.layoutViewState && this.app.campaign)
|
|
285
287
|
this.app.campaign.layout = template.layoutViewState;
|
|
286
288
|
});
|
package/src/app/pepti-hit-app.ts
CHANGED
|
@@ -48,7 +48,7 @@ export class PeptiHitApp extends HitDesignApp<PeptiHitTemplate> {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
protected getDesignView(): DG.TableView {
|
|
51
|
+
protected override getDesignView(): DG.TableView {
|
|
52
52
|
const subs: Subscription[] = [];
|
|
53
53
|
const isNew = this.dataFrame!.col(this.helmColName)?.toList().every((m) => !m && m === '');
|
|
54
54
|
const helmCol = this.dataFrame!.col(this.helmColName);
|
package/src/app/types.ts
CHANGED
package/src/app/utils.ts
CHANGED
|
@@ -215,7 +215,7 @@ export function getGroupedCampaigns<T extends HitDesignCampaign | HitTriageCampa
|
|
|
215
215
|
return groupedCampaigns;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
export function processGroupingTable<T extends HitDesignCampaign | HitTriageCampaign = HitDesignCampaign>(table: HTMLTableElement, groupedCampaigns: {[key: string]: T[]}, numCols =
|
|
218
|
+
export function processGroupingTable<T extends HitDesignCampaign | HitTriageCampaign = HitDesignCampaign>(table: HTMLTableElement, groupedCampaigns: {[key: string]: T[]}, numCols = 9) {
|
|
219
219
|
table.classList.add('hit-design-groupped-campaigns-table');
|
|
220
220
|
const keys = Object.keys(groupedCampaigns);
|
|
221
221
|
if (keys.length < 2)
|
|
@@ -252,3 +252,73 @@ export function processGroupingTable<T extends HitDesignCampaign | HitTriageCamp
|
|
|
252
252
|
curRow += l;
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
|
+
|
|
256
|
+
export type EditableFieldOptions = {
|
|
257
|
+
onChange?: (val?: string | null) => void,
|
|
258
|
+
onOk?: (val?: string | null) => Promise<void>,
|
|
259
|
+
validator?: (val?: string | null) => Promise<string | null>,
|
|
260
|
+
onCancel?: () => void,
|
|
261
|
+
afterEditTextContent?: (val: string) => string,
|
|
262
|
+
beforeEditTextContent?: (val: string) => string,
|
|
263
|
+
nullable?: boolean,
|
|
264
|
+
tooltip?: string,
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function editableTableField(field: HTMLElement, options?: EditableFieldOptions) {
|
|
268
|
+
const editIcon = ui.icons.edit(() => {
|
|
269
|
+
let tooltipMsg = options?.tooltip ?? field.textContent ?? '';
|
|
270
|
+
const beforeEditTextContent = options?.beforeEditTextContent ?? ((val) => val);
|
|
271
|
+
const afterEditTextContent = options?.afterEditTextContent ?? ((val) => val);
|
|
272
|
+
const input = ui.input.string('smth', {value: beforeEditTextContent(field.textContent ?? ''), onValueChanged: async () => {
|
|
273
|
+
const vr = await internalValidator();
|
|
274
|
+
setTimeout(() => {
|
|
275
|
+
if (vr) {
|
|
276
|
+
input.input.classList.add('d4-invalid');
|
|
277
|
+
tooltipMsg = vr;
|
|
278
|
+
return;
|
|
279
|
+
} else {
|
|
280
|
+
input.input.classList.remove('d4-invalid');
|
|
281
|
+
tooltipMsg = options?.tooltip ?? field.textContent ?? '';
|
|
282
|
+
}
|
|
283
|
+
}, 100);
|
|
284
|
+
options?.onChange?.(input.value);
|
|
285
|
+
}});
|
|
286
|
+
ui.tooltip.bind(input.input, () => tooltipMsg);
|
|
287
|
+
const labelElement = input.root.getElementsByTagName('label').item(0);
|
|
288
|
+
if (labelElement)
|
|
289
|
+
labelElement.remove();
|
|
290
|
+
input.root.style.width = '100%';
|
|
291
|
+
const internalValidator = async () => {
|
|
292
|
+
const initialRes = !!input.value?.trim() ? null : !!options?.nullable ? null :'Field cannot be empty';
|
|
293
|
+
return initialRes ?? (options?.validator ? await options.validator(input.value) : null);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const saveButton = ui.button('Save', async () => {
|
|
297
|
+
const vr = await internalValidator();
|
|
298
|
+
if (vr) {
|
|
299
|
+
grok.shell.error(vr);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
await options?.onOk?.(input.value);
|
|
303
|
+
field.textContent = afterEditTextContent(input.value) ?? '';
|
|
304
|
+
ui.empty(container);
|
|
305
|
+
container.appendChild(field);
|
|
306
|
+
container.appendChild(editIcon);
|
|
307
|
+
});
|
|
308
|
+
const cancelButton = ui.button('Cancel', () => {
|
|
309
|
+
ui.empty(container);
|
|
310
|
+
container.appendChild(field);
|
|
311
|
+
container.appendChild(editIcon);
|
|
312
|
+
options?.onCancel?.();
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
ui.empty(container);
|
|
316
|
+
input.addOptions(cancelButton);
|
|
317
|
+
input.addOptions(saveButton);
|
|
318
|
+
container.appendChild(input.root);
|
|
319
|
+
input.input.focus();
|
|
320
|
+
}, options?.tooltip ?? 'Edit');
|
|
321
|
+
const container = ui.divH([field, editIcon], {style: {display: 'flex', alignItems: 'center'}});
|
|
322
|
+
editIcon.style.marginLeft = '5px';
|
|
323
|
+
return container;
|
|
324
|
+
}
|