@platforma-sdk/ui-vue 1.44.15 → 1.45.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 (83) hide show
  1. package/.turbo/turbo-build.log +28 -17
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +16 -0
  4. package/dist/AgGridVue/useAgGridOptions.js +3 -2
  5. package/dist/AgGridVue/useAgGridOptions.js.map +1 -1
  6. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js +8 -7
  7. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js.map +1 -1
  8. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js +10 -9
  9. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js.map +1 -1
  10. package/dist/components/PlAgRowNumHeader.vue.js +3 -2
  11. package/dist/components/PlAgRowNumHeader.vue.js.map +1 -1
  12. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue.d.ts +4 -4
  13. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue2.js.map +1 -1
  14. package/dist/components/PlAnnotations/components/DynamicForm.vue.d.ts +5 -4
  15. package/dist/components/PlAnnotations/components/DynamicForm.vue2.js +64 -61
  16. package/dist/components/PlAnnotations/components/DynamicForm.vue2.js.map +1 -1
  17. package/dist/components/PlAnnotations/components/FilterSidebar.vue.d.ts +13 -12
  18. package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js +49 -48
  19. package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js.map +1 -1
  20. package/dist/components/PlAnnotations/components/PlAnnotations.vue.d.ts +19 -0
  21. package/dist/components/PlAnnotations/components/PlAnnotations.vue.js +10 -0
  22. package/dist/components/PlAnnotations/components/PlAnnotations.vue.js.map +1 -0
  23. package/dist/components/PlAnnotations/components/PlAnnotations.vue2.js +66 -0
  24. package/dist/components/PlAnnotations/components/PlAnnotations.vue2.js.map +1 -0
  25. package/dist/components/PlAnnotations/components/PlAnnotations.vue3.js +13 -0
  26. package/dist/components/PlAnnotations/components/PlAnnotations.vue3.js.map +1 -0
  27. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue.d.ts +6 -13
  28. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue2.js +35 -78
  29. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue2.js.map +1 -1
  30. package/dist/components/PlAnnotations/index.d.ts +1 -0
  31. package/dist/components/PlAnnotations/types.d.ts +12 -6
  32. package/dist/components/PlAnnotations/utils.d.ts +4 -4
  33. package/dist/components/PlAnnotations/utils.js +2 -11
  34. package/dist/components/PlAnnotations/utils.js.map +1 -1
  35. package/dist/components/PlBtnExportArchive/Item.vue.d.ts +6 -0
  36. package/dist/components/PlBtnExportArchive/Item.vue.js +10 -0
  37. package/dist/components/PlBtnExportArchive/Item.vue.js.map +1 -0
  38. package/dist/components/PlBtnExportArchive/Item.vue2.js +43 -0
  39. package/dist/components/PlBtnExportArchive/Item.vue2.js.map +1 -0
  40. package/dist/components/PlBtnExportArchive/Item.vue3.js +15 -0
  41. package/dist/components/PlBtnExportArchive/Item.vue3.js.map +1 -0
  42. package/dist/components/PlBtnExportArchive/PlBtnExportArchive.vue.d.ts +34 -0
  43. package/dist/components/PlBtnExportArchive/PlBtnExportArchive.vue.js +10 -0
  44. package/dist/components/PlBtnExportArchive/PlBtnExportArchive.vue.js.map +1 -0
  45. package/dist/components/PlBtnExportArchive/PlBtnExportArchive.vue2.js +154 -0
  46. package/dist/components/PlBtnExportArchive/PlBtnExportArchive.vue2.js.map +1 -0
  47. package/dist/components/PlBtnExportArchive/PlBtnExportArchive.vue3.js +13 -0
  48. package/dist/components/PlBtnExportArchive/PlBtnExportArchive.vue3.js.map +1 -0
  49. package/dist/components/PlBtnExportArchive/Summary.vue.d.ts +10 -0
  50. package/dist/components/PlBtnExportArchive/Summary.vue.js +10 -0
  51. package/dist/components/PlBtnExportArchive/Summary.vue.js.map +1 -0
  52. package/dist/components/PlBtnExportArchive/Summary.vue2.js +42 -0
  53. package/dist/components/PlBtnExportArchive/Summary.vue2.js.map +1 -0
  54. package/dist/components/PlBtnExportArchive/Summary.vue3.js +13 -0
  55. package/dist/components/PlBtnExportArchive/Summary.vue3.js.map +1 -0
  56. package/dist/components/PlBtnExportArchive/index.d.ts +2 -0
  57. package/dist/components/PlBtnExportArchive/types.d.ts +14 -0
  58. package/dist/index.js +44 -40
  59. package/dist/index.js.map +1 -1
  60. package/dist/lib/util/helpers/dist/prettyBytes.js +68 -0
  61. package/dist/lib/util/helpers/dist/prettyBytes.js.map +1 -0
  62. package/dist/lib.d.ts +1 -0
  63. package/package.json +6 -4
  64. package/src/components/PlAnnotations/components/AnnotationsSidebar.vue +2 -2
  65. package/src/components/PlAnnotations/components/DynamicForm.vue +16 -9
  66. package/src/components/PlAnnotations/components/FilterSidebar.vue +23 -13
  67. package/src/components/PlAnnotations/components/PlAnnotations.vue +92 -0
  68. package/src/components/PlAnnotations/components/PlAnnotationsModal.vue +19 -77
  69. package/src/components/PlAnnotations/index.ts +1 -0
  70. package/src/components/PlAnnotations/types.ts +7 -6
  71. package/src/components/PlAnnotations/utils.ts +5 -13
  72. package/src/components/PlBtnExportArchive/Item.vue +66 -0
  73. package/src/components/PlBtnExportArchive/PlBtnExportArchive.vue +235 -0
  74. package/src/components/PlBtnExportArchive/Summary.vue +56 -0
  75. package/src/components/PlBtnExportArchive/index.ts +2 -0
  76. package/src/components/PlBtnExportArchive/types.ts +17 -0
  77. package/src/lib.ts +2 -0
  78. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue.d.ts +0 -18
  79. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue.js +0 -73
  80. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue.js.map +0 -1
  81. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue2.js +0 -5
  82. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue2.js.map +0 -1
  83. package/src/components/PlAnnotations/components/PlAnnotationCreateDialog.vue +0 -64
