@platforma-sdk/ui-vue 1.41.2 → 1.41.4

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 (56) hide show
  1. package/.turbo/turbo-build.log +20 -30
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +17 -0
  4. package/dist/aggrid.js +4 -4
  5. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts +14 -3
  6. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts.map +1 -1
  7. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js +252 -179
  8. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js.map +1 -1
  9. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js +1 -1
  10. package/dist/components/PlAgDataTable/sources/focus-row.d.ts +8 -7
  11. package/dist/components/PlAgDataTable/sources/focus-row.d.ts.map +1 -1
  12. package/dist/components/PlAgDataTable/sources/focus-row.js +46 -31
  13. package/dist/components/PlAgDataTable/sources/focus-row.js.map +1 -1
  14. package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts +10 -5
  15. package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts.map +1 -1
  16. package/dist/components/PlAgDataTable/sources/table-source-v2.js +144 -141
  17. package/dist/components/PlAgDataTable/sources/table-source-v2.js.map +1 -1
  18. package/dist/components/PlAgDataTable/types.d.ts +27 -1
  19. package/dist/components/PlAgDataTable/types.d.ts.map +1 -1
  20. package/dist/components/PlAgDataTable/types.js.map +1 -1
  21. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js +1 -1
  22. package/dist/components/PlAgRowNumHeader.vue.js +1 -1
  23. package/dist/components/PlAnnotations/components/DynamicForm.vue2.js +29 -29
  24. package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js +2 -2
  25. package/dist/components/PlMultiSequenceAlignment/data.js +14 -14
  26. package/dist/defineApp.js +12 -12
  27. package/dist/internal/test-helpers/BlockMock.d.ts +1 -0
  28. package/dist/internal/test-helpers/BlockMock.d.ts.map +1 -1
  29. package/dist/lib/ui/uikit/dist/components/PlAccordion/{ExpandTransition.vue.js → ExpandTransition.vue2.js} +1 -1
  30. package/dist/lib/ui/uikit/dist/components/PlAccordion/ExpandTransition.vue2.js.map +1 -0
  31. package/dist/lib/ui/uikit/dist/components/PlAccordion/PlAccordionSection.vue2.js +1 -1
  32. package/dist/lib/ui/uikit/dist/components/PlAutocomplete/PlAutocomplete.vue.js +1 -1
  33. package/dist/lib/ui/uikit/dist/components/PlDropdown/PlDropdown.vue.js +1 -1
  34. package/dist/lib/ui/uikit/dist/components/PlDropdownLegacy/PlDropdownLegacy.vue.js +1 -1
  35. package/dist/lib/ui/uikit/dist/components/PlDropdownMulti/PlDropdownMulti.vue.js +1 -1
  36. package/dist/lib/ui/uikit/dist/components/PlFileDialog/Local.vue.js +4 -4
  37. package/dist/lib/ui/uikit/dist/components/PlFileInput/PlFileInput.vue.js +1 -1
  38. package/dist/lib/ui/uikit/dist/components/PlTextArea/PlTextArea.vue.js +1 -1
  39. package/dist/lib/ui/uikit/dist/components/PlTextField/PlTextField.vue.js +1 -1
  40. package/dist/lib/ui/uikit/dist/generated/components/svg/images/{SvgRequired.vue2.js → SvgRequired.vue.js} +1 -1
  41. package/dist/lib/ui/uikit/dist/generated/components/svg/images/SvgRequired.vue.js.map +1 -0
  42. package/dist/lib/ui/uikit/dist/sdk/model/dist/index.js +25 -25
  43. package/dist/lib/ui/uikit/dist/sdk/model/dist/index.js.map +1 -1
  44. package/dist/lib/util/helpers/dist/index.js +88 -73
  45. package/dist/lib/util/helpers/dist/index.js.map +1 -1
  46. package/dist/lib.js +73 -73
  47. package/dist/sdk/model/dist/index.js +110 -103
  48. package/dist/sdk/model/dist/index.js.map +1 -1
  49. package/package.json +8 -8
  50. package/src/components/PlAgDataTable/PlAgDataTableV2.vue +172 -45
  51. package/src/components/PlAgDataTable/sources/focus-row.ts +38 -37
  52. package/src/components/PlAgDataTable/sources/table-source-v2.ts +113 -110
  53. package/src/components/PlAgDataTable/types.ts +30 -1
  54. package/src/internal/test-helpers/BlockMock.ts +5 -0
  55. package/dist/lib/ui/uikit/dist/components/PlAccordion/ExpandTransition.vue.js.map +0 -1
  56. package/dist/lib/ui/uikit/dist/generated/components/svg/images/SvgRequired.vue2.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/ui-vue",
