@platforma-sdk/ui-vue 1.30.26 → 1.30.38

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 (64) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/assets/chemical-properties.worker-thYtrBDn.js.map +1 -0
  3. package/dist/lib.js +11603 -10388
  4. package/dist/lib.js.map +1 -1
  5. package/dist/lib.umd.cjs +36 -29
  6. package/dist/lib.umd.cjs.map +1 -1
  7. package/dist/src/components/PlAgDataTable/PlAgDataTable.vue.d.ts +2 -1
  8. package/dist/src/components/PlAgDataTable/PlAgDataTable.vue.d.ts.map +1 -1
  9. package/dist/src/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts +12 -12
  10. package/dist/src/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts.map +1 -1
  11. package/dist/src/components/PlAgDataTable/sources/common.d.ts +6 -16
  12. package/dist/src/components/PlAgDataTable/sources/common.d.ts.map +1 -1
  13. package/dist/src/components/PlAgDataTable/sources/table-source-v2.d.ts +13 -4
  14. package/dist/src/components/PlAgDataTable/sources/table-source-v2.d.ts.map +1 -1
  15. package/dist/src/components/PlAgDataTable/sources/table-source.d.ts +12 -3
  16. package/dist/src/components/PlAgDataTable/sources/table-source.d.ts.map +1 -1
  17. package/dist/src/components/PlAgDataTable/types.d.ts +25 -5
  18. package/dist/src/components/PlAgDataTable/types.d.ts.map +1 -1
  19. package/dist/src/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.d.ts +8 -0
  20. package/dist/src/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.d.ts.map +1 -0
  21. package/dist/src/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.d.ts +40 -0
  22. package/dist/src/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.d.ts.map +1 -0
  23. package/dist/src/components/PlMultiSequenceAlignment/data.d.ts +86 -0
  24. package/dist/src/components/PlMultiSequenceAlignment/data.d.ts.map +1 -0
  25. package/dist/src/components/PlMultiSequenceAlignment/highlight/chemical-properties.d.ts +19 -0
  26. package/dist/src/components/PlMultiSequenceAlignment/highlight/chemical-properties.d.ts.map +1 -0
  27. package/dist/src/components/PlMultiSequenceAlignment/highlight/chemical-properties.worker.d.ts +9 -0
  28. package/dist/src/components/PlMultiSequenceAlignment/highlight/chemical-properties.worker.d.ts.map +1 -0
  29. package/dist/src/components/PlMultiSequenceAlignment/highlight/index.d.ts +7 -0
  30. package/dist/src/components/PlMultiSequenceAlignment/highlight/index.d.ts.map +1 -0
  31. package/dist/src/components/PlMultiSequenceAlignment/highlight/types.d.ts +6 -0
  32. package/dist/src/components/PlMultiSequenceAlignment/highlight/types.d.ts.map +1 -0
  33. package/dist/src/components/PlMultiSequenceAlignment/index.d.ts +2 -0
  34. package/dist/src/components/PlMultiSequenceAlignment/index.d.ts.map +1 -0
  35. package/dist/src/components/PlMultiSequenceAlignment/multi-sequence-alignment.d.ts +7 -0
  36. package/dist/src/components/PlMultiSequenceAlignment/multi-sequence-alignment.d.ts.map +1 -0
  37. package/dist/src/components/PlMultiSequenceAlignment/types.d.ts +6 -0
  38. package/dist/src/components/PlMultiSequenceAlignment/types.d.ts.map +1 -0
  39. package/dist/src/lib.d.ts +1 -0
  40. package/dist/src/lib.d.ts.map +1 -1
  41. package/dist/style.css +1 -1
  42. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  43. package/package.json +13 -11
  44. package/src/components/PlAgDataTable/PlAgDataTable.vue +8 -7
  45. package/src/components/PlAgDataTable/PlAgDataTableV2.vue +157 -86
  46. package/src/components/PlAgDataTable/sources/common.ts +7 -101
  47. package/src/components/PlAgDataTable/sources/table-source-v2.ts +106 -11
  48. package/src/components/PlAgDataTable/sources/table-source.ts +95 -2
  49. package/src/components/PlAgDataTable/types.ts +29 -5
  50. package/src/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue +135 -0
  51. package/src/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue +173 -0
  52. package/src/components/PlMultiSequenceAlignment/data.ts +404 -0
  53. package/src/components/PlMultiSequenceAlignment/highlight/chemical-properties.ts +78 -0
  54. package/src/components/PlMultiSequenceAlignment/highlight/chemical-properties.worker.ts +191 -0
  55. package/src/components/PlMultiSequenceAlignment/highlight/index.ts +25 -0
  56. package/src/components/PlMultiSequenceAlignment/highlight/types.ts +5 -0
  57. package/src/components/PlMultiSequenceAlignment/index.ts +1 -0
  58. package/src/components/PlMultiSequenceAlignment/multi-sequence-alignment.ts +134 -0
  59. package/src/components/PlMultiSequenceAlignment/types.ts +5 -0
  60. package/src/lib.ts +2 -0
  61. package/src/vite-env.d.ts +13 -0
  62. package/tsconfig.lib.json +3 -1
  63. package/vite.config.ts +5 -2
  64. package/vitest.config.ts +7 -0