@@ -1,99 +1,41 @@
1
1
  <script setup lang="ts">
2
- import { isNil } from '@milaboratories/helpers';
3
- import { PlPureSlideModal, PlSidebarGroup, useConfirm } from '@milaboratories/uikit';
4
- import type { AnnotationScriptUi, PObjectId } from '@platforma-sdk/model';
5
- import { computed, effect, shallowRef } from 'vue';
6
- import type { SimplifiedUniversalPColumnEntry } from '../types';
7
- import { getDefaultAnnotationScript } from '../utils';
8
- import AnnotationsSidebar from './AnnotationsSidebar.vue';
9
- import FilterSidebar from './FilterSidebar.vue';
10
- import PlAnnotationCreateDialog from './PlAnnotationCreateDialog.vue';
2
+ import { PlPureSlideModal } from '@milaboratories/uikit';
3
+ import { effect, shallowRef } from 'vue';
4
+
5
+ import type { Annotation } from '../types';
6
+ import type { Props } from './PlAnnotations.vue';
7
+ import PlAnnotations from './PlAnnotations.vue';
11
8
 
12
9
  // Models
13
- const annotation = defineModel<AnnotationScriptUi>('annotation', { required: true, default: getDefaultAnnotationScript });
10
+ const annotation = defineModel<Annotation>('annotation', { required: true });
14
11
  const opened = defineModel<boolean>('opened', { required: true });
15
12
  // Props
16
- const props = defineProps<{
17
- columns: SimplifiedUniversalPColumnEntry[];
18
- hasSelectedColumns: boolean;
19
- getValuesForSelectedColumns: () => Promise<undefined | { columnId: PObjectId; values: string[] }>;
20
- }>();
13
+ const props = defineProps<Props>();
21
14
  // State
22
15
  const selectedStepId = shallowRef<number | undefined>(undefined);
23
- const selectedStep = computed(() => {
24
- return isNil(selectedStepId.value) || isNil(annotation.value)
25
- ? undefined
26
- : annotation.value.steps.find((step) => step.id === selectedStepId.value);
27
- });
28
- const hasAnnotation = computed(() => annotation.value.isCreated === true);
29
-
30
- const openedDialog = computed({
31
- get: () => !hasAnnotation.value && opened.value,
32
- set: (value: boolean) => (opened.value = value),
33
- });
34
- const openedModal = computed({
35
- get: () => hasAnnotation.value && opened.value,
36
- set: (value: boolean) => (opened.value = value),
37
- });
38
16
  // Watchers
