@milaboratories/uikit 1.2.29 → 2.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/uikit",
3
- "version": "1.2.29",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "main": "dist/pl-uikit.umd.js",
6
6
  "module": "dist/pl-uikit.js",
@@ -22,6 +22,7 @@
22
22
  },
23
23
  "devDependencies": {
24
24
  "@vue/test-utils": "^2.4.6",
25
+ "@vueuse/core": "^11.1.0",
25
26
  "jsdom": "^24.1.3",
26
27
  "resize-observer-polyfill": "^1.5.1",
27
28
  "@vitejs/plugin-vue": "^5.1.4",
@@ -39,7 +39,12 @@ async function generateSCSS() {
39
39
  return ` ${icon}: '${name}'`; // keep extension
40
40
  });
41
41
 
42
+ const names = Object.keys(json)
43
+ .map((k) => ` '${k}'`)
44
+ .join(',\n');
45
+
42
46
  await fp.writeFile(`./src/assets/icons/icons-${size}-generated.scss`, `$icons${size}: (\n${lines.join(',\n')}\n)`);
47
+ await fp.writeFile(`./src/generated/icons-${size}.ts`, `export const maskIcons${size} = [\n${names},\n] as const;\n`);
43
48
  await fp.writeFile(`./src/assets/icons/icons-${size}-generated.json`, JSON.stringify(json, null, 2));
44
49
  }
45
50
  }
@@ -51,13 +51,13 @@
51
51
  min-height: 0;
52
52
  line-height: 20px;
53
53
  // background-color: #33333333; //
54
+ @include scrollbar();
54
55
  &.no-content-gutters {
55
56
  padding: 0 0;
56
57
  }
57
58
  &.no-top-content-gutter {
58
59
  padding-top: 0;
59
60
  }
60
- @include scrollbar();
61
61
  }
62
62
 
