@platforma-sdk/ui-vue 1.14.20 → 1.14.22

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 (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/lib.js +5715 -5554
  3. package/dist/lib.umd.cjs +25 -25
  4. package/dist/src/components/PlAgColumnHeader/PlAgColumnHeader.vue.d.ts +9 -0
  5. package/dist/src/components/PlAgColumnHeader/PlAgColumnHeader.vue.d.ts.map +1 -0
  6. package/dist/src/components/PlAgColumnHeader/index.d.ts +3 -0
  7. package/dist/src/components/PlAgColumnHeader/index.d.ts.map +1 -0
  8. package/dist/src/components/PlAgColumnHeader/types.d.ts +5 -0
  9. package/dist/src/components/PlAgColumnHeader/types.d.ts.map +1 -0
  10. package/dist/src/components/PlAgDataTable/PlAgDataTable.vue.d.ts.map +1 -1
  11. package/dist/src/components/PlAgDataTable/index.d.ts +1 -0
  12. package/dist/src/components/PlAgDataTable/index.d.ts.map +1 -1
  13. package/dist/src/components/PlAgDataTable/sources/row-number.d.ts +5 -0
  14. package/dist/src/components/PlAgDataTable/sources/row-number.d.ts.map +1 -0
  15. package/dist/src/components/PlAgDataTable/sources/table-source.d.ts +0 -5
  16. package/dist/src/components/PlAgDataTable/sources/table-source.d.ts.map +1 -1
  17. package/dist/src/components/PlAgDataTable/types.d.ts +6 -0
  18. package/dist/src/components/PlAgDataTable/types.d.ts.map +1 -1
  19. package/dist/src/components/PlAgGridColumnManager/PlAgGridColumnManager.vue.d.ts.map +1 -1
  20. package/dist/src/lib.d.ts +1 -0
  21. package/dist/src/lib.d.ts.map +1 -1
  22. package/dist/style.css +1 -1
  23. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  24. package/package.json +1 -1
  25. package/src/components/PlAgColumnHeader/PlAgColumnHeader.vue +71 -0
  26. package/src/components/PlAgColumnHeader/index.ts +3 -0
  27. package/src/components/PlAgColumnHeader/pl-ag-column-header.scss +29 -0
  28. package/src/components/PlAgColumnHeader/types.ts +3 -0
  29. package/src/components/PlAgDataTable/PlAgDataTable.vue +12 -7
  30. package/src/components/PlAgDataTable/index.ts +2 -0
  31. package/src/components/PlAgDataTable/sources/row-number.ts +91 -0
  32. package/src/components/PlAgDataTable/sources/table-source.ts +26 -10
  33. package/src/components/PlAgDataTable/types.ts +7 -0
  34. package/src/components/PlAgGridColumnManager/PlAgGridColumnManager.vue +16 -5
  35. package/src/components/PlAgGridColumnManager/pl-ag-grid-column-manager.scss +12 -1
  36. package/src/lib.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/ui-vue",
3
- "version": "1.14.20",
3
+ "version": "1.14.22",
4
4
  "type": "module",
5
5
  "main": "dist/lib.umd.cjs",
6
6
  "module": "dist/lib.js",
@@ -0,0 +1,71 @@
1
+ <script setup lang="ts">
2
+ import type { IHeaderParams, SortDirection } from '@ag-grid-community/core';
3
+ import type { MaskIconName16 } from '@milaboratories/uikit';
4
+ import { PlMaskIcon16 } from '@milaboratories/uikit';
5
+ import { computed, onMounted, ref } from 'vue';
6
+ import './pl-ag-column-header.scss';
7
+ import type { PlAgHeaderComponentParams } from './types';
8
+
9
+ const props = defineProps<{ params: IHeaderParams & PlAgHeaderComponentParams }>();
10
+
11
+ const icon = computed<MaskIconName16>(() => {
12
+ const type = (props.params.column.getUserProvidedColDef()?.headerComponentParams as PlAgHeaderComponentParams)?.type;
13
+ switch (type) {
14
+ case undefined:
15
+ case 'Text':
16
+ return 'cell-type-txt';
17
+ case 'Number':
18
+ return 'cell-type-num';
19
+ case 'File':
20
+ return 'paper-clip';
21
+ case 'Date':
22
+ return 'calendar';
23
+ case 'Duration':
24
+ return 'time';
25
+ default:
26
+ throw Error(`unsupported data type: ${type satisfies never} for PlAgColumnHeader component. Column ${props.params.column.getColId()}`);
27
+ }
28
+ });
29
+
30
+ const sortDirection = ref<SortDirection>(null);
31
+ const refreshSortDirection = () => (sortDirection.value = props.params.column.getSort() ?? null);
32
+ onMounted(() => refreshSortDirection());
33
+ function onSortRequested() {
34
+ if (props.params.column.isSortable()) {
35
+ props.params.progressSort();
36
+ refreshSortDirection();
37
+ }
38
+ }
39
+ const sortIcon = computed<MaskIconName16 | null>(() => {
40
+ const direction = sortDirection.value;
41
+ switch (direction) {
42
+ case 'asc':
43
+ return 'arrow-up';
44
+ case 'desc':
45
+ return 'arrow-down';
46
+ case null:
47
+ return null;
48
+ default:
49
+ throw Error(`unsupported sort direction: ${direction satisfies never}. Column ${props.params.column.getColId()}`);
50
+ }
51
+ });
52
+
53
+ const menuActivatorBtn = ref<HTMLElement>();
54
+ function showMenu() {
55
+ const menuActivatorBtnValue = menuActivatorBtn.value;
56
+ if (menuActivatorBtnValue) props.params.showColumnMenu(menuActivatorBtnValue);
57
+ }
58
+ </script>
59
+
60
+ <template>
61
+ <div class="pl-ag-column-header d-flex align-center gap-6" @click="onSortRequested">
62
+ <div class="pl-ag-column-header__title d-flex align-center gap-6 flex-grow-1">
63
+ <PlMaskIcon16 :name="icon" class="pl-ag-column-header__type-icon" />
64
+ <span>{{ params.displayName }}</span>
65
+ <PlMaskIcon16 v-if="sortIcon" :name="sortIcon" />
66
+ </div>
67
+ <div v-if="params.enableMenu" ref="menuActivatorBtn" class="pl-ag-column-header__menu-icon" @click.stop="showMenu">
68
+ <PlMaskIcon16 name="more" />
69
+ </div>
70
+ </div>
71
+ </template>
@@ -0,0 +1,3 @@
1
+ export { default as PlAgColumnHeader } from './PlAgColumnHeader.vue';
2
+
3
+ export * from './types';
@@ -0,0 +1,29 @@
1
+ .pl-ag-column-header {
2
+ cursor: pointer;
3
+ width: 100%;
4
+ overflow: hidden;
5
+
6
+ .mask-16 {
7
+ min-width: 16px;
8
+ }
9
+
10
+ span {
11
+ word-break: break-word;
12
+ overflow: hidden;
13
+ text-overflow: ellipsis;
14
+ }
15
+
16
+ &__title {
17
+ overflow: hidden;
18
+ }
19
+
20
+ &__type-icon {
21
+ background-color: var(--ic-02);
22
+ }
23
+
24
+ &__menu-icon {
25
+ transform: rotate(90deg);
26
+ position: relative;
27
+ line-height: 0;
28
+ }
29
+ }
@@ -0,0 +1,3 @@
1
+ export type PlAgHeaderComponentType = 'Text' | 'Number' | 'File' | 'Date' | 'Duration';
2
+
3
+ export type PlAgHeaderComponentParams = { type?: PlAgHeaderComponentType };
@@ -28,10 +28,10 @@ import { AgGridTheme, useWatchFetch } from '../../lib';
28
28
  import PlOverlayLoading from './PlAgOverlayLoading.vue';
29
29
  import PlOverlayNoRows from './PlAgOverlayNoRows.vue';
30
30
  import { updateXsvGridOptions } from './sources/file-source';
31
- import type { PlAgDataTableRow } from './sources/table-source';
32
31
  import { enrichJoinWithLabelColumns, makeSheets, parseColId, updatePFrameGridOptions } from './sources/table-source';
33
- import type { PlAgDataTableController, PlDataTableSettings } from './types';
32
+ import type { PlAgDataTableController, PlDataTableSettings, PlAgDataTableRow } from './types';
34
33
  import { PlAgGridColumnManager } from '../PlAgGridColumnManager';
34
+ import { autoSizeRowNumberColumn, PlAgDataTableRowNumberColId } from './sources/row-number';
35
35
 
36
36
  ModuleRegistry.registerModules([
37
37
  ClientSideRowModelModule,
@@ -260,11 +260,14 @@ const gridOptions = shallowRef<GridOptions<PlAgDataTableRow>>({
260
260
  cellSelection: true,
261
261
  initialState: gridState.value,
262
262
  autoSizeStrategy: { type: 'fitCellContents' },
263
- onRowDataUpdated: (event) => {
264
- event.api.autoSizeAllColumns();
263
+ onRowDoubleClicked: (event) => {
264
+ if (event.data) emit('onRowDoubleClicked', event.data.key);
265
265
  },
266
- onRowDoubleClicked: (value) => {
267
- if (value.data) emit('onRowDoubleClicked', value.data.key);
266
+ onSortChanged: (event) => {
267
+ event.api.refreshCells();
268
+ },
269
+ onFilterChanged: (event) => {
270
+ event.api.refreshCells();
268
271
  },
269
272
  defaultColDef: {
270
273
  suppressHeaderMenuButton: true,
@@ -313,6 +316,7 @@ const gridOptions = shallowRef<GridOptions<PlAgDataTableRow>>({
313
316
  });
314
317
  const onGridReady = (event: GridReadyEvent) => {
315
318
  const api = event.api;
319
+ autoSizeRowNumberColumn(api);
316
320
  gridApi.value = new Proxy(api, {
317
321
  get(target, prop, receiver) {
318
322
  switch (prop) {
@@ -346,7 +350,7 @@ const makePartialState = (state: GridState) => {
346
350
  };
347
351
  const onStateUpdated = (event: StateUpdatedEvent) => {
348
352
  gridOptions.value.initialState = gridState.value = makePartialState(event.state);
349
- event.api.autoSizeAllColumns();
353
+ event.api.autoSizeColumns(event.api.getAllDisplayedColumns().filter((column) => column.getColId() !== PlAgDataTableRowNumberColId));
350
354
  };
351
355
  const onGridPreDestroyed = () => {
352
356
  gridOptions.value.initialState = gridState.value = makePartialState(gridApi.value!.getState());
@@ -428,6 +432,7 @@ watch(
428
432
  const columns = colDefs
429
433
  ?.map((def) => def.colId)
430
434
  .filter((colId) => colId !== undefined)
435
+ .filter((colId) => colId !== PlAgDataTableRowNumberColId)
431
436
  .map((colId) => parseColId(colId));
432
437
  emit('columnsChanged', columns ?? []);
433
438
  }
@@ -3,3 +3,5 @@ export { default as PlAgOverlayLoading } from './PlAgOverlayLoading.vue';
3
3
  export { default as PlAgOverlayNoRows } from './PlAgOverlayNoRows.vue';
4
4
 
5
5
  export * from './types';
6
+
7
+ export * from './sources/row-number';
@@ -0,0 +1,91 @@
1
+ import type { ColDef, GridApi, ValueGetterParams } from '@ag-grid-community/core';
2
+ import { nextTick } from 'vue';
3
+
4
+ export const PlAgDataTableRowNumberColId = '"##RowNumberColumnId##"';
5
+
6
+ export function makeRowNumberColDef(): ColDef {
7
+ return {
8
+ colId: PlAgDataTableRowNumberColId,
9
+ headerName: '#',
10
+ valueGetter: (params: ValueGetterParams) => {
11
+ if (params.node === null) return null;
12
+ if (params.node.rowIndex === null) return null;
13
+ return params.node.rowIndex + 1;
14
+ },
15
+ suppressNavigable: true,
16
+ lockPosition: 'left',
17
+ suppressMovable: true,
18
+ mainMenuItems: [],
19
+ contextMenuItems: [],
20
+ pinned: 'left',
21
+ lockPinned: true,
22
+ minWidth: 45, // header size
23
+ suppressSizeToFit: true,
24
+ suppressAutoSize: true,
25
+ cellStyle: {
26
+ color: 'var(--txt-03)',
27
+ 'background-color': 'var(--bg-base-light)',
28
+ overflow: 'visible !important',
29
+ 'text-align': 'center',
30
+ },
31
+ sortable: false,
32
+ resizable: false,
33
+ };
34
+ }
35
+
36
+ function createCellFake(): HTMLDivElement {
37
+ const div = document.createElement('div');
38
+
39
+ div.style.visibility = 'hidden';
40
+ div.style.position = 'absolute';
41
+ div.style.boxSizing = 'border-box';
42
+
43
+ div.style.padding = '15.5px';
44
+ div.style.border = '1px solid';
45
+ div.style.width = 'auto';
46
+
47
+ document.body.appendChild(div);
48
+ return div;
49
+ }
50
+
51
+ function adjustRowNumberColumnWidth(gridApi: GridApi, cellFake: HTMLDivElement) {
52
+ const lastDisplayedRowNumber = gridApi.getCellValue({
53
+ rowNode: gridApi.getDisplayedRowAtIndex(gridApi.getLastDisplayedRowIndex())!,
54
+ colKey: PlAgDataTableRowNumberColId,
55
+ });
56
+ if (typeof lastDisplayedRowNumber !== 'number') return;
57
+
58
+ const lastDisplayedRowNumberDigitCount = lastDisplayedRowNumber.toString().length;
59
+ if (cellFake.innerHTML.length === lastDisplayedRowNumberDigitCount) return;
60
+
61
+ const WidestDigit = '5';
62
+ cellFake.innerHTML = WidestDigit.repeat(lastDisplayedRowNumberDigitCount);
63
+
64
+ nextTick(() => {
65
+ gridApi.setColumnWidths([
66
+ {
67
+ key: PlAgDataTableRowNumberColId,
68
+ newWidth: cellFake.offsetWidth,
69
+ },
70
+ ]);
71
+ });
72
+ }
73
+
74
+ function destroyCellFake(cellFake: HTMLElement) {
75
+ document.body.removeChild(cellFake);
76
+ }
77
+
78
+ export function autoSizeRowNumberColumn(gridApi: GridApi) {
79
+ const cellFake = createCellFake();
80
+ gridApi.addEventListener('viewportChanged', () => {
81
+ adjustRowNumberColumnWidth(gridApi, cellFake);
82
+ });
83
+ gridApi.addEventListener('columnVisible', (event) => {
84
+ if (event.columns && event.columns.some((column) => column.isVisible() && column.getColId() === PlAgDataTableRowNumberColId)) {
85
+ adjustRowNumberColumnWidth(gridApi, cellFake);
86
+ }
87
+ });
88
+ gridApi.addEventListener('gridPreDestroyed', () => {
89
+ destroyCellFake(cellFake);
90
+ });
91
+ }
@@ -1,5 +1,6 @@
1
1
  import type { ColDef, IServerSideDatasource, IServerSideGetRowsParams, RowModelType } from '@ag-grid-community/core';
2
2
  import type { AxisId, JoinEntry, PColumnIdAndSpec, PFrameHandle, PlDataTableSheet, PObjectId } from '@platforma-sdk/model';
3
+
3
4
  import {
4
5
  type PColumnSpec,
5
6
  type PFrameDriver,
@@ -17,6 +18,9 @@ import {
17
18
  import canonicalize from 'canonicalize';
18
19
  import * as lodash from 'lodash';
19
20
  import { getHeterogeneousColumns, updatePFrameGridOptionsHeterogeneousAxes } from './table-source-heterogeneous';
21
+ import type { PlAgDataTableRow } from '../types';
22
+ import { makeRowNumberColDef } from './row-number';
23
+ import { PlAgColumnHeader, type PlAgHeaderComponentType, type PlAgHeaderComponentParams } from '../../PlAgColumnHeader';
20
24
 
21
25
  /**
22
26
  * Generate unique colId based on the column spec.
@@ -45,11 +49,13 @@ export const defaultValueFormatter = (value: any) => {
45
49
  return value.value.toString();
46
50
  }
47
51
  };
52
+
48
53
  /**
49
54
  * Calculates column definition for a given p-table column
50
55
  */
51
56
  function getColDef(iCol: number, spec: PTableColumnSpec, hiddenColIds?: string[]): ColDef {
52
57
  const colId = makeColId(spec);
58
+ const valueType = spec.type === 'axis' ? spec.spec.type : spec.spec.valueType;
53
59
  return {
54
60
  colId,
55
61
  field: iCol.toString(),
@@ -57,7 +63,24 @@ function getColDef(iCol: number, spec: PTableColumnSpec, hiddenColIds?: string[]
57
63
  lockPosition: spec.type === 'axis',
58
64
  hide: hiddenColIds?.includes(colId) ?? spec.spec.annotations?.['pl7.app/table/visibility'] === 'optional',
59
65
  valueFormatter: defaultValueFormatter,
60
- cellDataType: ((valueType: ValueType) => {
66
+ headerComponent: PlAgColumnHeader,
67
+ headerComponentParams: {
68
+ type: ((): PlAgHeaderComponentType => {
69
+ switch (valueType) {
70
+ case 'Int':
71
+ case 'Long':
72
+ case 'Float':
73
+ case 'Double':
74
+ return 'Number';
75
+ case 'String':
76
+ case 'Bytes':
77
+ return 'Text';
78
+ default:
79
+ throw Error(`unsupported data type: ${valueType satisfies never}`);
80
+ }
81
+ })(),
82
+ } satisfies PlAgHeaderComponentParams,
83
+ cellDataType: (() => {
61
84
  switch (valueType) {
62
85
  case 'Int':
63
86
  case 'Long':
@@ -70,7 +93,7 @@ function getColDef(iCol: number, spec: PTableColumnSpec, hiddenColIds?: string[]
70
93
  default:
71
94
  throw Error(`unsupported data type: ${valueType satisfies never}`);
72
95
  }
73
- })(spec.type === 'axis' ? spec.spec.type : spec.spec.valueType),
96
+ })(),
74
97
  };
75
98
  }
76
99
 
@@ -227,12 +250,6 @@ export async function makeSheets(
227
250
  });
228
251
  }
229
252
 
230
- export type PlAgDataTableRow = {
231
- id: string;
232
- key: unknown[];
233
- [field: `${number}`]: undefined | null | number | string;
234
- };
235
-
236
253
  /**
237
254
  * Convert columnar data from the driver to rows, used by ag-grid
238
255
  * @param specs column specs
@@ -338,7 +355,7 @@ export async function updatePFrameGridOptions(
338
355
 
339
356
  const ptShape = await pfDriver.getShape(pt);
340
357
  const rowCount = ptShape.rows;
341
- const columnDefs = fields.map((i) => getColDef(i, specs[i], hiddenColIds));
358
+ const columnDefs: ColDef<PlAgDataTableRow>[] = [makeRowNumberColDef(), ...fields.map((i) => getColDef(i, specs[i], hiddenColIds))];
342
359
 
343
360
  if (hColumns.length > 1) {
344
361
  console.warn('Currently, only one heterogeneous axis is supported in the table, got', hColumns.length, ' transposition will not be applied.');
@@ -413,7 +430,6 @@ export async function updatePFrameGridOptions(
413
430
  }
414
431
 
415
432
  params.success({ rowData, rowCount });
416
- params.api.autoSizeAllColumns();
417
433
  params.api.setGridOption('loading', false);
418
434
  } catch (error: unknown) {
419
435
  params.api.setGridOption('loading', true);
@@ -62,3 +62,10 @@ export type PlAgDataTableController = {
62
62
  /** Export table data as Csv file */
63
63
  exportCsv: () => void;
64
64
  };
65
+
66
+ /** PlAgDataTable row */
67
+ export type PlAgDataTableRow = {
68
+ id: string;
69
+ key: unknown[];
70
+ [field: `${number}`]: undefined | null | number | string;
71
+ };
@@ -4,6 +4,7 @@ import { PlBtnGhost, PlMaskIcon16, PlMaskIcon24, PlSlideModal, PlTooltip, useSor
4
4
  import { onMounted, ref, toRefs } from 'vue';
5
5
  import './pl-ag-grid-column-manager.scss';
6
6
  import { PlAgDataTableToolsPanelId } from '../PlAgDataTableToolsPanel/PlAgDataTableToolsPanelId';
7
+ import { PlAgDataTableRowNumberColId } from '../PlAgDataTable';
7
8
 
8
9
  const props = defineProps<{
9
10
  /**
@@ -24,10 +25,7 @@ const listKey = ref(0);
24
25
  useSortable(listRef, {
25
26
  handle: '.handle',
26
27
  onChange(indices) {
27
- gridApi.value.moveColumns(
28
- indices.map((i) => columns.value[i]),
29
- 0,
30
- );
28
+ gridApi.value.moveColumns(getReorderedColumns(indices.map((i) => columns.value[i])), 0);
31
29
  },
32
30
  });
33
31
 
@@ -35,7 +33,20 @@ function toggleColumnVisibility(col: Column) {
35
33
  gridApi.value.setColumnsVisible([col], !col.isVisible());
36
34
  }
37
35
 
36
+ function getReorderedColumns(columns: Column[]) {
37
+ const numRowsIndex = columns.findIndex((v) => v.getId() === PlAgDataTableRowNumberColId);
38
+ if (numRowsIndex !== 0) {
39
+ const [numRowsCol] = columns.splice(numRowsIndex, 1);
40
+ return columns.splice(0, 0, numRowsCol);
41
+ }
42
+ return columns;
43
+ }
44
+
38
45
  onMounted(() => {
46
+ if (gridApi.value.getAllGridColumns().length > 0) {
47
+ gridApi.value.moveColumns(getReorderedColumns(gridApi.value.getAllGridColumns()), 0);
48
+ }
49
+
39
50
  gridApi.value.addEventListener('displayedColumnsChanged', () => {
40
51
  columns.value = [...(gridApi.value.getAllGridColumns() ?? [])];
41
52
  listKey.value++;
@@ -64,7 +75,7 @@ onMounted(() => {
64
75
  <div :class="{ handle: !col.getColDef().lockPosition }" class="pl-ag-columns__drag">
65
76
  <PlMaskIcon16 name="drag-dots" />
66
77
  </div>
67
- <div :class="{ visible: col.isVisible() }" class="pl-ag-columns__title">{{ col.getColDef().headerName }}</div>
78
+ <div :class="{ visible: col.isVisible() }" class="pl-ag-columns__title text-s-btn">{{ col.getColDef().headerName }}</div>
68
79
  <div class="pl-ag-columns__visibility" @click="toggleColumnVisibility(col)">
69
80
  <PlTooltip :close-delay="500" position="top">
70
81
  <PlMaskIcon24 :name="col.isVisible() ? 'view-show' : 'view-hide'" />
@@ -6,10 +6,15 @@
6
6
  .sortable__item {
7
7
  border-radius: 6px;
8
8
  box-shadow: 0px 0px 0px 4px rgba(73, 204, 73, 0.24);
9
+ border-width: 2px;
9
10
  }
10
11
 
11
- &__drag {
12
+ &__drag.handle {
12
13
  cursor: grab;
14
+ }
15
+
16
+ &__drag {
17
+
13
18
  line-height: 0;
14
19
 
15
20
  .mask-16 {
@@ -17,8 +22,13 @@
17
22
  }
18
23
  }
19
24
 
25
+ &__item:hover &__visibility {
26
+ display: block;
27
+ }
28
+
20
29
  &__visibility {
21
30
  cursor: pointer;
31
+ display: none;
22
32
 
23
33
  .mask-24 {
24
34
  background-color: var(--ic-02);
@@ -29,6 +39,7 @@
29
39
  }
30
40
  }
31
41
 
42
+
32
43
  &__item {
33
44
  padding: 10px 12px;
34
45
  display: flex;
package/src/lib.ts CHANGED
@@ -7,6 +7,8 @@ import ValueOrErrorsComponent from './components/ValueOrErrorsComponent.vue';
7
7
 
8
8
  export { BlockLayout, PlAgDataTable, PlAgOverlayLoading, PlAgOverlayNoRows, ValueOrErrorsComponent };
9
9
 
10
+ export * from './components/PlAgColumnHeader';
11
+
10
12
  export * from './components/PlAgCellFile';
11
13
 
12
14
  export * from './components/PlAgDataTable/types';