39
17
  effect(function setDefaultStepId() {
40
18
  if (selectedStepId.value === undefined && annotation.value.steps.length > 0) {
41
19
  selectedStepId.value = annotation.value.steps[0].id;
42
20
  }
43
21
  });
44
- // Hooks
45
- const confirmResetSchema = useConfirm({
46
- title: 'Reset Schema',
47
- message: 'Are you sure you want to reset the schema? This action cannot be undone.',
48
- confirmLabel: 'Yes, reset',
49
- cancelLabel: 'No, cancel',
50
- });
51
22
  // Actions
52
- function handleCreateAnnotation(props: { type: 'byClonotype' | 'bySampleAndClonotype'; name: string }) {
53
- annotation.value.isCreated = true;
54
- annotation.value.mode = props.type;
55
- annotation.value.title = props.name;
56
- annotation.value.steps = [];
57
- }
58
-
59
23
  async function handleDeleteSchema() {
60
- if (await confirmResetSchema()) {
61
- annotation.value.isCreated = false;
62
- annotation.value.title = '';
63
- annotation.value.mode = 'byClonotype';
64
- annotation.value.steps = [];
65
- opened.value = false;
66
- selectedStepId.value = undefined;
67
- }
24
+ opened.value = false;
25
+ props.onDeleteSchema?.();
68
26
  }
69
-
70
27
  </script>
71
28
 
72
29
  <template>
73
- <PlAnnotationCreateDialog v-model="openedDialog" @submit="handleCreateAnnotation"/>
74
- <PlPureSlideModal v-model="openedModal" :class="$style.modal" width="768px">
75
- <PlSidebarGroup :class="$style.sidebarGroup">
76
- <template #item-0>
77
- <AnnotationsSidebar
78
- v-model:annotation="annotation"
79
- v-model:selectedStepId="selectedStepId"
80
- :class="$style.sidebarItem"
81
- :columns="props.columns"
82
- @delete-schema="handleDeleteSchema"
83
- />
84
- </template>
85
- <template #item-1>
86
- <FilterSidebar
87
- v-if="selectedStep"
88
- v-model:step="selectedStep"
89
- :class="$style.sidebarItem"
90
- :columns="props.columns"
91
- :selectedStepId="selectedStepId"
92
- :hasSelectedColumns="props.hasSelectedColumns"
93
- :getValuesForSelectedColumns="props.getValuesForSelectedColumns"
94
- />
95
- </template>
96
- </PlSidebarGroup>
30
+ <PlPureSlideModal v-model="opened" :class="$style.modal" width="768px">
31
+ <PlAnnotations
32
+ v-model:annotation="annotation"
33
+ :class="$style.sidebarItem"
34
+ :columns="props.columns"
35
+ :has-selected-columns="props.hasSelectedColumns"
36
+ :getValuesForSelectedColumns="props.getValuesForSelectedColumns"
37
+ @delete-schema="handleDeleteSchema"
38
+ />
97
39
  </PlPureSlideModal>
98
40
  </template>
99
41
 
@@ -1 +1,2 @@
1
+ export { default as PlAnnotations } from './components/PlAnnotations.vue';
1
2
  export { default as PlAnnotationsModal } from './components/PlAnnotationsModal.vue';
@@ -1,9 +1,10 @@
1
- import type { PColumnSpec, SUniversalPColumnId } from '@platforma-sdk/model';
1
+ import type { FilterSpec as _FilterSpec, AnnotationSpecUi, FilterSpecLeaf, FilterSpecUi } from '@platforma-sdk/model';
2
+ export type { FilterSpecType } from '@platforma-sdk/model';
2
3
 
3
- export type SimplifiedPColumnSpec = Pick<PColumnSpec, 'valueType' | 'annotations'>;
4
+ export type FilterSpec = _FilterSpec<FilterSpecLeaf, { id?: number; name?: string; isExpanded?: boolean }>;
4
5
 
