@platforma-sdk/ui-vue 1.42.51 → 1.43.0

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 (88) hide show
  1. package/.turbo/turbo-build.log +213 -206
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +21 -0
  4. package/dist/AgGridVue/useAgGridOptions.js +2 -3
  5. package/dist/AgGridVue/useAgGridOptions.js.map +1 -1
  6. package/dist/assets/multi-sequence-alignment.worker-Cm0gZp19.js +6 -0
  7. package/dist/assets/multi-sequence-alignment.worker-Cm0gZp19.js.map +1 -0
  8. package/dist/assets/phylogenetic-tree.worker-4CrExYEo.js +5 -0
  9. package/dist/assets/phylogenetic-tree.worker-4CrExYEo.js.map +1 -0
  10. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js +2 -3
  11. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js.map +1 -1
  12. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js +11 -12
  13. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js.map +1 -1
  14. package/dist/components/PlAgRowNumHeader.vue.js +8 -9
  15. package/dist/components/PlAgRowNumHeader.vue.js.map +1 -1
  16. package/dist/components/PlMultiSequenceAlignment/Consensus.vue.d.ts +1 -0
  17. package/dist/components/PlMultiSequenceAlignment/Consensus.vue2.js +48 -46
  18. package/dist/components/PlMultiSequenceAlignment/Consensus.vue2.js.map +1 -1
  19. package/dist/components/PlMultiSequenceAlignment/Consensus.vue3.js +5 -7
  20. package/dist/components/PlMultiSequenceAlignment/Consensus.vue3.js.map +1 -1
  21. package/dist/components/PlMultiSequenceAlignment/Legend.vue2.js +14 -13
  22. package/dist/components/PlMultiSequenceAlignment/Legend.vue2.js.map +1 -1
  23. package/dist/components/PlMultiSequenceAlignment/Legend.vue3.js +9 -8
  24. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue.d.ts +16 -9
  25. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue2.js +117 -85
  26. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue2.js.map +1 -1
  27. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue3.js +25 -18
  28. package/dist/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue3.js.map +1 -1
  29. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue.d.ts +8 -0
  30. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue.js +10 -0
  31. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue.js.map +1 -0
  32. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue2.js +77 -0
  33. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue2.js.map +1 -0
  34. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue3.js +9 -0
  35. package/dist/components/PlMultiSequenceAlignment/PhylogeneticTree.vue3.js.map +1 -0
  36. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue.d.ts +26 -18
  37. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue2.js +119 -120
  38. package/dist/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue2.js.map +1 -1
  39. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue.js +7 -124
  40. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue.js.map +1 -1
  41. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue2.js +124 -2
  42. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue2.js.map +1 -1
  43. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue3.js +9 -0
  44. package/dist/components/PlMultiSequenceAlignment/SeqLogo.vue3.js.map +1 -0
  45. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js +90 -90
  46. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js.map +1 -1
  47. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue3.js +9 -7
  48. package/dist/components/PlMultiSequenceAlignment/cell-size.d.ts +4 -0
  49. package/dist/components/PlMultiSequenceAlignment/cell-size.js +8 -0
  50. package/dist/components/PlMultiSequenceAlignment/cell-size.js.map +1 -0
  51. package/dist/components/PlMultiSequenceAlignment/data.d.ts +15 -10
  52. package/dist/components/PlMultiSequenceAlignment/data.js +309 -202
  53. package/dist/components/PlMultiSequenceAlignment/data.js.map +1 -1
  54. package/dist/components/PlMultiSequenceAlignment/markup.js +9 -7
  55. package/dist/components/PlMultiSequenceAlignment/markup.js.map +1 -1
  56. package/dist/components/PlMultiSequenceAlignment/migrations.js +15 -13
  57. package/dist/components/PlMultiSequenceAlignment/migrations.js.map +1 -1
  58. package/dist/components/PlMultiSequenceAlignment/multi-sequence-alignment.worker.d.ts +6 -0
  59. package/dist/components/PlMultiSequenceAlignment/phylogenetic-tree.worker.d.ts +7 -0
  60. package/dist/components/PlMultiSequenceAlignment/settings.js +3 -4
  61. package/dist/components/PlMultiSequenceAlignment/settings.js.map +1 -1
  62. package/dist/index.js +25 -27
  63. package/dist/index.js.map +1 -1
  64. package/dist/lib/util/helpers/dist/parse.js +24 -0
  65. package/dist/lib/util/helpers/dist/parse.js.map +1 -0
  66. package/dist/lib/util/helpers/dist/test_timeouts.js +5 -24
  67. package/dist/lib/util/helpers/dist/test_timeouts.js.map +1 -1
  68. package/dist/lib.d.ts +1 -2
  69. package/package.json +8 -8
  70. package/src/components/PlMultiSequenceAlignment/Consensus.vue +38 -39
  71. package/src/components/PlMultiSequenceAlignment/Legend.vue +9 -9
  72. package/src/components/PlMultiSequenceAlignment/MultiSequenceAlignmentView.vue +222 -126
  73. package/src/components/PlMultiSequenceAlignment/PhylogeneticTree.vue +110 -0
  74. package/src/components/PlMultiSequenceAlignment/PlMultiSequenceAlignment.vue +28 -22
  75. package/src/components/PlMultiSequenceAlignment/SeqLogo.vue +77 -69
  76. package/src/components/PlMultiSequenceAlignment/Toolbar.vue +47 -39
  77. package/src/components/PlMultiSequenceAlignment/cell-size.ts +4 -0
  78. package/src/components/PlMultiSequenceAlignment/data.ts +361 -149
  79. package/src/components/PlMultiSequenceAlignment/markup.ts +10 -8
  80. package/src/components/PlMultiSequenceAlignment/migrations.ts +6 -1
  81. package/src/components/PlMultiSequenceAlignment/multi-sequence-alignment.worker.ts +54 -0
  82. package/src/components/PlMultiSequenceAlignment/phylogenetic-tree.worker.ts +89 -0
  83. package/src/components/PlMultiSequenceAlignment/settings.ts +1 -2
  84. package/src/lib.ts +1 -3
  85. package/dist/components/PlMultiSequenceAlignment/multi-sequence-alignment.d.ts +0 -7
  86. package/dist/components/PlMultiSequenceAlignment/multi-sequence-alignment.js +0 -51
  87. package/dist/components/PlMultiSequenceAlignment/multi-sequence-alignment.js.map +0 -1
  88. package/src/components/PlMultiSequenceAlignment/multi-sequence-alignment.ts +0 -101
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/ui-vue",
3
- "version": "1.42.51",
3
+ "version": "1.43.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "styles": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "@types/node": "~20.16.15",
16
16
  "@types/semver": "^7.7.0",