@@ -1,35 +1,44 @@
1
1
  import type {
2
2
  ColDef,
3
+ ICellRendererParams,
3
4
  IServerSideDatasource,
4
5
  IServerSideGetRowsParams,
5
6
  RowModelType,
6
7
  } from 'ag-grid-enterprise';
7
8
  import type {
9
+ AxisId,
8
10
  PlDataTableGridStateWithoutSheets,
9
11
  PlDataTableModel,
10
12
  PTableColumnSpec,
13
+ PTableKey,
11
14
  } from '@platforma-sdk/model';
12
15
  import {
16
+ canonicalizeJson,
13
17
  getAxisId,
18
+ isColumnOptional,
19
+ mapPTableValueToAxisKey,
14
20
  pTableValue,
21
+ stringifyPTableColumnSpec,
15
22
  type PColumnSpec,
16
23
  type PFrameDriver,
17
24
  type PlDataTableSheet,
18
25
  type PTableVector,
19
26
  } from '@platforma-sdk/model';
20
27
  import * as lodash from 'lodash';
21
- import type { PlAgDataTableRow } from '../types';
28
+ import type { PlAgDataTableV2Row, PTableKeyJson } from '../types';
22
29
  import { makeRowNumberColDef, PlAgDataTableRowNumberColId } from './row-number';
23
30
  import { objectHash } from '../../../objectHash';
24
31
  import type { Ref } from 'vue';
25
32
  import {
33
+ defaultValueFormatter,
26
34
  isLabelColumn,
27
- makeColDef,
28
- makeRowId,
29
35
  PTableHidden,
30
- type PlAgCellButtonAxisParams,
31
36
  } from './common';
32
37
  import canonicalize from 'canonicalize';
38
+ import type { PlAgHeaderComponentType, PlAgHeaderComponentParams } from '../../PlAgColumnHeader';
39
+ import { PlAgColumnHeader } from '../../PlAgColumnHeader';
40
+ import { PlAgTextAndButtonCell } from '../../PlAgTextAndButtonCell';
41
+ import { defaultMainMenuItems } from './menu-items';
33
42
 
34
43
  /** Convert columnar data from the driver to rows, used by ag-grid */
