@platforma-sdk/ui-vue 1.40.4 → 1.40.6

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 (154) hide show
  1. package/.turbo/turbo-build.log +60 -33
  2. package/.turbo/turbo-type-check.log +1 -1
  3. package/CHANGELOG.md +18 -0
  4. package/dist/AgGridVue/useAgGridOptions.js +1 -1
  5. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts +1 -4
  6. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts.map +1 -1
  7. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js +150 -167
  8. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js.map +1 -1
  9. package/dist/components/PlAgDataTable/PlAgRowCount.vue.js +1 -1
  10. package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts +2 -2
  11. package/dist/components/PlAgDataTable/sources/table-source-v2.d.ts.map +1 -1
  12. package/dist/components/PlAgDataTable/sources/table-source-v2.js +114 -109
  13. package/dist/components/PlAgDataTable/sources/table-source-v2.js.map +1 -1
  14. package/dist/components/PlAgDataTable/types.d.ts +27 -43
  15. package/dist/components/PlAgDataTable/types.d.ts.map +1 -1
  16. package/dist/components/PlAgDataTable/types.js +32 -43
  17. package/dist/components/PlAgDataTable/types.js.map +1 -1
  18. package/dist/components/PlAgGridColumnManager/PlAgGridColumnManager.vue.js +6 -6
  19. package/dist/components/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue.js +1 -1
  20. package/dist/components/PlAgRowNumHeader.vue.js +1 -1
  21. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue.d.ts +17 -0
  22. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue.d.ts.map +1 -0
  23. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue.js +10 -0
  24. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue.js.map +1 -0
  25. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue2.js +99 -0
  26. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue2.js.map +1 -0
  27. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue3.js +15 -0
  28. package/dist/components/PlAnnotations/components/AnnotationsSidebar.vue3.js.map +1 -0
  29. package/dist/components/PlAnnotations/components/DynamicForm.vue.d.ts +25 -0
  30. package/dist/components/PlAnnotations/components/DynamicForm.vue.d.ts.map +1 -0
  31. package/dist/components/PlAnnotations/components/DynamicForm.vue.js +10 -0
  32. package/dist/components/PlAnnotations/components/DynamicForm.vue.js.map +1 -0
  33. package/dist/components/PlAnnotations/components/DynamicForm.vue2.js +110 -0
  34. package/dist/components/PlAnnotations/components/DynamicForm.vue2.js.map +1 -0
  35. package/dist/components/PlAnnotations/components/DynamicForm.vue3.js +9 -0
  36. package/dist/components/PlAnnotations/components/DynamicForm.vue3.js.map +1 -0
  37. package/dist/components/PlAnnotations/components/FilterSidebar.vue.d.ts +20 -0
  38. package/dist/components/PlAnnotations/components/FilterSidebar.vue.d.ts.map +1 -0
  39. package/dist/components/PlAnnotations/components/FilterSidebar.vue.js +10 -0
  40. package/dist/components/PlAnnotations/components/FilterSidebar.vue.js.map +1 -0
  41. package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js +124 -0
  42. package/dist/components/PlAnnotations/components/FilterSidebar.vue2.js.map +1 -0
  43. package/dist/components/PlAnnotations/components/FilterSidebar.vue3.js +13 -0
  44. package/dist/components/PlAnnotations/components/FilterSidebar.vue3.js.map +1 -0
  45. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue.d.ts +19 -0
  46. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue.d.ts.map +1 -0
  47. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue.js +78 -0
  48. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue.js.map +1 -0
  49. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue2.js +5 -0
  50. package/dist/components/PlAnnotations/components/PlAnnotationCreateDialog.vue2.js.map +1 -0
  51. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue.d.ts +23 -0
  52. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue.d.ts.map +1 -0
  53. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue.js +10 -0
  54. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue.js.map +1 -0
  55. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue2.js +97 -0
  56. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue2.js.map +1 -0
  57. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue3.js +13 -0
  58. package/dist/components/PlAnnotations/components/PlAnnotationsModal.vue3.js.map +1 -0
  59. package/dist/components/PlAnnotations/index.d.ts +2 -0
  60. package/dist/components/PlAnnotations/index.d.ts.map +1 -0
  61. package/dist/components/PlAnnotations/types.d.ts +8 -0
  62. package/dist/components/PlAnnotations/types.d.ts.map +1 -0
  63. package/dist/components/PlAnnotations/utils.d.ts +6 -0
  64. package/dist/components/PlAnnotations/utils.d.ts.map +1 -0
  65. package/dist/components/PlAnnotations/utils.js +29 -0
  66. package/dist/components/PlAnnotations/utils.js.map +1 -0
  67. package/dist/components/PlAppErrorNotificationAlert/PlAppErrorNotificationAlert.vue.js +8 -8
  68. package/dist/components/PlMultiSequenceAlignment/Toolbar.vue2.js +5 -5
  69. package/dist/components/PlMultiSequenceAlignment/data.js +62 -62
  70. package/dist/components/PlTableFilters/PlTableAddFilterV2.vue.js +8 -8
  71. package/dist/defineApp.js +6 -6
  72. package/dist/lib/model/common/dist/index.js.map +1 -1
  73. package/dist/lib/ui/uikit/dist/components/DataTable/TableComponent.vue.js +22 -22
  74. package/dist/lib/ui/uikit/dist/components/PlAccordion/{ExpandTransition.vue2.js → ExpandTransition.vue.js} +1 -1
  75. package/dist/lib/ui/uikit/dist/components/PlAccordion/ExpandTransition.vue.js.map +1 -0
  76. package/dist/lib/ui/uikit/dist/components/PlAccordion/PlAccordionSection.vue2.js +1 -1
  77. package/dist/lib/ui/uikit/dist/components/PlAutocomplete/PlAutocomplete.vue.js +1 -1
  78. package/dist/lib/ui/uikit/dist/components/PlBtnGhost/PlBtnGhost.vue.js +3 -3
  79. package/dist/lib/ui/uikit/dist/components/PlConfirmDialog.vue.js +63 -0
  80. package/dist/lib/ui/uikit/dist/components/PlConfirmDialog.vue.js.map +1 -0
  81. package/dist/lib/ui/uikit/dist/components/PlDialogModal/PlDialogModal.vue.js +37 -37
  82. package/dist/lib/ui/uikit/dist/components/PlDialogModal/PlDialogModal.vue.js.map +1 -1
  83. package/dist/lib/ui/uikit/dist/components/PlDropdown/PlDropdown.vue.js +1 -1
  84. package/dist/lib/ui/uikit/dist/components/PlDropdownLegacy/PlDropdownLegacy.vue.js +1 -1
  85. package/dist/lib/ui/uikit/dist/components/PlDropdownMulti/PlDropdownMulti.vue.js +1 -1
  86. package/dist/lib/ui/uikit/dist/components/PlEditableTitle/PlEditableTitle.vue.js +42 -38
  87. package/dist/lib/ui/uikit/dist/components/PlEditableTitle/PlEditableTitle.vue.js.map +1 -1
  88. package/dist/lib/ui/uikit/dist/components/PlElementList/PlElementList.vue2.js +20 -16
  89. package/dist/lib/ui/uikit/dist/components/PlElementList/PlElementList.vue2.js.map +1 -1
  90. package/dist/lib/ui/uikit/dist/components/PlElementList/PlElementListItem.vue.js +2 -2
  91. package/dist/lib/ui/uikit/dist/components/PlElementList/PlElementListItem.vue2.js +17 -16
  92. package/dist/lib/ui/uikit/dist/components/PlElementList/PlElementListItem.vue2.js.map +1 -1
  93. package/dist/lib/ui/uikit/dist/components/PlElementList/PlElementListItem.vue3.js +27 -27
  94. package/dist/lib/ui/uikit/dist/components/PlElementList/utils.js +7 -6
  95. package/dist/lib/ui/uikit/dist/components/PlElementList/utils.js.map +1 -1
  96. package/dist/lib/ui/uikit/dist/components/PlFileDialog/PlFileDialog.vue.js +19 -19
  97. package/dist/lib/ui/uikit/dist/components/PlFileInput/PlFileInput.vue.js +1 -1
  98. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarGroup.vue.js +10 -0
  99. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarGroup.vue.js.map +1 -0
  100. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarGroup.vue2.js +21 -0
  101. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarGroup.vue2.js.map +1 -0
  102. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarGroup.vue3.js +20 -0
  103. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarGroup.vue3.js.map +1 -0
  104. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarItem.vue.js +10 -0
  105. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarItem.vue.js.map +1 -0
  106. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarItem.vue2.js +39 -0
  107. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarItem.vue2.js.map +1 -0
  108. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarItem.vue3.js +26 -0
  109. package/dist/lib/ui/uikit/dist/components/PlSidebar/PlSidebarItem.vue3.js.map +1 -0
  110. package/dist/lib/ui/uikit/dist/components/PlSlideModal/PlPureSlideModal.vue.js +116 -0
  111. package/dist/lib/ui/uikit/dist/components/PlSlideModal/PlPureSlideModal.vue.js.map +1 -0
  112. package/dist/lib/ui/uikit/dist/components/PlSlideModal/PlSlideModal.vue.js +7 -128
  113. package/dist/lib/ui/uikit/dist/components/PlSlideModal/PlSlideModal.vue.js.map +1 -1
  114. package/dist/lib/ui/uikit/dist/components/PlSlideModal/PlSlideModal.vue2.js +56 -0
  115. package/dist/lib/ui/uikit/dist/components/PlSlideModal/PlSlideModal.vue2.js.map +1 -0
  116. package/dist/lib/ui/uikit/dist/components/PlSlideModal/PlSlideModal.vue3.js +18 -0
  117. package/dist/lib/ui/uikit/dist/components/PlSlideModal/PlSlideModal.vue3.js.map +1 -0
  118. package/dist/lib/ui/uikit/dist/components/PlSlideModal/props.js +10 -0
  119. package/dist/lib/ui/uikit/dist/components/PlSlideModal/props.js.map +1 -0
  120. package/dist/lib/ui/uikit/dist/components/PlTextArea/PlTextArea.vue.js +1 -1
  121. package/dist/lib/ui/uikit/dist/components/PlTextField/PlTextField.vue.js +1 -1
  122. package/dist/lib/ui/uikit/dist/composition/useConfirm.js +23 -0
  123. package/dist/lib/ui/uikit/dist/composition/useConfirm.js.map +1 -0
  124. package/dist/lib/ui/uikit/dist/generated/components/svg/images/{SvgRequired.vue.js → SvgRequired.vue2.js} +1 -1
  125. package/dist/lib/ui/uikit/dist/generated/components/svg/images/SvgRequired.vue2.js.map +1 -0
  126. package/dist/lib/ui/uikit/dist/index.js +92 -88
  127. package/dist/lib/ui/uikit/dist/index.js.map +1 -1
  128. package/dist/lib/ui/uikit/dist/lib/model/common/dist/index.js +5 -5
  129. package/dist/lib/ui/uikit/dist/sdk/model/dist/index.js +42 -42
  130. package/dist/lib/ui/uikit/dist/sdk/model/dist/index.js.map +1 -1
  131. package/dist/lib/util/helpers/dist/index.js +60 -50
  132. package/dist/lib/util/helpers/dist/index.js.map +1 -1
  133. package/dist/lib.d.ts +1 -0
  134. package/dist/lib.d.ts.map +1 -1
  135. package/dist/lib.js +261 -251
  136. package/dist/lib.js.map +1 -1
  137. package/dist/plugins/Monetization/MonetizationSidebar.vue.js +9 -9
  138. package/dist/sdk/model/dist/index.js +926 -539
  139. package/dist/sdk/model/dist/index.js.map +1 -1
  140. package/package.json +6 -8
  141. package/src/components/PlAgDataTable/PlAgDataTableV2.vue +20 -74
  142. package/src/components/PlAgDataTable/sources/table-source-v2.ts +29 -29
  143. package/src/components/PlAgDataTable/types.ts +42 -70
  144. package/src/components/PlAnnotations/components/AnnotationsSidebar.vue +103 -0
  145. package/src/components/PlAnnotations/components/DynamicForm.vue +144 -0
  146. package/src/components/PlAnnotations/components/FilterSidebar.vue +143 -0
  147. package/src/components/PlAnnotations/components/PlAnnotationCreateDialog.vue +64 -0
  148. package/src/components/PlAnnotations/components/PlAnnotationsModal.vue +114 -0
  149. package/src/components/PlAnnotations/index.ts +1 -0
  150. package/src/components/PlAnnotations/types.ts +9 -0
  151. package/src/components/PlAnnotations/utils.ts +25 -0
  152. package/src/lib.ts +2 -0
  153. package/dist/lib/ui/uikit/dist/components/PlAccordion/ExpandTransition.vue2.js.map +0 -1
  154. package/dist/lib/ui/uikit/dist/generated/components/svg/images/SvgRequired.vue.js.map +0 -1
