@v1nt1248/3nclient-lib 0.3.25 → 0.3.28
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/README.md +2 -0
- package/dist/chunks/emoticons-DMgiBINj.js +2517 -0
- package/dist/chunks/plugins-keys-jqAZ5D94.js +4466 -0
- package/dist/chunks/ui3n-notification-DlDM-u-R.js +276 -0
- package/dist/components/index.d.ts +39 -35
- package/dist/components/ui3n-scrollbar-horizontal/types.d.ts +22 -0
- package/dist/components/ui3n-scrollbar-horizontal/ui3n-scroolbar-horizontal.d.ts +41 -0
- package/dist/components/ui3n-scrollbar-vertical/types.d.ts +34 -0
- package/dist/components/ui3n-scrollbar-vertical/ui3n-scrollbar-vertical.d.ts +41 -0
- package/dist/index.d.ts +37 -33
- package/dist/mixins.scss +102 -0
- package/dist/style.css +1 -1
- package/dist/ui3n-components.d.ts +37 -33
- package/dist/ui3n-components.js +2496 -18935
- package/dist/ui3n-plugins.js +60 -4121
- package/dist/ui3n-utils.js +5 -7911
- package/dist/variables.css +2 -2
- package/package.json +6 -13
- package/src/components/index.ts +24 -0
- package/src/components/ui3n-list/types.ts +1 -1
- package/src/components/ui3n-list/ui3n-list.vue +4 -11
- package/src/components/ui3n-scrollbar-horizontal/types.ts +26 -0
- package/src/components/ui3n-scrollbar-horizontal/ui3n-scroolbar-horizontal.vue +337 -0
- package/src/components/ui3n-scrollbar-vertical/types.ts +38 -0
- package/src/components/ui3n-scrollbar-vertical/ui3n-scrollbar-vertical.vue +337 -0
- package/src/components/ui3n-virtual-scroll/ui3n-virtual-scroll.vue +52 -58
- package/dist/components/ui3n-autocomplete/{ui3n-autocomplete.vue.d.ts → ui3n-autocomplete.d.ts} +0 -0
- package/dist/components/ui3n-badge/{ui3n-badge-simple.vue.d.ts → ui3n-badge-simple.d.ts} +0 -0
- package/dist/components/ui3n-badge/{ui3n-badge.vue.d.ts → ui3n-badge.d.ts} +0 -0
- package/dist/components/ui3n-breadcrumbs/{ui3n-breadcrumb.vue.d.ts → ui3n-breadcrumb.d.ts} +0 -0
- package/dist/components/ui3n-breadcrumbs/{ui3n-breadcrumbs.vue.d.ts → ui3n-breadcrumbs.d.ts} +0 -0
- package/dist/components/ui3n-button/{ui3n-button.vue.d.ts → ui3n-button.d.ts} +0 -0
- package/dist/components/ui3n-checkbox/{ui3n-checkbox.vue.d.ts → ui3n-checkbox.d.ts} +0 -0
- package/dist/components/ui3n-chip/{ui3n-chip.vue.d.ts → ui3n-chip.d.ts} +0 -0
- package/dist/components/ui3n-dialog/{ui3n-dialog-provider.vue.d.ts → ui3n-dialog-provider.d.ts} +0 -0
- package/dist/components/ui3n-dialog/{ui3n-dialog.vue.d.ts → ui3n-dialog.d.ts} +0 -0
- package/dist/components/ui3n-drop-files/{ui3n-drop-files.vue.d.ts → ui3n-drop-files.d.ts} +0 -0
- package/dist/components/ui3n-editable/{ui3n-editable.vue.d.ts → ui3n-editable.d.ts} +0 -0
- package/dist/components/ui3n-emoji/{ui3n-emoji.vue.d.ts → ui3n-emoji.d.ts} +0 -0
- package/dist/components/ui3n-icon/{ui3n-icon.vue.d.ts → ui3n-icon.d.ts} +0 -0
- package/dist/components/ui3n-input/{ui3n-input.vue.d.ts → ui3n-input.d.ts} +0 -0
- package/dist/components/ui3n-input-file/{ui3n-input-file.vue.d.ts → ui3n-input-file.d.ts} +0 -0
- package/dist/components/ui3n-list/{ui3n-list.vue.d.ts → ui3n-list.d.ts} +0 -0
- package/dist/components/ui3n-menu/{ui3n-menu.vue.d.ts → ui3n-menu.d.ts} +0 -0
- package/dist/components/ui3n-mobile-menu/{ui3n-mobile-menu.vue.d.ts → ui3n-mobile-menu.d.ts} +0 -0
- package/dist/components/ui3n-mobile-menu-item/{ui3n-mobile-menu-item.vue.d.ts → ui3n-mobile-menu-item.d.ts} +0 -0
- package/dist/components/ui3n-notification/{ui3n-notification.vue.d.ts → ui3n-notification.d.ts} +0 -0
- package/dist/components/ui3n-progress/{ui3n-progress-circular.vue.d.ts → ui3n-progress-circular.d.ts} +0 -0
- package/dist/components/ui3n-progress/{ui3n-progress-linear.vue.d.ts → ui3n-progress-linear.d.ts} +0 -0
- package/dist/components/ui3n-radio-group/{ui3n-radio-group.vue.d.ts → ui3n-radio-group.d.ts} +0 -0
- package/dist/components/ui3n-radio-group/{ui3n-radio.vue.d.ts → ui3n-radio.d.ts} +0 -0
- package/dist/components/ui3n-selector/{ui3n-selector.vue.d.ts → ui3n-selector.d.ts} +0 -0
- package/dist/components/ui3n-slider/{ui3n-slider.vue.d.ts → ui3n-slider.d.ts} +2 -2
- /package/dist/components/ui3n-step-line-bar/{ui3n-step-line-bar.vue.d.ts → ui3n-step-line-bar.d.ts} +0 -0
- /package/dist/components/ui3n-switch/{ui3n-switch.vue.d.ts → ui3n-switch.d.ts} +0 -0
- /package/dist/components/ui3n-table/{ui3n-table-sort-icon.vue.d.ts → ui3n-table-sort-icon.d.ts} +0 -0
- /package/dist/components/ui3n-table/{ui3n-table.vue.d.ts → ui3n-table.d.ts} +0 -0
- /package/dist/components/ui3n-tabs/{ui3n-tabs.vue.d.ts → ui3n-tabs.d.ts} +0 -0
- /package/dist/components/ui3n-text/{ui3n-text.vue.d.ts → ui3n-text.d.ts} +0 -0
- /package/dist/components/ui3n-tooltip/{ui3n-tooltip.vue.d.ts → ui3n-tooltip.d.ts} +0 -0
- /package/dist/components/ui3n-virtual-scroll/{ui3n-virtual-scroll.vue.d.ts → ui3n-virtual-scroll.d.ts} +0 -0
package/dist/variables.css
CHANGED
|
@@ -949,11 +949,11 @@ div {
|
|
|
949
949
|
}
|
|
950
950
|
|
|
951
951
|
::-webkit-scrollbar-track {
|
|
952
|
-
background-color:
|
|
952
|
+
background-color: transparent;
|
|
953
953
|
}
|
|
954
954
|
|
|
955
955
|
::-webkit-scrollbar-thumb {
|
|
956
|
-
border-radius:
|
|
956
|
+
border-radius: 4px;
|
|
957
957
|
height: 50px;
|
|
958
958
|
background-color: var(--default-fill-pressed);
|
|
959
959
|
}
|
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.3.
|
|
5
|
+
"version": "0.3.28",
|
|
6
6
|
"description": "Library for 3NWeb clients",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"files": [
|
|
@@ -25,25 +25,17 @@
|
|
|
25
25
|
"types": "./dist/ui3n-utils.d.ts"
|
|
26
26
|
},
|
|
27
27
|
"./style.css": "./dist/style.css",
|
|
28
|
-
"./variables.css": "./dist/variables.css"
|
|
28
|
+
"./variables.css": "./dist/variables.css",
|
|
29
|
+
"./mixins.scss": "./dist/mixins.scss"
|
|
29
30
|
},
|
|
30
31
|
"scripts": {
|
|
31
|
-
"dev": "
|
|
32
|
-
"generate:types": "vue-tsc --declaration --emitDeclarationOnly --outdir ./dist",
|
|
33
|
-
"copy:dts": "cp ./src/types.d.ts ./dist",
|
|
34
|
-
"copy:css": "cp ./src/assets/styles/variables.css ./dist",
|
|
35
|
-
"copy:icons:css": "cp ./src/assets/styles/icons.css ./dist",
|
|
32
|
+
"dev": "vite",
|
|
36
33
|
"clean:dist": "rm -rf dist/",
|
|
37
|
-
"build
|
|
38
|
-
"build:plugins": "LIB_NAME=plugins vite build",
|
|
39
|
-
"build:utils": "LIB_NAME=utils vite build",
|
|
40
|
-
"build": "pnpm clean:dist && vue-tsc --noEmit && pnpm build:plugins && pnpm build:utils && pnpm build:components && pnpm copy:css",
|
|
34
|
+
"build": "pnpm clean:dist && vite build",
|
|
41
35
|
"pack": "pnpm build && npm pack && cp *.tgz ./dist && rm *.tgz",
|
|
42
36
|
"preview": "vite preview",
|
|
43
37
|
"stylelint": "stylelint --config .stylelint.config.cjs 'src/**/*.{vue,scss,css}'",
|
|
44
38
|
"stylelint:fix": "stylelint --config .stylelint.config.cjs --fix 'src/**/*.{vue,scss,css}'",
|
|
45
|
-
"linter": "eslint -c eslint.config.js --ext .js,.vue,.ts, src",
|
|
46
|
-
"linter:fix": "eslint -c eslint.config.js --fix --ext .js,.vue,.ts, src",
|
|
47
39
|
"lint": "pnpm linter && pnpm stylelint",
|
|
48
40
|
"lint:fix": "pnpm linter:fix && pnpm stylelint:fix",
|
|
49
41
|
"docs:dev": "vitepress dev docs",
|
|
@@ -103,6 +95,7 @@
|
|
|
103
95
|
"vite-plugin-css-injected-by-js": "4.0.1",
|
|
104
96
|
"vite-plugin-dts": "4.5.4",
|
|
105
97
|
"vite-plugin-node-polyfills": "^0.28.0",
|
|
98
|
+
"vite-plugin-static-copy": "^4.1.1",
|
|
106
99
|
"vite-plugin-vue-devtools": "8.1.2",
|
|
107
100
|
"vitepress": "1.6.4",
|
|
108
101
|
"vue": "3.5.35",
|
package/src/components/index.ts
CHANGED
|
@@ -120,6 +120,20 @@ import type {
|
|
|
120
120
|
Ui3nMobileMenuItemProps,
|
|
121
121
|
Ui3nMobileMenuItemEmits,
|
|
122
122
|
} from './ui3n-mobile-menu-item/types';
|
|
123
|
+
import Ui3nScrollbarVertical from './ui3n-scrollbar-vertical/ui3n-scrollbar-vertical.vue';
|
|
124
|
+
import type {
|
|
125
|
+
Ui3nScrollbarVerticalProps,
|
|
126
|
+
Ui3nScrollbarVerticalEmits,
|
|
127
|
+
Ui3nScrollbarVerticalSlots,
|
|
128
|
+
Ui3nScrollbarVerticalExpose,
|
|
129
|
+
} from './ui3n-scrollbar-vertical/types';
|
|
130
|
+
import Ui3nScrollbarHorizontal from './ui3n-scrollbar-horizontal/ui3n-scroolbar-horizontal.vue';
|
|
131
|
+
import type {
|
|
132
|
+
Ui3nScrollbarHorizontalProps,
|
|
133
|
+
Ui3nScrollbarHorizontalEmits,
|
|
134
|
+
Ui3nScrollbarHorizontalSlots,
|
|
135
|
+
Ui3nScrollbarHorizontalExpose,
|
|
136
|
+
} from './ui3n-scrollbar-horizontal/types';
|
|
123
137
|
|
|
124
138
|
export {
|
|
125
139
|
Ui3nIcon,
|
|
@@ -251,4 +265,14 @@ export {
|
|
|
251
265
|
Ui3nMobileMenuItemBase,
|
|
252
266
|
Ui3nMobileMenuItemProps,
|
|
253
267
|
Ui3nMobileMenuItemEmits,
|
|
268
|
+
Ui3nScrollbarVertical,
|
|
269
|
+
Ui3nScrollbarVerticalProps,
|
|
270
|
+
Ui3nScrollbarVerticalEmits,
|
|
271
|
+
Ui3nScrollbarVerticalSlots,
|
|
272
|
+
Ui3nScrollbarVerticalExpose,
|
|
273
|
+
Ui3nScrollbarHorizontal,
|
|
274
|
+
Ui3nScrollbarHorizontalProps,
|
|
275
|
+
Ui3nScrollbarHorizontalEmits,
|
|
276
|
+
Ui3nScrollbarHorizontalSlots,
|
|
277
|
+
Ui3nScrollbarHorizontalExpose,
|
|
254
278
|
};
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
<div
|
|
33
33
|
v-for="(item, index) in items"
|
|
34
34
|
:key="item[keyField as keyof T] as PropertyKey"
|
|
35
|
-
:class="$style.ui3nListItem
|
|
35
|
+
:class="$style.ui3nListItem"
|
|
36
36
|
>
|
|
37
37
|
<slot
|
|
38
38
|
name="item"
|
|
@@ -40,10 +40,7 @@
|
|
|
40
40
|
:index="index"
|
|
41
41
|
>
|
|
42
42
|
<div
|
|
43
|
-
:class="[
|
|
44
|
-
$style.ui3nListItemContent,
|
|
45
|
-
disabled && $style.ui3nListItemContentDisabled,
|
|
46
|
-
]"
|
|
43
|
+
:class="[$style.ui3nListItemContent, disabled && $style.ui3nListItemContentDisabled]"
|
|
47
44
|
@click.stop="emits('select', { value: item, index })"
|
|
48
45
|
>
|
|
49
46
|
{{ item }}
|
|
@@ -63,16 +60,12 @@
|
|
|
63
60
|
width: 100%;
|
|
64
61
|
height: 100%;
|
|
65
62
|
background-color: var(--ui3n-list-bg-color);
|
|
66
|
-
display: flex;
|
|
67
|
-
flex-direction: column;
|
|
68
|
-
justify-content: flex-start;
|
|
69
|
-
align-items: stretch;
|
|
70
63
|
}
|
|
71
64
|
|
|
72
65
|
.ui3nListTitle {
|
|
73
66
|
position: relative;
|
|
74
67
|
top: 0;
|
|
75
|
-
z-index:
|
|
68
|
+
z-index: 2;
|
|
76
69
|
}
|
|
77
70
|
|
|
78
71
|
.ui3nListTitleSticky {
|
|
@@ -81,7 +74,6 @@
|
|
|
81
74
|
|
|
82
75
|
.ui3nListContent {
|
|
83
76
|
position: relative;
|
|
84
|
-
flex-grow: 1;
|
|
85
77
|
}
|
|
86
78
|
|
|
87
79
|
.ui3nListTitleContent,
|
|
@@ -106,6 +98,7 @@
|
|
|
106
98
|
font-size: 14px;
|
|
107
99
|
font-weight: 400;
|
|
108
100
|
cursor: pointer;
|
|
101
|
+
width: 100%;
|
|
109
102
|
}
|
|
110
103
|
|
|
111
104
|
.ui3nListItemContentDisabled {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { VNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
export interface Ui3nScrollbarHorizontalProps {
|
|
4
|
+
thumbMinWidth?: number | string;
|
|
5
|
+
thumbWidth?: number | string | 'auto';
|
|
6
|
+
thumbRadius?: number | string;
|
|
7
|
+
thumbColor?: string;
|
|
8
|
+
thumbHoverColor?: string;
|
|
9
|
+
thumbActiveColor?: string;
|
|
10
|
+
trackHeight?: number | string;
|
|
11
|
+
trackRadius?: number | string;
|
|
12
|
+
trackColor?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Ui3nScrollbarHorizontalEmits {
|
|
16
|
+
(ev: 'scroll', value: Event): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Ui3nScrollbarHorizontalSlots {
|
|
20
|
+
default: () => VNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface Ui3nScrollbarHorizontalExpose {
|
|
24
|
+
scrollTo(options: ScrollToOptions): void;
|
|
25
|
+
getContainer(): HTMLDivElement;
|
|
26
|
+
}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
|
3
|
+
import type {
|
|
4
|
+
Ui3nScrollbarHorizontalProps,
|
|
5
|
+
Ui3nScrollbarHorizontalEmits,
|
|
6
|
+
Ui3nScrollbarHorizontalSlots,
|
|
7
|
+
Ui3nScrollbarHorizontalExpose,
|
|
8
|
+
} from './types';
|
|
9
|
+
|
|
10
|
+
const MIN_WIDTH = 32;
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(defineProps<Ui3nScrollbarHorizontalProps>(), {
|
|
13
|
+
thumbMinWidth: MIN_WIDTH,
|
|
14
|
+
thumbWidth: 'auto',
|
|
15
|
+
thumbRadius: 4,
|
|
16
|
+
thumbColor: 'var(--color-bg-control-accent-default)',
|
|
17
|
+
thumbHoverColor: 'var(--color-bg-control-accent-hover)',
|
|
18
|
+
thumbActiveColor: 'var(--color-bg-control-accent-focused)',
|
|
19
|
+
trackHeight: 6,
|
|
20
|
+
trackRadius: 4,
|
|
21
|
+
trackColor: 'transparent',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const emits = defineEmits<Ui3nScrollbarHorizontalEmits>();
|
|
25
|
+
defineSlots<Ui3nScrollbarHorizontalSlots>();
|
|
26
|
+
|
|
27
|
+
const containerRef = ref<HTMLDivElement | null>(null);
|
|
28
|
+
const trackRef = ref<HTMLDivElement | null>(null);
|
|
29
|
+
const thumbRef = ref<HTMLDivElement | null>(null);
|
|
30
|
+
|
|
31
|
+
const scrollWidth = ref(0);
|
|
32
|
+
const clientWidth = ref(0);
|
|
33
|
+
const scrollLeft = ref(0);
|
|
34
|
+
|
|
35
|
+
const isDragging = ref(false);
|
|
36
|
+
const isHovered = ref(false);
|
|
37
|
+
const isScrollingNow = ref(false);
|
|
38
|
+
|
|
39
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
40
|
+
let scrollTimeout: number | undefined;
|
|
41
|
+
let startX = 0;
|
|
42
|
+
let startScrollLeft = 0;
|
|
43
|
+
|
|
44
|
+
const isScrollable = computed(() => scrollWidth.value > clientWidth.value);
|
|
45
|
+
|
|
46
|
+
const isBarVisible = computed(() => {
|
|
47
|
+
if (isDragging.value) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return isScrollable.value && (isHovered.value || isScrollingNow.value);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const minWidth = computed(() => {
|
|
55
|
+
const num = Number(props.thumbMinWidth);
|
|
56
|
+
return isNaN(num) ? MIN_WIDTH : num;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const thumbWidth = computed(() => {
|
|
60
|
+
if (!scrollWidth.value) {
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (props.thumbWidth !== 'auto') {
|
|
65
|
+
const fixedNum = Number(props.thumbWidth);
|
|
66
|
+
return isNaN(fixedNum) ? minWidth.value : fixedNum;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const ratio = clientWidth.value / scrollWidth.value;
|
|
70
|
+
const calculated = clientWidth.value * ratio;
|
|
71
|
+
return Math.max(minWidth.value, calculated);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const thumbLeft = computed(() => {
|
|
75
|
+
if (!scrollWidth.value || scrollWidth.value === clientWidth.value) {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const maxScrollLeft = scrollWidth.value - clientWidth.value;
|
|
80
|
+
const maxThumbLeft = clientWidth.value - thumbWidth.value;
|
|
81
|
+
return (scrollLeft.value / maxScrollLeft) * maxThumbLeft;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const thumbRadiusCss = computed(() => {
|
|
85
|
+
const num = Number(props.thumbRadius);
|
|
86
|
+
return isNaN(num) ? String(props.thumbRadius) : `${num}px`;
|
|
87
|
+
});
|
|
88
|
+
const thumbColorCss = computed(() => props.thumbColor);
|
|
89
|
+
const thumbHoverColorCss = computed(() => props.thumbHoverColor);
|
|
90
|
+
const thumbActiveColorCss = computed(() => props.thumbActiveColor);
|
|
91
|
+
const trackHeightCss = computed(() => {
|
|
92
|
+
const num = Number(props.trackHeight);
|
|
93
|
+
return isNaN(num) ? String(props.trackHeight) : `${num}px`;
|
|
94
|
+
});
|
|
95
|
+
const trackRadiusCss = computed(() => {
|
|
96
|
+
const num = Number(props.trackRadius);
|
|
97
|
+
return isNaN(num) ? String(props.trackRadius) : `${num}px`;
|
|
98
|
+
});
|
|
99
|
+
const trackColorCss = computed(() => props.trackColor);
|
|
100
|
+
|
|
101
|
+
function updateMetrics() {
|
|
102
|
+
if (!containerRef.value) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
scrollWidth.value = containerRef.value.scrollWidth;
|
|
107
|
+
clientWidth.value = containerRef.value.clientWidth;
|
|
108
|
+
scrollLeft.value = containerRef.value.scrollLeft;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function onScroll(event: Event) {
|
|
112
|
+
if (!isDragging.value) {
|
|
113
|
+
scrollLeft.value = (event.target as HTMLDivElement).scrollLeft;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
isScrollingNow.value = true;
|
|
117
|
+
window.clearTimeout(scrollTimeout);
|
|
118
|
+
|
|
119
|
+
scrollTimeout = window.setTimeout(() => {
|
|
120
|
+
isScrollingNow.value = false;
|
|
121
|
+
}, 1500);
|
|
122
|
+
|
|
123
|
+
emits('scroll', event);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function onTrackClick(e: MouseEvent) {
|
|
127
|
+
if (e.target !== trackRef.value || !containerRef.value || !trackRef.value) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const trackRect = trackRef.value.getBoundingClientRect();
|
|
132
|
+
const clickX = e.clientX - trackRect.left;
|
|
133
|
+
const targetThumbLeft = clickX - thumbWidth.value / 2;
|
|
134
|
+
const maxScrollLeft = scrollWidth.value - clientWidth.value;
|
|
135
|
+
const maxThumbLeft = clientWidth.value - thumbWidth.value;
|
|
136
|
+
|
|
137
|
+
let targetScrollLeft = (targetThumbLeft / maxThumbLeft) * maxScrollLeft;
|
|
138
|
+
targetScrollLeft = Math.max(0, Math.min(maxScrollLeft, targetScrollLeft));
|
|
139
|
+
|
|
140
|
+
containerRef.value.scrollTo({
|
|
141
|
+
left: targetScrollLeft,
|
|
142
|
+
behavior: 'smooth',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function onThumbPointerDown(e: PointerEvent) {
|
|
147
|
+
if (!thumbRef.value || !containerRef.value) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
e.preventDefault();
|
|
152
|
+
e.stopPropagation();
|
|
153
|
+
|
|
154
|
+
thumbRef.value.setPointerCapture(e.pointerId);
|
|
155
|
+
|
|
156
|
+
isDragging.value = true;
|
|
157
|
+
startX = e.clientX;
|
|
158
|
+
startScrollLeft = scrollLeft.value;
|
|
159
|
+
|
|
160
|
+
document.body.style.userSelect = 'none';
|
|
161
|
+
|
|
162
|
+
thumbRef.value.addEventListener('pointermove', onThumbPointerMove);
|
|
163
|
+
thumbRef.value.addEventListener('pointerup', onThumbPointerUp);
|
|
164
|
+
thumbRef.value.addEventListener('pointercancel', onThumbPointerUp);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function onThumbPointerMove(e: PointerEvent) {
|
|
168
|
+
if (!isDragging.value || !containerRef.value) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const deltaX = e.clientX - startX;
|
|
173
|
+
const maxScrollLeft = scrollWidth.value - clientWidth.value;
|
|
174
|
+
const maxThumbLeft = clientWidth.value - thumbWidth.value;
|
|
175
|
+
|
|
176
|
+
const scrollDelta = (deltaX / maxThumbLeft) * maxScrollLeft;
|
|
177
|
+
containerRef.value.scrollLeft = startScrollLeft + scrollDelta;
|
|
178
|
+
scrollLeft.value = containerRef.value.scrollLeft;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function onThumbPointerUp(e: PointerEvent) {
|
|
182
|
+
if (!isDragging.value) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
isDragging.value = false;
|
|
187
|
+
document.body.style.userSelect = '';
|
|
188
|
+
|
|
189
|
+
if (thumbRef.value) {
|
|
190
|
+
try {
|
|
191
|
+
thumbRef.value.releasePointerCapture(e.pointerId);
|
|
192
|
+
} catch (err) {}
|
|
193
|
+
thumbRef.value.removeEventListener('pointermove', onThumbPointerMove);
|
|
194
|
+
thumbRef.value.removeEventListener('pointerup', onThumbPointerUp);
|
|
195
|
+
thumbRef.value.removeEventListener('pointercancel', onThumbPointerUp);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
onMounted(() => {
|
|
200
|
+
updateMetrics();
|
|
201
|
+
if (containerRef.value) {
|
|
202
|
+
resizeObserver = new ResizeObserver(updateMetrics);
|
|
203
|
+
resizeObserver.observe(containerRef.value);
|
|
204
|
+
|
|
205
|
+
if (containerRef.value.firstElementChild) {
|
|
206
|
+
resizeObserver.observe(containerRef.value.firstElementChild);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
onBeforeUnmount(() => {
|
|
212
|
+
resizeObserver?.disconnect();
|
|
213
|
+
window.clearTimeout(scrollTimeout);
|
|
214
|
+
|
|
215
|
+
if (thumbRef.value) {
|
|
216
|
+
thumbRef.value.removeEventListener('pointermove', onThumbPointerMove);
|
|
217
|
+
thumbRef.value.removeEventListener('pointerup', onThumbPointerUp);
|
|
218
|
+
thumbRef.value.removeEventListener('pointercancel', onThumbPointerUp);
|
|
219
|
+
}
|
|
220
|
+
document.body.style.userSelect = '';
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
defineExpose<Ui3nScrollbarHorizontalExpose>({
|
|
224
|
+
scrollTo: (options: ScrollToOptions) => {
|
|
225
|
+
containerRef.value?.scrollTo(options);
|
|
226
|
+
},
|
|
227
|
+
getContainer: () => containerRef.value!,
|
|
228
|
+
});
|
|
229
|
+
</script>
|
|
230
|
+
|
|
231
|
+
<template>
|
|
232
|
+
<div
|
|
233
|
+
:class="$style.ui3nScrollbarHorizontal"
|
|
234
|
+
:style="{
|
|
235
|
+
'--ui3n-scrollbar-horizontal-thumb-radius': thumbRadiusCss,
|
|
236
|
+
'--ui3n-scrollbar-horizontal-thumb-color': thumbColorCss,
|
|
237
|
+
'--ui3n-scrollbar-horizontal-thumb-hover-color': thumbHoverColorCss,
|
|
238
|
+
'--ui3n-scrollbar-horizontal-thumb-active-color': thumbActiveColorCss,
|
|
239
|
+
'--ui3n-scrollbar-horizontal-track-height': trackHeightCss,
|
|
240
|
+
'--ui3n-scrollbar-horizontal-track-radius': trackRadiusCss,
|
|
241
|
+
'--ui3n-scrollbar-horizontal-track-color': trackColorCss,
|
|
242
|
+
}"
|
|
243
|
+
@mouseenter="isHovered = true"
|
|
244
|
+
@mouseleave="isHovered = false"
|
|
245
|
+
>
|
|
246
|
+
<div
|
|
247
|
+
ref="containerRef"
|
|
248
|
+
:class="$style.scrollbarContainer"
|
|
249
|
+
@scroll="onScroll"
|
|
250
|
+
>
|
|
251
|
+
<slot />
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
<div
|
|
255
|
+
ref="trackRef"
|
|
256
|
+
:class="[$style.track, { [$style.trackVisible]: isBarVisible }]"
|
|
257
|
+
@mousedown="onTrackClick"
|
|
258
|
+
>
|
|
259
|
+
<div
|
|
260
|
+
ref="thumbRef"
|
|
261
|
+
:class="[$style.thumb, { [$style.active]: isDragging }]"
|
|
262
|
+
:style="{
|
|
263
|
+
width: `${thumbWidth}px`,
|
|
264
|
+
transform: `translateX(${thumbLeft}px)`,
|
|
265
|
+
}"
|
|
266
|
+
@pointerdown="onThumbPointerDown"
|
|
267
|
+
/>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
</template>
|
|
271
|
+
|
|
272
|
+
<style lang="scss" module>
|
|
273
|
+
.ui3nScrollbarHorizontal {
|
|
274
|
+
position: relative;
|
|
275
|
+
width: 100%;
|
|
276
|
+
height: 100%;
|
|
277
|
+
overflow: hidden;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.scrollbarContainer {
|
|
281
|
+
position: relative;
|
|
282
|
+
width: 100%;
|
|
283
|
+
height: 100%;
|
|
284
|
+
overflow-x: auto;
|
|
285
|
+
overflow-y: hidden;
|
|
286
|
+
scrollbar-width: none;
|
|
287
|
+
|
|
288
|
+
&::-webkit-scrollbar {
|
|
289
|
+
display: none !important;
|
|
290
|
+
width: 0 !important;
|
|
291
|
+
height: 0 !important;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.track {
|
|
296
|
+
position: absolute;
|
|
297
|
+
left: 0;
|
|
298
|
+
right: 0;
|
|
299
|
+
bottom: 2px;
|
|
300
|
+
height: var(--ui3n-scrollbar-horizontal-track-height);
|
|
301
|
+
background: var(--ui3n-scrollbar-horizontal-track-color);
|
|
302
|
+
border-radius: var(--ui3n-scrollbar-horizontal-track-radius);
|
|
303
|
+
z-index: 10;
|
|
304
|
+
visibility: hidden;
|
|
305
|
+
opacity: 0;
|
|
306
|
+
pointer-events: none;
|
|
307
|
+
transition:
|
|
308
|
+
opacity 0.3s ease,
|
|
309
|
+
visibility 0.3s ease;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.trackVisible {
|
|
313
|
+
visibility: visible;
|
|
314
|
+
opacity: 1;
|
|
315
|
+
pointer-events: auto;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.thumb {
|
|
319
|
+
position: absolute;
|
|
320
|
+
top: 0;
|
|
321
|
+
height: 100%;
|
|
322
|
+
border-radius: var(--ui3n-scrollbar-horizontal-thumb-radius);
|
|
323
|
+
background-color: var(--ui3n-scrollbar-horizontal-thumb-color);
|
|
324
|
+
cursor: pointer;
|
|
325
|
+
transition: background-color 0.15s ease;
|
|
326
|
+
touch-action: none;
|
|
327
|
+
|
|
328
|
+
&:hover {
|
|
329
|
+
background-color: var(--ui3n-scrollbar-horizontal-thumb-hover-color);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
&:active,
|
|
333
|
+
&.active {
|
|
334
|
+
background-color: var(--ui3n-scrollbar-horizontal-thumb-active-color);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
</style>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { VNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ScrollbarVertical component properties
|
|
5
|
+
*/
|
|
6
|
+
export interface Ui3nScrollbarVerticalProps {
|
|
7
|
+
thumbMinHeight?: number | string;
|
|
8
|
+
thumbHeight?: number | string | 'auto';
|
|
9
|
+
thumbRadius?: number | string;
|
|
10
|
+
thumbColor?: string;
|
|
11
|
+
thumbHoverColor?: string;
|
|
12
|
+
thumbActiveColor?: string;
|
|
13
|
+
trackWidth?: number;
|
|
14
|
+
trackRadius?: number | string;
|
|
15
|
+
trackColor?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* ScrollbarVertical component emits
|
|
20
|
+
*/
|
|
21
|
+
export interface Ui3nScrollbarVerticalEmits {
|
|
22
|
+
(ev: 'scroll', value: Event): void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* ScrollbarVertical component slots
|
|
27
|
+
*/
|
|
28
|
+
export interface Ui3nScrollbarVerticalSlots {
|
|
29
|
+
/**
|
|
30
|
+
* Default slot
|
|
31
|
+
*/
|
|
32
|
+
default: () => VNode;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Ui3nScrollbarVerticalExpose {
|
|
36
|
+
scrollTo(options: ScrollToOptions): void;
|
|
37
|
+
getContainer(): HTMLDivElement;
|
|
38
|
+
}
|