5
- export type SimplifiedUniversalPColumnEntry = {
6
- id: SUniversalPColumnId;
7
- label: string;
8
- obj: SimplifiedPColumnSpec;
6
+ export type Filter = FilterSpecUi<Extract<FilterSpec, { type: 'and' | 'or' }>> & {
7
+ id: number;
9
8
  };
9
+
10
+ export type Annotation = AnnotationSpecUi<Filter>;
@@ -1,15 +1,7 @@
1
- import type { AnnotationScriptUi, FilterUi, TypeFieldRecord } from '@platforma-sdk/model';
1
+ import type { FilterSpecTypeFieldRecord } from '@milaboratories/uikit';
2
+ import type { FilterSpec } from './types';
2
3
 
3
- export function getDefaultAnnotationScript(): AnnotationScriptUi {
4
- return {
5
- isCreated: false,
6
- title: 'My Annotation',
7
- mode: 'byClonotype',
8
- steps: [],
9
- };
10
- }
11
-
12
- export function createDefaultFilterMetadata<T extends Extract<FilterUi, { column: unknown }>>(): TypeFieldRecord<T> {
4
+ export function createDefaultFilterMetadata<T extends Extract<FilterSpec, { column: unknown }>>(): FilterSpecTypeFieldRecord<T> {
13
5
  return {
14
6
  column: {
15
7
  label: 'Column',
@@ -18,8 +10,8 @@ export function createDefaultFilterMetadata<T extends Extract<FilterUi, { column
18
10
  },
19
11
  type: {
20
12
  label: 'Predicate',
21
- fieldType: 'FilterUiType',
13
+ fieldType: 'FilterType',
22
14
  defaultValue: () => undefined,
23
15
  },
24
- } as TypeFieldRecord<T>;
16
+ } as FilterSpecTypeFieldRecord<T>;
25
17
  };
@@ -0,0 +1,66 @@
1
+ <script setup lang="ts">
2
+ import type { ExportItem } from './types';
3
+ import { prettyBytes } from '@milaboratories/helpers';
4
+
5
+ defineProps<{
6
+ item: ExportItem;
7
+ }>();
8
+ </script>
9
+
10
+ <template>
11
+ <div
12
+ :class="$style.item"
13
+ >
14
+ <div :class="$style.name">{{ item.fileName }}</div>
15
+ <div v-if="item.status === 'in-progress'" :class="$style.details">
16
+ <span>{{ prettyBytes(item.current, {}) }}</span>
17
+ <span>/</span>
18
+ <span>{{ prettyBytes(item.size, {}) }}</span>
19
+ </div>
20
+ <div v-else-if="item.status === 'completed'" :class="$style.details">
21
+ Done <span>{{ prettyBytes(item.size, {}) }}</span>
22
+ </div>
23
+ <div v-else-if="item.status === 'error'" :class="$style.error">
24
+ <span>{{ item.error }}</span>
25
+ </div>
26
+ <div v-else :class="$style.details">
27
+ Pending
28
+ </div>
29
+ </div>
30
+ </template>
31
+
32
+ <style module>
33
+ .item {
34
+ display: flex;
35
+ flex-direction: column;
36
+ margin-bottom: 8px;
37
+ overflow: hidden;
38
+ --name-font-size: 12px;
39
+ --details-font-size: 10px;
40
+ }
41
+ .name {
42
+ white-space: nowrap;
43
+ overflow: hidden;
44
+ text-overflow: ellipsis;
45
+ font-size: var(--name-font-size);
46
+ font-weight: 600;
47
+ }
48
+
49
+ .details {
50
+ font-size: var(--details-font-size);
51
+ font-weight: 400;
52
+ color: rgba(255, 255, 255, 0.6);
53
+ }
54
+
55
+ .error {
56
+ font-size: var(--details-font-size);
57
+ font-weight: 400;
58
+ color: var(--txt-error);
59
+ span {
60
+ white-space: nowrap;
61
+ overflow: hidden;
62
+ text-overflow: ellipsis;
63
+ max-width: 100%;
64
+ }
65
+ }
66
+ </style>
@@ -0,0 +1,235 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ PlBtnGhost,
4
+ PlIcon16,
5
+ PlIcon24,
6
+ useClickOutside,
7
+ } from '@milaboratories/uikit';
8
+ import { ZipWriter } from '@zip.js/zip.js';
9
+ import { reactive, computed, ref } from 'vue';
10
+ import type { ExportItem, ExportsMap, FileExportEntry } from './types';
11
+ import Item from './Item.vue';
12
+ import { getFileNameFromHandle, ChunkedStreamReader } from '@platforma-sdk/model';
13
+ import { getRawPlatformaInstance } from '@platforma-sdk/model';
14
+ import { uniqueId } from '@milaboratories/helpers';
15
+ import Summary from './Summary.vue';
16
+
17
+ type FilePickerAcceptType = {
18
+ description?: string;
19
+ accept?: Record<string, string[]>;
20
+ };
21
+
22
+ const props = defineProps<{
23
+ fileExports?: FileExportEntry[];
24
+ suggestedFileName?: string;
25
+ disabled?: boolean;
26
+ filePickerTypes?: FilePickerAcceptType[];
27
+ strategy?: 'parallel'; // default is sequential
28
+ debugFn?: (fileName: string) => Promise<void>;
29
+ }>();
30
+
31
+ const defaultData = () => ({
32
+ loading: false,
33
+ name: '',
34
+ exports: undefined as ExportsMap | undefined,
35
+ showExports: false,
36
+ });
37
+
38
+ const data = reactive(defaultData());
39
+
40
+ const updateExportsItem = (id: string, partial: Partial<ExportItem>) => {
41
+ const it = data.exports?.get(id);
42
+ if (it) {
43
+ data.exports?.set(id, { ...it, ...partial });
44
+ }
45
+ };
46
+
47
+ const isReadyToExport = computed(() => {
48
+ return props.fileExports !== undefined && !props.disabled;
49
+ });
50
+
51
+ const items = computed(() => {
52
+ return Array.from(data.exports?.values() ?? []);
53
+ });
54
+
55
+ const archive = computed<ExportItem>(() => {
56
+ return {
57
+ fileName: data.name,
58
+ current: items.value.reduce((acc, item) => acc + item.current, 0),
59
+ size: items.value.reduce((acc, item) => acc + item.size, 0),
60
+ status: items.value.some((item) => item.status === 'in-progress') ? 'in-progress' : items.value.every((item) => item.status === 'completed') ? 'completed' : 'pending',
61
+ hasErrors: items.value.some((item) => item.status === 'error'),
62
+ };
63
+ });
64
+
65
+ type ZipRequest = {
66
+ id: string;
67
+ fileName: string;
68
+ size: number;
69
+ stream: ReadableStream<Uint8Array>;
70
+ };
71
+
72
+ const exportRawTsvs = async () => {
73
+ if (data.loading) {
74
+ data.showExports = true;
75
+ return;
76
+ }
77
+
78
+ if (!isReadyToExport.value || !props.fileExports) {
79
+ return;
80
+ }
81
+
82
+ const defaultFileName = `${new Date().toISOString().split('T')[0]}_Export.zip`;
83
+ const defaultTypes = [{
84
+ description: 'ZIP files',
85
+ accept: {
86
+ 'application/zip': ['.zip'],
87
+ },
88
+ }];
89
+
90
+ // @ts-expect-error - type definition issue TODO: fix this
91
+ const newHandle = await window.showSaveFilePicker({
92
+ types: props.filePickerTypes || defaultTypes,
93
+ suggestedName: props.suggestedFileName || defaultFileName,
94
+ });
95
+
96
+ data.loading = true;
97
+ data.name = newHandle.name;
98
+ data.showExports = true;
99
+ data.exports = new Map();
100
+
101
+ try {
102
+ const writableStream = await newHandle.createWritable();
103
+ const zip = new ZipWriter(writableStream, { keepOrder: true, zip64: true, bufferedWrite: false });
104
+ try {
105
+ const requests = [] as ZipRequest[];
106
+
107
+ for (const entry of props.fileExports) {
108
+ const { importHandle, blobHandle, fileName: customFileName } = entry;
109
+ const fileName = customFileName ?? getFileNameFromHandle(importHandle);
110
+ const { handle, size } = blobHandle;
111
+
112
+ const id = uniqueId();
113
+
114
+ data.exports?.set(id, { fileName, current: 0, size, status: 'pending' });
115
+
116
+ const stream = ChunkedStreamReader.create({
117
+ fetchChunk: async ({ from, to }) => {
118
+ if (props.debugFn) {
119
+ await props.debugFn(fileName);
120
+ }
121
+
122
+ return await getRawPlatformaInstance().blobDriver.getContent(handle, { from, to });
123
+ },
124
+ totalSize: size,
125
+ onError: async (error) => {
126
+ updateExportsItem(id, { status: 'error', error });
127
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // primitive for now
128
+ return 'continue';
129
+ },
130
+ });
131
+
132
+ // Create a chunked stream reader for efficient streaming
133
+ requests.push({ id, fileName, size, stream });
134
+ }
135
+
136
+ const processRequest = async (request: ZipRequest) => {
137
+ const { id, fileName, size, stream } = request;
138
+ const update = (partial: Partial<ExportItem>) => {
139
+ const it = data.exports?.get(id);
140
+ if (it) {
141
+ data.exports?.set(id, { ...it, ...partial });
142
+ }
143
+ };
144
+ await zip.add(fileName, stream, {
145
+ bufferedWrite: true,
146
+ onstart: () => {
147
+ update({ status: 'in-progress' });
148
+ return undefined;
149
+ },
150
+ onprogress: (current: number) => {
151
+ update({ current, status: 'in-progress' });
152
+ return undefined;
153
+ },
154
+ onend() {
155
+ update({ current: size, status: 'completed' });
156
+ return undefined;
157
+ },
158
+ });
159
+ };
160
+
161
+ if (props.strategy === 'parallel') {
162
+ await Promise.all(requests.map(processRequest));
163
+ } else {
164
+ for (const request of requests) {
165
+ await processRequest(request);
166
+ }
167
+ }
168
+ } finally {
169
+ await zip.close().catch((error) => {
170
+ console.error('Error closing zip', error);
171
+ });
172
+ }
173
+ } finally {
174
+ data.loading = false;
175
+ }
176
+ };
177
+
178
+ const progressesRef = ref();
179
+
180
+ useClickOutside([progressesRef], () => {
181
+ data.showExports = false;
182
+ });
183
+ </script>
184
+
185
+ <template>
186
+ <PlBtnGhost
187
+ :disabled="!isReadyToExport" :loading="data.loading" :class="{ [$style['has-exports']]: data.exports }"
188
+ @click.stop="exportRawTsvs"
189
+ >
190
+ <slot />
191
+ <template #append>
192
+ <PlIcon24 :class="$style.icon" name="download" />
193
+ </template>
194
+ </PlBtnGhost>
195
+ <Teleport to="body">
196
+ <div v-if="data.exports && data.showExports" ref="progressesRef" :class="$style.progresses">
197
+ <PlIcon16 :class="$style.close" name="close" @click.stop="data.showExports = false" />
198
+ <Summary :item="archive" />
199
+ <div :class="$style.itemsContainer" class="pl-scrollable-y">
200
+ <Item v-for="item in data.exports?.values()" :key="item.fileName" :item="item" />
201
+ </div>
202
+ </div>
203
+ </Teleport>
204
+ </template>
205
+
206
+ <style module>
207
+ .progresses {
208
+ position: fixed;
209
+ top: 8px;
210
+ right: 8px;
211
+ width: 350px;
212
+ height: auto;
213
+ max-height: 400px;
214
+ overflow: auto;
215
+ background: rgba(0, 0, 0, 0.85);
216
+ border-radius: 8px;
217
+ padding: 20px 8px 8px 20px;
218
+ color: white;
219
+ font-size: 12px;
220
+ font-weight: 600;
221
+ z-index: 1000;
222
+
223
+ .itemsContainer {
224
+ max-height: 300px;
225
+ }
226
+
227
+ .close {
228
+ position: absolute;
229
+ top: 8px;
230
+ right: 8px;
231
+ cursor: pointer;
232
+ --icon-color: white;
233
+ }
234
+ }
235
+ </style>
@@ -0,0 +1,56 @@
1
+ <script setup lang="ts">
2
+ import type { ExportItem } from './types';
3
+ import { prettyBytes } from '@milaboratories/helpers';
4
+
5
+ defineProps<{
6
+ item: ExportItem;
7
+ }>();
8
+
9
+ const emit = defineEmits<{
10
+ (e: 'cancel'): void;
11
+ }>();
12
+ </script>
13
+
14
+ <template>
15
+ <div
16
+ :class="$style.summary"
17
+ >
18
+ <div :class="$style.name">{{ item.fileName }}<span v-if="false" @click.stop="emit('cancel')">[TODO: Cancel]</span></div>
19
+ <div v-if="item.status === 'in-progress'" :class="$style.details">
20
+ <span>{{ prettyBytes(item.current, {}) }}</span>
21
+ <span>/</span>
22
+ <span>{{ prettyBytes(item.size, {}) }}</span>
23
+ </div>
24
+ <div v-else-if="item.status === 'completed'" :class="$style.details">
25
+ Done <span>{{ prettyBytes(item.size, {}) }}</span>
26
+ </div>
27
+ <div v-else :class="$style.details">
28
+ Pending
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <style module>
34
+ .summary {
35
+ display: flex;
36
+ flex-direction: column;
37
+ margin-bottom: 8px;
38
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
39
+ padding-bottom: 8px;
40
+ --name-font-size: 14px;
41
+ --details-font-size: 12px;
42
+ }
43
+
44
+ .name {
45
+ white-space: nowrap;
46
+ overflow: hidden;
47
+ text-overflow: ellipsis;
48
+ font-size: var(--name-font-size);
49
+ font-weight: 600;
50
+ }
51
+ .details {
52
+ font-size: var(--details-font-size);
53
+ font-weight: 400;
54
+ color: rgba(255, 255, 255, 0.6);
55
+ }
56
+ </style>
@@ -0,0 +1,2 @@
1
+ export { default as PlBtnExportArchive } from './PlBtnExportArchive.vue';
2
+ export type { FileExportEntry } from './types';
@@ -0,0 +1,17 @@
1
+ import type { ImportFileHandle, RemoteBlobHandleAndSize } from '@platforma-sdk/model';
2
+
3
+ export type FileExportEntry = {
4
+ importHandle: ImportFileHandle;
5
+ blobHandle: RemoteBlobHandleAndSize;
6
+ fileName?: string;
7
+ };
8
+
9
+ export type ExportItem = {
10
+ fileName: string;
11
+ current: number;
12
+ size: number;
13
+ status: 'pending' | 'in-progress' | 'completed' | 'error';
14
+ error?: unknown;
15
+ };
16
+
17
+ export type ExportsMap = Map<string, ExportItem>;
package/src/lib.ts CHANGED
@@ -32,6 +32,8 @@ export * from './components/PlMultiSequenceAlignment';
32
32
 
33
33
  export * from './components/PlAnnotations';
34
34
 
35
+ export * from './components/PlBtnExportArchive';
36
+
35
37
  export * from './defineApp';
36
38
 
37
39
  export * from './createModel';
@@ -1,18 +0,0 @@
1
- type __VLS_PublicProps = {
2
- modelValue: boolean;
3
- };
4
- declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
5
- "update:modelValue": (value: boolean) => any;
6
- } & {
7
- submit: (props: {
8
- type: "byClonotype" | "bySampleAndClonotype";
9
- name: string;
10
- }) => any;
11
- }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
12
- onSubmit?: ((props: {
13
- type: "byClonotype" | "bySampleAndClonotype";
14
- name: string;
15
- }) => any) | undefined;
16
- "onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
17
- }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
18
- export default _default;
@@ -1,73 +0,0 @@
1
- import { defineComponent as v, mergeModels as b, useModel as C, ref as P, computed as x, createBlock as g, openBlock as S, unref as t, withCtx as a, createVNode as n, withModifiers as i, createTextVNode as d } from "vue";
2
- import { PlDialogModal as A, PlBtnPrimary as B, PlBtnGhost as M, PlRadioGroup as c, PlTextField as h } from "@milaboratories/uikit";
3
- const G = /* @__PURE__ */ v({
4
- __name: "PlAnnotationCreateDialog",
5
- props: {
6
- modelValue: { type: Boolean, required: !0 },
7
- modelModifiers: {}
8
- },
9
- emits: /* @__PURE__ */ b(["submit"], ["update:modelValue"]),
10
- setup(p, { emit: r }) {
11
- const u = C(p, "modelValue"), s = r, f = [
12
- { label: "Global", value: "byClonotype" },
13
- { label: "Per sample", value: "bySampleAndClonotype" }
14
- ], l = P({
15
- type: "byClonotype",
16
- name: ""
17
- }), m = x(() => l.value.name.length > 3), y = () => {
18
- m.value && s("submit", l.value);
19
- }, V = () => {
20
- u.value = !1;
21
- };
22
- return (k, e) => (S(), g(t(A), {
23
- modelValue: u.value,
24
- "onUpdate:modelValue": e[2] || (e[2] = (o) => u.value = o),
25
- width: "600px"
26
- }, {
27
- title: a(() => e[3] || (e[3] = [
28
- d(" Choose the Annotation Scheme type ")
29
- ])),
30
- default: a(() => [
31
- n(t(c), {
32
- modelValue: l.value.type,
33
- "onUpdate:modelValue": e[0] || (e[0] = (o) => l.value.type = o),
34
- options: f
35
- }, null, 8, ["modelValue"]),
36
- n(t(h), {
37
- modelValue: l.value.name,
38
- "onUpdate:modelValue": e[1] || (e[1] = (o) => l.value.name = o),
39
- label: "Name your Scheme",
40
- "min-length": "3",
41
- "max-length": "40",
42
- placeholder: "Annotation Name",
43
- autofocus: "",
44
- required: ""
45
- }, null, 8, ["modelValue"])
46
- ]),
47
- actions: a(() => [
48
- n(t(B), {
49
- disabled: !m.value,
50
- onClick: i(y, ["stop"])
51
- }, {
52
- default: a(() => e[4] || (e[4] = [
53
- d("Apply")
54
- ])),
55
- _: 1
56
- }, 8, ["disabled"]),
57
- n(t(M), {
58
- onClick: i(V, ["stop"])
59
- }, {
60
- default: a(() => e[5] || (e[5] = [
61
- d("Cancel")
62
- ])),
63
- _: 1
64
- })
65
- ]),
66
- _: 1
67
- }, 8, ["modelValue"]));
68
- }
69
- });
70
- export {
71
- G as default
72
- };
73
- //# sourceMappingURL=PlAnnotationCreateDialog.vue.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PlAnnotationCreateDialog.vue.js","sources":["../../../../src/components/PlAnnotations/components/PlAnnotationCreateDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { SimpleOption } from '@milaboratories/uikit';\nimport { PlBtnGhost, PlBtnPrimary, PlDialogModal, PlRadioGroup, PlTextField } from '@milaboratories/uikit';\nimport type { AnnotationMode } from '@platforma-sdk/model';\nimport { computed, ref } from 'vue';\n\n// Models\nconst opened = defineModel<boolean>({ required: true });\n// Emits\nconst emits = defineEmits<{\n (e: 'submit', props: { type: 'byClonotype' | 'bySampleAndClonotype'; name: string }): void;\n}>();\n\nconst annotationSchemaTypes = [\n { label: 'Global', value: 'byClonotype' },\n { label: 'Per sample', value: 'bySampleAndClonotype' },\n] satisfies SimpleOption<AnnotationMode>[];\n\nconst modalState = ref<{\n type: 'byClonotype' | 'bySampleAndClonotype';\n name: string;\n}>({\n type: 'byClonotype',\n name: '',\n});\n\nconst isValidForm = computed(() => {\n return modalState.value.name.length > 3;\n});\n\nconst handleSubmit = () => {\n if (isValidForm.value) {\n emits('submit', modalState.value);\n }\n};\n\nconst handleCancel = () => {\n opened.value = false;\n};\n</script>\n\n<template>\n <PlDialogModal v-model=\"opened\" width=\"600px\">\n <template #title>\n Choose the Annotation Scheme type\n </template>\n <template #default>\n <PlRadioGroup v-model=\"modalState.type\" :options=\"annotationSchemaTypes\" />\n <PlTextField\n v-model=\"modalState.name\"\n label=\"Name your Scheme\"\n min-length=\"3\"\n max-length=\"40\"\n placeholder=\"Annotation Name\"\n autofocus\n required\n />\n </template>\n <template #actions>\n <PlBtnPrimary :disabled=\"!isValidForm\" @click.stop=\"handleSubmit\">Apply</PlBtnPrimary>\n <PlBtnGhost @click.stop=\"handleCancel\">Cancel</PlBtnGhost>\n </template>\n </PlDialogModal>\n</template>\n"],"names":["opened","_useModel","__props","emits","__emit","annotationSchemaTypes","modalState","ref","isValidForm","computed","handleSubmit","handleCancel"],"mappings":";;;;;;;;;;AAOA,UAAMA,IAASC,EAAoBC,GAAA,YAAmB,GAEhDC,IAAQC,GAIRC,IAAwB;AAAA,MAC5B,EAAE,OAAO,UAAU,OAAO,cAAA;AAAA,MAC1B,EAAE,OAAO,cAAc,OAAO,uBAAA;AAAA,IAAuB,GAGjDC,IAAaC,EAGhB;AAAA,MACD,MAAM;AAAA,MACN,MAAM;AAAA,IAAA,CACP,GAEKC,IAAcC,EAAS,MACpBH,EAAW,MAAM,KAAK,SAAS,CACvC,GAEKI,IAAe,MAAM;AACzB,MAAIF,EAAY,SACdL,EAAM,UAAUG,EAAW,KAAK;AAAA,IAEpC,GAEMK,IAAe,MAAM;AACzB,MAAAX,EAAO,QAAQ;AAAA,IACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,5 +0,0 @@
1
- import f from "./PlAnnotationCreateDialog.vue.js";
2
- export {
3
- f as default
4
- };
5
- //# sourceMappingURL=PlAnnotationCreateDialog.vue2.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PlAnnotationCreateDialog.vue2.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}