@jetlinks-web/components 1.0.4 → 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/index.ts +54 -15
- package/package.json +9 -6
- package/src/AMap/Map.vue +110 -107
- package/src/BadgeStatus/Badge.vue +41 -40
- package/src/BadgeStatus/color.ts +25 -23
- package/src/CardSelect/CardSelect.vue +136 -0
- package/src/CardSelect/index.ts +3 -0
- package/src/CheckButton/CheckButton.vue +146 -0
- package/src/CheckButton/index.md +27 -0
- package/src/CheckButton/index.ts +3 -0
- package/src/ConfigProvider/context.ts +17 -0
- package/src/ConfigProvider/index.tsx +42 -0
- package/src/Echarts/Echarts.vue +43 -43
- package/src/Ellipsis/Ellipsis.vue +182 -0
- package/src/Ellipsis/index.ts +3 -0
- package/src/Empty/Empty.vue +43 -0
- package/src/Empty/image.ts +3 -0
- package/src/Empty/index.ts +2 -0
- package/src/FullPage/{index.vue → FullPage.vue} +30 -27
- package/src/FullPage/index.ts +3 -0
- package/src/Icon/icon.tsx +40 -0
- package/src/Icon/index.ts +3 -0
- package/src/MonacoEditor/Monaco.vue +242 -0
- package/src/MonacoEditor/index.md +26 -0
- package/src/MonacoEditor/index.ts +2 -0
- package/src/PermissionButton/index.tsx +95 -92
- package/src/ProLayout/Basic/BasicLayout.less +66 -0
- package/src/ProLayout/Basic/BasicLayout.tsx +392 -0
- package/src/ProLayout/Basic/Header.less +8 -0
- package/src/ProLayout/Basic/Header.tsx +115 -0
- package/src/ProLayout/PageContainer/index.less +103 -0
- package/src/ProLayout/PageContainer/index.tsx +441 -0
- package/src/ProLayout/PageContainer/typings.ts +56 -0
- package/src/ProLayout/RouteContext.ts +87 -0
- package/src/ProLayout/SiderMenu/BaseMenu.tsx +296 -0
- package/src/ProLayout/SiderMenu/SiderMenu.tsx +320 -0
- package/src/ProLayout/SiderMenu/index.less +212 -0
- package/src/ProLayout/SiderMenu/index.ts +2 -0
- package/src/ProLayout/SiderMenu/typings.ts +49 -0
- package/src/ProLayout/TopHeader/index.less +174 -0
- package/src/ProLayout/TopHeader/index.tsx +210 -0
- package/src/ProLayout/defaultSettings.ts +106 -0
- package/src/ProLayout/index.ts +6 -0
- package/src/ProLayout/style/default.less +4 -0
- package/src/ProLayout/style/index.ts +1 -0
- package/src/ProLayout/style/style.less +7 -0
- package/src/ProLayout/typings.ts +87 -0
- package/src/ProLayout/util.ts +64 -0
- package/src/ProTable/index.md +53 -0
- package/src/ProTable/index.tsx +551 -0
- package/src/ProTable/proTableTypes.ts +70 -0
- package/src/ProTable/style/index.less +92 -0
- package/src/ProTable/style/index.ts +1 -0
- package/src/Scrollbar/Bar.vue +75 -0
- package/src/Scrollbar/Scrollbar.vue +260 -0
- package/src/Scrollbar/Thumb.vue +163 -0
- package/src/Scrollbar/constants.ts +10 -0
- package/src/Scrollbar/index.ts +4 -0
- package/src/Scrollbar/scrollbar.ts +108 -0
- package/src/Scrollbar/thumb.ts +16 -0
- package/src/Scrollbar/util.ts +38 -0
- package/src/Search/Advanced/History.vue +140 -0
- package/src/Search/Advanced/SaveHistory.vue +108 -0
- package/src/Search/Advanced/index.vue +435 -0
- package/src/Search/Item.vue +525 -0
- package/src/Search/Search.vue +398 -0
- package/src/Search/hooks/index.ts +1 -0
- package/src/Search/hooks/useSearchItems.ts +68 -0
- package/src/Search/index.md +57 -0
- package/src/Search/index.ts +5 -0
- package/src/Search/setting.ts +55 -0
- package/src/Search/typing.ts +76 -0
- package/src/Search/util.ts +263 -0
- package/src/ValueItem/ValueItem.vue +50 -35
- package/src/ValueItem/util.ts +2 -1
- package/src/style/index.ts +3 -0
- package/src/style/variable.less +4 -0
- package/src/GeoComponent/GeoComponent.vue +0 -139
- package/src/GeoComponent/index.ts +0 -3
- package/src/PermissionButton/hooks.ts +0 -20
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
@import '../../style/variable.less';
|
|
2
|
+
//@import 'ant-design-vue/lib/style/themes/variable.less';
|
|
3
|
+
|
|
4
|
+
.jtable-body-spin {
|
|
5
|
+
min-height: 100%;
|
|
6
|
+
width: 100%;
|
|
7
|
+
padding: 16px 24px 24px;
|
|
8
|
+
background-color: #fff;
|
|
9
|
+
|
|
10
|
+
.ant-spin-nested-loading {
|
|
11
|
+
height: 100%;
|
|
12
|
+
.ant-spin-container {
|
|
13
|
+
height: 100%;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.jtable-body {
|
|
18
|
+
width: 100%;
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
height: 100%;
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
|
|
24
|
+
.jtable-body-header {
|
|
25
|
+
display: flex;
|
|
26
|
+
justify-content: space-between;
|
|
27
|
+
align-items: center;
|
|
28
|
+
|
|
29
|
+
.jtable-body-header-right {
|
|
30
|
+
display: flex;
|
|
31
|
+
gap: 8px;
|
|
32
|
+
align-items: center;
|
|
33
|
+
|
|
34
|
+
.jtable-body-header-right-button {
|
|
35
|
+
.right-button-icon {
|
|
36
|
+
font-size: 16px;
|
|
37
|
+
color: #d9d9d9;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ant-radio-button-wrapper {
|
|
41
|
+
padding: 0 8px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.ant-radio-button-wrapper-checked {
|
|
45
|
+
.right-button-icon {
|
|
46
|
+
color: @primary-color;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.jtable-content {
|
|
54
|
+
flex: 1;
|
|
55
|
+
padding: 16px 0;
|
|
56
|
+
|
|
57
|
+
.jtable-alert {
|
|
58
|
+
margin-bottom: 16px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.jtable-card {
|
|
62
|
+
.jtable-card-items {
|
|
63
|
+
display: grid;
|
|
64
|
+
grid-gap: 26px;
|
|
65
|
+
|
|
66
|
+
.jtable-card-item {
|
|
67
|
+
display: flex;
|
|
68
|
+
min-width: 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.jtable-pagination {
|
|
75
|
+
// position: absolute;
|
|
76
|
+
// background-color: white;
|
|
77
|
+
// padding: 10px 0;
|
|
78
|
+
// right: 0;
|
|
79
|
+
// bottom: 0;
|
|
80
|
+
width: 100%;
|
|
81
|
+
display: flex;
|
|
82
|
+
justify-content: flex-end;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.j-table-empty {
|
|
86
|
+
display: flex;
|
|
87
|
+
justify-content: center;
|
|
88
|
+
align-items: center;
|
|
89
|
+
margin: 10% 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './index.less';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<thumb :move="moveX" :ratio="ratioX" :size="sizeWidth" :always="always"/>
|
|
3
|
+
<thumb
|
|
4
|
+
:move="moveY"
|
|
5
|
+
:ratio="ratioY"
|
|
6
|
+
:size="sizeHeight"
|
|
7
|
+
vertical
|
|
8
|
+
:always="always"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
<script lang="ts" setup>
|
|
12
|
+
import { inject, ref } from 'vue'
|
|
13
|
+
import { GAP } from './util'
|
|
14
|
+
import Thumb from './Thumb.vue'
|
|
15
|
+
import { scrollbarContextKey } from './constants'
|
|
16
|
+
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
always: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
default: true,
|
|
21
|
+
},
|
|
22
|
+
minSize: {
|
|
23
|
+
type: Number,
|
|
24
|
+
required: true,
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const scrollbar = inject(scrollbarContextKey)
|
|
29
|
+
|
|
30
|
+
const moveX = ref(0)
|
|
31
|
+
const moveY = ref(0)
|
|
32
|
+
const sizeWidth = ref('')
|
|
33
|
+
const sizeHeight = ref('')
|
|
34
|
+
const ratioY = ref(1)
|
|
35
|
+
const ratioX = ref(1)
|
|
36
|
+
|
|
37
|
+
const handleScroll = (wrap: HTMLDivElement) => {
|
|
38
|
+
if (wrap) {
|
|
39
|
+
const offsetHeight = wrap.offsetHeight - GAP
|
|
40
|
+
const offsetWidth = wrap.offsetWidth - GAP
|
|
41
|
+
|
|
42
|
+
moveY.value = ((wrap.scrollTop * 100) / offsetHeight) * ratioY.value
|
|
43
|
+
moveX.value = ((wrap.scrollLeft * 100) / offsetWidth) * ratioX.value
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const update = () => {
|
|
48
|
+
const wrap = scrollbar?.wrapElement
|
|
49
|
+
if (!wrap) return
|
|
50
|
+
const offsetHeight = wrap.offsetHeight - GAP
|
|
51
|
+
const offsetWidth = wrap.offsetWidth - GAP
|
|
52
|
+
|
|
53
|
+
const originalHeight = offsetHeight ** 2 / wrap.scrollHeight
|
|
54
|
+
const originalWidth = offsetWidth ** 2 / wrap.scrollWidth
|
|
55
|
+
const height = Math.max(originalHeight, props.minSize)
|
|
56
|
+
const width = Math.max(originalWidth, props.minSize)
|
|
57
|
+
|
|
58
|
+
ratioY.value =
|
|
59
|
+
originalHeight /
|
|
60
|
+
(offsetHeight - originalHeight) /
|
|
61
|
+
(height / (offsetHeight - height))
|
|
62
|
+
ratioX.value =
|
|
63
|
+
originalWidth /
|
|
64
|
+
(offsetWidth - originalWidth) /
|
|
65
|
+
(width / (offsetWidth - width))
|
|
66
|
+
|
|
67
|
+
sizeHeight.value = height + GAP < offsetHeight ? `${height}px` : ''
|
|
68
|
+
sizeWidth.value = width + GAP < offsetWidth ? `${width}px` : ''
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
defineExpose({
|
|
72
|
+
handleScroll,
|
|
73
|
+
update,
|
|
74
|
+
})
|
|
75
|
+
</script>
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="scrollbarRef" class="j-scrollbar">
|
|
3
|
+
<div
|
|
4
|
+
ref="wrapRef"
|
|
5
|
+
:class="wrapKls"
|
|
6
|
+
:style="wrapStyle"
|
|
7
|
+
@scroll="handleScroll"
|
|
8
|
+
>
|
|
9
|
+
<component
|
|
10
|
+
:is="tag"
|
|
11
|
+
:id="id"
|
|
12
|
+
ref="resizeRef"
|
|
13
|
+
:class="resizeKls"
|
|
14
|
+
:role="role"
|
|
15
|
+
:aria-label="ariaLabel"
|
|
16
|
+
:aria-orientation="ariaOrientation"
|
|
17
|
+
>
|
|
18
|
+
<slot/>
|
|
19
|
+
</component>
|
|
20
|
+
</div>
|
|
21
|
+
<template v-if="!native">
|
|
22
|
+
<bar ref="barRef" :always="always" :min-size="minSize"/>
|
|
23
|
+
</template>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
<script lang="ts" setup>
|
|
27
|
+
import {
|
|
28
|
+
computed,
|
|
29
|
+
nextTick,
|
|
30
|
+
onMounted,
|
|
31
|
+
onUpdated,
|
|
32
|
+
provide,
|
|
33
|
+
reactive,
|
|
34
|
+
ref,
|
|
35
|
+
watch,
|
|
36
|
+
} from 'vue'
|
|
37
|
+
import { useEventListener, useResizeObserver } from '@vueuse/core'
|
|
38
|
+
import { isNumber, isObject } from '@jetlinks-web/utils'
|
|
39
|
+
import Bar from './Bar.vue'
|
|
40
|
+
import { scrollbarContextKey } from './constants'
|
|
41
|
+
import { scrollbarEmits, scrollbarProps, BarInstance } from './scrollbar'
|
|
42
|
+
import type { CSSProperties, StyleValue } from 'vue'
|
|
43
|
+
|
|
44
|
+
const COMPONENT_NAME = 'JScrollbar'
|
|
45
|
+
|
|
46
|
+
defineOptions({
|
|
47
|
+
name: COMPONENT_NAME,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const props = defineProps(scrollbarProps)
|
|
51
|
+
const emit = defineEmits(scrollbarEmits)
|
|
52
|
+
|
|
53
|
+
let stopResizeObserver: (() => void) | undefined = undefined
|
|
54
|
+
let stopResizeListener: (() => void) | undefined = undefined
|
|
55
|
+
|
|
56
|
+
const scrollbarRef = ref<HTMLDivElement>()
|
|
57
|
+
const wrapRef = ref<HTMLDivElement>()
|
|
58
|
+
const resizeRef = ref<HTMLElement>()
|
|
59
|
+
const barRef = ref<BarInstance>()
|
|
60
|
+
|
|
61
|
+
const wrapStyle = computed<StyleValue>(() => {
|
|
62
|
+
const style: CSSProperties = {}
|
|
63
|
+
if (props.height) style.height = `${props.height}px`
|
|
64
|
+
if (props.maxHeight) style.maxHeight = `${props.maxHeight}px`
|
|
65
|
+
return [props.wrapStyle, style]
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const wrapKls = computed(() => {
|
|
69
|
+
return [
|
|
70
|
+
'j-scrollbar__wrap',
|
|
71
|
+
'j-scrollbar__wrap--hidden-default'
|
|
72
|
+
]
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const resizeKls = computed(() => {
|
|
76
|
+
return ['j-scrollbar__view', props.viewClass]
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const handleScroll = () => {
|
|
80
|
+
if (wrapRef.value) {
|
|
81
|
+
barRef.value?.handleScroll(wrapRef.value)
|
|
82
|
+
|
|
83
|
+
emit('scroll', {
|
|
84
|
+
scrollTop: wrapRef.value.scrollTop,
|
|
85
|
+
scrollLeft: wrapRef.value.scrollLeft,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function scrollTo(xCord: number, yCord?: number): void
|
|
91
|
+
function scrollTo(options: ScrollToOptions): void
|
|
92
|
+
function scrollTo(arg1: any, arg2?: number) {
|
|
93
|
+
if (isObject(arg1)) {
|
|
94
|
+
wrapRef.value!.scrollTo(arg1)
|
|
95
|
+
} else if (isNumber(arg1) && isNumber(arg2)) {
|
|
96
|
+
wrapRef.value!.scrollTo(arg1, arg2)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const setScrollTop = (value: number) => {
|
|
101
|
+
if (!isNumber(value)) {
|
|
102
|
+
throw new Error('value must be a number');
|
|
103
|
+
}
|
|
104
|
+
wrapRef.value!.scrollTop = value
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const setScrollLeft = (value: number) => {
|
|
108
|
+
if (!isNumber(value)) {
|
|
109
|
+
throw new Error(`[${COMPONENT_NAME}] value must be a number`);
|
|
110
|
+
}
|
|
111
|
+
wrapRef.value!.scrollLeft = value
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const update = () => {
|
|
115
|
+
barRef.value?.update()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
watch(
|
|
119
|
+
() => props.noresize,
|
|
120
|
+
(noResize) => {
|
|
121
|
+
if (noResize) {
|
|
122
|
+
stopResizeObserver?.()
|
|
123
|
+
stopResizeListener?.()
|
|
124
|
+
} else {
|
|
125
|
+
;({stop: stopResizeObserver} = useResizeObserver(resizeRef, update))
|
|
126
|
+
stopResizeListener = useEventListener('resize', update)
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{immediate: true}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
watch(
|
|
133
|
+
() => [props.maxHeight, props.height],
|
|
134
|
+
() => {
|
|
135
|
+
if (!props.native)
|
|
136
|
+
nextTick(() => {
|
|
137
|
+
update()
|
|
138
|
+
if (wrapRef.value) {
|
|
139
|
+
barRef.value?.handleScroll(wrapRef.value)
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
provide(
|
|
146
|
+
scrollbarContextKey,
|
|
147
|
+
reactive({
|
|
148
|
+
scrollbarElement: scrollbarRef,
|
|
149
|
+
wrapElement: wrapRef,
|
|
150
|
+
})
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
onMounted(() => {
|
|
154
|
+
if (!props.native)
|
|
155
|
+
nextTick(() => {
|
|
156
|
+
update()
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
onUpdated(() => update())
|
|
160
|
+
|
|
161
|
+
defineExpose({
|
|
162
|
+
/** @description scrollbar wrap ref */
|
|
163
|
+
wrapRef,
|
|
164
|
+
/** @description update scrollbar state manually */
|
|
165
|
+
update,
|
|
166
|
+
/** @description scrolls to a particular set of coordinates */
|
|
167
|
+
scrollTo,
|
|
168
|
+
/** @description set distance to scroll top */
|
|
169
|
+
setScrollTop,
|
|
170
|
+
/** @description set distance to scroll left */
|
|
171
|
+
setScrollLeft,
|
|
172
|
+
/** @description handle scroll event */
|
|
173
|
+
handleScroll,
|
|
174
|
+
})
|
|
175
|
+
</script>
|
|
176
|
+
|
|
177
|
+
<style lang="less">
|
|
178
|
+
@scrollbar-bg-color: #909399;
|
|
179
|
+
@scrollbar-hover-bg-color: #909399;
|
|
180
|
+
@scrollbar-hover-opacity: 0.5;
|
|
181
|
+
@scrollbar-opacity: 0.3;
|
|
182
|
+
@transition-duration: 0.3s;
|
|
183
|
+
|
|
184
|
+
.j-scrollbar {
|
|
185
|
+
overflow: hidden;
|
|
186
|
+
position: relative;
|
|
187
|
+
height: 100%;
|
|
188
|
+
|
|
189
|
+
&__wrap {
|
|
190
|
+
overflow: auto;
|
|
191
|
+
height: 100%;
|
|
192
|
+
|
|
193
|
+
&--hidden-default {
|
|
194
|
+
scrollbar-width: none;
|
|
195
|
+
|
|
196
|
+
&::-webkit-scrollbar {
|
|
197
|
+
display: none;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
&__thumb {
|
|
203
|
+
position: relative;
|
|
204
|
+
display: block;
|
|
205
|
+
width: 0;
|
|
206
|
+
height: 0;
|
|
207
|
+
cursor: pointer;
|
|
208
|
+
border-radius: inherit;
|
|
209
|
+
background-color: @scrollbar-bg-color;
|
|
210
|
+
transition: @transition-duration background-color;
|
|
211
|
+
opacity: @scrollbar-opacity;
|
|
212
|
+
|
|
213
|
+
&:hover {
|
|
214
|
+
background-color: @scrollbar-hover-bg-color;
|
|
215
|
+
opacity: @scrollbar-hover-opacity;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
&__bar {
|
|
220
|
+
position: absolute;
|
|
221
|
+
right: 2px;
|
|
222
|
+
bottom: 2px;
|
|
223
|
+
z-index: 1;
|
|
224
|
+
border-radius: 4px;
|
|
225
|
+
|
|
226
|
+
&--vertical {
|
|
227
|
+
width: 6px;
|
|
228
|
+
top: 2px;
|
|
229
|
+
|
|
230
|
+
> div {
|
|
231
|
+
width: 100%;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
&--horizontal {
|
|
236
|
+
height: 6px;
|
|
237
|
+
left: 2px;
|
|
238
|
+
|
|
239
|
+
> div {
|
|
240
|
+
height: 100%;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.j-scrollbar-fade {
|
|
247
|
+
&-enter-active {
|
|
248
|
+
transition: opacity 340ms ease-out;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
&-leave-active {
|
|
252
|
+
transition: opacity 120ms ease-out;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
&-enter-from,
|
|
256
|
+
&-leave-active {
|
|
257
|
+
opacity: 0;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
</style>
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<transition name="j-scrollbar-fade">
|
|
3
|
+
<div
|
|
4
|
+
v-show="always || visible"
|
|
5
|
+
ref="instance"
|
|
6
|
+
:class="['j-scrollbar__bar', props.vertical ? 'is-vertical' : 'is-horizontal']"
|
|
7
|
+
@mousedown="clickTrackHandler"
|
|
8
|
+
>
|
|
9
|
+
<div
|
|
10
|
+
ref="thumb"
|
|
11
|
+
class="j-scrollbar__thumb"
|
|
12
|
+
:style="thumbStyle"
|
|
13
|
+
@mousedown="clickThumbHandler"
|
|
14
|
+
/>
|
|
15
|
+
</div>
|
|
16
|
+
</transition>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script lang="ts" setup>
|
|
20
|
+
import { computed, inject, onBeforeUnmount, ref, toRef } from 'vue'
|
|
21
|
+
import { useEventListener } from '@vueuse/core'
|
|
22
|
+
import { isClient } from '@jetlinks-web/utils'
|
|
23
|
+
import { scrollbarContextKey } from './constants'
|
|
24
|
+
import { BAR_MAP, renderThumbStyle } from './util'
|
|
25
|
+
import { thumbProps } from './thumb'
|
|
26
|
+
|
|
27
|
+
const props = defineProps(thumbProps)
|
|
28
|
+
|
|
29
|
+
const scrollbar = inject(scrollbarContextKey)
|
|
30
|
+
|
|
31
|
+
if (!scrollbar) throw new Error('can not inject scrollbar context')
|
|
32
|
+
|
|
33
|
+
const instance = ref<HTMLDivElement>()
|
|
34
|
+
const thumb = ref<HTMLDivElement>()
|
|
35
|
+
|
|
36
|
+
const thumbState = ref<Partial<Record<'X' | 'Y', number>>>({})
|
|
37
|
+
const visible = ref(false)
|
|
38
|
+
|
|
39
|
+
let cursorDown = false
|
|
40
|
+
let cursorLeave = false
|
|
41
|
+
let originalOnSelectStart:
|
|
42
|
+
| ((this: GlobalEventHandlers, ev: Event) => any)
|
|
43
|
+
| null = isClient ? document.onselectstart : null
|
|
44
|
+
|
|
45
|
+
const bar = computed(() => BAR_MAP[props.vertical ? 'vertical' : 'horizontal'])
|
|
46
|
+
|
|
47
|
+
const thumbStyle = computed(() =>
|
|
48
|
+
renderThumbStyle({
|
|
49
|
+
size: props.size,
|
|
50
|
+
move: props.move,
|
|
51
|
+
bar: bar.value,
|
|
52
|
+
})
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
const offsetRatio = computed(
|
|
56
|
+
() =>
|
|
57
|
+
instance.value![bar.value.offset] ** 2 /
|
|
58
|
+
scrollbar.wrapElement![bar.value.scrollSize] /
|
|
59
|
+
props.ratio /
|
|
60
|
+
Thumb.value![bar.value.offset]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const clickThumbHandler = (e: MouseEvent) => {
|
|
64
|
+
// prevent click event of middle and right button
|
|
65
|
+
e.stopPropagation()
|
|
66
|
+
if (e.ctrlKey || [1, 2].includes(e.button)) return
|
|
67
|
+
|
|
68
|
+
window.getSelection()?.removeAllRanges()
|
|
69
|
+
startDrag(e)
|
|
70
|
+
|
|
71
|
+
const el = e.currentTarget as HTMLDivElement
|
|
72
|
+
if (!el) return
|
|
73
|
+
thumbState.value[bar.value.axis] =
|
|
74
|
+
el[bar.value.offset] -
|
|
75
|
+
(e[bar.value.client] - el.getBoundingClientRect()[bar.value.direction])
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const clickTrackHandler = (e: MouseEvent) => {
|
|
79
|
+
if (!Thumb.value || !instance.value || !scrollbar.wrapElement) return
|
|
80
|
+
|
|
81
|
+
const offset = Math.abs(
|
|
82
|
+
(e.target as HTMLElement).getBoundingClientRect()[bar.value.direction] -
|
|
83
|
+
e[bar.value.client]
|
|
84
|
+
)
|
|
85
|
+
const thumbHalf = Thumb.value[bar.value.offset] / 2
|
|
86
|
+
const thumbPositionPercentage =
|
|
87
|
+
((offset - thumbHalf) * 100 * offsetRatio.value) /
|
|
88
|
+
instance.value[bar.value.offset]
|
|
89
|
+
|
|
90
|
+
scrollbar.wrapElement[bar.value.scroll] =
|
|
91
|
+
(thumbPositionPercentage * scrollbar.wrapElement[bar.value.scrollSize]) /
|
|
92
|
+
100
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const startDrag = (e: MouseEvent) => {
|
|
96
|
+
e.stopImmediatePropagation()
|
|
97
|
+
cursorDown = true
|
|
98
|
+
document.addEventListener('mousemove', mouseMoveDocumentHandler)
|
|
99
|
+
document.addEventListener('mouseup', mouseUpDocumentHandler)
|
|
100
|
+
originalOnSelectStart = document.onselectstart
|
|
101
|
+
document.onselectstart = () => false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const mouseMoveDocumentHandler = (e: MouseEvent) => {
|
|
105
|
+
if (!instance.value || !Thumb.value) return
|
|
106
|
+
if (cursorDown === false) return
|
|
107
|
+
|
|
108
|
+
const prevPage = thumbState.value[bar.value.axis]
|
|
109
|
+
if (!prevPage) return
|
|
110
|
+
|
|
111
|
+
const offset =
|
|
112
|
+
(instance.value.getBoundingClientRect()[bar.value.direction] -
|
|
113
|
+
e[bar.value.client]) *
|
|
114
|
+
-1
|
|
115
|
+
const thumbClickPosition = Thumb.value[bar.value.offset] - prevPage
|
|
116
|
+
const thumbPositionPercentage =
|
|
117
|
+
((offset - thumbClickPosition) * 100 * offsetRatio.value) /
|
|
118
|
+
instance.value[bar.value.offset]
|
|
119
|
+
scrollbar.wrapElement[bar.value.scroll] =
|
|
120
|
+
(thumbPositionPercentage * scrollbar.wrapElement[bar.value.scrollSize]) /
|
|
121
|
+
100
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const mouseUpDocumentHandler = () => {
|
|
125
|
+
cursorDown = false
|
|
126
|
+
thumbState.value[bar.value.axis] = 0
|
|
127
|
+
document.removeEventListener('mousemove', mouseMoveDocumentHandler)
|
|
128
|
+
document.removeEventListener('mouseup', mouseUpDocumentHandler)
|
|
129
|
+
restoreOnselectstart()
|
|
130
|
+
if (cursorLeave) visible.value = false
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const mouseMoveScrollbarHandler = () => {
|
|
134
|
+
cursorLeave = false
|
|
135
|
+
visible.value = !!props.size
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const mouseLeaveScrollbarHandler = () => {
|
|
139
|
+
cursorLeave = true
|
|
140
|
+
visible.value = cursorDown
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
onBeforeUnmount(() => {
|
|
144
|
+
restoreOnselectstart()
|
|
145
|
+
document.removeEventListener('mouseup', mouseUpDocumentHandler)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const restoreOnselectstart = () => {
|
|
149
|
+
if (document.onselectstart !== originalOnSelectStart)
|
|
150
|
+
document.onselectstart = originalOnSelectStart
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
useEventListener(
|
|
154
|
+
toRef(scrollbar, 'scrollbarElement'),
|
|
155
|
+
'mousemove',
|
|
156
|
+
mouseMoveScrollbarHandler
|
|
157
|
+
)
|
|
158
|
+
useEventListener(
|
|
159
|
+
toRef(scrollbar, 'scrollbarElement'),
|
|
160
|
+
'mouseleave',
|
|
161
|
+
mouseLeaveScrollbarHandler
|
|
162
|
+
)
|
|
163
|
+
</script>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { isNumber } from '@jetlinks-web/utils'
|
|
2
|
+
import type { StyleValue, PropType } from 'vue'
|
|
3
|
+
import type Bar from './Bar.vue'
|
|
4
|
+
|
|
5
|
+
export const scrollbarProps = {
|
|
6
|
+
/**
|
|
7
|
+
* @description height of scrollbar
|
|
8
|
+
*/
|
|
9
|
+
height: {
|
|
10
|
+
type: [String, Number],
|
|
11
|
+
default: '',
|
|
12
|
+
},
|
|
13
|
+
/**
|
|
14
|
+
* @description max height of scrollbar
|
|
15
|
+
*/
|
|
16
|
+
maxHeight: {
|
|
17
|
+
type: [String, Number],
|
|
18
|
+
default: '',
|
|
19
|
+
},
|
|
20
|
+
/**
|
|
21
|
+
* @description whether to use the native scrollbar
|
|
22
|
+
*/
|
|
23
|
+
native: {
|
|
24
|
+
type: Boolean,
|
|
25
|
+
default: false,
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* @description style of wrap
|
|
29
|
+
*/
|
|
30
|
+
wrapStyle: {
|
|
31
|
+
type: [String, Object, Array] as PropType<StyleValue>,
|
|
32
|
+
default: '',
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* @description class of wrap
|
|
36
|
+
*/
|
|
37
|
+
wrapClass: {
|
|
38
|
+
type: [String, Array],
|
|
39
|
+
default: '',
|
|
40
|
+
},
|
|
41
|
+
/**
|
|
42
|
+
* @description class of view
|
|
43
|
+
*/
|
|
44
|
+
viewClass: {
|
|
45
|
+
type: [String, Array],
|
|
46
|
+
default: '',
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* @description style of view
|
|
50
|
+
*/
|
|
51
|
+
viewStyle: {
|
|
52
|
+
type: [String, Array, Object],
|
|
53
|
+
default: '',
|
|
54
|
+
},
|
|
55
|
+
/**
|
|
56
|
+
* @description do not respond to container size changes, if the container size does not change, it is better to set it to optimize performance
|
|
57
|
+
*/
|
|
58
|
+
noresize: Boolean, // 如果 container 尺寸不会发生变化,最好设置它可以优化性能
|
|
59
|
+
/**
|
|
60
|
+
* @description element tag of the view
|
|
61
|
+
*/
|
|
62
|
+
tag: {
|
|
63
|
+
type: String,
|
|
64
|
+
default: 'div',
|
|
65
|
+
},
|
|
66
|
+
/**
|
|
67
|
+
* @description always show
|
|
68
|
+
*/
|
|
69
|
+
always: Boolean,
|
|
70
|
+
/**
|
|
71
|
+
* @description minimum size of scrollbar
|
|
72
|
+
*/
|
|
73
|
+
minSize: {
|
|
74
|
+
type: Number,
|
|
75
|
+
default: 20,
|
|
76
|
+
},
|
|
77
|
+
/**
|
|
78
|
+
* @description id of view
|
|
79
|
+
*/
|
|
80
|
+
id: String,
|
|
81
|
+
/**
|
|
82
|
+
* @description role of view
|
|
83
|
+
*/
|
|
84
|
+
role: String,
|
|
85
|
+
/**
|
|
86
|
+
* @description aria-label of view
|
|
87
|
+
*/
|
|
88
|
+
ariaLabel: String,
|
|
89
|
+
/**
|
|
90
|
+
* @description aria-orientation of view
|
|
91
|
+
*/
|
|
92
|
+
ariaOrientation: {
|
|
93
|
+
type: String,
|
|
94
|
+
values: ['horizontal', 'vertical'],
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const scrollbarEmits = {
|
|
99
|
+
scroll: ({
|
|
100
|
+
scrollTop,
|
|
101
|
+
scrollLeft,
|
|
102
|
+
}: {
|
|
103
|
+
scrollTop: number
|
|
104
|
+
scrollLeft: number
|
|
105
|
+
}) => [scrollTop, scrollLeft].every(isNumber),
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type BarInstance = InstanceType<typeof Bar>
|