@platforma-sdk/ui-vue 1.45.34 → 1.45.36

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 (150) hide show
  1. package/.turbo/turbo-build.log +216 -222
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +16 -0
  4. package/dist/AgGridVue/useAgGridOptions.js +2 -3
  5. package/dist/AgGridVue/useAgGridOptions.js.map +1 -1
  6. package/dist/components/{PlMultiSequenceAlignment/Legend.vue.d.ts → PlAdvancedFilter/OperandButton.vue.d.ts} +4 -2
  7. package/dist/components/{PlMultiSequenceAlignment/Toolbar.vue.js → PlAdvancedFilter/OperandButton.vue.js} +3 -3
  8. package/dist/components/PlAdvancedFilter/OperandButton.vue.js.map +1 -0
  9. package/dist/components/PlAdvancedFilter/OperandButton.vue2.js +25 -0
  10. package/dist/components/PlAdvancedFilter/OperandButton.vue2.js.map +1 -0
  11. package/dist/components/PlAdvancedFilter/OperandButton.vue3.js +13 -0
  12. package/dist/components/PlAdvancedFilter/OperandButton.vue3.js.map +1 -0
  13. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue.d.ts +39 -0
  14. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue.js +10 -0
  15. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue.js.map +1 -0
  16. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue2.js +199 -0
  17. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue2.js.map +1 -0
  18. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue3.js +17 -0
  19. package/dist/components/PlAdvancedFilter/PlAdvancedFilter.vue3.js.map +1 -0
  20. package/dist/components/PlAdvancedFilter/SingleFilter.vue.d.ts +37 -0
  21. package/dist/components/{PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.js → PlAdvancedFilter/SingleFilter.vue.js} +3 -3
  22. package/dist/components/PlAdvancedFilter/SingleFilter.vue.js.map +1 -0
  23. package/dist/components/PlAdvancedFilter/SingleFilter.vue2.js +306 -0
  24. package/dist/components/PlAdvancedFilter/SingleFilter.vue2.js.map +1 -0
  25. package/dist/components/PlAdvancedFilter/SingleFilter.vue3.js +35 -0
  26. package/dist/components/PlAdvancedFilter/SingleFilter.vue3.js.map +1 -0
  27. package/dist/components/PlAdvancedFilter/constants.d.ts +4 -0
  28. package/dist/components/PlAdvancedFilter/constants.js +41 -0
  29. package/dist/components/PlAdvancedFilter/constants.js.map +1 -0
  30. package/dist/components/PlAdvancedFilter/index.d.ts +1 -0
  31. package/dist/components/PlAdvancedFilter/types.d.ts +57 -0
  32. package/dist/components/PlAdvancedFilter/types.js +8 -0
  33. package/dist/components/PlAdvancedFilter/types.js.map +1 -0
  34. package/dist/components/PlAdvancedFilter/utils.js +150 -0
  35. package/dist/components/PlAdvancedFilter/utils.js.map +1 -0
  36. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js +7 -8
  37. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js.map +1 -1
  38. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js +9 -10
  39. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js.map +1 -1
  40. package/dist/components/PlAgRowNumHeader.vue.js +2 -3
  41. package/dist/components/PlAgRowNumHeader.vue.js.map +1 -1
  42. package/dist/index.js +32 -32
  43. package/dist/lib.d.ts +1 -1
  44. package/package.json +6 -7
  45. package/src/components/PlAdvancedFilter/OperandButton.vue +53 -0
  46. package/src/components/PlAdvancedFilter/PlAdvancedFilter.vue +209 -0
  47. package/src/components/PlAdvancedFilter/SingleFilter.vue +425 -0
  48. package/src/components/PlAdvancedFilter/constants.ts +42 -0
  49. package/src/components/PlAdvancedFilter/index.ts +1 -0
  50. package/src/components/PlAdvancedFilter/types.ts +77 -0
  51. package/src/components/PlAdvancedFilter/utils.ts +215 -0
  52. package/src/lib.ts +2 -2
  53. package/dist/assets/multi-sequence-alignment.worker-Cm0gZp19.js +0 -6
  54. package/dist/assets/multi-sequence-alignment.worker-Cm0gZp19.js.map +0 -1
  55. package/dist/assets/phylogenetic-tree.worker-4CrExYEo.js +0 -5
  56. package/dist/assets/phylogenetic-tree.worker-4CrExYEo.js.map +0 -1
  57. package/dist/components/PlMultiSequenceAlignment/Consensus.vue.d.ts +0 -9
  58. package/dist/components/PlMultiSequenceAlignment/Consensus.vue.js +0 -10
  59. package/dist/components/PlMultiSequenceAlignment/Consensus.vue.js.map +0 -1
  60. package/dist/components/PlMultiSequenceAlignment/Consensus.vue2.js +0 -122
  61. package/dist/components/PlMultiSequenceAlignment/Consensus.vue2.js.map +0 -1
  62. package/dist/components/PlMultiSequenceAlignment/Consensus.vue3.js +0 -9
  63. package/dist/components/PlMultiSequenceAlignment/Consensus.vue3.js.map +0 -1
  64. package/dist/components/PlMultiSequenceAlignment/Legend.vue.js +0 -10
  65. package/dist/components/PlMultiSequenceAlignment/Legend.vue.js.map +0 -1
  66. package/dist/components/PlMultiSequenceAlignment/Legend.vue2.js +0 -28
  67. package/dist/components/PlMultiSequenceAlignment/Legend.vue2.js.map +0 -1
  68. package/dist/components/PlMultiSequenceAlignment/Legend.vue3.js +0 -13
  69. package/dist/components/PlMultiSequenceAlignment/Legend.vue3.js.map +0 -1
  70. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.d.ts +0 -25
  71. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.js +0 -10
  72. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.js.map +0 -1
  73. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue2.js +0 -138
  74. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue2.js.map +0 -1
  75. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue3.js +0 -31
  76. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue3.js.map +0 -1
  77. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue.d.ts +0 -8
  78. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue.js +0 -10
  79. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue.js.map +0 -1
  80. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue2.js +0 -77
  81. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue2.js.map +0 -1
  82. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue3.js +0 -9
  83. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue3.js.map +0 -1
  84. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.d.ts +0 -71
  85. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.js.map +0 -1
  86. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue2.js +0 -224
  87. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue2.js.map +0 -1
  88. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue3.js +0 -9
  89. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue3.js.map +0 -1
  90. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue.d.ts +0 -8
  91. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue.js +0 -10
  92. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue.js.map +0 -1
  93. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue2.js +0 -127
  94. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue2.js.map +0 -1
  95. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue3.js +0 -9
  96. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue3.js.map +0 -1
  97. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue.d.ts +0 -16
  98. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue.js.map +0 -1
  99. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js +0 -228
  100. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js.map +0 -1
  101. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue3.js +0 -19
  102. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue3.js.map +0 -1
  103. package/dist/components/PlMultiSequenceAlignment/cell-size.d.ts +0 -4
  104. package/dist/components/PlMultiSequenceAlignment/cell-size.js +0 -8
  105. package/dist/components/PlMultiSequenceAlignment/cell-size.js.map +0 -1
  106. package/dist/components/PlMultiSequenceAlignment/chemical-properties.d.ts +0 -44
  107. package/dist/components/PlMultiSequenceAlignment/chemical-properties.js +0 -132
  108. package/dist/components/PlMultiSequenceAlignment/chemical-properties.js.map +0 -1
  109. package/dist/components/PlMultiSequenceAlignment/data.d.ts +0 -61
  110. package/dist/components/PlMultiSequenceAlignment/data.js +0 -370
  111. package/dist/components/PlMultiSequenceAlignment/data.js.map +0 -1
  112. package/dist/components/PlMultiSequenceAlignment/index.d.ts +0 -1
  113. package/dist/components/PlMultiSequenceAlignment/markup.d.ts +0 -16
  114. package/dist/components/PlMultiSequenceAlignment/markup.js +0 -84
  115. package/dist/components/PlMultiSequenceAlignment/markup.js.map +0 -1
  116. package/dist/components/PlMultiSequenceAlignment/migrations.d.ts +0 -3
  117. package/dist/components/PlMultiSequenceAlignment/migrations.js +0 -24
  118. package/dist/components/PlMultiSequenceAlignment/migrations.js.map +0 -1
  119. package/dist/components/PlMultiSequenceAlignment/multi-sequence-alignment.worker.d.ts +0 -6
  120. package/dist/components/PlMultiSequenceAlignment/phylogenetic-tree.worker.d.ts +0 -7
  121. package/dist/components/PlMultiSequenceAlignment/residue-counts.d.ts +0 -2
  122. package/dist/components/PlMultiSequenceAlignment/residue-counts.js +0 -13
  123. package/dist/components/PlMultiSequenceAlignment/residue-counts.js.map +0 -1
  124. package/dist/components/PlMultiSequenceAlignment/settings.d.ts +0 -2
  125. package/dist/components/PlMultiSequenceAlignment/settings.js +0 -9
  126. package/dist/components/PlMultiSequenceAlignment/settings.js.map +0 -1
  127. package/dist/components/PlMultiSequenceAlignment/types.d.ts +0 -5
  128. package/dist/components/PlMultiSequenceAlignment/useMiPlots.d.ts +0 -4
  129. package/dist/components/PlMultiSequenceAlignment/useMiPlots.js +0 -19
  130. package/dist/components/PlMultiSequenceAlignment/useMiPlots.js.map +0 -1
  131. package/src/components/PlMultiSequenceAlignment/Consensus.vue +0 -165
  132. package/src/components/PlMultiSequenceAlignment/Legend.vue +0 -44
  133. package/src/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue +0 -299
  134. package/src/components/PlMultiSequenceAlignment/PhylogeneticTree.vue +0 -110
  135. package/src/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue +0 -314
  136. package/src/components/PlMultiSequenceAlignment/README.md +0 -216
  137. package/src/components/PlMultiSequenceAlignment/SeqLogo.vue +0 -166
  138. package/src/components/PlMultiSequenceAlignment/Toolbar.vue +0 -228
  139. package/src/components/PlMultiSequenceAlignment/cell-size.ts +0 -4
  140. package/src/components/PlMultiSequenceAlignment/chemical-properties.ts +0 -199
  141. package/src/components/PlMultiSequenceAlignment/data.ts +0 -661
  142. package/src/components/PlMultiSequenceAlignment/index.ts +0 -1
  143. package/src/components/PlMultiSequenceAlignment/markup.ts +0 -141
  144. package/src/components/PlMultiSequenceAlignment/migrations.ts +0 -46
  145. package/src/components/PlMultiSequenceAlignment/multi-sequence-alignment.worker.ts +0 -54
  146. package/src/components/PlMultiSequenceAlignment/phylogenetic-tree.worker.ts +0 -89
  147. package/src/components/PlMultiSequenceAlignment/residue-counts.ts +0 -124
  148. package/src/components/PlMultiSequenceAlignment/settings.ts +0 -7
  149. package/src/components/PlMultiSequenceAlignment/types.ts +0 -3
  150. package/src/components/PlMultiSequenceAlignment/useMiPlots.ts +0 -23
