@v1nt1248/3nclient-lib 0.2.39 → 0.2.41

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.
@@ -1,8 +1,4 @@
1
- export type Task = () => Promise<void>;
2
- export interface TaskRunnerInstance {
3
- addTask: (task: Task) => void;
4
- cancelTasks: () => void;
5
- }
1
+ import { Task, TaskRunnerInstance } from '../../types';
6
2
  export declare class TaskRunner implements TaskRunnerInstance {
7
3
  private readonly maxNumberOfRunners;
8
4
  private arrayOfTasks;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@v1nt1248/3nclient-lib",
3
3
  "license": "AGPL-3.0-or-later",
4
4
  "author": "v1nt1248",
5
- "version": "0.2.39",
5
+ "version": "0.2.41",
6
6
  "description": "Library for 3NWeb clients",
7
7
  "type": "module",
8
8
  "files": [
@@ -91,6 +91,8 @@ import type {
91
91
  Ui3nAutocompleteSlots,
92
92
  Ui3nAutocompleteOptionBase,
93
93
  } from './ui3n-autocomplete/types';
94
+ import Ui3nSlider from './ui3n-slider/ui3n-slider.vue';
95
+ import type { UI3nSliderProps, UI3nSliderEmits } from './ui3n-slider/types';
94
96
 
95
97
  export {
96
98
  Ui3nIcon,
@@ -202,4 +204,7 @@ export {
202
204
  Ui3nAutocompleteEmits,
203
205
  Ui3nAutocompleteSlots,
204
206
  Ui3nAutocompleteOptionBase,
207
+ Ui3nSlider,
208
+ UI3nSliderProps,
209
+ UI3nSliderEmits,
205
210
  };
@@ -0,0 +1,21 @@
1
+ export interface UI3nSliderProps {
2
+ modelValue: number | [number, number];
3
+ step?: string | number;
4
+ range?: boolean;
5
+ min?: string | number;
6
+ max?: string | number;
7
+ labelVisible?: 'always' | 'normal' | 'never';
8
+ labelTextColor?: string;
9
+ labelColor?: string;
10
+ activeColor?: string;
11
+ trackColor?: string;
12
+ trackHeight?: string | number;
13
+ thumbSize?: string | number;
14
+ thumbColor?: string;
15
+ transformValueMethod?: (value: number) => string;
16
+ disabled?: boolean;
17
+ }
18
+
19
+ export interface UI3nSliderEmits {
20
+ (event: 'update:modelValue', value: number | [number, number]): void;
21
+ }
@@ -0,0 +1,432 @@
1
+ <script lang="ts" setup>
2
+ import { computed, ref, useTemplateRef, watch, ComputedRef } from 'vue';
3
+ import cloneDeep from 'lodash/cloneDeep';
4
+ import Ui3nTooltip from '../ui3n-tooltip/ui3n-tooltip.vue';
5
+ import { round } from '@/utils';
6
+ import type { Nullable } from '@/types';
7
+ import type { UI3nSliderProps, UI3nSliderEmits } from './types';
8
+
9
+ const props = withDefaults(defineProps<UI3nSliderProps>(), {
10
+ min: 0,
11
+ max: 100,
12
+ labelVisible: 'normal',
13
+ labelColor: 'var(--color-bg-block-tritery-default)',
14
+ labelTextColor: 'var(--color-text-block-darkery-default)',
15
+ activeColor: 'var(--color-bg-control-accent-default)',
16
+ trackColor: 'var(--color-bg-control-secondary-default)',
17
+ trackHeight: 8,
18
+ thumbSize: 16,
19
+ thumbColor: 'var(--color-bg-control-accent-default)',
20
+ });
21
+ const emits = defineEmits<UI3nSliderEmits>();
22
+
23
+ const sliderRef = useTemplateRef('sliderEl');
24
+ const pointer1Ref = useTemplateRef('pointerEl1');
25
+ const pointer2Ref = useTemplateRef('pointerEl2');
26
+ const shiftX = ref({
27
+ '1': 0,
28
+ '2': 0,
29
+ });
30
+ const showLabel = ref({
31
+ '1': props.labelVisible === 'always',
32
+ '2': props.labelVisible === 'always',
33
+ });
34
+ const innerValue = ref<number | [number, number]>(0);
35
+ const selectedPointer = ref<Nullable<1 | 2>>(null);
36
+
37
+ const sliderHeight = computed(() => {
38
+ const height = Math.max(Number(props.trackHeight), Number(props.thumbSize));
39
+ return `${height}px`;
40
+ });
41
+ const trackHeightCss = computed(() => `${props.trackHeight}px`);
42
+ const thumbSizeCss = computed(() => `${props.thumbSize}px`);
43
+ const diff = computed(() => Number(props.max) - Number(props.min));
44
+ const innerStep = computed(() => props.step ? Number(props.step) : diff.value / 100);
45
+
46
+ const activeBlockStyle = computed(() => {
47
+ const style = {} as Record<'left' | 'width', string>;
48
+ if (props.range) {
49
+ const [minVal, maxVal] = innerValue.value as [number, number];
50
+
51
+ if (minVal <= Number(props.min)) {
52
+ style.left = '0px';
53
+ } else if (minVal < Number(props.max)) {
54
+ style.left = `${round((minVal - Number(props.min)) / diff.value * 100, 0)}%`;
55
+ }
56
+
57
+ if (maxVal >= Number(props.max)) {
58
+ style.width = minVal <= Number(props.min)
59
+ ? '100%'
60
+ : `${round((Number(props.max) - minVal) / diff.value * 100, 0)}%`;
61
+ } else if (maxVal > Number(props.min)) {
62
+ style.width = `${round((maxVal - minVal) / diff.value * 100, 0)}%`;
63
+ }
64
+
65
+ return style;
66
+ }
67
+
68
+ if ((innerValue.value as number) <= Number(props.min)) {
69
+ style.left = '0px';
70
+ style.width = '0px';
71
+ } else if ((innerValue.value as number) >= Number(props.max)) {
72
+ style.left = '0px';
73
+ style.width = '100%';
74
+ } else {
75
+ style.left = '0px';
76
+ style.width = `${round(((innerValue.value as number) - Number(props.min)) / diff.value * 100, 0)}%`;
77
+ }
78
+
79
+ return style;
80
+ }) as ComputedRef<Record<string, string>>;
81
+
82
+ const pointer1PositionCss = computed(() => props.range
83
+ ? `calc(${activeBlockStyle.value.left} - calc(${thumbSizeCss.value} / 2))`
84
+ : `calc(${activeBlockStyle.value.width} - calc(${thumbSizeCss.value} / 2))`,
85
+ );
86
+ const pointer2PositionCss = computed(() => props.range
87
+ ? `calc(${activeBlockStyle.value.width} + ${activeBlockStyle.value.left} - calc(${thumbSizeCss.value} / 2))`
88
+ : '200%',
89
+ );
90
+
91
+ function onMouseenter(pointer: 1 | 2) {
92
+ if (props.labelVisible === 'normal') {
93
+ showLabel.value[`${pointer}`] = true;
94
+ }
95
+ }
96
+
97
+ function onMouseleave(pointer: 1 | 2) {
98
+ if (props.labelVisible === 'normal') {
99
+ showLabel.value[`${pointer}`] = false;
100
+ }
101
+ }
102
+
103
+ function onDragstart() {
104
+ return false;
105
+ }
106
+
107
+ function onPointerDown(event: PointerEvent, pointer: 1 | 2) {
108
+ selectedPointer.value = pointer;
109
+ if (props.labelVisible === 'normal') {
110
+ showLabel.value[`${pointer}`] = true;
111
+ }
112
+ event.preventDefault();
113
+
114
+ const currentPointer = pointer === 1 ? pointer1Ref.value! : pointer2Ref.value!;
115
+ shiftX.value[`${selectedPointer.value}`] = event.clientX - currentPointer.getBoundingClientRect().left;
116
+ currentPointer.setPointerCapture(event.pointerId);
117
+
118
+ currentPointer.onpointermove = onPointerMove;
119
+
120
+ currentPointer.onpointerup = () => {
121
+ currentPointer.onpointermove = null;
122
+ currentPointer.onpointerup = null;
123
+ if (props.labelVisible === 'normal' && selectedPointer.value) {
124
+ showLabel.value[`${selectedPointer.value}`] = false;
125
+ }
126
+ selectedPointer.value = null;
127
+ };
128
+ }
129
+
130
+ function onPointerMove(event: PointerEvent) {
131
+ if (!selectedPointer.value) {
132
+ return;
133
+ }
134
+
135
+ const sliderRefClientRect = sliderRef.value!.getBoundingClientRect();
136
+ const getValue = (leftPosition: number): number => Math.round(leftPosition / sliderRefClientRect.width * diff.value) + Number(props.min);
137
+
138
+ let newLeft = event.clientX - shiftX.value[`${selectedPointer.value}`] - sliderRefClientRect.left;
139
+ if (newLeft < 0) {
140
+ newLeft = 0;
141
+ }
142
+
143
+ let rightEdge = sliderRef.value!.offsetWidth;
144
+ if (newLeft > rightEdge) {
145
+ newLeft = rightEdge;
146
+ }
147
+
148
+ let newValue = getValue(newLeft);
149
+
150
+ if (props.range) {
151
+ if (selectedPointer.value === 1 && newValue >= (innerValue.value as [number, number])[1]) {
152
+ newValue = (innerValue.value as [number, number])[1] - innerStep.value;
153
+ } else if (selectedPointer.value === 2 && newValue <= (innerValue.value as [number, number])[0]) {
154
+ newValue = (innerValue.value as [number, number])[0] + innerStep.value;
155
+ }
156
+ }
157
+
158
+ if (props.range) {
159
+ selectedPointer.value === 1 && ((innerValue.value as [number, number])[0] = newValue);
160
+ selectedPointer.value === 2 && ((innerValue.value as [number, number])[1] = newValue);
161
+ } else {
162
+ innerValue.value = newValue;
163
+ }
164
+
165
+ emits('update:modelValue', innerValue.value);
166
+ }
167
+
168
+ function onTimelineClick(event: MouseEvent) {
169
+ const { x } = event;
170
+ const { x: sliderElStart, width: sliderElWidth } = sliderRef.value!.getBoundingClientRect();
171
+ const selectedValue = Math.round((x - sliderElStart) / sliderElWidth * diff.value) + Number(props.min);
172
+
173
+ if (props.range) {
174
+ const [minVal, maxVal] = innerValue.value as [number, number];
175
+ const diffWithMinVal = Math.abs(selectedValue - minVal);
176
+ const diffWithMaxVal = Math.abs(maxVal - selectedValue);
177
+ innerValue.value = [
178
+ diffWithMinVal <= diffWithMaxVal ? selectedValue : (innerValue.value as [number, number])[0],
179
+ diffWithMinVal > diffWithMaxVal ? selectedValue : (innerValue.value as [number, number])[1],
180
+ ];
181
+ } else {
182
+ (innerValue.value as number) = selectedValue;
183
+ }
184
+
185
+ emits('update:modelValue', innerValue.value);
186
+ }
187
+
188
+ watch(
189
+ () => JSON.stringify(props.modelValue),
190
+ (val) => {
191
+ if (props.range) {
192
+ if (val !== JSON.stringify(innerValue.value)) {
193
+ innerValue.value = cloneDeep(props.modelValue);
194
+ }
195
+ return;
196
+ }
197
+
198
+ const newVal = Array.isArray(props.modelValue)
199
+ ? props.modelValue[props.modelValue.length - 1]
200
+ : props.modelValue;
201
+ if (newVal !== innerValue.value) {
202
+ innerValue.value = newVal;
203
+ }
204
+ }, {
205
+ immediate: true,
206
+ },
207
+ );
208
+ </script>
209
+
210
+ <template>
211
+ <div
212
+ ref="sliderEl"
213
+ :class="[$style.ui3nSlider, disabled && $style.disabled]"
214
+ @click.stop.prevent="onTimelineClick"
215
+ >
216
+ <div :class="$style.track">
217
+ <div
218
+ :class="$style.active"
219
+ :style="activeBlockStyle"
220
+ />
221
+ </div>
222
+
223
+ <div
224
+ ref="pointerEl1"
225
+ :class="[$style.pointerWrapper, $style.pointer1]"
226
+ @pointerdown="onPointerDown($event, 1)"
227
+ @click.stop.prevent
228
+ @dragstart="onDragstart"
229
+ @mouseenter="onMouseenter(1)"
230
+ @mouseleave="onMouseleave(1)"
231
+ >
232
+ <ui3n-tooltip
233
+ :model-value="showLabel['1']"
234
+ :content="range ? `${(innerValue as [number, number])[0]}` : `${innerValue}`"
235
+ trigger="manual"
236
+ placement="top-start"
237
+ position-strategy="absolute"
238
+ :color="labelColor"
239
+ :text-color="labelTextColor"
240
+ :disabled="labelVisible === 'never'"
241
+ >
242
+ <div :class="[$style.pointer, selectedPointer === 1 && $style.selected]" />
243
+ </ui3n-tooltip>
244
+ </div>
245
+
246
+ <div
247
+ v-if="range"
248
+ ref="pointerEl2"
249
+ :class="[$style.pointerWrapper, $style.pointer2]"
250
+ @pointerdown="onPointerDown($event, 2)"
251
+ @click.stop.prevent
252
+ @dragstart="onDragstart"
253
+ @mouseenter="onMouseenter(2)"
254
+ @mouseleave="onMouseleave(2)"
255
+ >
256
+ <ui3n-tooltip
257
+ :model-value="showLabel['2']"
258
+ :content="`${(innerValue as [number, number])[1]}`"
259
+ trigger="manual"
260
+ placement="top-start"
261
+ position-strategy="absolute"
262
+ :color="labelColor"
263
+ :text-color="labelTextColor"
264
+ :disabled="labelVisible === 'never'"
265
+ >
266
+ <div :class="[$style.pointer, selectedPointer === 2 && $style.selected]" />
267
+ </ui3n-tooltip>
268
+ </div>
269
+
270
+ <!-- <ui3n-tooltip-->
271
+ <!-- :model-value="labelVisible === 'always'"-->
272
+ <!-- :content="range ? `${(innerValue as [number, number])[0]}` : `${innerValue}`"-->
273
+ <!-- :trigger="labelVisible === 'always' ? 'manual' : 'hover'"-->
274
+ <!-- placement="top-start"-->
275
+ <!-- position-strategy="fixed"-->
276
+ <!-- :color="labelColor"-->
277
+ <!-- :text-color="labelTextColor"-->
278
+ <!-- :disabled="labelVisible === 'never'"-->
279
+ <!-- >-->
280
+ <!-- <div-->
281
+ <!-- ref="pointerEl1"-->
282
+ <!-- :class="[$style.pointer, $style.pointer1, selectedPointer === 1 && $style.selected]"-->
283
+ <!-- @pointerdown="onPointerDown($event, 1)"-->
284
+ <!-- @click.stop.prevent-->
285
+ <!-- @dragstart="onDragstart"-->
286
+ <!-- />-->
287
+ <!-- </ui3n-tooltip>-->
288
+
289
+ <!-- <ui3n-tooltip-->
290
+ <!-- v-if="range"-->
291
+ <!-- :model-value="labelVisible === 'always'"-->
292
+ <!-- :content="`${(innerValue as [number, number])[1]}`"-->
293
+ <!-- :trigger="labelVisible === 'always' ? 'manual' : 'hover'"-->
294
+ <!-- placement="top-end"-->
295
+ <!-- position-strategy="absolute"-->
296
+ <!-- :color="labelColor"-->
297
+ <!-- :text-color="labelTextColor"-->
298
+ <!-- :disabled="labelVisible === 'never'"-->
299
+ <!-- >-->
300
+ <!-- <div-->
301
+ <!-- v-if="range"-->
302
+ <!-- ref="pointerEl2"-->
303
+ <!-- :class="[$style.pointer, $style.pointer2, selectedPointer === 2 && $style.selected]"-->
304
+ <!-- @pointerdown="onPointerDown($event, 2)"-->
305
+ <!-- @click.stop.prevent-->
306
+ <!-- @dragstart="onDragstart"-->
307
+ <!-- />-->
308
+ <!-- </ui3n-tooltip>-->
309
+ </div>
310
+ </template>
311
+
312
+ <style lang="scss" module>
313
+ .ui3nSlider {
314
+ --ui3n-slider-height: v-bind(sliderHeight);
315
+ --ui3n-slider-label-color: v-bind(labelColor);
316
+ --ui3n-slider-label-text-color: v-bind(labelTextColor);
317
+ --ui3n-slider-active-color: v-bind(activeColor);
318
+ --ui3n-slider-track-color: v-bind(trackColor);
319
+ --ui3n-slider-track-height: v-bind(trackHeightCss);
320
+ --ui3n-slider-thumb-size: v-bind(thumbSizeCss);
321
+ --ui3n-slider-thumb-color: v-bind(thumbColor);
322
+ --ui3n-slider-pointer1-position: v-bind(pointer1PositionCss);
323
+ --ui3n-slider-pointer2-position: v-bind(pointer2PositionCss);
324
+
325
+ position: relative;
326
+ width: 100%;
327
+ height: var(--ui3n-slider-height);
328
+ display: flex;
329
+ justify-content: center;
330
+ align-items: center;
331
+
332
+ &.disabled {
333
+ pointer-events: none;
334
+ opacity: 0.5;
335
+ }
336
+ }
337
+
338
+ .track {
339
+ position: relative;
340
+ width: 100%;
341
+ height: var(--ui3n-slider-track-height);
342
+ background-color: var(--ui3n-slider-track-color);
343
+ border-radius: var(--ui3n-slider-track-height);
344
+ cursor: pointer;
345
+ }
346
+
347
+ .active {
348
+ position: absolute;
349
+ height: var(--ui3n-slider-track-height);
350
+ background-color: var(--ui3n-slider-active-color);
351
+ border-radius: var(--ui3n-slider-track-height);
352
+ }
353
+
354
+ .pointerWrapper {
355
+ position: absolute;
356
+ top: 0;
357
+ width: var(--ui3n-slider-thumb-size);
358
+ height: var(--ui3n-slider-thumb-size);
359
+ border-radius: 50%;
360
+ background-color: transparent;
361
+ cursor: pointer;
362
+ touch-action: none;
363
+ pointer-events: all;
364
+ z-index: 3;
365
+ }
366
+
367
+ //.pointer {
368
+ // position: absolute;
369
+ // top: 0;
370
+ // width: var(--ui3n-slider-thumb-size);
371
+ // height: var(--ui3n-slider-thumb-size);
372
+ // border-radius: 50%;
373
+ // background-color: var(--ui3n-slider-thumb-color);
374
+ // cursor: pointer;
375
+ // touch-action: none;
376
+ // z-index: 3;
377
+ //
378
+ // &:hover {
379
+ // background-color: hsl(from var(--ui3n-slider-thumb-color) h s calc(l + 5));
380
+ // }
381
+ //
382
+ // &.selected::before {
383
+ // position: absolute;
384
+ // content: '';
385
+ // width: calc(var(--ui3n-slider-thumb-size) * 2);
386
+ // height: calc(var(--ui3n-slider-thumb-size) * 2);
387
+ // left: 50%;
388
+ // top: 50%;
389
+ // transform: translate(-50%, -50%);
390
+ // border-radius: 50%;
391
+ // opacity: 0.2;
392
+ // transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
393
+ // background-color: var(--ui3n-slider-thumb-color);
394
+ // }
395
+ //}
396
+
397
+ .pointer {
398
+ position: relative;
399
+ width: 100%;
400
+ height: 100%;
401
+ border-radius: 50%;
402
+ background-color: var(--ui3n-slider-thumb-color);
403
+ touch-action: none;
404
+ z-index: 5;
405
+
406
+ &:hover {
407
+ background-color: hsl(from var(--ui3n-slider-thumb-color) h s calc(l + 5));
408
+ }
409
+
410
+ &.selected::before {
411
+ position: absolute;
412
+ content: '';
413
+ width: calc(var(--ui3n-slider-thumb-size) * 2);
414
+ height: calc(var(--ui3n-slider-thumb-size) * 2);
415
+ left: 50%;
416
+ top: 50%;
417
+ transform: translate(-50%, -50%);
418
+ border-radius: 50%;
419
+ opacity: 0.2;
420
+ transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
421
+ background-color: var(--ui3n-slider-thumb-color);
422
+ }
423
+ }
424
+
425
+ .pointer1 {
426
+ left: var(--ui3n-slider-pointer1-position);
427
+ }
428
+
429
+ .pointer2 {
430
+ left: var(--ui3n-slider-pointer2-position);
431
+ }
432
+ </style>