3
- "version": "1.41.2",
3
+ "version": "1.41.4",
4
4
  "type": "module",
5
5
  "main": "dist/lib.js",
6
6
  "styles": "dist/lib.js",
@@ -16,8 +16,8 @@
16
16
  "@types/semver": "^7.7.0",
17
17
  "@types/d3-format": "^3.0.4",
18
18
  "@milaboratories/miplots4": "^1.0.123",
19
- "ag-grid-enterprise": "^33.3.2",
20
- "ag-grid-vue3": "^33.3.2",
19
+ "ag-grid-enterprise": "^34.0.2",
20
+ "ag-grid-vue3": "^34.0.2",
21
21
  "canonicalize": "~2.1.0",
22
22
  "lru-cache": "^11.1.0",
23
23
  "vue": "^3.5.13",
@@ -25,9 +25,9 @@
25
25
  "@vueuse/integrations": "^13.3.0",
26
26
  "d3-format": "^3.1.0",
27
27
  "zod": "~3.23.8",
28
+ "@milaboratories/uikit": "2.3.20",
28
29
  "@milaboratories/biowasm-tools": "^1.1.0",
29
- "@platforma-sdk/model": "~1.41.2",
30
- "@milaboratories/uikit": "2.3.18"
30
+ "@platforma-sdk/model": "~1.41.4"
31
31
  },
32
32
  "devDependencies": {
33
33
  "happy-dom": "^15.11.7",
@@ -43,10 +43,10 @@
43
43
  "yarpm": "^1.2.0",
44
44
  "fast-json-patch": "^3.1.1",
45
45
  "@faker-js/faker": "^9.2.0",
46
- "@milaboratories/eslint-config": "^1.0.4",
46
+ "@milaboratories/ts-configs": "1.0.4",
47
47
  "@milaboratories/build-configs": "1.0.4",
48
- "@milaboratories/helpers": "^1.6.18",
49
- "@milaboratories/ts-configs": "1.0.4"
48
+ "@milaboratories/eslint-config": "^1.0.4",
49
+ "@milaboratories/helpers": "^1.6.18"
50
50
  },