@@ -0,0 +1,144 @@
1
+ <script setup lang="ts" generic="T extends FilterUi = FilterUi">
2
+ import { computed, watch } from 'vue';
3
+ import type { FilterUi, SUniversalPColumnId, TypeFieldRecord } from '@platforma-sdk/model';
4
+ import type { FilterUiType } from '@platforma-sdk/model';
5
+ import { isNil } from '@milaboratories/helpers';
6
+ import { PlTextField, PlDropdown, PlNumberField, PlCheckbox } from '@milaboratories/uikit';
7
+ import { getFilterUiTypeOptions, getFilterUiMetadata } from '@platforma-sdk/model';
8
+ import type { SimplifiedUniversalPColumnEntry } from '../types';
9
+
10
+ type ObjectEntries<T, K extends keyof T = keyof T> = [K, T[K]][];
11
+
12
+ const formData = defineModel<T>({ default: () => ({}) });
13
+
14
+ const props = defineProps<{
15
+ columns: SimplifiedUniversalPColumnEntry[];
16
+ formMetadata: TypeFieldRecord<T>;
17
+ }>();
18
+
19
+ const columnSpecRef = computed(() => {
20
+ const value = formData.value;
21
+ if ('column' in value) {
22
+ return props.columns.find((c) => c.id === value.column)?.obj;
23
+ }
24
+ return undefined;
25
+ });
26
+
27
+ const typeMetadataRef = computed(() => {
28
+ const value = formData.value;
29
+ if (value.type && typeof value.type === 'string') {
30
+ return getFilterUiMetadata(value.type);
31
+ }
32
+ return undefined;
33
+ });
34
+
35
+ const filterUiTypeOptions = computed(() => {
36
+ return getFilterUiTypeOptions(columnSpecRef.value);
37
+ });
38
+
39
+ const firstColumnsOptions = computed(() => props.columns.map((c) => ({ label: c.label, value: c.id })));
40
+ const secondColumnOptions = computed(() => {
41
+ const typeMetadata = typeMetadataRef.value;
42
+ const columnSpec = columnSpecRef.value;
43
+ if (typeMetadata && columnSpec) {
44
+ return props.columns.filter((c) => typeMetadata.supportedFor(columnSpec, c.obj)).map((c) => ({
45
+ label: c.label,
46
+ value: c.id,
47
+ }));
48
+ }
49
+ return [];
50
+ });
51
+
52
+ const setFieldValue = <K extends keyof T>(fieldName: K, value: T[K]) => {
53
+ formData.value[fieldName] = value;
54
+ };
55
+
56
+ watch(() => props.formMetadata, (newForm) => {
57
+ for (const [fieldName, field] of Object.entries(newForm) as ObjectEntries<typeof newForm>) {
58
+ if (formData.value[fieldName] === undefined) {
59
+ const value = field.defaultValue();
60
+ if (!isNil(value)) {
61
+ formData.value[fieldName] = value;
62
+ }
63
+ }
64
+ }
65
+ },
66
+ { immediate: true, deep: true },
67
+ );
68
+
69
+ </script>
70
+
71
+ <template>
72
+ <div v-if="formMetadata" :class="$style.form">
73
+ <template v-for="(field, fieldName) in formMetadata" :key="fieldName">
74
+ <template v-if="field.fieldType === 'form'">
75
+ <!-- TODO: Nested Form not described in FilterUi, we need to define it later. Even more in type it don't possible situations -->
76
+ <DynamicForm
77
+ v-if="'form' in field"
78
+ :model-value="formData[fieldName] as any"
79
+ :form-metadata="field.form as any"
80
+ :columns="props.columns"
81
+ @update:model-value="setFieldValue(fieldName, $event as T[keyof T])"
82
+ />
83
+ </template>
84
+ <template v-else-if="field.fieldType === 'FilterUiType'">
85
+ <PlDropdown
86
+ :model-value="formData[fieldName] as FilterUiType"
87
+ :label="field.label ?? fieldName"
88
+ :options="filterUiTypeOptions"
89
+ @update:model-value="setFieldValue(fieldName, $event as T[keyof T])"
90
+ />
91
+ </template>
92
+ <template v-else-if="field.fieldType === 'string'">
93
+ <PlTextField
94
+ :model-value="formData[fieldName] as string"
95
+ :label="field.label ?? fieldName"
96
+ @update:model-value="setFieldValue(fieldName, $event as T[keyof T])"
97
+ />
98
+ </template>
99
+ <template v-else-if="field.fieldType === 'SUniversalPColumnId'">
100
+ <PlDropdown
101
+ :model-value="formData[fieldName] as SUniversalPColumnId"
102
+ :label="field.label ?? fieldName"
103
+ :options="fieldName === 'column' ? firstColumnsOptions : secondColumnOptions"
104
+ @update:model-value="setFieldValue(fieldName, $event as T[keyof T])"
105
+ />
106
+ </template>
107
+ <template v-else-if="field.fieldType === 'number'">
108
+ <PlNumberField
109
+ :model-value="formData[fieldName] as number"
110
+ :label="field.label ?? fieldName"
111
+ @update:model-value="setFieldValue(fieldName, $event as T[keyof T])"
112
+ />
113
+ </template>
114
+ <template v-else-if="field.fieldType === 'number?'">
115
+ <PlNumberField
116
+ :model-value="formData[fieldName] as (undefined | number)"
117
+ :label="field.label ?? fieldName"
118
+ :clearable="true"
119
+ @update:model-value="setFieldValue(fieldName, $event as T[keyof T])"
120
+ />
121
+ </template>
122
+ <template v-else-if="field.fieldType === 'boolean' || field.fieldType === 'boolean?'">
123
+ <PlCheckbox
124
+ :model-value="Boolean(formData[fieldName])"
125
+ :label="field.label ?? fieldName"
126
+ @update:model-value="setFieldValue(fieldName, $event as T[keyof T])"
127
+ >
128
+ {{ field.label ?? fieldName }}
129
+ </PlCheckbox>
130
+ </template>
131
+ <template v-else>
132
+ <pre>TODO:{{ field.fieldType }}</pre>
133
+ </template>
134
+ </template>
135
+ </div>
136
+ </template>
137
+
138
+ <style module>
139
+ .form {
140
+ display: flex;
141
+ flex-direction: column;
142
+ gap: 12px;
143
+ }
144
+ </style>
@@ -0,0 +1,143 @@
1
+ <script setup lang="ts">
2
+ import { isNil, randomInt } from '@milaboratories/helpers';
3
+ import {
4
+ PlBtnSecondary,
5
+ PlEditableTitle,
6
+ PlElementList,
7
+ PlSidebarItem,
8
+ } from '@milaboratories/uikit';
9
+ import type { AnnotationStepUi, FilterUi, PObjectId, SUniversalPColumnId } from '@platforma-sdk/model';
10
+ import { getFilterUiMetadata } from '@platforma-sdk/model';
11
+ import type { SimplifiedUniversalPColumnEntry } from '../types';
12
+ import { createDefaultFilterMetadata } from '../utils';
13
+ import DynamicForm from './DynamicForm.vue';
14
+
15
+ // Models
16
+ const step = defineModel<AnnotationStepUi>('step', { required: true });
17
+ // Props
18
+ const props = defineProps<{
19
+ columns: SimplifiedUniversalPColumnEntry[];
20
+ hasSelectedColumns: boolean;
21
+ getValuesForSelectedColumns: () => Promise<undefined | { columnId: PObjectId; values: string[] }>;
22
+ }>();
23
+ // Actions
24
+ const addFilterPlaceholder = () => {
25
+ step.value.filter.filters.push({
26
+ id: randomInt(),
27
+ isExpanded: true,
28
+ type: undefined,
29
+ });
30
+ };
31
+
32
+ async function addFilterFromSelected() {
33
+ const data = await props.getValuesForSelectedColumns();
34
+ if (!data || data.values.length === 0) return;
35
+
36
+ const { columnId, values } = data;
37
+ const shortReminder = values.slice(0, 3).join(', ') + (values.length > 3 ? ` and ${values.length - 3} more` : '');
38
+
39
+ step.value.filter.filters.push({
40
+ id: randomInt(),
41
+ name: `Selected list (${shortReminder})`,
42
+ isExpanded: false,
43
+ type: 'or',
44
+ filters: values.map((value) => ({
45
+ type: 'patternEquals',
46
+ column: columnId as SUniversalPColumnId,
47
+ value,
48
+ })),
49
+ });
50
+ }
51
+
52
+ // Getters
53
+ const getColumnLabel = (filter: FilterUi) => {
54
+ if (!isNil(filter.name)) return filter.name;
55
+ return props.columns
56
+ .find((c) => 'column' in filter ? c.id === filter.column : false)?.label
57
+ ?? filter.type;
58
+ };
59
+
60
+ const getFormMetadata = (filter: FilterUi) => {
61
+ return !isNil(filter.type) ? getFilterUiMetadata(filter.type).form : createDefaultFilterMetadata();
62
+ };
63
+
64
+ const getFilterValues = (filter: FilterUi) => {
65
+ if (filter.type === 'or' || filter.type === 'and') {
66
+ return filter.filters.map((f) => 'value' in f && !isNil(f.value) ? f.value : null).filter((v) => !isNil(v)).join (', ');
67
+ }
68
+ return null;
69
+ };
70
+ </script>
71
+
72
+ <template>
73
+ <PlSidebarItem v-if="step">
74
+ <template #header-content>
75
+ <PlEditableTitle
76
+ :key="step.id"
77
+ v-model="step.label"
78
+ :max-length="40"
79
+ max-width="600px"
80
+ placeholder="Annotation Name"
81
+ :autofocus="step.label.length === 0"
82
+ />
83
+ </template>
84
+ <template #body-content>
85
+ <div :class="$style.root">
86
+ <div :class="$style.actions">
87
+ <PlBtnSecondary style="width: 100%;" icon="add" @click="addFilterPlaceholder">
88
+ Filter
89
+ </PlBtnSecondary>
90
+ <PlBtnSecondary style="width: 100%;" icon="add" :disabled="!props.hasSelectedColumns" @click="addFilterFromSelected">
91
+ From selection
92
+ </PlBtnSecondary>
93
+ </div>
94
+
95
+ <span :class="$style.tip">Lower annotations override the ones above. Rearrange them by dragging.</span>
96
+
97
+ <PlElementList
98
+ v-model:items="step.filter.filters"
99
+ :get-item-key="(item) => item.id!"
100
+ :is-expanded="(item) => Boolean(item.isExpanded)"
101
+ :on-expand="(item) => item.isExpanded = !Boolean(item.isExpanded)"
102
+ >
103
+ <template #item-title="{ item }">
104
+ {{ getColumnLabel(item) }}
105
+ </template>
106
+ <template #item-content="{ item, index }">
107
+ <template v-if="item.type !== 'or' && item.type !== 'and'">
108
+ <DynamicForm
109
+ v-model="step.filter.filters[index]"
110
+ :form-metadata="getFormMetadata(item)"
111
+ :columns="props.columns"
112
+ />
113
+ </template>
114
+ <template v-else>
115
+ <div>{{ getFilterValues(item) }}</div>
116
+ </template>
117
+ </template>
118
+ </PlElementList>
119
+ </div>
120
+ </template>
121
+ </PlSidebarItem>
122
+ </template>
123
+
124
+ <style lang="scss" module>
125
+ @use '@milaboratories/uikit/styles/variables' as *;
126
+
127
+ .root {
128
+ display: flex;
129
+ flex-direction: column;
130
+ gap: 12px;
131
+ }
132
+
133
+ .actions {
134
+ display: flex;
135
+ flex-direction: row;
136
+ gap: 12px;
137
+ }
138
+
139
+ .tip {
140
+ margin-top: 12px;
141
+ color: var(--txt-03);
142
+ }
143
+ </style>
@@ -0,0 +1,64 @@
1
+ <script setup lang="ts">
2
+ import type { SimpleOption } from '@milaboratories/uikit';
3
+ import { PlBtnGhost, PlBtnPrimary, PlDialogModal, PlRadioGroup, PlTextField } from '@milaboratories/uikit';
4
+ import type { AnnotationMode } from '@platforma-sdk/model';
5
+ import { computed, ref } from 'vue';
6
+
7
+ // Models
8
+ const opened = defineModel<boolean>({ required: true });
9
+ // Emits
10
+ const emits = defineEmits<{
11
+ (e: 'submit', props: { type: 'byClonotype' | 'bySampleAndClonotype'; name: string }): void;
12
+ }>();
13
+
14
+ const annotationSchemaTypes = [
15
+ { label: 'Global', value: 'byClonotype' },
16
+ { label: 'Per sample', value: 'bySampleAndClonotype' },
17
+ ] satisfies SimpleOption<AnnotationMode>[];
18
+
19
+ const modalState = ref<{
20
+ type: 'byClonotype' | 'bySampleAndClonotype';
21
+ name: string;
22
+ }>({
23
+ type: 'byClonotype',
24
+ name: '',
25
+ });
26
+
27
+ const isValidForm = computed(() => {
28
+ return modalState.value.name.length > 3;
29
+ });
30
+
31
+ const handleSubmit = () => {
32
+ if (isValidForm.value) {
33
+ emits('submit', modalState.value);
34
+ }
35
+ };
36
+
37
+ const handleCancel = () => {
38
+ opened.value = false;
39
+ };
40
+ </script>
41
+
42
+ <template>
43
+ <PlDialogModal v-model="opened" width="600px">
44
+ <template #title>
45
+ Choose the Annotation Scheme type
46
+ </template>
47
+ <template #default>
48
+ <PlRadioGroup v-model="modalState.type" :options="annotationSchemaTypes" />
49
+ <PlTextField
50
+ v-model="modalState.name"
51
+ label="Name your Scheme"
52
+ min-length="3"
53
+ max-length="40"
54
+ placeholder="Annotation Name"
55
+ autofocus
56
+ required
57
+ />
58
+ </template>
59
+ <template #actions>
60
+ <PlBtnPrimary :disabled="!isValidForm" @click.stop="handleSubmit">Apply</PlBtnPrimary>
61
+ <PlBtnGhost @click.stop="handleCancel">Cancel</PlBtnGhost>
62
+ </template>
63
+ </PlDialogModal>
64
+ </template>
@@ -0,0 +1,114 @@
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';
11
+
12
+ // Models
13
+ const annotation = defineModel<AnnotationScriptUi>('annotation', { required: true, default: getDefaultAnnotationScript });
14
+ const opened = defineModel<boolean>('opened', { required: true });
15
+ // Props
16
+ const props = defineProps<{
17
+ columns: SimplifiedUniversalPColumnEntry[];
18
+ hasSelectedColumns: boolean;
19
+ getValuesForSelectedColumns: () => Promise<undefined | { columnId: PObjectId; values: string[] }>;
20
+ }>();
21
+ // State
22
+ 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
+ // Watchers
39
+ effect(function setDefaultStepId() {
40
+ if (selectedStepId.value === undefined && annotation.value.steps.length > 0) {
41
+ selectedStepId.value = annotation.value.steps[0].id;
42
+ }
43
+ });
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
+ // 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
+ 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
+ }
68
+ }
69
+
70
+ </script>
71
+
72
+ <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>
97
+ </PlPureSlideModal>
98
+ </template>
99
+
100
+ <style lang="scss" module>
101
+ .modal {
102
+ display: flex;
103
+ }
104
+
105
+ .sidebarGroup {
106
+ width: 100%;
107
+ height: 100%;
108
+ }
109
+
110
+ .sidebarItem {
111
+ width: 100%;
112
+ height: 100%;
113
+ }
114
+ </style>
@@ -0,0 +1 @@
1
+ export { default as PlAnnotationsModal } from './components/PlAnnotationsModal.vue';
@@ -0,0 +1,9 @@
1
+ import type { PColumnSpec, SUniversalPColumnId } from '@platforma-sdk/model';
2
+
3
+ export type SimplifiedPColumnSpec = Pick<PColumnSpec, 'valueType' | 'annotations'>;
4
+
5
+ export type SimplifiedUniversalPColumnEntry = {
6
+ id: SUniversalPColumnId;
7
+ label: string;
8
+ obj: SimplifiedPColumnSpec;
9
+ };
@@ -0,0 +1,25 @@
1
+ import type { AnnotationScriptUi, FilterUi, TypeFieldRecord } from '@platforma-sdk/model';
2
+
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> {
13
+ return {
14
+ column: {
15
+ label: 'Column',
16
+ fieldType: 'SUniversalPColumnId',
17
+ defaultValue: () => undefined,
18
+ },
19
+ type: {
20
+ label: 'Predicate',
21
+ fieldType: 'FilterUiType',
22
+ defaultValue: () => undefined,
23
+ },
24
+ } as TypeFieldRecord<T>;
25
+ };
package/src/lib.ts CHANGED
@@ -28,6 +28,8 @@ export * from './components/PlTableFilters';
28
28
 