17
17
  "@types/d3-format": "^3.0.4",
18
- "@milaboratories/miplots4": "^1.0.123",
18
+ "@milaboratories/miplots4": "^1.0.143",
19
19
  "ag-grid-enterprise": "^34.1.2",
20
20
  "ag-grid-vue3": "^34.1.2",
21
21
  "canonicalize": "~2.1.0",
@@ -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/biowasm-tools": "1.1.2",
29
- "@milaboratories/uikit": "2.4.12",
30
- "@platforma-sdk/model": "1.42.51"
28
+ "@milaboratories/biowasm-tools": "2.0.0",
29
+ "@milaboratories/uikit": "2.4.14",
30
+ "@platforma-sdk/model": "1.43.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "happy-dom": "^15.11.7",
@@ -42,11 +42,11 @@
42
42
  "yarpm": "^1.2.0",
43
43
  "fast-json-patch": "^3.1.1",
44
44
  "@faker-js/faker": "^9.2.0",
45
+ "@milaboratories/eslint-config": "1.0.4",
45
46
  "@milaboratories/ts-configs": "1.0.6",
47
+ "@milaboratories/helpers": "1.8.0",
46
48
  "@milaboratories/build-configs": "1.0.8",
47
- "@milaboratories/helpers": "1.7.0",
48
- "@milaboratories/ts-builder": "1.0.5",
49
- "@milaboratories/eslint-config": "1.0.4"
49
+ "@milaboratories/ts-builder": "1.0.5"
50
50
  },