@@ -1,661 +0,0 @@
1
- import { isJsonEqual } from '@milaboratories/helpers';
2
- import type { ListOptionNormalized } from '@milaboratories/uikit';
3
- import {
4
- Annotation,
5
- type CalculateTableDataRequest,
6
- type CalculateTableDataResponse,
7
- type CanonicalizedJson,
8
- canonicalizeJson,
9
- createRowSelectionColumn,
10
- ensureError,
11
- getAxisId,
12
- getRawPlatformaInstance,
13
- isLabelColumn,
14
- isLinkerColumn,
15
- type JoinEntry,
16
- matchAxisId,
17
- parseJson,
18
- type PColumnIdAndSpec,
19
- type PFrameHandle,
20
- type PlMultiSequenceAlignmentColorSchemeOption,
21
- type PlMultiSequenceAlignmentSettings,
22
- type PlSelectionModel,
23
- type PObjectId,
24
- type PTableColumnId,
25
- type PTableSorting,
26
- pTableValue,
27
- readAnnotation,
28
- readAnnotationJson,
29
- } from '@platforma-sdk/model';
30
- import { onWatcherCleanup, ref, watch } from 'vue';
31
- import { objectHash } from '../../objectHash';
32
- import { highlightByChemicalProperties } from './chemical-properties';
33
- import type { Markup } from './markup';
34
- import {
35
- highlightByMarkup,
36
- markupAlignedSequence,
37
- parseMarkup,
38
- } from './markup';
39
- import type * as MultiSequenceAlignmentWorker from './multi-sequence-alignment.worker';
40
- import type * as PhylogeneticTreeWorker from './phylogenetic-tree.worker';
41
- import { getResidueCounts } from './residue-counts';
42
- import type { HighlightLegend, ResidueCounts } from './types';
43
-
44
- const getPFrameDriver = () => getRawPlatformaInstance().pFrameDriver;
45
-
46
- export const SEQUENCE_LIMIT = 1000;
47
-
48
- export const useSequenceColumnsOptions = refreshOnDeepChange(
49
- getSequenceColumnsOptions,
50
- );
51
-
52
- export const useLabelColumnsOptions = refreshOnDeepChange(
53
- getLabelColumnsOptions,
54
- );
55
-
56
- export const useMarkupColumnsOptions = refreshOnDeepChange(
57
- getMarkupColumnsOptions,
58
- );
59
-
60
- export const useMultipleAlignmentData = refreshOnDeepChange(
61
- getMultipleAlignmentData,
62
- );
63
-
64
- async function getSequenceColumnsOptions({ pFrame, sequenceColumnPredicate }: {
65
- pFrame: PFrameHandle | undefined;
66
- sequenceColumnPredicate: (column: PColumnIdAndSpec) => boolean;
67
- }): Promise<OptionsWithDefaults<PObjectId> | undefined> {
68
- if (!pFrame) return;
69
-
70
- const pFrameDriver = getPFrameDriver();
71
- const columns = await pFrameDriver.listColumns(pFrame);
72
-
73
- const options = columns.values()
74
- .filter((column) => sequenceColumnPredicate(column))
75
- .map(({ spec, columnId }) => ({
76
- label: readAnnotation(spec, Annotation.Label) ?? 'Unlabeled column',
77
- value: columnId,
78
- }))
79
- .toArray();
80
-
81
- const defaults = options.map(({ value }) => value);
82
-
83
- return { options, defaults };
84
- }
85
-
86
- async function getLabelColumnsOptions({ pFrame, sequenceColumnIds }: {
87
- pFrame: PFrameHandle | undefined;
88
- sequenceColumnIds: PObjectId[] | undefined;
89
- }): Promise<OptionsWithDefaults<PTableColumnId> | undefined> {
90
- if (!pFrame || !sequenceColumnIds) return;
91
-
92
- const pFrameDriver = getPFrameDriver();
93
- const columns = await pFrameDriver.listColumns(pFrame);
94
-
95
- const sequenceColumnsAxes = new Map(
96
- sequenceColumnIds.values().flatMap((id) => {
97
- const column = columns.find(({ columnId }) => columnId === id);
98
- if (!column) {
99
- throw new Error(`Couldn't find sequence column (ID: \`${id}\`).`);
100
- }
101
- return column.spec.axesSpec.values()
102
- .map((spec) => [canonicalizeJson(getAxisId(spec)), spec]);
103
- }),
104
- );
105
-
106
- const optionMap = new Map<CanonicalizedJson<PTableColumnId>, string>();
107
- for (const [axisIdJson, axisSpec] of sequenceColumnsAxes.entries()) {
108
- const axisId = parseJson(axisIdJson);
109
- const labelColumn = columns.find(({ spec }) =>
110
- isLabelColumn(spec) && matchAxisId(axisId, getAxisId(spec.axesSpec[0])),
111
- );
112
- optionMap.set(
113
- labelColumn
114
- ? canonicalizeJson({ type: 'column', id: labelColumn.columnId })
115
- : canonicalizeJson({ type: 'axis', id: axisId }),
116
- readAnnotation(labelColumn?.spec, Annotation.Label)
117
- ?? readAnnotation(axisSpec, Annotation.Label)
118
- ?? 'Unlabeled axis',
119
- );
120
- }
121
-
122
- const { hits: compatibleColumns } = await pFrameDriver.findColumns(pFrame, {
123
- columnFilter: {},
124
- compatibleWith: sequenceColumnsAxes.keys()
125
- .map((axisIdJson) => parseJson(axisIdJson))
126
- .toArray(),
127
- strictlyCompatible: false,
128
- });
129
-
130
- for (const { columnId, spec } of compatibleColumns) {
131
- const columnIdJson = canonicalizeJson<PTableColumnId>({
132
- type: 'column',
133
- id: columnId,
134
- });
135
- if (optionMap.has(columnIdJson)) continue;
136
- optionMap.set(
137
- columnIdJson,
138
- readAnnotation(spec, Annotation.Label) ?? 'Unlabeled column',
139
- );
140
- }
141
-
142
- const options = optionMap.entries()
143
- .map(([value, label]) => ({ label, value: parseJson(value) }))
144
- .toArray();
145
-
146
- const defaults = options.values()
147
- .filter(({ value }) => {
148
- if (value.type === 'axis') return true;
149
- const column = columns.find(({ columnId }) => columnId === value.id);
150
- return column && isLabelColumn(column.spec);
151
- })
152
- .map(({ value }) => value)
153
- .toArray();
154
-
155
- return { options, defaults };
156
- }
157
-
158
- async function getMarkupColumnsOptions({ pFrame, sequenceColumnIds }: {
159
- pFrame: PFrameHandle | undefined;
160
- sequenceColumnIds: PObjectId[] | undefined;
161
- }): Promise<ListOptionNormalized<PObjectId[]>[] | undefined> {
162
- if (!pFrame || !sequenceColumnIds) return;
163
-
164
- const pFrameDriver = getPFrameDriver();
165
- const columns = await pFrameDriver.listColumns(pFrame);
166
-
167
- const sequenceColumns = sequenceColumnIds.map((columnId) => {
168
- const column = columns.find((column) => column.columnId === columnId);
169
- if (!column) {
170
- throw new Error(
171
- `Couldn't find sequence column (ID: \`${sequenceColumnIds[0]}\`).`,
172
- );
173
- }
174
- return column;
175
- });
176
-
177
- const columnPairs = sequenceColumns
178
- .flatMap((sequenceColumn) =>
179
- columns
180
- .filter((column) =>
181
- readAnnotationJson(column.spec, Annotation.Sequence.IsAnnotation)
182
- && isJsonEqual(sequenceColumn.spec.axesSpec, column.spec.axesSpec)
183
- && Object.entries(sequenceColumn.spec.domain ?? {})
184
- .every(([key, value]) => column.spec.domain?.[key] === value),
185
- )
186
- .map((markupColumn) => ({ markupColumn, sequenceColumn })),
187
- );
188
-
189
- const groupedByDomainDiff = Map.groupBy(
190
- columnPairs,
191
- ({ markupColumn, sequenceColumn }) => {
192
- const domainDiff = Object.fromEntries(
193
- Object.entries(markupColumn.spec.domain ?? {})
194
- .filter(([key]) => sequenceColumn.spec.domain?.[key] == undefined),
195
- );
196
- return canonicalizeJson(domainDiff);
197
- },
198
- );
199
-
200
- return groupedByDomainDiff.entries()
201
- .map(([domainDiffJson, columnPairs]) => ({
202
- label: Object.values(parseJson(domainDiffJson)).join(', '),
203
- value: columnPairs.map(({ markupColumn }) => markupColumn.columnId),
204
- }))
205
- .toArray();
206
- }
207
-
208
- async function getMultipleAlignmentData(
209
- {
210
- pFrame,
211
- sequenceColumnIds,
212
- labelColumnIds,
213
- selection,
214
- colorScheme,
215
- alignmentParams,
216
- shouldBuildPhylogeneticTree,
217
- }: {
218
- pFrame: PFrameHandle | undefined;
219
- sequenceColumnIds: PObjectId[] | undefined;
220
- labelColumnIds: PTableColumnId[] | undefined;
221
- selection: PlSelectionModel | undefined;
222
- colorScheme: PlMultiSequenceAlignmentColorSchemeOption;
223
- alignmentParams: PlMultiSequenceAlignmentSettings['alignmentParams'];
224
- shouldBuildPhylogeneticTree: boolean;
225
- },
226
- abortSignal: AbortSignal,
227
- ): Promise<MultipleAlignmentData | undefined> {
228
- if (!pFrame || !sequenceColumnIds?.length || !labelColumnIds) return;
229
-
230
- const table = await getTableData({
231
- pFrame,
232
- sequenceColumnIds,
233
- labelColumnIds,
234
- selection,
235
- colorScheme,
236
- });
237
-
238
- const rowCount = table.at(0)?.data.data.length ?? 0;
239
-
240
- if (rowCount < 2) return;
241
-
242
- const exceedsLimit = rowCount > SEQUENCE_LIMIT;
243
- const rawSequences = extractSequences(sequenceColumnIds, table);
244
- const labels = extractLabels(labelColumnIds, table);
245
- const markups = colorScheme.type === 'markup'
246
- ? extractMarkups(colorScheme.columnIds, table)
247
- : undefined;
248
-
249
- const highlightLegend: HighlightLegend = {};
250
-
251
- const alignedSequences = await Promise.all(
252
- rawSequences.map(async ({ name, rows }) => ({
253
- name,
254
- rows: await alignSequences(
255
- rows,
256
- JSON.parse(JSON.stringify(alignmentParams)),
257
- abortSignal,
258
- ),
259
- })),
260
- );
261
-
262
- let phylogeneticTree: PhylogeneticTreeWorker.TreeNodeData[] | undefined;
263
- if (shouldBuildPhylogeneticTree) {
264
- phylogeneticTree = await buildPhylogeneticTree(
265
- alignedSequences,
266
- abortSignal,
267
- );
268
- const rowOrder = phylogeneticTree.values()
269
- .filter(({ id }) => id >= 0)
270
- .map(({ id }) => id)
271
- .toArray();
272
- for (const sequencesColumn of alignedSequences) {
273
- sequencesColumn.rows = rowOrder.map((i) => sequencesColumn.rows[i]);
274
- }
275
- for (const labelsColumn of labels) {
276
- labelsColumn.rows = rowOrder.map((i) => labelsColumn.rows[i]);
277
- }
278
- for (const markupsColumn of markups ?? []) {
279
- markupsColumn.rows = rowOrder.map((i) => markupsColumn.rows[i]);
280
- }
281
- }
282
-
283
- const sequences = await Promise.all(
284
- alignedSequences.map(async ({ name, rows }, index) => {
285
- const residueCounts = getResidueCounts(rows);
286
- const image = generateHighlightImage({
287
- colorScheme,
288
- sequences: rows,
289
- residueCounts,
290
- markup: markups?.at(index),
291
- });
292
- if (image) {
293
- Object.assign(highlightLegend, image.legend);
294
- }
295
- return {
296
- name,
297
- rows,
298
- residueCounts,
299
- ...image && {
300
- highlightImageUrl: await blobToBase64(image.blob),
301
- },
302
- } satisfies MultipleAlignmentData['sequences'][number];
303
- }),
304
- );
305
-
306
- return {
307
- sequences,
308
- labels,
309
- ...Object.keys(highlightLegend).length && {
310
- highlightLegend,
311
- },
312
- ...phylogeneticTree && {
313
- phylogeneticTree,
314
- },
315
- exceedsLimit,
316
- };
317
- }
318
-
319
- async function getTableData(
320
- { pFrame, sequenceColumnIds, labelColumnIds, selection, colorScheme }: {
321
- pFrame: PFrameHandle;
322
- sequenceColumnIds: PObjectId[];
323
- labelColumnIds: PTableColumnId[];
324
- selection: PlSelectionModel | undefined;
325
- colorScheme: PlMultiSequenceAlignmentColorSchemeOption;
326
- },
327
- ): Promise<CalculateTableDataResponse> {
328
- const pFrameDriver = getPFrameDriver();
329
- const columns = await pFrameDriver.listColumns(pFrame);
330
- const linkerColumns = columns.filter((column) => isLinkerColumn(column.spec));
331
-
332
- const filterColumn = createRowSelectionColumn({ selection });
333
-
334
- // inner join of sequence columns
335
- let primaryEntry: JoinEntry<PObjectId> = {
336
- type: 'inner',
337
- entries: sequenceColumnIds.map((column) => ({
338
- type: 'column',
339
- column,
340
- })),
341
- };
342
-
343
- // if we have linkers, left join them
344
- if (linkerColumns.length > 0) {
345
- primaryEntry = {
346
- type: 'outer',
347
- primary: primaryEntry,
348
- secondary: linkerColumns.map(({ columnId }) => ({
349
- type: 'column',
350
- column: columnId,
351
- })),
352
- };
353
- }
354
-
355
- // inner join with filters
356
- if (filterColumn) {
357
- primaryEntry = {
358
- type: 'inner',
359
- entries: [
360
- primaryEntry,
361
- {
362
- type: 'inlineColumn',
363
- column: filterColumn,
364
- },
365
- ],
366
- };
367
- }
368
-
369
- // left join with labels
370
- const secondaryEntry: JoinEntry<PObjectId>[] = labelColumnIds
371
- .flatMap((column) => {
372
- if (column.type !== 'column') return [];
373
- return { type: 'column', column: column.id };
374
- });
375
-
376
- // and markup
377
- if (colorScheme.type === 'markup') {
378
- for (const column of colorScheme.columnIds) {
379
- secondaryEntry.push({ type: 'column', column });
380
- }
381
- }
382
-
383
- const sorting: PTableSorting[] = Array.from(
384
- new Set(
385
- sequenceColumnIds.values().flatMap((id) => {
386
- const column = columns.find(({ columnId }) => columnId === id);
387
- if (!column) {
388
- throw new Error(`Couldn't find sequence column (ID: ${id})`);
389
- }
390
- return column.spec.axesSpec
391
- .map((spec) => canonicalizeJson(getAxisId(spec)));
392
- }),
393
- ),
394
- )
395
- .sort()
396
- .map((id) => ({
397
- column: { type: 'axis', id: parseJson(id) },
398
- ascending: true,
399
- naAndAbsentAreLeastValues: true,
400
- }));
401
-
402
- const request: CalculateTableDataRequest<PObjectId> = {
403
- src: {
404
- type: 'outer',
405
- primary: primaryEntry,
406
- secondary: secondaryEntry,
407
- },
408
- filters: [],
409
- sorting,
410
- };
411
-
412
- return pFrameDriver.calculateTableData(
413
- pFrame,
414
- JSON.parse(JSON.stringify(request)),
415
- {
416
- offset: 0,
417
- // +1 is a hack to check whether the selection is over the limit
418
- length: SEQUENCE_LIMIT + 1,
419
- },
420
- );
421
- }
422
-
423
- const extractSequences = (
424
- columnIds: PObjectId[],
425
- table: CalculateTableDataResponse,
426
- ): { name: string; rows: string[] }[] =>
427
- columnIds.map((columnId) => {
428
- const column = table.find(({ spec }) => spec.id === columnId);
429
- if (!column) {
430
- throw new Error(`Couldn't find sequence column (ID: \`${columnId}\`).`);
431
- }
432
- const name = readAnnotation(column.spec.spec, Annotation.Label)
433
- ?? 'Unlabeled column';
434
- const rows = column.data.data
435
- .keys()
436
- .take(SEQUENCE_LIMIT)
437
- .map((row) =>
438
- pTableValue(column.data, row, { absent: '', na: '' })?.toString()
439
- ?? '',
440
- )
441
- .toArray();
442
- return { name, rows };
443
- });
444
-
445
- const extractLabels = (
446
- columnIds: PTableColumnId[],
447
- table: CalculateTableDataResponse,
448
- ): { rows: string[] }[] =>
449
- columnIds.map((columnId) => {
450
- const column = table.find(({ spec }) => {
451
- if (columnId.type === 'axis' && spec.type === 'axis') {
452
- return isJsonEqual(columnId.id, spec.id);
453
- }
454
- if (columnId.type === 'column' && spec.type === 'column') {
455
- return columnId.id === spec.id;
456
- }
457
- });
458
- if (!column) {
459
- throw new Error(`Couldn't find label column (ID: \`${columnId}\`).`);
460
- }
461
- const rows = column.data.data
462
- .keys()
463
- .take(SEQUENCE_LIMIT)
464
- .map((row) =>
465
- pTableValue(column.data, row, { absent: '', na: '' })?.toString()
466
- ?? '',
467
- )
468
- .toArray();
469
- return { rows };
470
- });
471
-
472
- const extractMarkups = (
473
- columnIds: PObjectId[],
474
- table: CalculateTableDataResponse,
475
- ): { labels: Record<string, string>; rows: Markup[] }[] =>
476
- columnIds.map((columnId) => {
477
- const column = table.find(({ spec }) => spec.id === columnId);
478
- if (!column) {
479
- throw new Error(`Couldn't find markup column (ID: \`${columnId}\`).`);
480
- }
481
- const labels = readAnnotationJson(
482
- column.spec.spec,
483
- Annotation.Sequence.Annotation.Mapping,
484
- ) ?? {};
485
- const rows = column.data.data
486
- .keys()
487
- .take(SEQUENCE_LIMIT)
488
- .map((row) =>
489
- parseMarkup(
490
- pTableValue(column.data, row, { absent: '', na: '' })?.toString()
491
- ?? '',
492
- ),
493
- )
494
- .toArray();
495
- return { labels, rows };
496
- });
497
-
498
- const alignSequences = (() => {
499
- const cache = new Map<string, string[]>();
500
- return async (
501
- sequences: string[],
502
- alignmentParams: PlMultiSequenceAlignmentSettings['alignmentParams'],
503
- abortSignal: AbortSignal,
504
- ): Promise<string[]> => {
505
- const hash = await objectHash([sequences, alignmentParams]);
506
- let result = cache.get(hash);
507
- if (result) return result;
508
- result = await runInWorker<
509
- MultiSequenceAlignmentWorker.RequestMessage,
510
- MultiSequenceAlignmentWorker.ResponseMessage
511
- >(
512
- new Worker(
513
- new URL('./multi-sequence-alignment.worker.ts', import.meta.url),
514
- { type: 'module' },
515
- ),
516
- { sequences, params: alignmentParams },
517
- abortSignal,
518
- );
519
- cache.set(hash, result);
520
- return result;
521
- };
522
- })();
523
-
524
- function generateHighlightImage(
525
- { colorScheme, sequences, residueCounts, markup }: {
526
- colorScheme: PlMultiSequenceAlignmentColorSchemeOption;
527
- sequences: string[];
528
- residueCounts: ResidueCounts;
529
- markup: { labels: Record<string, string>; rows: Markup[] } | undefined;
530
- },
531
- ): { blob: Blob; legend: HighlightLegend } | undefined {
532
- if (colorScheme.type === 'chemical-properties') {
533
- return highlightByChemicalProperties({ sequences, residueCounts });
534
- }
535
- if (colorScheme.type === 'markup') {
536
- if (!markup) {
537
- throw new Error('Missing markup data.');
538
- }
539
- return highlightByMarkup({
540
- markupRows: sequences.map((sequence, row) => {
541
- const markupRow = markup.rows.at(row);
542
- if (!markupRow) throw new Error(`Missing markup for row ${row}.`);
543
- return markupAlignedSequence(sequence, markupRow);
544
- }),
545
- columnCount: sequences.at(0)?.length ?? 0,
546
- labels: markup.labels,
547
- });
548
- }
549
- }
550
-
551
- const blobToBase64 = (blob: Blob): Promise<string> =>
552
- new Promise<string>((resolve, reject) => {
553
- const reader = new FileReader();
554
- reader.addEventListener('load', () => resolve(reader.result as string));
555
- reader.addEventListener('error', () => reject(reader.error));
556
- reader.readAsDataURL(blob);
557
- });
558
-
559
- const buildPhylogeneticTree = (() => {
560
- const cache = new Map<string, PhylogeneticTreeWorker.TreeNodeData[]>();
561
- return async (data: { rows: string[] }[], abortSignal: AbortSignal) => {
562
- const concatenatedSequences = data.at(0)?.rows
563
- .keys()
564
- .map((row) => data.map((column) => column.rows.at(row) ?? '').join(''))
565
- .toArray() ?? [];
566
- const hash = await objectHash(concatenatedSequences);
567
- let result = cache.get(hash);
568
- if (result) return result;
569
- result = await runInWorker<
570
- PhylogeneticTreeWorker.RequestMessage,
571
- PhylogeneticTreeWorker.ResponseMessage
572
- >(
573
- new Worker(
574
- new URL('./phylogenetic-tree.worker.ts', import.meta.url),
575
- { type: 'module' },
576
- ),
577
- concatenatedSequences,
578
- abortSignal,
579
- );
580
- cache.set(hash, result);
581
- return result;
582
- };
583
- })();
584
-
585
- const runInWorker = <RequestMessage, ResponseMessage>(
586
- worker: Worker,
587
- message: RequestMessage,
588
- abortSignal: AbortSignal,
589
- ) =>
590
- new Promise<ResponseMessage>((resolve, reject) => {
591
- worker.addEventListener('message', ({ data }) => {
592
- resolve(data);
593
- worker.terminate();
594
- });
595
- worker.addEventListener('error', ({ error, message }) => {
596
- reject(error ?? message);
597
- worker.terminate();
598
- });
599
- abortSignal.addEventListener('abort', () => {
600
- reject(abortSignal.reason);
601
- worker.terminate();
602
- });
603
- worker.postMessage(message);
604
- });
605
-
606
- function refreshOnDeepChange<T, P>(
607
- cb: (params: P, abortSignal: AbortSignal) => Promise<T>,
608
- ) {
609
- const data = ref<T>();
610
- const isLoading = ref(true);
611
- const error = ref<Error>();
612
- let requestId: symbol;
613
- return (paramsGetter: () => P) => {
614
- watch(paramsGetter, async (params, prevParams) => {
615
- if (isJsonEqual(params, prevParams)) return;
616
- const abortController = new AbortController();
617
- const currentRequestId = requestId = Symbol();
618
- onWatcherCleanup(() => {
619
- abortController.abort();
620
- });
621
- try {
622
- error.value = undefined;
623
- isLoading.value = true;
624
- const result = await cb(params, abortController.signal);
625
- if (currentRequestId === requestId) {
626
- data.value = result;
627
- }
628
- } catch (err) {
629
- console.error(err);
630
- if (currentRequestId === requestId) {
631
- error.value = ensureError(err);
632
- }
633
- } finally {
634
- if (currentRequestId === requestId) {
635
- isLoading.value = false;
636
- }
637
- }
638
- }, { immediate: true });
639
- return { data, isLoading, error };
640
- };
641
- }
642
-
643
- type MultipleAlignmentData = {
644
- sequences: {
645
- name: string;
646
- rows: string[];
647
- residueCounts: ResidueCounts;
648
- highlightImageUrl?: string;
649
- }[];
650
- labels: {
651
- rows: string[];
652
- }[];
653
- highlightLegend?: HighlightLegend;
654
- phylogeneticTree?: PhylogeneticTreeWorker.TreeNodeData[];
655
- exceedsLimit: boolean;
656
- };
657
-
658
- type OptionsWithDefaults<T> = {
659
- options: ListOptionNormalized<T>[];
660
- defaults: T[];
661
- };
@@ -1 +0,0 @@
1
- export { default as PlMultiSequenceAlignment } from './PlMultiSequenceAlignment.vue';