35
44
  function columns2rows(
@@ -37,11 +46,15 @@ function columns2rows(
37
46
  columns: PTableVector[],
38
47
  axes: number[],
39
48
  resultMapping: number[],
40
- ): PlAgDataTableRow[] {
41
- const rowData: PlAgDataTableRow[] = [];
49
+ ): PlAgDataTableV2Row[] {
50
+ const rowData: PlAgDataTableV2Row[] = [];
42
51
  for (let iRow = 0; iRow < columns[0].data.length; ++iRow) {
43
- const key = axes.map((iAxis) => pTableValue(columns[resultMapping[iAxis]], iRow));
44
- const row: PlAgDataTableRow = { id: makeRowId(key), key };
52
+ const key = axes.map((iAxis) => {
53
+ return mapPTableValueToAxisKey(
54
+ pTableValue(columns[resultMapping[iAxis]], iRow),
55
+ );
56
+ });
57
+ const row: PlAgDataTableV2Row = { id: makeRowId(key), key };
45
58
  fields.forEach((field, iCol) => {
46
59
  row[field.toString() as `${number}`] = resultMapping[iCol] === -1
47
60
  ? PTableHidden
@@ -66,7 +79,7 @@ export async function updatePFrameGridOptions(
66
79
  columnDefs: ColDef[];
67
80
  serverSideDatasource?: IServerSideDatasource;
68
81
  rowModelType: RowModelType;
69
- rowData?: PlAgDataTableRow[];
82
+ rowData?: PlAgDataTableV2Row[];
70
83
  }> {
71
84
  const pt = model.tableHandle;
72
85
  const specs = model.tableSpec;
@@ -153,7 +166,7 @@ export async function updatePFrameGridOptions(
153
166
 
154
167
  const ptShape = await pfDriver.getShape(pt);
155
168
  const rowCount = ptShape.rows;
156
- const columnDefs: ColDef<PlAgDataTableRow>[] = [
169
+ const columnDefs: ColDef<PlAgDataTableV2Row>[] = [
157
170
  makeRowNumberColDef(),
158
171
  ...fields.map((i) => makeColDef(i, specs[i], columnVisibility?.hiddenColIds, cellButtonAxisParams)),
159
172
  ];
@@ -229,7 +242,7 @@ export async function updatePFrameGridOptions(
229
242
  lastParams = params;
230
243
 
231
244
  let length = 0;
232
- let rowData: PlAgDataTableRow[] = [];
245
+ let rowData: PlAgDataTableV2Row[] = [];
233
246
  if (rowCount > 0 && params.request.startRow !== undefined && params.request.endRow !== undefined) {
234
247
  length = Math.min(rowCount, params.request.endRow) - params.request.startRow;
235
248
  if (length > 0) {
@@ -258,3 +271,85 @@ export async function updatePFrameGridOptions(
258
271
  serverSideDatasource,
259
272
  };
260
273
  }
274
+
275
+ export type PlAgCellButtonAxisParams = {
276
+ showCellButtonForAxisId?: AxisId;
277
+ cellButtonInvokeRowsOnDoubleClick?: boolean;
278
+ trigger: (key?: PTableKey) => void;
279
+ };
280
+
281
+ /**
282
+ * Calculates column definition for a given p-table column
283
+ */
284
+ export function makeColDef(
285
+ iCol: number,
286
+ spec: PTableColumnSpec,
287
+ hiddenColIds?: string[],
288
+ cellButtonAxisParams?: PlAgCellButtonAxisParams,
289
+ ): ColDef {
290
+ const colId = stringifyPTableColumnSpec(spec);
291
+ const valueType = spec.type === 'axis' ? spec.spec.type : spec.spec.valueType;
292
+ return {
293
+ colId,
294
+ mainMenuItems: defaultMainMenuItems,
295
+ context: spec,
296
+ field: iCol.toString(),
297
+ headerName: spec.spec.annotations?.['pl7.app/label']?.trim() ?? 'Unlabeled ' + spec.type + ' ' + iCol.toString(),
298
+ lockPosition: spec.type === 'axis',
299
+ hide: hiddenColIds?.includes(colId) ?? isColumnOptional(spec.spec),
300
+ valueFormatter: defaultValueFormatter,
301
+ headerComponent: PlAgColumnHeader,
302
+ cellRendererSelector: cellButtonAxisParams?.showCellButtonForAxisId
303
+ ? (params: ICellRendererParams) => {
304
+ if (spec.type !== 'axis') return;
305
+
306
+ const axisId = (params.colDef?.context as PTableColumnSpec)?.id as AxisId;
307
+ if (lodash.isEqual(axisId, cellButtonAxisParams.showCellButtonForAxisId)) {
308
+ return {
309
+ component: PlAgTextAndButtonCell,
310
+ params: {
311
+ invokeRowsOnDoubleClick: cellButtonAxisParams.cellButtonInvokeRowsOnDoubleClick,
312
+ onClick: (params: ICellRendererParams<PlAgDataTableV2Row>) => {
313
+ cellButtonAxisParams.trigger(params.data?.key);
314
+ },
315
+ },
316
+ };
317
+ }
318
+ }
319
+ : undefined,
320
+ headerComponentParams: {
321
+ type: ((): PlAgHeaderComponentType => {
322
+ switch (valueType) {
323
+ case 'Int':
324
+ case 'Long':
325
+ case 'Float':
326
+ case 'Double':
327
+ return 'Number';
328
+ case 'String':
329
+ case 'Bytes':
330
+ return 'Text';
331
+ default:
332
+ throw Error(`unsupported data type: ${valueType}`);
333
+ }
334
+ })(),
335
+ } satisfies PlAgHeaderComponentParams,
336
+ cellDataType: (() => {
337
+ switch (valueType) {
338
+ case 'Int':
339
+ case 'Long':
340
+ case 'Float':
341
+ case 'Double':
342
+ return 'number';
343
+ case 'String':
344
+ case 'Bytes':
345
+ return 'text';
346
+ default:
347
+ throw Error(`unsupported data type: ${valueType}`);
348
+ }
349
+ })(),
350
+ };
351
+ }
352
+
353
+ export function makeRowId(rowKey: PTableKey): PTableKeyJson {
354
+ return canonicalizeJson(rowKey);
355
+ }
@@ -1,15 +1,22 @@
1
1
  import type {
2
2
  ColDef,
3
+ ICellRendererParams,
3
4
  IServerSideDatasource,
4
5
  IServerSideGetRowsParams,
5
6
  RowModelType,
6
7
  } from 'ag-grid-enterprise';
7
8
  import type {
9
+ AxisId,
8
10
  PlDataTableGridStateWithoutSheets,
11
+ PTableColumnSpec,
12
+ PTableRowKey,
9
13
  } from '@platforma-sdk/model';
10
14
  import {
15
+ canonicalizeJson,
11
16
  getAxisId,
17
+ isColumnOptional,
12
18
  pTableValue,
19
+ stringifyPTableColumnSpec,
13
20
  type PColumnSpec,
14
21
  type PFrameDriver,
15
22
  type PlDataTableSheet,
@@ -17,12 +24,16 @@ import {
17
24
  type PTableVector,
18
25
  } from '@platforma-sdk/model';
19
26
  import * as lodash from 'lodash';
20
- import type { PlAgDataTableRow } from '../types';
27
+ import type { PlAgDataTableRow, PTableRowKeyJson } from '../types';
21
28
  import { makeRowNumberColDef, PlAgDataTableRowNumberColId } from './row-number';
22
29
  import { getHeterogeneousColumns, updatePFrameGridOptionsHeterogeneousAxes } from './table-source-heterogeneous';
23
30
  import { objectHash } from '../../../objectHash';
24
31
  import type { Ref } from 'vue';
25
- import { isLabelColumn, makeColDef, makeRowId, type PlAgCellButtonAxisParams } from './common';
32
+ import { defaultValueFormatter, isLabelColumn } from './common';
33
+ import { defaultMainMenuItems } from './menu-items';
34
+ import type { PlAgHeaderComponentParams, PlAgHeaderComponentType } from '../../PlAgColumnHeader';
35
+ import { PlAgColumnHeader } from '../../PlAgColumnHeader';
36
+ import { PlAgTextAndButtonCell } from '../../PlAgTextAndButtonCell';
26
37
 
27
38
  /** Convert columnar data from the driver to rows, used by ag-grid */
28
39
  function columns2rows(fields: number[], columns: PTableVector[], axes: number[]): PlAgDataTableRow[] {
@@ -246,3 +257,85 @@ export async function updatePFrameGridOptions(
246
257
  serverSideDatasource,
247
258
  };
248
259
  }
260
+
261
+ export type PlAgCellButtonAxisParams = {
262
+ showCellButtonForAxisId?: AxisId;
263
+ cellButtonInvokeRowsOnDoubleClick?: boolean;
264
+ trigger: (key?: PTableRowKey) => void;
265
+ };
266
+
267
+ /**
268
+ * Calculates column definition for a given p-table column
269
+ */
270
+ export function makeColDef(
271
+ iCol: number,
272
+ spec: PTableColumnSpec,
273
+ hiddenColIds?: string[],
274
+ cellButtonAxisParams?: PlAgCellButtonAxisParams,
275
+ ): ColDef {
276
+ const colId = stringifyPTableColumnSpec(spec);
277
+ const valueType = spec.type === 'axis' ? spec.spec.type : spec.spec.valueType;
278
+ return {
279
+ colId,
280
+ mainMenuItems: defaultMainMenuItems,
281
+ context: spec,
282
+ field: iCol.toString(),
283
+ headerName: spec.spec.annotations?.['pl7.app/label']?.trim() ?? 'Unlabeled ' + spec.type + ' ' + iCol.toString(),
284
+ lockPosition: spec.type === 'axis',
285
+ hide: hiddenColIds?.includes(colId) ?? isColumnOptional(spec.spec),
286
+ valueFormatter: defaultValueFormatter,
287
+ headerComponent: PlAgColumnHeader,
288
+ cellRendererSelector: cellButtonAxisParams?.showCellButtonForAxisId
289
+ ? (params: ICellRendererParams) => {
290
+ if (spec.type !== 'axis') return;
291
+
292
+ const axisId = (params.colDef?.context as PTableColumnSpec)?.id as AxisId;
293
+ if (lodash.isEqual(axisId, cellButtonAxisParams.showCellButtonForAxisId)) {
294
+ return {
295
+ component: PlAgTextAndButtonCell,
296
+ params: {
297
+ invokeRowsOnDoubleClick: cellButtonAxisParams.cellButtonInvokeRowsOnDoubleClick,
298
+ onClick: (prms: ICellRendererParams<PlAgDataTableRow>) => {
299
+ cellButtonAxisParams.trigger(prms.data?.key);
300
+ },
301
+ },
302
+ };
303
+ }
304
+ }
305
+ : undefined,
306
+ headerComponentParams: {
307
+ type: ((): PlAgHeaderComponentType => {
308
+ switch (valueType) {
309
+ case 'Int':
310
+ case 'Long':
311
+ case 'Float':
312
+ case 'Double':
313
+ return 'Number';
314
+ case 'String':
315
+ case 'Bytes':
316
+ return 'Text';
317
+ default:
318
+ throw Error(`unsupported data type: ${valueType}`);
319
+ }
320
+ })(),
321
+ } satisfies PlAgHeaderComponentParams,
322
+ cellDataType: (() => {
323
+ switch (valueType) {
324
+ case 'Int':
325
+ case 'Long':
326
+ case 'Float':
327
+ case 'Double':
328
+ return 'number';
329
+ case 'String':
330
+ case 'Bytes':
331
+ return 'text';
332
+ default:
333
+ throw Error(`unsupported data type: ${valueType}`);
334
+ }
335
+ })(),
336
+ };
337
+ }
338
+
339
+ export function makeRowId(rowKey: PTableRowKey): PTableRowKeyJson {
340
+ return canonicalizeJson(rowKey);
341
+ }
@@ -7,9 +7,12 @@ import type {
7
7
  PlTableFilterType,
8
8
  PTableColumnId,
9
9
  PTableHandle,
10
+ PTableKey,
11
+ PTableRowKey,
10
12
  PTableValue,
11
13
  RemoteBlobHandleAndSize,
12
14
  } from '@platforma-sdk/model';
15
+ import type { PTableHidden } from './sources/common';
13
16
 
14
17
  export type PlDataTableSettingsPTable = {
15
18
  /** The type of the source to feed the data into the table */
@@ -62,9 +65,6 @@ export type PlTableFiltersDefault = {
62
65
  default: PlTableFilter;
63
66
  };
64
67
 
65
- /** Key is a set of all axes values, which means it is unique across rows */
66
- export type PTableRowKey = PTableValue[];
67
-
68
68
  /** PlAgDataTable controller contains all exported methods */
69
69
  export type PlAgDataTableController = {
70
70
  /**
@@ -74,8 +74,20 @@ export type PlAgDataTableController = {
74
74
  focusRow: (rowKey: PTableRowKey) => Promise<void>;
75
75
  };
76
76
 
77
- /** Canonicalized PTableValue array JSON string */
78
- export type PTableRowKeyJson = CanonicalizedJson<PTableValue[]>;
77
+ /** PlAgDataTable controller contains all exported methods */
78
+ export type PlAgDataTableV2Controller = {
79
+ /**
80
+ * Scroll table to make row with provided key visible
81
+ * Warning: works reliably only in client side mode.
82
+ */
83
+ focusRow: (rowKey: PTableKey) => Promise<void>;
84
+ };
85
+
86
+ /**
87
+ * Canonicalized PTableValue array JSON string
88
+ * @deprecated Migrate to PlAgDataTableV2
89
+ */
90
+ export type PTableRowKeyJson = CanonicalizedJson<PTableRowKey>;
79
91
 
80
92
  /** PlAgDataTable row */
81
93
  export type PlAgDataTableRow = {
@@ -87,6 +99,18 @@ export type PlAgDataTableRow = {
87
99
  [field: `${number}` | `hC${number}`]: PTableValue;
88
100
  };
89
101
 
102
+ export type PTableKeyJson = CanonicalizedJson<PTableKey>;
103
+
104
+ /** PlAgDataTableV2 row */
105
+ export type PlAgDataTableV2Row = {
106
+ /** Axis key is not present for heterogeneous axes */
107
+ key?: PTableKey;
108
+ /** Unique row identifier, created as canonicalize(key)! when key is present */
109
+ id: PTableKeyJson;
110
+ /** Row values by column; sheet axes and labeled axes are excluded */
111
+ [field: `${number}`]: PTableValue | PTableHidden;
112
+ };
113
+
90
114
  export type PlAgOverlayLoadingParams = {
91
115
  /**
92
116
  * Required flag, that shows catInBag icon with message if `true`, shows PlSplash component if `false`.
@@ -0,0 +1,135 @@
1
+ <script lang="ts" setup>
2
+ import { computed, reactive } from 'vue';
3
+ import { getCssBackgroundImage } from './highlight';
4
+ import {
5
+ chemicalPropertiesColors,
6
+ useChemicalPropertiesHighlight,
7
+ } from './highlight/chemical-properties';
8
+ import { useAlignedRows } from './multi-sequence-alignment';
9
+ import type { SequenceRow } from './types';
10
+
11
+ const { sequenceRows, highlight } = defineProps<{
12
+ sequenceRows: SequenceRow[];
13
+ highlight?: 'chemical-properties' | undefined;
14
+ }>();
15
+
16
+ const alignedRows = useAlignedRows(() => sequenceRows);
17
+
18
+ const chemicalPropertiesHighlight = reactive(
19
+ useChemicalPropertiesHighlight(() => {
20
+ if (highlight !== 'chemical-properties' || alignedRows.loading) {
21
+ return;
22
+ }
23
+ return sequenceRows.map(({ header }) => {
24
+ const row = alignedRows.value.get(header);
25
+ if (!row) {
26
+ console.error(`Missing aligned row for header ${header}`);
27
+ }
28
+ return alignedRows.value.get(header) ?? '';
29
+ });
30
+ }),
31
+ );
32
+
33
+ const columnCount = computed(
34
+ () => (sequenceRows.at(0)?.labels.length ?? 0) + 1,
35
+ );
36
+ const rowCount = computed(() => sequenceRows.length);
37
+
38
+ const selectedHighlight = computed(() => {
39
+ if (!highlight) {
40
+ return;
41
+ }
42
+ switch (highlight) {
43
+ case 'chemical-properties':
44
+ return chemicalPropertiesHighlight;
45
+ default:
46
+ throw new Error(`Unknown highlight ${highlight}`);
47
+ }
48
+ });
49
+
50
+ const sequenceLetterSpacing = '12px';
51
+ </script>
52
+
53
+ <template>
54
+ <div v-if="!alignedRows.loading" :class="['pl-scrollable', $style.container]">
55
+ <div
56
+ v-if="selectedHighlight && !selectedHighlight.loading"
57
+ :class="$style.highlight"
58
+ :style="
59
+ {
60
+ backgroundImage: getCssBackgroundImage({
61
+ columns: selectedHighlight.data,
62
+ rowCount: sequenceRows.length,
63
+ colors: chemicalPropertiesColors,
64
+ }),
65
+ }
66
+ "
67
+ />
68
+ <template
69
+ v-for="row of sequenceRows"
70
+ :key="row.header"
71
+ >
72
+ <div
73
+ v-for="(label, labelIndex) of row.labels"
74
+ :key="labelIndex"
75
+ :class="
76
+ [
77
+ $style.label,
78
+ labelIndex === row.labels.length - 1 && $style.last,
79
+ ]
80
+ "
81
+ >
82
+ {{ label }}
83
+ </div>
84
+ <div :class="$style.sequence">
85
+ {{ alignedRows.value.get(row.header) }}
86
+ </div>
87
+ </template>
88
+ </div>
89
+ </template>
90
+
91
+ <style module>
92
+ .container {
93
+ flex: 1;
94
+ position: relative;
95
+ font-family: Spline Sans Mono;
96
+ text-wrap: nowrap;
97
+ display: grid;
98
+ grid-template-columns: repeat(v-bind(columnCount), min-content);
99
+ grid-template-rows: repeat(v-bind(rowCount), min-content);
100
+ justify-content: start;
101
+ column-gap: 16px;
102
+ line-height: calc(24 / 14);
103
+
104
+ * {
105
+ content-visibility: auto;
106
+ }
107
+ }
108
+
109
+ .highlight {
110
+ position: absolute;
111
+ grid-area: 1 / -2 / -1 / -1; /* all rows and last column */
112
+ inset: 0 calc(v-bind(sequenceLetterSpacing) / -2);
113
+ background-size: calc(100% - v-bind(sequenceLetterSpacing));
114
+ background-repeat: no-repeat;
115
+ z-index: -1;
116
+ }
117
+
118
+ .label {
119
+ background-color: white;
120
+ position: sticky;
121
+ inset-inline-start: 0;
122
+ text-align: end;
123
+ z-index: 1;
124
+
125
+ &.last {
126
+ padding-inline-end: 16px;
127
+ border-inline-end: 1px solid black;
128
+ }
129
+ }
130
+
131
+ .sequence {
132
+ font-weight: 600;
133
+ letter-spacing: v-bind(sequenceLetterSpacing);
134
+ }
135
+ </style>
@@ -0,0 +1,173 @@
1
+ <script lang="ts" setup>
2
+ import {
3
+ PlAlert,
4
+ PlBtnGhost,
5
+ PlDropdownMulti,
6
+ PlIcon24,
7
+ PlSlideModal,
8
+ PlTooltip,
9
+ } from '@milaboratories/uikit';
10
+ import type {
11
+ PColumnPredicate,
12
+ PFrameHandle,
13
+ PlMultiSequenceAlignmentModel,
14
+ PlSelectionModel,
15
+ } from '@platforma-sdk/model';
16
+ import { computed, onMounted, reactive, ref } from 'vue';
17
+ import { useDataTableToolsPanelTarget } from '../PlAgDataTableToolsPanel';
18
+ import { useLabelColumns, useSequenceColumns, useSequenceRows } from './data';
19
+ import {
20
+ chemicalCategories,
21
+ chemicalPropertiesColors,
22
+ chemicalPropertiesLabels,
23
+ } from './highlight/chemical-properties';
24
+ import MultiSequenceAlignmentView from './MultiSequenceAlignmentView.vue';
25
+
26
+ const model = defineModel<PlMultiSequenceAlignmentModel>({ default: {} });
27
+
28
+ const props = defineProps<{
29
+ /**
30
+ * Handle to PFrame created using `createPFrameForGraphs`.
31
+ * Should contain all desired sequence and label columns.
32
+ */
33
+ readonly pFrame: PFrameHandle | undefined;
34
+ /**
35
+ * Return true if column should be shown in sequence columns dropdown.
36
+ * By default, all sequence columns are selected.
37
+ */
38
+ readonly sequenceColumnPredicate: PColumnPredicate;
39
+ /**
40
+ * Return true if column should be shown in label columns dropdown.
41
+ * By default, common axes of selected sequence columns are selected.
42
+ */
43
+ readonly labelColumnOptionPredicate?: PColumnPredicate;
44
+ /**
45
+ * Sometimes sequence column and label column have disjoint axes.
46
+ * In this case you have to define `linkerColumnPredicate` to select columns
47
+ * connecting axes of sequence and label columns.
48
+ */
49
+ readonly linkerColumnPredicate?: PColumnPredicate;
50
+ /**
51
+ * Row selection model (from `PlAgDataTableV2` or `GraphMaker`).
52
+ * If not provided or empty, all rows will be considered selected.
53
+ * Warning: should be forwarded as a field of `reactive` object
54
+ */
55
+ readonly selection?: PlSelectionModel | undefined;
56
+ }>();
57
+
58
+ // SlidePanel visibility flag
59
+ const show = ref(false);
60
+
61
+ // Teleport open button to DataTableToolsPanel after mount
62
+ const mounted = ref(false);
63
+ onMounted(() => {
64
+ mounted.value = true;
65
+ });
66
+ const teleportTarget = useDataTableToolsPanelTarget();
67
+
68
+ const sequenceColumns = reactive(useSequenceColumns(() => ({
69
+ pframe: props.pFrame,
70
+ sequenceColumnPredicate: props.sequenceColumnPredicate,
71
+ })));
72
+
73
+ const labelColumns = reactive(useLabelColumns(() => ({
74
+ pframe: props.pFrame,
75
+ sequenceColumnIds: sequenceColumns.defaults,
76
+ labelColumnOptionPredicate: props.labelColumnOptionPredicate,
77
+ })));
78
+
79
+ const selectedSequenceColumnIds = computed({
80
+ get: () => model.value.sequenceColumnIds ?? sequenceColumns.defaults,
81
+ set: (value) => {
82
+ model.value.sequenceColumnIds = value;
83
+ },
84
+ });
85
+ const selectedLabelColumnIds = computed({
86
+ get: () => model.value.labelColumnIds ?? labelColumns.defaults,
87
+ set: (value) => {
88
+ model.value.labelColumnIds = value;
89
+ },
90
+ });
91
+
92
+ const sequenceRows = reactive(useSequenceRows(() => ({
93
+ pframe: props.pFrame,
94
+ sequenceColumnIds: selectedSequenceColumnIds.value,
95
+ labelColumnIds: selectedLabelColumnIds.value,
96
+ linkerColumnPredicate: props.linkerColumnPredicate,
97
+ selection: props.selection,
98
+ })));
99
+ </script>
100
+
101
+ <template>
102
+ <Teleport v-if="mounted && teleportTarget" :to="teleportTarget">
103
+ <PlBtnGhost icon="dna" @click.stop="show = true">
104
+ Multi Alignment
105
+ </PlBtnGhost>
106
+ </Teleport>
107
+
108
+ <PlSlideModal v-model="show" width="100%" :close-on-outside-click="false">
109
+ <template #title>
110
+ Multi Alignment
111
+ <PlTooltip :class="$style.tooltip" position="southwest">
112
+ <PlIcon24 name="info" />
113
+ <template #tooltip>
114
+ <div
115
+ v-for="category in chemicalCategories"
116
+ :key="category"
117
+ >
118
+ <span
119
+ :class="$style['color-sample']"
120
+ :style="
121
+ {
122
+ backgroundColor:
123
+ chemicalPropertiesColors[category],
124
+ }
125
+ "
126
+ />
127
+ {{ chemicalPropertiesLabels[category] }}
128
+ </div>
129
+ </template>
130
+ </PlTooltip>
131
+ </template>
132
+
133
+ <PlDropdownMulti
134
+ v-model="selectedSequenceColumnIds"
135
+ label="Sequence Columns"
136
+ :options="sequenceColumns.options"
137
+ :disabled="sequenceColumns.loading"
138
+ clearable
139
+ />
140
+ <PlDropdownMulti
141
+ v-model="selectedLabelColumnIds"
142
+ label="Label Columns"
143
+ :options="labelColumns.options"
144
+ :disabled="labelColumns.loading"
145
+ clearable
146
+ />
147
+
148
+ <PlAlert v-if="sequenceRows.data.length < 2" type="warn">
149
+ Please select at least one sequence column and two or more rows to run
150
+ alignment
151
+ </PlAlert>
152
+
153
+ <MultiSequenceAlignmentView
154
+ v-else
155
+ :sequenceRows="sequenceRows.data"
156
+ highlight="chemical-properties"
157
+ />
158
+ </PlSlideModal>
159
+ </template>
160
+
161
+ <style module>
162
+ .tooltip {
163
+ display: inline-flex;
164
+ }
165
+ .color-sample {
166
+ display: inline-block;
167
+ width: 12px;
168
+ height: 12px;
169
+ border: 1px solid #ccc;
170
+ margin-right: 8px;
171
+ vertical-align: middle;
172
+ }
173
+ </style>