51
51
  "scripts": {
52
52
  "test": "vitest run --passWithNoTests",
@@ -9,20 +9,25 @@ import {
9
9
  computed,
10
10
  onBeforeUnmount,
11
11
  shallowRef,
12
+ useCssModule,
12
13
  useTemplateRef,
13
14
  watchEffect,
14
15
  } from 'vue';
16
+ import { cellSize } from './cell-size';
15
17
  import type { ResidueCounts } from './types';
16
18
  import { useMiPlots } from './useMiPlots';
17
19
 
18
- const { residueCounts } = defineProps<{
20
+ const props = defineProps<{
19
21
  residueCounts: ResidueCounts;
22
+ labelsClass: string;
20
23
  }>();
21
24
 
25
+ const classes = useCssModule();
26
+
22
27
  const plotEl = useTemplateRef('plotEl');
23
28
 
24
- const columns = computed(() => {
25
- return residueCounts.map((column) => {
29
+ const columns = computed(() =>
30
+ props.residueCounts.map((column) => {
26
31
  let totalCount = 0;
27
32
  let topResidue = { label: '', count: 0 };
28
33
  for (const [residue, count] of Object.entries(column)) {
@@ -35,11 +40,11 @@ const columns = computed(() => {
35
40
  label: topResidue.label,
36
41
  color: `color-mix(in oklab, ${confidence} #3056AE, #C1CDE9)`,
37
42
  };
38
- });
39
- });
43
+ }),
44
+ );
40
45
 
41
- const settings = computed<Settings | undefined>(() => {
42
- const width = residueCounts.length * 20;
46
+ const settings = computed(() => {
47
+ const width = props.residueCounts.length * cellSize.inline;
43
48
  return ({
44
49
  type: 'discrete',
45
50
  y: {
@@ -52,7 +57,7 @@ const settings = computed<Settings | undefined>(() => {
52
57
  type: 'column',
53
58
  value: 'columnKey',
54
59
  },
55
- order: residueCounts.map((_, i) => i),
60
+ order: props.residueCounts.map((_, i) => i),
56
61
  inheritedAes: Object.fromEntries(
57
62
  columns.value.map(({ color }) => ({ fillColor: color })).entries(),
58
63
  ),
@@ -61,8 +66,9 @@ const settings = computed<Settings | undefined>(() => {
61
66
  type: 'bar',
62
67
  height: 'max',
63
68
  aes: {
64
- ...residueCounts.length && {
65
- width: (width - residueCounts.length + 1) / residueCounts.length,
69
+ ...props.residueCounts.length && {
70
+ width: (width - props.residueCounts.length + 1)
71
+ / props.residueCounts.length,
66
72
  },
67
73
  fillColor: {
68
74
  type: 'primaryGrouping',
@@ -95,34 +101,32 @@ const settings = computed<Settings | undefined>(() => {
95
101
  frame: {
96
102
  type: 'empty',
97
103
  },
98
- });
104
+ } satisfies Settings);
99
105
  });
100
106
 
101
- const data = computed<DataByColumns>(
102
- () => {
103
- const countKey: number[] = [];
104
- const columnKey: number[] = [];
105
- for (const [columnIndex, column] of residueCounts.entries()) {
106
- for (const [residue, count] of Object.entries(column)) {
107
- if (residue === '-') continue;
108
- countKey.push(residue === ' ' ? 0 : count);
109
- columnKey.push(columnIndex);
110
- }
107
+ const data = computed<DataByColumns>(() => {
108
+ const countKey: number[] = [];
109
+ const columnKey: number[] = [];
110
+ for (const [columnIndex, column] of props.residueCounts.entries()) {
111
+ for (const [residue, count] of Object.entries(column)) {
112
+ if (residue === '-') continue;
113
+ countKey.push(count);
114
+ columnKey.push(columnIndex);
111
115
  }
112
- return ({
113
- type: 'columns',
114
- id: `consensus-${crypto.randomUUID()}`,
115
- values: { countKey, columnKey },
116
- });
117
- },
118
- );
116
+ }
117
+ return ({
118
+ type: 'columns',
119
+ id: `consensus-${crypto.randomUUID()}`,
120
+ values: { countKey, columnKey },
121
+ });
122
+ });
119
123
 
120
124
  const plot = shallowRef<ChartInterface>();
121
125
 
122
126
  const { miplots, error } = useMiPlots();
123
127
 
124
128
  watchEffect(async () => {
125
- if (!settings.value || !plotEl.value || !miplots.value) return;
129
+ if (!plotEl.value || !miplots.value) return;
126
130
  if (!plot.value) {
127
131
  plot.value = miplots.value.newPlot(data.value, settings.value);
128
132
  plot.value.mount(plotEl.value);
@@ -140,8 +144,8 @@ onBeforeUnmount(() => {
140
144
  <PlAlert v-if="error" type="error">
141
145
  {{ error.message }}
142
146
  </PlAlert>
143
- <div v-else :class="$style.container">
144
- <div :class="$style.labels">
147
+ <div v-else :class="classes.container">
148
+ <div :class="props.labelsClass">
145
149
  {{ columns.map(column => column.label).join('') }}
146
150
  </div>
147
151
  <div ref="plotEl" />
@@ -153,14 +157,9 @@ onBeforeUnmount(() => {
153
157
  display: flex;
154
158
  flex-direction: column;
155
159
  gap: 4px;
156
- }
157
160
 
158
- .labels {
159
- font-family: Spline Sans Mono;
160
- font-weight: 600;
161
- line-height: 24px;
162
- letter-spacing: 11.6px;
163
- text-indent: 5.8px;
164
- margin-inline-end: -5.8px;
161
+ svg {
162
+ display: block;
163
+ }
165
164
  }
166
165
  </style>
@@ -1,22 +1,22 @@
1
1
  <script setup lang="ts">
2
+ import { useCssModule } from 'vue';
2
3
  import type { HighlightLegend } from './types';
3
4
 
4
- defineProps<{
5
+ const props = defineProps<{
5
6
  legend: HighlightLegend;
6
7
  }>();
8
+
9
+ const classes = useCssModule();
7
10
  </script>
8
11
 
9
12
  <template>
10
- <div :class="$style.container">
13
+ <div :class="classes.container">
11
14
  <div
12
- v-for="([key, { label, color }]) of Object.entries(legend)"
15
+ v-for="({ label, color }, key) of props.legend"
13
16
  :key="key"
14
- :class="$style.item"
17
+ :class="classes.item"
15
18
  >
16
- <div
17
- :class="$style['color-sample']"
18
- :style="{ backgroundColor: color }"
19
- />
19
+ <div :class="classes.colorSample" :style="{ backgroundColor: color }" />
20
20
  {{ label }}
21
21
  </div>
22
22
  </div>
@@ -35,7 +35,7 @@ defineProps<{
35
35
  gap: 4px;
36
36
  }
37
37
 
38
- .color-sample {
38
+ .colorSample {
39
39
  display: inline-block;
40
40
  block-size: 18px;
41
41
  inline-size: 18px;
@@ -1,83 +1,165 @@
1
1
  <script lang="ts" setup>
2
- import { useObjectUrl } from '@vueuse/core';
3
- import { computed, useTemplateRef } from 'vue';
2
+ import type { PlMultiSequenceAlignmentWidget } from '@platforma-sdk/model';
3
+ import {
4
+ computed,
5
+ onBeforeMount,
6
+ onBeforeUnmount,
7
+ onWatcherCleanup,
8
+ ref,
9
+ useCssModule,
10
+ useTemplateRef,
11
+ watch,
12
+ } from 'vue';
13
+ import { cellSize } from './cell-size';
4
14
  import Consensus from './Consensus.vue';
5
15
  import Legend from './Legend.vue';
16
+ import type { TreeNodeData } from './phylogenetic-tree.worker';
17
+ import PhylogeneticTree from './PhylogeneticTree.vue';
6
18
  import SeqLogo from './SeqLogo.vue';
7
19
  import type { HighlightLegend, ResidueCounts } from './types';
8
20
 
9
- const { sequences, highlightImage } = defineProps<{
10
- sequences: string[];
11
- sequenceNames: string[];
12
- labelRows: string[][];
13
- residueCounts: ResidueCounts;
14
- highlightImage: {
15
- blob: Blob;
16
- legend: HighlightLegend;
17
- } | undefined;
18
- widgets: ('consensus' | 'seqLogo' | 'legend')[];
21
+ const props = defineProps<{
22
+ sequences: {
23
+ name: string;
24
+ rows: string[];
25
+ residueCounts: ResidueCounts;
26
+ highlightImageUrl?: string;
27
+ }[];
28
+ labels: {
29
+ rows: string[];
30
+ }[];
31
+ highlightLegend: HighlightLegend | undefined;
32
+ phylogeneticTree: TreeNodeData[] | undefined;
33
+ widgets: PlMultiSequenceAlignmentWidget[];
19
34
  }>();
20
35
 
36
+ const classes = useCssModule();
37
+
21
38
  const rootEl = useTemplateRef('rootRef');
22
39
  defineExpose({ rootEl });
23
40
 
24
- const highlightImageObjectUrl = useObjectUrl(() => highlightImage?.blob);
25
- const highlightImageCssUrl = computed(() =>
26
- highlightImageObjectUrl.value
27
- ? `url('${highlightImageObjectUrl.value}')`
28
- : 'none',
41
+ const rowCount = computed(() => props.sequences.at(0)?.rows.length ?? 0);
42
+
43
+ const targetCellInlineSize = CSS.px(cellSize.inline).toString();
44
+ const targetCellBlockSize = CSS.px(cellSize.block).toString();
45
+
46
+ const referenceCellRef = useTemplateRef('referenceCell');
47
+ const referenceCellInlineSize = ref<number>();
48
+
49
+ const cornerRef = useTemplateRef('corner');
50
+ const cornerInlineSize = ref<number>();
51
+
52
+ const letterSpacing = computed(() =>
53
+ referenceCellInlineSize.value
54
+ ? CSS.px(cellSize.inline - referenceCellInlineSize.value).toString()
55
+ : undefined,
29
56
  );
30
57
 
31
- const sequenceLengths = computed(() =>
32
- sequences.at(0)?.split(' ').map(({ length }) => length),
58
+ const sequenceNameInsetInlineStart = computed(() =>
59
+ CSS.px(cornerInlineSize.value ?? 0).toString(),
33
60
  );
61
+
62
+ let observer: ResizeObserver;
63
+
64
+ onBeforeMount(() => {
65
+ const getInlineSize = (entry: ResizeObserverEntry) =>
66
+ entry.borderBoxSize.find(Boolean)?.inlineSize;
67
+
68
+ observer = new ResizeObserver((entries) => {
69
+ for (const entry of entries) {
70
+ switch (entry.target) {
71
+ case referenceCellRef.value:
72
+ referenceCellInlineSize.value = getInlineSize(entry);
73
+ break;
74
+ case cornerRef.value:
75
+ cornerInlineSize.value = getInlineSize(entry);
76
+ break;
77
+ }
78
+ }
79
+ });
80
+ });
81
+
82
+ onBeforeUnmount(() => {
83
+ observer.disconnect();
84
+ });
85
+
86
+ for (const ref of [referenceCellRef, cornerRef]) {
87
+ watch(ref, (el, prevEl) => {
88
+ if (el) observer.observe(el);
89
+ onWatcherCleanup(() => {
90
+ if (prevEl) observer.unobserve(prevEl);
91
+ });
92
+ });
93
+ }
34
94
  </script>
35
95
 
36
96
  <template>
37
- <div ref="rootRef" :class="$style.root">
38
- <div :class="['pl-scrollable', $style.table]">
39
- <div :class="$style.corner">
40
- <div :class="$style['label-scroll-indicator']" />
41
- </div>
42
- <div :class="$style.header">
43
- <div v-if="sequenceNames.length > 1" :class="$style['sequence-names']">
44
- <span
45
- v-for="(name, index) of sequenceNames"
46
- :key="index"
47
- :style="{
48
- inlineSize: ((sequenceLengths?.at(index) ?? 0) * 20)
49
- + 'px',
50
- }"
51
- >{{ name }}</span>
52
- </div>
53
- <Consensus v-if="widgets.includes('consensus')" :residue-counts />
54
- <SeqLogo v-if="widgets.includes('seqLogo')" :residue-counts />
55
- </div>
56
- <div :class="$style.labels">
57
- <div :class="$style['labels-grid']">
58
- <template v-for="(labelRow, rowIndex) of labelRows">
59
- <div
60
- v-for="(label, labelIndex) of labelRow"
61
- :key="labelIndex"
62
- :style="{ gridRow: rowIndex + 1 }"
63
- >
64
- {{ label }}
97
+ <div ref="rootRef" :class="classes.root">
98
+ <div ref="referenceCell" :class="classes.referenceCell">x</div>
99
+ <div :class="['pl-scrollable', classes.table]">
100
+ <div :class="classes.sidebar">
101
+ <PhylogeneticTree
102
+ v-if="props.widgets.includes('tree') && props.phylogeneticTree"
103
+ :tree="props.phylogeneticTree"
104
+ :class="classes.phylogeneticTree"
105
+ />
106
+ <div :class="classes.labels">
107
+ <template
108
+ v-for="({ rows }, columnIndex) of props.labels"
109
+ :key="columnIndex"
110
+ >
111
+ <div v-for="(row, rowIndex) of rows" :key="rowIndex">
112
+ {{ row }}
65
113
  </div>
66
114
  </template>
67
115
  </div>
68
116
  </div>
69
- <div :class="$style.sequences">
117
+ <template v-if="letterSpacing !== undefined">
70
118
  <div
71
- v-for="(sequence, index) of sequences"
72
- :key="index"
119
+ v-for="(column, columnIndex) of props.sequences"
120
+ :key="columnIndex"
121
+ :class="classes.sequenceColumn"
73
122
  >
74
- {{ sequence }}
123
+ <div :class="classes.sequenceHeader">
124
+ <div
125
+ v-show="props.sequences.length > 1"
126
+ :class="classes.sequenceName"
127
+ >
128
+ {{ column.name }}
129
+ </div>
130
+ <Consensus
131
+ v-if="props.widgets.includes('consensus')"
132
+ :residue-counts="column.residueCounts"
133
+ :labels-class="classes.sequenceRow"
134
+ />
135
+ <SeqLogo
136
+ v-if="props.widgets.includes('seqLogo')"
137
+ :residue-counts="column.residueCounts"
138
+ />
139
+ </div>
140
+ <div
141
+ :class="classes.sequenceRowsContainer"
142
+ :style="{
143
+ backgroundImage: column.highlightImageUrl
144
+ ? `url(${column.highlightImageUrl})`
145
+ : undefined,
146
+ }"
147
+ >
148
+ <div
149
+ v-for="(row, rowIndex) of column.rows"
150
+ :key="rowIndex"
151
+ :class="classes.sequenceRow"
152
+ >
153
+ {{ row }}
154
+ </div>
155
+ </div>
75
156
  </div>
76
- </div>
157
+ </template>
158
+ <div ref="corner" :class="classes.corner" />
77
159
  </div>
78
160
  <Legend
79
- v-if="widgets?.includes('legend') && highlightImage?.legend"
80
- :legend="highlightImage.legend"
161
+ v-if="props.widgets.includes('legend') && props.highlightLegend"
162
+ :legend="props.highlightLegend"
81
163
  />
82
164
  </div>
83
165
  </template>
@@ -90,114 +172,128 @@ const sequenceLengths = computed(() =>
90
172
  min-block-size: 0;
91
173
  -webkit-print-color-adjust: exact;
92
174
  print-color-adjust: exact;
175
+ container-type: inline-size;
93
176
 
94
177
  &[data-pre-print] {
95
- .table {
96
- container-type: unset;
97
- }
98
- .labels {
178
+ container-type: unset;
179
+
180
+ .sidebar {
99
181
  max-inline-size: unset;
100
182
  }
101
183
  }
102
184
  }
103
185
 
186
+ .referenceCell {
187
+ position: fixed;
188
+ visibility: hidden;
189
+ font-family: Spline Sans Mono;
190
+ font-weight: 600;
191
+ line-height: v-bind('targetCellBlockSize');
192
+ }
193
+
104
194
  .table {
105
- container-type: inline-size;
106
195
  display: grid;
107
- grid-template-areas:
108
- "corner header"
109
- "labels sequences";
196
+ grid-template-columns:
197
+ [sidebar-start] auto [sidebar-end] repeat(
198
+ v-bind('props.sequences.length'),
199
+ [column-start] auto [column-end]
200
+ );
201
+ grid-template-rows:
202
+ [header-start] auto [header-end]
203
+ repeat(v-bind('rowCount'), [row-start] auto [row-end]);
110
204
  justify-content: start;
111
- timeline-scope: --msa-labels-scroll;
205
+ position: relative;
112
206
  @media print {
113
207
  overflow: visible;
114
208
  }
115
209
  }
116
210
 
117
- .corner {
118
- grid-area: corner;
119
- background-color: #fff;
211
+ .sidebar {
212
+ grid-column: sidebar;
213
+ grid-row: 1 row-start / -1 row-end;
214
+ display: grid;
215
+ grid-template-rows: subgrid;
120
216
  position: sticky;
121
217
  inset-inline-start: 0;
122
- inset-block-start: 0;
123
- z-index: 2;
218
+ background-color: #fff;
219
+ inline-size: min-content;
220
+ max-inline-size: 30cqi;
221
+ overflow: scroll;
222
+ overscroll-behavior-inline: none;
223
+ scrollbar-width: none;
124
224
  }
125
225
 
126
- .label-scroll-indicator {
127
- position: absolute;
128
- inset-inline-end: 0;
129
- block-size: 100cqb;
130
- inline-size: 8px;
131
- animation-name: hide;
132
- animation-timeline: --msa-labels-scroll;
133
- visibility: hidden;
134
- background: #fff;
135
- box-shadow: -4px 0 4px -2px rgba(0, 0, 0, 0.10);
226
+ .phylogeneticTree {
227
+ grid-row: 1 row-start / -1 row-end;
136
228
  }
137
229
 
138
- .header {
139
- grid-area: header;
140
- background-color: #fff;
230
+ .labels {
231
+ grid-row: 1 row-start / -1 row-end;
232
+ display: grid;
233
+ grid-template-columns: repeat(v-bind('props.labels.length'), auto);
234
+ grid-template-rows: subgrid;
235
+ grid-auto-flow: column;
236
+ column-gap: 12px;
237
+ padding-inline-end: 12px;
238
+ font-family: Spline Sans Mono;
239
+ line-height: v-bind('targetCellBlockSize');
240
+ white-space: nowrap;
241
+ }
242
+
243
+ .sequenceColumn {
244
+ grid-row: header-start / -1 row-end;
245
+ display: grid;
246
+ grid-template-rows: subgrid;
247
+ & + & {
248
+ margin-inline-start: 24px;
249
+ }
250
+ }
251
+
252
+ .sequenceHeader {
253
+ grid-row: header;
254
+ display: flex;
255
+ flex-direction: column;
256
+ justify-content: end;
257
+ min-inline-size: 0;
141
258
  position: sticky;
142
259
  inset-block-start: 0;
143
- z-index: 1;
260
+ background-color: #fff;
144
261
  }
145
262
 
146
- .sequence-names {
147
- display: flex;
263
+ .sequenceName {
264
+ margin-block-end: 4px;
148
265
  font-weight: 700;
149
266
  line-height: 20px;
150
- margin-block-end: 4px;
151
- gap: 20px;
152
- }
153
-
154
- .labels {
155
- grid-area: labels;
156
- background-color: #fff;
267
+ inline-size: fit-content;
157
268
  position: sticky;
158
- inset-inline-start: 0;
159
- z-index: 1;
160
- inline-size: max-content;
161
- max-inline-size: 30cqi;
162
- overflow: scroll;
163
- scrollbar-width: none;
164
- overscroll-behavior-inline: none;
165
- scroll-timeline: --msa-labels-scroll inline;
269
+ inset-inline-start: v-bind('sequenceNameInsetInlineStart');
166
270
  }
167
271
 
168
- .labels-grid {
272
+ .sequenceRowsContainer {
273
+ grid-row: 1 row-start / -1 row-end;
169
274
  display: grid;
170
- grid-auto-flow: dense;
171
- font-family: Spline Sans Mono;
172
- line-height: 24px;
173
- text-wrap: nowrap;
174
-
175
- > * {
176
- padding-inline-end: 12px;
177
- }
275
+ grid-template-rows: subgrid;
178
276
  }
179
277
 
180
- .sequences {
181
- grid-area: sequences;
182
- display: flex;
183
- flex-direction: column;
278
+ .sequenceRow {
184
279
  font-family: Spline Sans Mono;
185
280
  font-weight: 600;
186
- line-height: 24px;
187
- letter-spacing: 11.6px;
188
- text-indent: 5.8px;
189
- margin-inline-end: -5.8px;
190
- background-image: v-bind(highlightImageCssUrl);
191
- background-repeat: no-repeat;
192
- background-size: calc(100% - 5.8px) 100%;
281
+ line-height: v-bind('targetCellBlockSize');
282
+ letter-spacing: v-bind('letterSpacing');
283
+ text-indent: calc(v-bind('letterSpacing') / 2);
284
+ inline-size: calc-size(
285
+ min-content,
286
+ round(down, size, v-bind('targetCellInlineSize'))
287
+ );
288
+ white-space: nowrap;
193
289
  }
194
290
 
195
- @keyframes hide {
196
- from {
197
- visibility: visible;
198
- }
199
- to {
200
- visibility: hidden;
201
- }
291
+ .corner {
292
+ grid-column: sidebar;
293
+ grid-row: header;
294
+ position: sticky;
295
+ inset-inline-start: 0;
296
+ inset-block-start: 0;
297
+ background-color: #fff;
202
298
  }
203
299
  </style>