51
51
  "scripts": {
52
52
  "test": "vitest run --passWithNoTests",
@@ -1,16 +1,23 @@
1
1
  <script lang="ts" setup>
2
- import { isJsonEqual } from '@milaboratories/helpers';
2
+ import { promiseTimeout, isJsonEqual } from '@milaboratories/helpers';
3
3
  import type {
4
4
  AxisId,
5
- AxisSpec,
6
5
  PlDataTableGridStateCore,
7
6
  PlDataTableStateV2,
8
7
  PlSelectionModel,
9
8
  PlTableColumnIdJson,
10
9
  PTableColumnSpec,
11
10
  PTableKey,
11
+ PTableValue,
12
+ } from '@platforma-sdk/model';
13
+ import {
14
+ getRawPlatformaInstance,
15
+ parseJson,
16
+ createPlSelectionModel,
17
+ matchAxisId,
18
+ getAxisId,
19
+ canonicalizeJson,
12
20
  } from '@platforma-sdk/model';
13
- import { getRawPlatformaInstance, parseJson } from '@platforma-sdk/model';
14
21
  import type {
15
22
  CellRendererSelectorFunc,
16
23
  ColDef,
@@ -22,7 +29,7 @@ import type {
22
29
  ManagedGridOptions,
23
30
  } from 'ag-grid-enterprise';
24
31
  import { AgGridVue } from 'ag-grid-vue3';
25
- import { computed, ref, shallowRef, toRefs, watch } from 'vue';
32
+ import { computed, effectScope, ref, shallowRef, toRefs, watch } from 'vue';
26
33
  import { AgGridTheme } from '../../aggrid';
27
34
  import PlAgCsvExporter from '../PlAgCsvExporter/PlAgCsvExporter.vue';
28
35
  import { PlAgGridColumnManager } from '../PlAgGridColumnManager';
@@ -32,7 +39,7 @@ import PlAgDataTableSheets from './PlAgDataTableSheets.vue';
32
39
  import PlOverlayLoading from './PlAgOverlayLoading.vue';
33
40
  import PlOverlayNoRows from './PlAgOverlayNoRows.vue';
34
41
  import PlAgRowCount from './PlAgRowCount.vue';
35
- import { focusRow, makeOnceTracker } from './sources/focus-row';
42
+ import { DeferredCircular, ensureNodeVisible } from './sources/focus-row';
36
43
  import { autoSizeRowNumberColumn, PlAgDataTableRowNumberColId } from './sources/row-number';
37
44
  import type { PlAgCellButtonAxisParams } from './sources/table-source-v2';
38
45
  import { calculateGridOptions } from './sources/table-source-v2';
@@ -44,13 +51,16 @@ import type {
44
51
  PlAgOverlayNoRowsParams,
45
52
  PlDataTableSettingsV2,
46
53
  PlDataTableSheetsSettings,
54
+ PlTableRowId,
47
55
  PlTableRowIdJson,
48
56
  } from './types';
49
57
  import { watchCached } from '@milaboratories/uikit';
58
+ import { isPTableHidden, type PTableHidden } from './sources/common';
50
59
 
51
60
  const tableState = defineModel<PlDataTableStateV2>({
52
61
  required: true,
53
62
  });
63
+ /** Warning: selection model value updates are ignored, use updateSelection instead */
54
64
  const selection = defineModel<PlSelectionModel>('selection');
55
65
  const props = defineProps<{
56
66
  /** Required component settings */
@@ -113,6 +123,7 @@ const { settings } = toRefs(props);
113
123
  const emit = defineEmits<{
114
124
  rowDoubleClicked: [key?: PTableKey];
115
125
  cellButtonClicked: [key?: PTableKey];
126
+ newDataRendered: [];
116
127
  }>();
117
128
 
118
129
  const { gridState, sheetsState, filtersState } = useTableState(tableState, settings);
@@ -149,7 +160,7 @@ const filtersSettings = computed<PlDataTableFiltersSettings>(() => {
149
160
  });
150
161
 
151
162
  const gridApi = shallowRef<GridApi<PlAgDataTableV2Row> | null>(null);
152
- const firstDataRenderedTracker = makeOnceTracker<GridApi<PlAgDataTableV2Row>>();
163
+ const dataRenderedTracker = new DeferredCircular<GridApi<PlAgDataTableV2Row>>();
153
164
  const gridOptions = shallowRef<GridOptions<PlAgDataTableV2Row>>({
154
165
  animateRows: false,
155
166
  suppressColumnMoveAnimation: true,
@@ -170,7 +181,9 @@ const gridOptions = shallowRef<GridOptions<PlAgDataTableV2Row>>({
170
181
  if (selection.value) {
171
182
  const state = event.api.getServerSideSelectionState();
172
183
  const selectedKeys = state?.toggledNodes?.map((nodeId) => parseJson(nodeId as PlTableRowIdJson)) ?? [];
173
- selection.value = { ...selection.value, selectedKeys };
184
+ if (!isJsonEqual(selection.value.selectedKeys, selectedKeys)) {
185
+ selection.value = { ...selection.value, selectedKeys };
186
+ }
174
187
  }
175
188
  },
176
189
  onRowDoubleClicked: (event) => {
@@ -331,12 +344,101 @@ watch(
331
344
  );
332
345
 
333
346
  defineExpose<PlAgDataTableV2Controller>({
334
- focusRow: (rowKey) => focusRow(
335
- (row) => isJsonEqual(row.data?.axesKey, rowKey),
336
- firstDataRenderedTracker,
337
- ),
347
+ focusRow: async (rowKey) => {
348
+ const gridApi = await dataRenderedTracker.promise;
349
+ if (gridApi.isDestroyed()) return false;
350
+
351
+ return ensureNodeVisible(gridApi, (row) => isJsonEqual(row.data?.axesKey, rowKey));
352
+ },
353
+ updateSelection: async ({ axesSpec, selectedKeys }) => {
354
+ const gridApi = await dataRenderedTracker.promise;
355
+ if (gridApi.isDestroyed()) return false;
356
+
357
+ const axes = selection.value?.axesSpec;
358
+ if (!axes || axes.length !== axesSpec.length) return false;
359
+
360
+ const mapping = axesSpec
361
+ .map((spec) => {
362
+ const id = getAxisId(spec);
363
+ return axes.findIndex((axis) => matchAxisId(axis, id));
364
+ });
365
+ const mappingSet = new Set(mapping);
366
+ if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) return false;
367
+
368
+ const selectedNodes = selectedKeys
369
+ .map((key) => canonicalizeJson<PlTableRowId>(mapping.map((index) => key[index])));
370
+ const oldSelectedKeys = gridApi.getServerSideSelectionState()?.toggledNodes ?? [];
371
+ if (!isJsonEqual(oldSelectedKeys, selectedNodes)) {
372
+ gridApi.setServerSideSelectionState({
373
+ selectAll: false,
374
+ toggledNodes: selectedNodes,
375
+ });
376
+
377
+ // wait for `onSelectionChanged` to update `selection` model
378
+ const scope = effectScope();
379
+ const { resolve, promise } = Promise.withResolvers();
380
+ scope.run(() => watch(selection, resolve, { once: true }));
381
+ try {
382
+ await promiseTimeout(promise, 500);
383
+ } catch {
384
+ return false;
385
+ } finally {
386
+ scope.stop();
387
+ }
388
+ }
389
+ return true;
390
+ },
391
+ getRowCount: async () => {
392
+ const gridApi = await dataRenderedTracker.promise;
393
+ if (gridApi.isDestroyed()) return;
394
+
395
+ return gridApi.getDisplayedRowCount();
396
+ },
397
+ getRow: async (index) => {
398
+ const gridApi = await dataRenderedTracker.promise;
399
+ if (gridApi.isDestroyed()) return;
400
+
401
+ const visibleColumns: {
402
+ spec: PTableColumnSpec;
403
+ field: `${number}`;
404
+ }[] = getDataColDefs(gridApi.getColumnDefs())
405
+ .filter((def) => !def.hide)
406
+ .map((def) => ({
407
+ spec: parseJson(def.colId! satisfies string as PlTableColumnIdJson).labeled,
408
+ field: def.field! as `${number}`,
409
+ }));
410
+
411
+ const row = Array.isArray(index)
412
+ ? gridApi.getRowNode(canonicalizeJson<PlTableRowId>(index))
413
+ : gridApi.getDisplayedRowAtIndex(index);
414
+ if (!row) return;
415
+
416
+ const rowData = row.data;
417
+ if (!rowData) return;
418
+
419
+ const getTableValue = (value: PTableValue | PTableHidden): PTableValue => {
420
+ if (isPTableHidden(value)) throw new Error('hidden value was not filtered out');
421
+ return value;
422
+ };
423
+
424
+ const spec: PTableColumnSpec[] = visibleColumns.map((column) => column.spec);
425
+ const data: PTableValue[] = visibleColumns.map((column) => getTableValue(rowData[column.field]));
426
+ return { spec, data };
427
+ },
338
428
  });
339
429
 
430
+ function getDataColDefs(
431
+ columnDefs: ColDef<PlAgDataTableV2Row, PTableValue | PTableHidden>[] | null | undefined,
432
+ ): ColDef<PlAgDataTableV2Row, PTableValue | PTableHidden>[] {
433
+ const isColDef = <TData, TValue>(
434
+ def: ColDef<TData, TValue> | ColGroupDef<TData>,
435
+ ): def is ColDef<TData, TValue> => !('children' in def);
436
+ if (!columnDefs) return [];
437
+ return columnDefs
438
+ .filter(isColDef)
439
+ .filter((def) => def.colId && def.colId !== PlAgDataTableRowNumberColId);
440
+ }
441
+
340
442
  // Propagate columns for filter component
341
443
  watchCached(
342
444
  () => gridOptions.value.columnDefs,
@@ -344,42 +446,17 @@ watchCached(
344
446
  const sourceId = settings.value.sourceId;
345
447
  if (sourceId === null) {
346
448
  filterableColumns.value = [];
347
- if (selection.value) {
348
- selection.value = {
349
- axesSpec: [],
350
- selectedKeys: [],
351
- };
352
- }
353
449
  } else {
354
- const isColDef = (def: ColDef | ColGroupDef): def is ColDef =>
355
- !('children' in def);
356
- const colDefs = columnDefs?.filter(isColDef) ?? [];
357
- const columns = colDefs
358
- .map((def) => def.colId)
359
- .filter((colId) => colId !== undefined)
360
- .filter((colId) => colId !== PlAgDataTableRowNumberColId)
361
- .map((colId) => parseJson(colId as PlTableColumnIdJson))
362
- ?? [];
363
- filterableColumns.value = columns.map((column) => column.labeled);
364
- if (selection.value) {
365
- const axesSpec = columns
366
- .reduce((acc, column) => {
367
- if (column.source.type === 'axis') {
368
- acc.push(column.source.spec);
369
- }
370
- return acc;
371
- }, [] as AxisSpec[]);
372
- selection.value = {
373
- ...selection.value,
374
- axesSpec,
375
- };
376
- }
450
+ const dataColumns = getDataColDefs(columnDefs);
451
+ filterableColumns.value = dataColumns
452
+ .map((def) => parseJson(def.colId! satisfies string as PlTableColumnIdJson).labeled);
377
453
  }
378
454
  },
379
455
  { immediate: true },
380
456
  );
381
457
 
382
458
  // Update AgGrid when settings change
459
+ const defaultSelection = createPlSelectionModel();
383
460
  let oldSettings: PlDataTableSettingsV2 | null = null;
384
461
  const generation = ref(0);
385
462
  watch(
@@ -393,7 +470,7 @@ watch(
393
470
  try {
394
471
  // Hide no rows overlay if it is shown, or else loading overlay will not be shown
395
472
  gridApi.hideOverlay();
396
- firstDataRenderedTracker.reset();
473
+ dataRenderedTracker.reset();
397
474
 
398
475
  // No data source selected -> reset state to default
399
476
  if (!settings.sourceId) {
@@ -407,6 +484,9 @@ watch(
407
484
  serverSideDatasource: undefined,
408
485
  });
409
486
  if (selection.value) {
487
+ if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {
488
+ selection.value = createPlSelectionModel();
489
+ }
410
490
  gridApi.setServerSideSelectionState({
411
491
  selectAll: false,
412
492
  toggledNodes: [],
@@ -424,7 +504,10 @@ watch(
424
504
  notReady: false,
425
505
  } satisfies PlAgOverlayLoadingParams,
426
506
  });
427
- if (selection.value) {
507
+ if (selection.value && oldSettings?.sourceId) {
508
+ if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {
509
+ selection.value = createPlSelectionModel();
510
+ }
428
511
  gridApi.setServerSideSelectionState({
429
512
  selectAll: false,
430
513
  toggledNodes: [],
@@ -455,7 +538,7 @@ watch(
455
538
  pfDriver: getRawPlatformaInstance().pFrameDriver,
456
539
  model: settings.model,
457
540
  sheets: settings.sheets ?? [],
458
- track: firstDataRenderedTracker.track,
541
+ dataRenderedTracker,
459
542
  hiddenColIds: gridState.value.columnVisibility?.hiddenColIds,
460
543
  cellButtonAxisParams: {
461
544
  showCellButtonForAxisId: props.showCellButtonForAxisId,
@@ -463,11 +546,54 @@ watch(
463
546
  props.cellButtonInvokeRowsOnDoubleClick,
464
547
  trigger: (key?: PTableKey) => emit('cellButtonClicked', key),
465
548
  } satisfies PlAgCellButtonAxisParams,
466
- }).then((options) => {
549
+ }).then((result) => {
467
550
  if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;
468
- return gridApi.updateGridOptions({
551
+ const { axesSpec, ...options } = result;
552
+ gridApi.updateGridOptions({
469
553
  ...options,
470
554
  });
555
+ if (selection.value) {
556
+ // Update selection if axesSpec changed, as order of axes may have changed and so we need to remap selected keys
557
+ const { axesSpec: oldAxesSpec, selectedKeys: oldSelectedKeys } = selection.value;
558
+ if (!isJsonEqual(oldAxesSpec, axesSpec)) {
559
+ if (!oldAxesSpec || axesSpec.length !== oldAxesSpec.length) {
560
+ const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };
561
+ if (!isJsonEqual(selection.value, newSelection)) {
562
+ selection.value = newSelection;
563
+ }
564
+ return gridApi.setServerSideSelectionState({
565
+ selectAll: false,
566
+ toggledNodes: [],
567
+ });
568
+ }
569
+
570
+ const mapping = oldAxesSpec
571
+ .map(getAxisId)
572
+ .map((id) => axesSpec.findIndex((axis) => matchAxisId(axis, id)));
573
+ const mappingSet = new Set(mapping);
574
+ if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) {
575
+ const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };
576
+ if (!isJsonEqual(selection.value, newSelection)) {
577
+ selection.value = newSelection;
578
+ }
579
+ return gridApi.setServerSideSelectionState({
580
+ selectAll: false,
581
+ toggledNodes: [],
582
+ });
583
+ }
584
+
585
+ const selectedNodes = oldSelectedKeys
586
+ .map((key) => mapping.map((index) => key[index]));
587
+ const newSelection: PlSelectionModel = { axesSpec, selectedKeys: selectedNodes };
588
+ if (!isJsonEqual(selection.value, newSelection)) {
589
+ selection.value = newSelection;
590
+ }
591
+ return gridApi.setServerSideSelectionState({
592
+ selectAll: false,
593
+ toggledNodes: selectedNodes.map((key) => canonicalizeJson<PlTableRowId>(key)),
594
+ });
595
+ }
596
+ }
471
597
  }).catch((error: unknown) => {
472
598
  if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;
473
599
  console.trace(error);
@@ -477,6 +603,7 @@ watch(
477
603
  loading: false,
478
604
  });
479
605
  });
606
+ dataRenderedTracker.promise.then(() => emit('newDataRendered'));
480
607
  } catch (error: unknown) {
481
608
  console.trace(error);
482
609
  } finally {
@@ -1,32 +1,44 @@
1
1
  import type { GridApi, IRowNode } from 'ag-grid-enterprise';
2
- import { nextTick, shallowRef, watch } from 'vue';
2
+ import { Deferred } from '@milaboratories/helpers';
3
3
 
4
- export function makeOnceTracker<TContext = undefined>() {
5
- const state = shallowRef<[false, undefined] | [true, TContext]>([false, undefined]);
6
- const track = (ctx: TContext): void => {
7
- state.value = [true, ctx];
8
- };
9
- const reset = (): void => {
10
- state.value = [false, undefined];
11
- };
12
- const onceTracked = (callback: (ctx: TContext) => void) => {
13
- const handle = watch(
14
- state,
15
- ([tracked, context]) => {
16
- if (tracked) {
17
- callback(context);
18
- nextTick(() => handle.stop());
19
- }
20
- },
21
- { immediate: true },
22
- );
23
- return handle;
24
- };
25
- return { track, reset, onceTracked };
4
+ class DeferredTracked<T> extends Deferred<T> {
5
+ #resolved = false;
6
+
7
+ constructor() {
8
+ super();
9
+ this.promise.finally(() => {
10
+ this.#resolved = true;
11
+ });
12
+ }
13
+
14
+ public get resolved(): boolean {
15
+ return this.#resolved;
16
+ }
17
+ }
18
+
19
+ export class DeferredCircular<T> {
20
+ private deferred = new DeferredTracked<T>();
21
+
22
+ public get promise(): Promise<T> {
23
+ return this.deferred.promise;
24
+ }
25
+
26
+ public resolve(ctx: T): void {
27
+ this.deferred.resolve(ctx);
28
+ }
29
+
30
+ public get resolved(): boolean {
31
+ return this.deferred.resolved;
32
+ }
33
+
34
+ public reset(): void {
35
+ if (this.resolved) {
36
+ this.deferred = new DeferredTracked<T>();
37
+ }
38
+ }
26
39
  }
27
- export type OnceTracker<TContext = undefined> = ReturnType<typeof makeOnceTracker<TContext>>;
28
40
 
29
- function ensureNodeVisible<TData>(api: GridApi<TData>, selector: (row: IRowNode<TData>) => boolean): void {
41
+ export function ensureNodeVisible<TData>(api: GridApi<TData>, selector: (row: IRowNode<TData>) => boolean): boolean {
30
42
  let rowIndex: number | null = null;
31
43
  const nodeSelector = (row: IRowNode<TData>): boolean => {
32
44
  if (selector(row)) {
@@ -43,16 +55,5 @@ function ensureNodeVisible<TData>(api: GridApi<TData>, selector: (row: IRowNode<
43
55
  api.setFocusedCell(rowIndex, columns[0]);
44
56
  }
45
57
  }
58
+ return rowIndex !== null;
46
59
  };
47
-
48
- export async function focusRow<TData>(
49
- selector: (row: IRowNode<TData>) => boolean,
50
- tracker: OnceTracker<GridApi<TData>>,
51
- ): Promise<void> {
52
- return new Promise((resolve) => {
53
- nextTick(() => tracker.onceTracked((gridApi) => {
54
- ensureNodeVisible(gridApi, selector);
55
- resolve();
56
- }));
57
- });
58
- }