63
63
  &__actions {
@@ -68,6 +68,11 @@ const props = withDefaults(
68
68
  * If `true`, only the file name is displayed, not the full path to it.
69
69
  */
70
70
  showFilenameOnly?: boolean;
71
+
72
+ /**
73
+ * Remove rounded border and change styles
74
+ */
75
+ cellStyle?: boolean;
71
76
  }>(),
72
77
  {
73
78
  label: undefined,
@@ -77,6 +82,7 @@ const props = withDefaults(
77
82
  progress: undefined,
78
83
  error: undefined,
79
84
  helper: undefined,
85
+ cellStyle: false,
80
86
  },
81
87
  );
82
88
 
@@ -142,14 +148,16 @@ const clear = () => emit('update:modelValue', undefined);
142
148
 
143
149
  const rootRef = ref();
144
150
 
145
- useLabelNotch(rootRef);
151
+ if (!props.cellStyle) {
152
+ useLabelNotch(rootRef);
153
+ }
146
154
  </script>
147
155
 
148
156
  <template>
149
- <div class="pl-file-input__envelope">
157
+ <div :class="{ 'pl-file-input__cell-style': !!cellStyle, 'has-file': !!fileName }" class="pl-file-input__envelope">
150
158
  <div ref="rootRef" class="pl-file-input" tabindex="0" :class="{ dashed, error: hasErrors }" @keyup.enter="openFileDialog">
151
159
  <div class="pl-file-input__progress" :style="progressStyle" />
152
- <label v-if="label" ref="label">
160
+ <label v-if="!cellStyle && label" ref="label">
153
161
  <i v-if="required" class="required-icon" />
154
162
  <span>{{ label }}</span>
155
163
  <PlTooltip v-if="slots.tooltip" class="info" position="top">
@@ -14,8 +14,8 @@
14
14
  --icon-color: #000;
15
15
 
16
16
  width: 100%;
17
- min-width: 160px;
18
- min-height: var(--control-height);
17
+ // min-width: 160px;
18
+ height: 40px;
19
19
  position: relative;
20
20
  border-radius: var(--border-radius-control);
21
21
  display: flex;
@@ -28,6 +28,10 @@
28
28
 
29
29
  &__envelope {
30
30
  font-family: var(--font-family-base);
31
+ height: var(--control-height);
32
+ display: flex;
33
+ align-items: center;
34
+ min-width: 160px;
31
35
  }
32
36
 
33
37
  .mask-24 {
@@ -52,14 +56,12 @@
52
56
  }
53
57
 
54
58
  &__filename {
55
- height: 100%;
56
59
  width: 100%;
57
60
  border: none;
58
61
  font-size: inherit;
59
62
  background-color: transparent;
60
63
  color: var(--text-color);
61
64
  caret-color: var(--border-color-focus);
62
- cursor: inherit;
63
65
  z-index: 1;
64
66
  white-space: nowrap;
65
67
  overflow: hidden;
@@ -118,9 +120,11 @@
118
120
  flex-direction: row;
119
121
  align-items: center;
120
122
  gap: 4px;
123
+
121
124
  .icon {
122
125
  cursor: pointer;
123
126
  }
127
+
124
128
  .mask {
125
129
  background-color: var(--control-mask-fill);
126
130
  cursor: pointer;
@@ -166,6 +170,7 @@
166
170
  --label-color: var(--txt-error);
167
171
  --text-color: var(--txt-error);
168
172
  --icon-color: var(--txt-error);
173
+
169
174
  .pl-file-input__progress {
170
175
  display: none;
171
176
  }
@@ -176,3 +181,76 @@
176
181
  cursor: not-allowed;
177
182
  }
178
183
  }
184
+
185
+ // @TODO
186
+ .pl-file-input__cell-style {
187
+ $root: &;
188
+
189
+ --border-radius-control: 0px;
190
+ --input-active-color: var(--bg-base-light);
191
+ --border-color: var(--border-color-div-grey);
192
+
193
+ background-color: var(--input-active-color);
194
+ padding: 4px;
195
+ height: 100%;
196
+ position: relative;
197
+
198
+ .mask-paper-clip {
199
+ display: none;
200
+ }
201
+
202
+ .pl-file-input {
203
+ height: 100% !important;
204
+ position: unset;
205
+
206
+ .double-contour {
207
+ inset: 2px;
208
+ }
209
+
210
+ &__contour {
211
+ border-width: 1px;
212
+ box-shadow: unset !important;
213
+ border-color: var(--border-color);
214
+ border-style: dashed;
215
+ }
216
+
217
+ &__filename {
218
+ text-align: center !important;
219
+ }
220
+
221
+ &__error {
222
+ margin-top: 0;
223
+ margin-right: 8px;
224
+ }
225
+ }
226
+
227
+ &:hover {
228
+ --border-color: var(--border-color-focus);
229
+ background-color: #D9F8CA;
230
+ }
231
+
232
+ &:hover .pl-file-input__filename::before {
233
+ color: #110529 !important;
234
+ }
235
+
236
+ &.has-file:hover {
237
+ background-color: var(--input-active-color);
238
+ }
239
+
240
+ &.has-file {
241
+ padding-left: 0;
242
+ padding-right: 0;
243
+
244
+ .pl-file-input__contour {
245
+ border: unset;
246
+ }
247
+
248
+ .pl-file-input__filename {
249
+ text-align: left !important;
250
+ }
251
+
252
+ .mask-paper-clip {
253
+ display: block;
254
+ }
255
+ }
256
+ }
@@ -12,7 +12,8 @@ import { computed, onMounted, onUpdated, ref } from 'vue';
12
12
  import MaskIcon24 from '../MaskIcon24.vue';
13
13
  import './pl-log-view.scss';
14
14
  import { okOptional, tapIf } from '@milaboratories/helpers';
15
- import type { ValueOrErrors } from '@platforma-sdk/model';
15
+ import type { AnyLogHandle, Platforma, ValueOrErrors } from '@platforma-sdk/model';
16
+ import { useLogHandle } from './useLogHandle';
16
17
 
17
18
  const getOutputError = <T,>(o?: ValueOrErrors<T>) => {
18
19
  if (o && o.ok === false) {
@@ -25,6 +26,14 @@ const props = defineProps<{
25
26
  * String contents
26
27
  */
27
28
  value?: string;
29
+ /**
30
+ * AnyLogHandle
31
+ */
32
+ logHandle?: AnyLogHandle;
33
+ /**
34
+ * Custom progress prefix (to filter logHandle results)
35
+ */
36
+ progressPrefix?: string;
28
37
  /**
29
38
  * String contents
30
39
  */
@@ -33,17 +42,23 @@ const props = defineProps<{
33
42
  * Block output (Note: error and value take precedence over output property)
34
43
  */
35
44
  output?: ValueOrErrors<unknown>;
45
+ /**
46
+ * @TODO
47
+ */
48
+ mockPlatforma?: Platforma;
36
49
  }>();
37
50
 
51
+ const logState = useLogHandle(props);
52
+
38
53
  const contentRef = ref<HTMLElement>();
39
54
 
40
- const computedError = computed(() => props.error ?? getOutputError(props.output));
55
+ const computedError = computed(() => logState.value?.error ?? props.error ?? getOutputError(props.output));
41
56
 
42
- const computedValue = computed(() => props.value ?? okOptional(props.output));
57
+ const computedValue = computed(() => logState.value?.lines ?? props.value ?? okOptional(props.output));
43
58
 
44
59
  const onClickCopy = () => {
45
- if (props.value) {
46
- navigator.clipboard.writeText(props.value);
60
+ if (computedValue.value && typeof computedValue.value === 'string') {
61
+ navigator.clipboard.writeText(computedValue.value);
47
62
  }
48
63
  };
49
64
 
@@ -0,0 +1,93 @@
1
+ import { type Reactive, ref, watch } from 'vue';
2
+ import { useTimeoutPoll, whenever } from '@vueuse/core';
3
+ import type { AnyLogHandle, Platforma } from '@platforma-sdk/model';
4
+
5
+ type LogState = {
6
+ logHandle: AnyLogHandle;
7
+ lines: string;
8
+ lastOffset: number;
9
+ finished: boolean;
10
+ error: unknown;
11
+ };
12
+
13
+ const ProgressPrefixDefault = '[==PROGRESS==]'; // @TODO ?
14
+
15
+ // from here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
16
+ function escapeRegExp(str: string) {
17
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
18
+ }
19
+
20
+ export function useLogHandle(props: Reactive<{ logHandle: AnyLogHandle | undefined; mockPlatforma?: Platforma; progressPrefix?: string }>) {
21
+ const logState = ref<LogState>();
22
+
23
+ async function fetchLogs() {
24
+ // making a snapshot of the ref
25
+ let currentLogState: LogState | undefined = logState.value;
26
+
27
+ if (currentLogState === undefined) return;
28
+
29
+ const platforma = props.mockPlatforma ?? window.platforma;
30
+
31
+ if (!platforma) {
32
+ console.warn('Platforma API is not available');
33
+ return;
34
+ }
35
+
36
+ // eslint-disable-next-line no-constant-condition
37
+ while (true) {
38
+ currentLogState.error = undefined;
39
+
40
+ const result = await platforma.logDriver.readText(currentLogState.logHandle, 100, currentLogState.lastOffset);
41
+
42
+ if (result.shouldUpdateHandle) return;
43
+
44
+ // somebody changed target log while we were fetching log data
45
+ // @TODO There may be a situation where the new descriptor points to the same file and we don't need to reread it
46
+ if (currentLogState.logHandle !== logState.value?.logHandle) return;
47
+
48
+ const progressPrefix = props.progressPrefix ?? ProgressPrefixDefault;
49
+
50
+ const newLines = new TextDecoder().decode(result.data).replace(new RegExp(`${escapeRegExp(progressPrefix)}`, 'g'), '');
51
+
52
+ // We simply change it in a mutable way: if logHandle has been changed, it points to the new object
53
+ currentLogState = Object.assign(currentLogState, {
54
+ lines: currentLogState.lines + newLines,
55
+ lastOffset: result.newOffset,
56
+ finished: !result.live,
57
+ });
58
+
59
+ if (result.newOffset >= result.size) break;
60
+ }
61
+ }
62
+
63
+ const fetchAndCatch = () =>
64
+ fetchLogs().catch((err) => {
65
+ if (logState.value) {
66
+ logState.value.error = err;
67
+ }
68
+ });
69
+
70
+ // Only trigger after last fetch is done
71
+ const timeoutPoll = useTimeoutPoll(fetchAndCatch, 1500, { immediate: false });
72
+
73
+ whenever(
74
+ () => logState?.value?.finished,
75
+ () => timeoutPoll.pause(),
76
+ );
77
+
78
+ watch(
79
+ () => props.logHandle,
80
+ (lh) => {
81
+ if (lh === undefined) {
82
+ logState.value = undefined;
83
+ timeoutPoll.pause();
84
+ } else if (lh !== logState.value?.logHandle) {
85
+ logState.value = { logHandle: lh, lastOffset: 0, lines: '', finished: false, error: undefined };
86
+ timeoutPoll.resume();
87
+ }
88
+ },
89
+ { immediate: true },
90
+ );
91
+
92
+ return logState;
93
+ }
File without changes
@@ -0,0 +1,56 @@
1
+ export const maskIcons16 = [
2
+ 'add',
3
+ 'arrow-down',
4
+ 'arrow-left',
5
+ 'arrow-right',
6
+ 'arrow-up',
7
+ 'cell-type-num',
8
+ 'cell-type-txt',
9
+ 'checkmark',
10
+ 'chevron-down',
11
+ 'chevron-first',
12
+ 'chevron-last',
13
+ 'chevron-left',
14
+ 'chevron-right',
15
+ 'chevron-up',
16
+ 'clear',
17
+ 'clipboard-copied',
18
+ 'clipboard',
19
+ 'close',
20
+ 'comp',
21
+ 'compare',
22
+ 'copy',
23
+ 'data-dimentions',
24
+ 'delete',
25
+ 'drag-dots',
26
+ 'edit',
27
+ 'error',
28
+ 'export',
29
+ 'filter-2',
30
+ 'filter',
31
+ 'help',
32
+ 'import',
33
+ 'info',
34
+ 'link-arrow',
35
+ 'link',
36
+ 'loading',
37
+ 'lock',
38
+ 'maximize',
39
+ 'minimize',
40
+ 'minus',
41
+ 'more-horizontal',
42
+ 'open',
43
+ 'pause',
44
+ 'play',
45
+ 'required',
46
+ 'restart',
47
+ 'settings-2',
48
+ 'sort',
49
+ 'sorter',
50
+ 'stop',
51
+ 'success',
52
+ 'warning',
53
+ 'x-axis',
54
+ 'y-axis',
55
+ 'zip',
56
+ ] as const;
@@ -0,0 +1,134 @@
1
+ export const maskIcons24 = [
2
+ 'add-layer',
3
+ 'add',
4
+ 'annotate',
5
+ 'annotation',
6
+ 'area',
7
+ 'arrow-down',
8
+ 'arrow-left',
9
+ 'arrow-right',
10
+ 'arrow-up',
11
+ 'axes',
12
+ 'bar-error',
13
+ 'bar-trend',
14
+ 'bar',
15
+ 'bindot',
16
+ 'box-dot',
17
+ 'boxplot-1',
18
+ 'boxplot-binned',
19
+ 'boxplot-jitter',
20
+ 'boxplot-notched',
21
+ 'boxplot',
22
+ 'bubble',
23
+ 'canvas',
24
+ 'checkbox',
25
+ 'checkmark',
26
+ 'chevron-down',
27
+ 'chevron-left',
28
+ 'chevron-right',
29
+ 'chevron-sort',
30
+ 'chevron-up',
31
+ 'clipboard-copied',
32
+ 'clipboard',
33
+ 'close',
34
+ 'cloud-down',
35
+ 'cloud-offline',
36
+ 'cloud-online',
37
+ 'cloud-up',
38
+ 'code-2',
39
+ 'code',
40
+ 'color',
41
+ 'columns',
42
+ 'connected-points',
43
+ 'copy',
44
+ 'cpu',
45
+ 'cross-bars',
46
+ 'dark-mode',
47
+ 'delete',
48
+ 'dendrogram-X-1',
49
+ 'dendrogram-X',
50
+ 'dendrogram-Y-1',
51
+ 'dendrogram-Y',
52
+ 'dna',
53
+ 'download-files',
54
+ 'duplicate',
55
+ 'edit',
56
+ 'error-bar',
57
+ 'error',
58
+ 'expand-left',
59
+ 'expand-right',
60
+ 'export-2',
61
+ 'export',
62
+ 'external-link',
63
+ 'fill-color',
64
+ 'filter-on',
65
+ 'filter',
66
+ 'filters-gate',
67
+ 'fire-tips',
68
+ 'folder-parent',
69
+ 'frame-type',
70
+ 'generate',
71
+ 'graph',
72
+ 'heatmap',
73
+ 'help',
74
+ 'hide',
75
+ 'import-2',
76
+ 'import-download',
77
+ 'info-circle-outline',
78
+ 'jitter',
79
+ 'layers',
80
+ 'learn',
81
+ 'legend',
82
+ 'light-mode',
83
+ 'line-binned',
84
+ 'line-error',
85
+ 'line-jitter',
86
+ 'line',
87
+ 'linetype',
88
+ 'link-disabled',
89
+ 'link',
90
+ 'loading',
91
+ 'local',
92
+ 'logout',
93
+ 'menu',
94
+ 'minus',
95
+ 'more-horizontal',
96
+ 'outlier-shape',
97
+ 'paper-clip',
98
+ 'pin',
99
+ 'position',
100
+ 'progress-2',
101
+ 'progress',
102
+ 'publications',
103
+ 'radio-btn',
104
+ 'restart',
105
+ 'reverse',
106
+ 'rotation',
107
+ 'save',
108
+ 'search',
109
+ 'server-2',
110
+ 'settings-2',
111
+ 'show',
112
+ 'sina',
113
+ 'skatterplot',
114
+ 'social-media',
115
+ 'sort',
116
+ 'stacked-bar',
117
+ 'statistics',
118
+ 'strip-plot',
119
+ 'stroke-non',
120
+ 'stroke',
121
+ 'success',
122
+ 'table-upload',
123
+ 'table',
124
+ 'template',
125
+ 'title-position',
126
+ 'upload-files',
127
+ 'venn',
128
+ 'violin-binned',
129
+ 'violin-jitter',
130
+ 'violin',
131
+ 'warning',
132
+ 'wetlab',
133
+ 'zoom-in',
134
+ ] as const;
package/src/types.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { ImportFileHandle, Platforma, StorageHandle, Ref as ModelRef } from '@platforma-sdk/model';
2
2
  import type { Ref, ComputedRef } from 'vue';
3
+ import { maskIcons16 } from './generated/icons-16';
4
+ import { maskIcons24 } from './generated/icons-24';
3
5
 
4
6
  export type Size = 'small' | 'medium' | 'large';
5
7
 
@@ -63,28 +65,10 @@ export type RefOption = {
63
65
 
64
66
  export type ListOptionType<Type> = Type extends ListOption<infer X>[] ? X : never;
65
67
 
66
- export const maskIcons16 = [
67
- 'checkmark',
68
- 'import',
69
- 'clear',
70
- 'chevron-right',
71
- 'add',
72
- 'play',
73
- 'loading',
74
- 'arrow-right',
75
- 'clipboard',
76
- 'link',
77
- 'comp',
78
- 'close',
79
- 'restart',
80
- 'stop',
81
- 'settings-2',
82
- ] as const;
68
+ export { maskIcons16, maskIcons24 };
83
69
 
84
70
  export type MaskIconName16 = (typeof maskIcons16)[number];
85
71
 
86
- export const maskIcons24 = ['paper-clip', 'cloud-up', 'success', 'restart', 'close', 'clipboard'];
87
-
88
72
  export type MaskIconName24 = (typeof maskIcons24)[number];
89
73
 
90
74
  export type SliderMode = 'input' | 'text';