29
29
  export * from './components/PlMultiSequenceAlignment';
30
30
 
31
+ export * from './components/PlAnnotations';
32
+
31
33
  export * from './defineApp';
32
34
 
33
35
  export * from './createModel';
@@ -1 +0,0 @@
1
- {"version":3,"file":"ExpandTransition.vue2.js","sources":["../../../../../../../../../lib/ui/uikit/src/components/PlAccordion/ExpandTransition.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nconst onStart = (el: Element) => {\n el.classList.add('expand-collapse-fix');\n (el as HTMLElement).style.setProperty('--component-height', el.scrollHeight + 'px');\n};\n\nconst onAfter = (el: Element) => {\n (el as HTMLElement).style.removeProperty('--component-height');\n el.classList.remove('expand-collapse-fix');\n};\n</script>\n\n<template>\n <Transition name=\"expand-collapse\" @enter=\"onStart\" @leave=\"onStart\" @after-enter=\"onAfter\" @after-leave=\"onAfter\">\n <slot/>\n </Transition>\n</template>\n\n<style>\n.expand-collapse-fix {\n overflow: hidden;\n}\n\n.expand-collapse-enter-active,\n.expand-collapse-leave-active {\n transition:\n height 0.2s ease-in-out,\n opacity 0.2s ease-in-out;\n height: var(--component-height);\n}\n\n.expand-collapse-enter-from,\n.expand-collapse-leave-to {\n opacity: 0.5;\n height: 0;\n}\n</style>\n"],"names":["onStart","el","onAfter"],"mappings":";;;;AACMA,UAAAA,IAAU,CAACC,MAAgB;AAC5BA,QAAA,UAAU,IAAI,qBAAqB,GACrCA,EAAmB,MAAM,YAAY,sBAAsBA,EAAG,eAAe,IAAI;AAAA,IAAA,GAG9EC,IAAU,CAACD,MAAgB;AAC9BA,QAAmB,MAAM,eAAe,oBAAoB,GAC1DA,EAAA,UAAU,OAAO,qBAAqB;AAAA,IAC3C;;;;;;;;;;;;;;;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"SvgRequired.vue.js","sources":["../../../../../../../../../../../lib/ui/uikit/src/generated/components/svg/images/SvgRequired.vue"],"sourcesContent":["<!-- ⚠️ AUTOGENERATED. DO NOT EDIT. -->\n<script lang=\"ts\">\nimport '../svg-styles.css';\nexport default { name: 'SvgRequired' };\n</script>\n\n<template>\n <div class=\"svg-icon SvgRequired\" style=\"width: 5px; height: 12px\" />\n</template>\n\n<style>\n .SvgRequired { background-image: url(\"data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%225%22%20height%3D%2212%22%20viewBox%3D%220%200%205%2012%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M1.51685%204.8L2.5%203.34159L3.47612%204.8L4.39607%204.12743L3.31461%202.7469L5%202.25133L4.64888%201.16106L3.00562%201.77699L3.06882%200H1.93118L1.99438%201.77699L0.351124%201.16106L0%202.25133L1.68539%202.7469L0.59691%204.12743L1.51685%204.8Z%22%20fill%3D%22%23F1222F%22%2F%3E%3C%2Fsvg%3E\"); }\n</style>\n"],"names":[],"mappings":";;;;;;;;;;;;;AAOoC,MAAA,IAAA;AAAA,EAAA,OAAA;AAAA;;;;;;"}