@datagrok/hit-triage 1.7.0 → 1.7.2

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/hit-triage",
3
3
  "friendlyName": "Hit Triage",
4
- "version": "1.7.0",
4
+ "version": "1.7.2",
5
5
  "author": {
6
6
  "name": "Davit Rizhinashvili",
7
7
  "email": "drizhinashvili@datagrok.ai"
package/src/app/consts.ts CHANGED
@@ -48,3 +48,4 @@ export enum CampaignGroupingType {
48
48
  Author = 'Author',
49
49
  LastModifiedUser = 'Last Modified User',
50
50
  }
51
+ export const HTFunctionOrderingLSKey = 'HTFunctionOrderingLS';
@@ -8,6 +8,7 @@ import {IChemFunctionsDialogResult, IComputeDialogResult, IDescriptorTree,
8
8
  import '../../../css/hit-triage.css';
9
9
  import {funcTypeNames, HTQueryPrefix, HTScriptPrefix} from '../consts';
10
10
  import {HitAppBase} from '../hit-app-base';
11
+ import { FunctionOrdering, getReorderingInput, getSavedFunctionOrdering } from '../utils';
11
12
 
12
13
  export async function chemFunctionsDialog(app: HitAppBase<any>,
13
14
  onOk: (result: IComputeDialogResult) => void, onCancel: () => void,
@@ -133,19 +134,48 @@ export async function chemFunctionsDialog(app: HitAppBase<any>,
133
134
  tc.root.style.minWidth = '350px';
134
135
  host.appendChild(tc.root);
135
136
  // add checkboxes to each hader
136
- tc.panes.forEach((pane)=> {
137
- const functionCheck =
138
- ui.input.bool('', {value: calculatedFunctions[funcNamesMap[pane.name]], onValueChanged: (value) => {
139
- calculatedFunctions[funcNamesMap[pane.name]] = !!value;
140
- if (!value)
141
- $(pane.content).find('input')?.attr('disabled', 'true');
142
- else
143
- $(pane.content).find('input')?.removeAttr('disabled');
144
- }});
145
- functionCheck.setTooltip('Toggle calculation of this function');
146
- pane.header.appendChild(functionCheck.root);
147
- pane.header.classList.add('hit-triage-compute-dialog-pane-header');
148
- });
137
+ function addCheckboxesToPaneHeaders () {
138
+ tc.panes.forEach((pane)=> {
139
+ const functionCheck =
140
+ ui.input.bool('', {value: calculatedFunctions[funcNamesMap[pane.name]], onValueChanged: (value) => {
141
+ calculatedFunctions[funcNamesMap[pane.name]] = !!value;
142
+ if (!value)
143
+ $(pane.content).find('input')?.attr('disabled', 'true');
144
+ else
145
+ $(pane.content).find('input')?.removeAttr('disabled');
146
+ }});
147
+ functionCheck.setTooltip('Toggle calculation of this function');
148
+ pane.header.appendChild(functionCheck.root);
149
+ pane.header.classList.add('hit-triage-compute-dialog-pane-header');
150
+ });
151
+ }
152
+
153
+ function initTabs(order: FunctionOrdering) {
154
+ tc.clear();
155
+ let actOrder = order.order ?? Object.keys(tabControlArgs);
156
+ if (actOrder.length === 0)
157
+ actOrder = Object.keys(tabControlArgs);
158
+
159
+ actOrder.forEach((n) => {
160
+ if (tabControlArgs[n])
161
+ tc.addPane(n, () => tabControlArgs[n]);
162
+ });
163
+ // after adding ordered panes, we also need to add new functions, not included in the order
164
+ Object.keys(tabControlArgs).forEach((n) => {
165
+ if (!actOrder.includes(n) && tabControlArgs[n] && !(order.hidden ?? []).includes(n)) {
166
+ tc.addPane(n, () => tabControlArgs[n]);
167
+ }
168
+ });
169
+ // make sure that hidden functions are not checked;
170
+ (order.hidden ?? []).forEach((n) => {
171
+ calculatedFunctions[funcNamesMap[n]] = false;
172
+ });
173
+ addCheckboxesToPaneHeaders();
174
+ if (tc.panes.length > 0)
175
+ tc.currentPane = tc.panes[0];
176
+ }
177
+
178
+ initTabs(getSavedFunctionOrdering());
149
179
  function onOkProxy() {
150
180
  const res: IComputeDialogResult = {descriptors: [], externals: {}, scripts: {}, queries: {}};
151
181
  res.descriptors = calculatedFunctions[descriptorsName] ?
@@ -182,12 +212,15 @@ export async function chemFunctionsDialog(app: HitAppBase<any>,
182
212
  onOk(res);
183
213
  }
184
214
 
215
+ tc.root.appendChild(getReorderingInput(Object.keys(tabControlArgs), (newOrder) => {
216
+ initTabs(newOrder);
217
+ }));
185
218
  if (dialog) {
186
219
  ui.dialog('Compute')
187
220
  .add(host)
188
221
  .onOK(() => onOkProxy())
189
222
  .onCancel(onCancel)
190
- .show();
223
+ .show({resizable: true});
191
224
  }
192
225
 
193
226
  return {
package/src/app/utils.ts CHANGED
@@ -3,7 +3,7 @@ import * as grok from 'datagrok-api/grok';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
  import {Subscription} from 'rxjs';
6
- import {CampaignGroupingType, CampaignJsonName, ComputeQueryMolColName, HDCampaignsGroupingLSKey, i18n} from './consts';
6
+ import {CampaignGroupingType, CampaignJsonName, ComputeQueryMolColName, HDCampaignsGroupingLSKey, HTFunctionOrderingLSKey, i18n} from './consts';
7
7
  import {AppName, CampaignsType, HitDesignCampaign, HitTriageCampaign, TriagePermissions} from './types';
8
8
  import {_package} from '../package';
9
9
 
@@ -11,6 +11,24 @@ export const toFormatedDateString = (d: Date): string => {
11
11
  return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}`;
12
12
  };
13
13
 
14
+ /**
15
+ * Modifies the current URL by updating or adding a query parameter with the specified key and value.
16
+ * Updates the browser's history state without reloading the page.
17
+ *
18
+ * @param key - The query parameter key to be added or updated in the URL.
19
+ * @param value - The value to be assigned to the specified query parameter key.
20
+ *
21
+ * @remarks
22
+ * This function uses the `history.replaceState` method to update the URL and browser history state.
23
+ * It ensures that the base URL remains unchanged and appends the query parameter.
24
+ * If `history.replaceState` is not supported, the function will not modify the URL.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * modifyUrl('page', '2');
29
+ * // Updates the URL to something like: http://example.com/?page=2
30
+ * ```
31
+ */
14
32
  export function modifyUrl(key: string, value: string) {
15
33
  const title = document.title;
16
34
  const url = window.location.href.split('?')[0] + '?' + key + '=' + value;
@@ -360,3 +378,98 @@ export function timeoutOneTimeEventListener(element: HTMLElement, eventName: str
360
378
  // df.name = 'Reinvent Design';
361
379
  // return df;
362
380
  // }
381
+
382
+ // #region Function ordering
383
+
384
+ export type FunctionOrdering = {
385
+ order: string[],
386
+ hidden: string[]
387
+ }
388
+
389
+ export function getSavedFunctionOrdering(): FunctionOrdering {
390
+ const orderingString = getLocalStorageValue<string>(HTFunctionOrderingLSKey) ?? '{}';
391
+ try {
392
+ const orderingP = JSON.parse(orderingString);
393
+ const ordering: FunctionOrdering = {
394
+ order: orderingP?.order ?? [],
395
+ hidden: orderingP?.hidden ?? [],
396
+ };
397
+ return ordering;
398
+ } catch (e) {
399
+ console.error('error parsing function ordering', e);
400
+ }
401
+ return {order: [], hidden: []};
402
+ }
403
+
404
+ export function setSavedFunctionOrdering(ordering: FunctionOrdering) {
405
+ setLocalStorageValue(HTFunctionOrderingLSKey, JSON.stringify(ordering));
406
+ }
407
+
408
+ export function getReorderedFunctionTabArgs(args: {[key: string]: HTMLElement}) {
409
+ const ordering = getSavedFunctionOrdering();
410
+ const orderedArgs: {[key: string]: HTMLElement} = {};
411
+ for (const key of ordering.order) {
412
+ if (args[key])
413
+ orderedArgs[key] = args[key];
414
+ }
415
+ for (const key in args) {
416
+ if (!orderedArgs[key] && !ordering.hidden.includes(key)) {
417
+ orderedArgs[key] = args[key];
418
+ }
419
+ }
420
+ return orderedArgs;
421
+ }
422
+
423
+ export function getReorderingInput(functions: string[], onOk: (ordering: FunctionOrdering) => void) {
424
+ const order = getSavedFunctionOrdering();
425
+ const dataFrame = DG.DataFrame.fromColumns(functions.map((f) => DG.Column.fromStrings(f, [f])));
426
+ dataFrame.columns.setOrder(order.order ?? []);
427
+ const columnsEditor = ui.input.columns('reorder', {table: dataFrame, value: dataFrame.columns.toList().filter((c) => !order.hidden.includes(c.name))});
428
+ columnsEditor.onChanged.subscribe(() => {
429
+ try {
430
+ const chosenColumns = columnsEditor.value.map((c) => c.name);
431
+ const hiddenColumns = functions.filter((f) => !chosenColumns.includes(f));
432
+ const newOrdering = {
433
+ order: columnsEditor.value.map((c) => c.name),
434
+ hidden: hiddenColumns,
435
+ };
436
+ dataFrame.columns.setOrder(newOrdering.order);
437
+ setSavedFunctionOrdering(newOrdering);
438
+ onOk(newOrdering);
439
+ } catch (e) {
440
+ console.error(e);
441
+ }
442
+ });
443
+
444
+ const children = Array.from(columnsEditor.root.children) as HTMLElement[];
445
+ setTimeout(() => {
446
+ children.forEach((child) => {
447
+ child.style.maxWidth = '0px';
448
+ child.style.overflow = 'hidden';
449
+ child.style.padding = '0px';
450
+ child.style.paddingRight = '0px';
451
+ child.style.visibility = 'hidden';
452
+ if (child instanceof HTMLLabelElement)
453
+ child.style.display = 'none';
454
+ });
455
+ },200);
456
+ columnsEditor.root.style.justifyContent = 'end';
457
+ columnsEditor.root.style.width = '40px';
458
+ columnsEditor.root.style.height = '0px';
459
+ columnsEditor.root.style.overflow = 'visible';
460
+ columnsEditor.root.style.padding = '0px';
461
+
462
+
463
+ const editIcon = ui.icons.edit(() => {
464
+ children.forEach((child) => {
465
+ child.click();
466
+ });
467
+ }, 'Order or hide functions');
468
+
469
+ columnsEditor.addOptions(editIcon);
470
+ (Array.from(columnsEditor.root.children) as HTMLElement[]).forEach((child) => {
471
+ child.style.borderBottom = 'unset';
472
+ });
473
+ editIcon.style.fontSize = '16px';
474
+ return columnsEditor.root;
475
+ }