@vyr/design 0.0.1

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 (83) hide show
  1. package/package.json +16 -0
  2. package/src/components/Button.vue +64 -0
  3. package/src/components/Card.vue +203 -0
  4. package/src/components/Cascader.vue +171 -0
  5. package/src/components/Checked.vue +124 -0
  6. package/src/components/CheckedGroup.vue +51 -0
  7. package/src/components/Col.vue +52 -0
  8. package/src/components/ColorPicker.vue +331 -0
  9. package/src/components/Confirm.vue +86 -0
  10. package/src/components/Dialog.vue +220 -0
  11. package/src/components/Divider.vue +40 -0
  12. package/src/components/Draggable.vue +50 -0
  13. package/src/components/Dropdown.vue +175 -0
  14. package/src/components/DynamicDialog.vue +113 -0
  15. package/src/components/DynamicLayouter.vue +235 -0
  16. package/src/components/Form.vue +88 -0
  17. package/src/components/Input.vue +254 -0
  18. package/src/components/InputNumber.vue +96 -0
  19. package/src/components/Label.vue +116 -0
  20. package/src/components/Loading.vue +196 -0
  21. package/src/components/Mask.vue +47 -0
  22. package/src/components/Notify.vue +130 -0
  23. package/src/components/Option.vue +159 -0
  24. package/src/components/Options.vue +202 -0
  25. package/src/components/Popover.vue +271 -0
  26. package/src/components/Provider.vue +12 -0
  27. package/src/components/RightMenu.vue +127 -0
  28. package/src/components/Row.vue +50 -0
  29. package/src/components/Scroll.vue +99 -0
  30. package/src/components/Select.vue +223 -0
  31. package/src/components/Slot.vue +23 -0
  32. package/src/components/SubTree.vue +262 -0
  33. package/src/components/Tree.vue +129 -0
  34. package/src/components/common/DraggableController.ts +113 -0
  35. package/src/components/common/ResizeListener.ts +49 -0
  36. package/src/components/composables/useDefaultProps.ts +179 -0
  37. package/src/components/composables/useDraggable.ts +65 -0
  38. package/src/components/composables/useGetter.ts +15 -0
  39. package/src/components/composables/useMarginStyle.ts +45 -0
  40. package/src/components/composables/usePopover.ts +33 -0
  41. package/src/components/composables/useProvider.ts +186 -0
  42. package/src/components/composables/useScroll.ts +46 -0
  43. package/src/components/composables/useSearch.ts +97 -0
  44. package/src/components/composables/useTimer.ts +5 -0
  45. package/src/components/index.ts +1 -0
  46. package/src/components/singleton/confirm.ts +25 -0
  47. package/src/components/singleton/dialog.ts +25 -0
  48. package/src/components/singleton/index.ts +5 -0
  49. package/src/components/singleton/loading.ts +36 -0
  50. package/src/components/singleton/notify.ts +36 -0
  51. package/src/components/types/index.ts +82 -0
  52. package/src/components/utils/Cascader.ts +52 -0
  53. package/src/components/utils/Confirm.ts +41 -0
  54. package/src/components/utils/Dialog.ts +38 -0
  55. package/src/components/utils/DynamicDialog.ts +41 -0
  56. package/src/components/utils/DynamicLayouter.ts +5 -0
  57. package/src/components/utils/FloatLayer.ts +40 -0
  58. package/src/components/utils/InputNumber.ts +3 -0
  59. package/src/components/utils/Notify.ts +25 -0
  60. package/src/components/utils/Popover.ts +58 -0
  61. package/src/components/utils/RightMenu.ts +21 -0
  62. package/src/components/utils/Scroll.ts +4 -0
  63. package/src/components/utils/Slot.ts +73 -0
  64. package/src/components/utils/index.ts +12 -0
  65. package/src/font/demo.css +539 -0
  66. package/src/font/demo_index.html +1292 -0
  67. package/src/font/iconfont.css +207 -0
  68. package/src/font/iconfont.js +1 -0
  69. package/src/font/iconfont.json +345 -0
  70. package/src/font/iconfont.ttf +0 -0
  71. package/src/font/iconfont.woff +0 -0
  72. package/src/font/iconfont.woff2 +0 -0
  73. package/src/index.ts +68 -0
  74. package/src/locale/Language.ts +10 -0
  75. package/src/locale/LanguageProvider.ts +38 -0
  76. package/src/locale/index.ts +2 -0
  77. package/src/theme/global.less +91 -0
  78. package/src/theme/index.ts +155 -0
  79. package/src/tool/ArrayUtils.ts +38 -0
  80. package/src/tool/Color.ts +7 -0
  81. package/src/tool/Draggable.ts +36 -0
  82. package/src/tool/Listener.ts +59 -0
  83. package/src/tool/index.ts +4 -0
@@ -0,0 +1,271 @@
1
+ <template>
2
+ <vyr-slot v-if="trigger === 'click'" @slot-change="changeTrigger" @click="click" @contextmenu="rightClick">
3
+ <slot name="trigger"></slot>
4
+ </vyr-slot>
5
+ <vyr-slot v-else @slot-change="changeTrigger" @mouseenter="mouseenter" @mouseleave="mouseleave"
6
+ @mousemove="delayDispatch">
7
+ <slot name="trigger"></slot>
8
+ </vyr-slot>
9
+ <teleport :to="tooltip.wrapper" :disabled="popoverProvider.appendToTooltip === false">
10
+ <div class="vyr-popover" ref="refPopover" data-vyr-popper-hidden :class="{
11
+ 'card': popoverCard,
12
+ [popoverClass]: true
13
+ }" :style="{ width: popoverWidth, zIndex: layerStyle }" @mouseenter="state.needClose = false"
14
+ @mouseleave="mouseleave" @contextmenu.prevent.stop @transitionend="animationEnd">
15
+ <div class="popover-arrow" data-popper-arrow :style="`opacity:${arrow ? 1 : 0}`"></div>
16
+ <div class="popover-wrapper" :class="{ 'flex': popoverProvider.useFlexLayout }">
17
+ <slot></slot>
18
+ </div>
19
+ </div>
20
+ </teleport>
21
+ </template>
22
+
23
+ <script lang="ts" setup>
24
+ import { Instance } from '@popperjs/core';
25
+ import { watch, ref, onBeforeUnmount, computed, useTemplateRef } from 'vue'
26
+ import { popoverCommonDefaultProps } from './composables/useDefaultProps'
27
+ import { usePopoverProvider, useFlotaLayerProvider } from './composables/useProvider'
28
+ import { createPopper, getOptions, isContains, tooltip } from './utils';
29
+ import { PopoverExpose } from './composables/usePopover';
30
+ import VyrSlot from './Slot.vue'
31
+
32
+ const props = defineProps({
33
+ ...popoverCommonDefaultProps,
34
+ popoverCard: { default: true },
35
+ popoverWidth: { default: 'auto' },
36
+ modelValue: { default: false },
37
+ trigger: { default: 'click' as 'click' | 'hover' },
38
+ disabled: { default: false },
39
+ })
40
+
41
+ let popper = null as unknown as Instance
42
+ const emit = defineEmits(['update:modelValue', 'show', 'close', 'triggerChange'])
43
+ const refPopover = useTemplateRef('refPopover')
44
+
45
+ const changeTrigger = (cur: Element, old: HTMLElement) => {
46
+ if (cur) setup(cur)
47
+ emit('triggerChange', cur, old)
48
+ }
49
+ const setup = (cur: Element) => {
50
+ if (popper) popper.destroy()
51
+ if (!refPopover.value) return
52
+ popper = createPopper(cur, refPopover.value, getOptions(props))
53
+ }
54
+ const unsetup = () => {
55
+ unbindTrigger()
56
+ popper.destroy()
57
+ }
58
+ onBeforeUnmount(unsetup)
59
+ const animationEnd = (event: TransitionEvent) => {
60
+ if (state.value.visible === true || event.target !== refPopover.value || event.propertyName !== 'opacity') return
61
+ popper.setOptions(getOptions(props, false))
62
+ }
63
+
64
+ const hideAttrbute = 'data-vyr-popper-hidden'
65
+ const updatePopoverOnMove = () => popper.update()
66
+ const closePopoverOnClick = (e: MouseEvent | KeyboardEvent) => {
67
+ if (state.value.visible === false || popoverProvider.value.autoClose === false) return
68
+ if ((e.target instanceof Node) && isContains(e.target, [refPopover.value, popper.state.elements.reference])) return
69
+ setVisible(false)
70
+ }
71
+ const closeOfKeydownESC = (e: KeyboardEvent) => {
72
+ if (e.code !== 'Escape') return
73
+ closePopoverOnClick(e)
74
+ }
75
+ const unbindTrigger = () => {
76
+ window.removeEventListener('click', closePopoverOnClick)
77
+ window.removeEventListener('keyup', closeOfKeydownESC)
78
+ if (flotaLayerProvider !== null) flotaLayerProvider.listener.unlisten('move', updatePopoverOnMove)
79
+ }
80
+ const bindTrigger = () => {
81
+ window.addEventListener('click', closePopoverOnClick)
82
+ window.addEventListener('keyup', closeOfKeydownESC)
83
+ if (flotaLayerProvider !== null) flotaLayerProvider.listener.listen('move', updatePopoverOnMove)
84
+ }
85
+ const setVisible = (v: boolean) => {
86
+ if (state.value.visible === v) return
87
+ emit('update:modelValue', v)
88
+ state.value.visible = v
89
+ const options = getOptions(props, v)
90
+ if (v) {
91
+ bindTrigger()
92
+ popper.setOptions(options)
93
+ refPopover.value?.removeAttribute(hideAttrbute)
94
+ emit('show')
95
+ } else {
96
+ unbindTrigger()
97
+ refPopover.value?.setAttribute(hideAttrbute, '')
98
+ emit('close')
99
+ }
100
+ }
101
+
102
+ const popoverProvider = usePopoverProvider()
103
+ const flotaLayerProvider = useFlotaLayerProvider()
104
+ const layerStyle = computed(() => {
105
+ const unit = 999
106
+ return flotaLayerProvider === null ? unit : (flotaLayerProvider.layer.value ?? 0) + 1
107
+ })
108
+
109
+ const state = ref({
110
+ visible: false,
111
+ needClose: false,
112
+ delayCount: 0,
113
+ needOpen: false,
114
+ })
115
+
116
+ const click = () => {
117
+ if (props.disabled === true) return
118
+ if (props.trigger !== 'click') return
119
+ setVisible(!state.value.visible)
120
+ }
121
+ const show = () => {
122
+ if (state.value.needClose === true) return
123
+ setVisible(true)
124
+ }
125
+ const delayShow = () => {
126
+ state.value.delayCount--
127
+ if (state.value.delayCount === 0 && state.value.needOpen === true) show()
128
+ }
129
+ const delayDispatch = () => {
130
+ if (props.disabled === true || props.trigger !== 'hover' || state.value.needOpen === false) return
131
+ state.value.delayCount++
132
+ setTimeout(delayShow, 200)
133
+ }
134
+ const mouseenter = () => {
135
+ if (props.disabled === true || props.trigger !== 'hover') return
136
+ state.value.needOpen = true
137
+ delayDispatch()
138
+ }
139
+ const close = () => {
140
+ if (state.value.needClose === false) return
141
+ state.value.needClose = false
142
+ setVisible(false)
143
+ }
144
+ const mouseleave = () => {
145
+ if (props.disabled === true || props.trigger !== 'hover') return
146
+ state.value.needOpen = false
147
+ if (state.value.needClose === true) return
148
+ state.value.needClose = true
149
+ setTimeout(close, 300)
150
+ }
151
+ const rightClick = () => {
152
+ state.value.needOpen = false
153
+ mouseleave()
154
+ }
155
+
156
+ watch(() => props.modelValue, setVisible)
157
+ watch(() => [props.placement, props.arrow], () => popper.setOptions(getOptions(props)))
158
+
159
+ const expose: PopoverExpose = {
160
+ update: () => popper.update(),
161
+ position: () => popper.state.placement.split('-'),
162
+ }
163
+ defineExpose(expose)
164
+ </script>
165
+
166
+ <style lang="less" scoped>
167
+ @import '../theme/global.less';
168
+
169
+ @popover-pad: 20px;
170
+ @popover-pad2: @popover-pad * 2;
171
+ @popover-max-width: calc(100% - @popover-pad2);
172
+ @popover-max-height: calc(100% - @popover-pad2);
173
+
174
+ .vyr-popover-location {
175
+ .vyr-font-family;
176
+ display: none;
177
+ }
178
+
179
+ .vyr-popover {
180
+ .vyr-font-family;
181
+ max-width: @popover-max-width;
182
+ max-height: @popover-max-width;
183
+ color: var(--vyr-font-color);
184
+ font-size: var(--vyr-font-size);
185
+ box-sizing: border-box;
186
+ transition: opacity var(--vyr-animation-time);
187
+
188
+ .popover-wrapper {
189
+ background-color: var(--vyr-popover-background-color);
190
+ position: relative;
191
+ left: 0;
192
+ top: 0;
193
+ }
194
+
195
+ .popover-wrapper.flex {
196
+ display: flex;
197
+ flex-wrap: nowrap;
198
+ flex-direction: row;
199
+ justify-content: flex-start;
200
+ align-items: flex-start;
201
+ align-content: flex-start;
202
+ }
203
+
204
+ .popover-arrow,
205
+ .popover-arrow::before {
206
+ position: absolute;
207
+ width: 6px;
208
+ height: 6px;
209
+ background: inherit;
210
+ }
211
+
212
+ .popover-arrow {
213
+ visibility: hidden;
214
+ }
215
+
216
+ .popover-arrow::before {
217
+ border: 1px solid var(--vyr-border-color);
218
+ content: '';
219
+ visibility: visible;
220
+ transform: rotate(45deg);
221
+ background-color: var(--vyr-topic-color);
222
+ }
223
+ }
224
+
225
+ .vyr-popover.dialog {
226
+ z-index: var(--vyr-popover-dialog-z-index);
227
+ }
228
+
229
+ .vyr-popover.dialog.active {
230
+ z-index: var(--vyr-popover-dialog-active-z-index);
231
+ }
232
+
233
+ .vyr-popover.card {
234
+ background-color: var(--vyr-popover-background-color);
235
+ border: 1px solid var(--vyr-border-color);
236
+ box-sizing: border-box;
237
+
238
+ .popover-wrapper {
239
+ width: 100%;
240
+ height: 100%;
241
+ padding: 10px;
242
+ box-sizing: border-box;
243
+ }
244
+ }
245
+
246
+ .vyr-popover[data-popper-placement^='top']>.popover-arrow {
247
+ bottom: -3px;
248
+ }
249
+
250
+ .vyr-popover[data-popper-placement^='bottom']>.popover-arrow {
251
+ top: -3px;
252
+ }
253
+
254
+ .vyr-popover[data-popper-placement^='left']>.popover-arrow {
255
+ right: -3px;
256
+ }
257
+
258
+ .vyr-popover[data-popper-placement^='right']>.popover-arrow {
259
+ left: -3px;
260
+ }
261
+
262
+ .vyr-popover[data-vyr-popper-hidden],
263
+ .vyr-popover[data-popper-escaped] {
264
+ opacity: 0;
265
+ pointer-events: none;
266
+
267
+ .popover-arrow {
268
+ opacity: 0;
269
+ }
270
+ }
271
+ </style>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <slot></slot>
3
+ </template>
4
+
5
+ <script lang="ts" setup>
6
+ import { computed } from 'vue';
7
+ import { providerDefaultProps } from './composables/useDefaultProps';
8
+ import { updateProvider } from './composables/useProvider';
9
+
10
+ const props = defineProps(providerDefaultProps)
11
+ updateProvider(computed(() => props) as any)
12
+ </script>
@@ -0,0 +1,127 @@
1
+ <template>
2
+ <teleport to="body">
3
+ <transition v-if="state.enabled" name="vyr-transition" @before-enter="state.transform = true"
4
+ @after-enter="state.transform = false">
5
+ <div class="vyr-right-menu" :class="{ 'transform': state.transform }" :style="wrapperStyle"
6
+ v-show="setting.visible" @contextmenu.prevent.stop @click.prevent.stop="cancel">
7
+ <vyr-cascader ref="refCascader" placement="bottom-start" :data="setting.group" :max-count="state.maxCount"
8
+ :min-count="state.maxCount" :arrow="false" :visible="visible" :popover-width="`${state.width}px`"
9
+ @change="change" @click.stop @close="setting.visible = false">
10
+ <template #trigger>
11
+ <span :style="style" @click.stop></span>
12
+ </template>
13
+ </vyr-cascader>
14
+ </div>
15
+ </transition>
16
+ </teleport>
17
+ </template>
18
+
19
+
20
+ <script lang="ts" setup>
21
+ import { watch, ref, computed, onMounted, onBeforeUnmount, useTemplateRef } from 'vue'
22
+ import { computedFloatLayer, generateFloatLayer, isActiveLayer, RightMenuSetting } from "./utils"
23
+ import { updatePopoverProvider, popoverProviderDefault, FlotaLayerProvider, updateFlotaLayerProvider } from './composables/useProvider';
24
+ import { Listener } from '../tool';
25
+ import { Theme } from '../theme';
26
+ import { Option, RightMenuVue } from './types'
27
+ import VyrCascader from './Cascader.vue';
28
+
29
+ updatePopoverProvider(ref({ ...popoverProviderDefault.value, appendToTooltip: false, autoClose: true }))
30
+
31
+ const refCascader = useTemplateRef('refCascader')
32
+
33
+ const setting = ref(new RightMenuSetting(0, 0, []))
34
+ const style = computed(() => `position:absolute;left:${setting.value.position.x}px;top:${setting.value.position.y}px;`)
35
+
36
+ const state = ref({
37
+ id: generateFloatLayer(),
38
+ width: 240,
39
+ maxCount: 5,
40
+ enabled: true,
41
+ transform: false,
42
+ })
43
+ const getGroupMaxCount = (Options: Option[]) => {
44
+ let count = 0
45
+ for (const Option of Options) {
46
+ if (visible(Option) === true) count++
47
+ }
48
+ return count
49
+ }
50
+ const watchVisible = () => {
51
+ if (setting.value.visible === false || !refCascader.value) return
52
+ const groupMaxCount = getGroupMaxCount(setting.value.group)
53
+ const height = Theme.config.inputHeight.value + Theme.config.optionsScrollMargin.value * 2
54
+ const maxHeight = window.innerHeight * 0.75
55
+ const count = Math.min(Math.floor(maxHeight / height), 10)
56
+ state.value.maxCount = groupMaxCount > count ? count : groupMaxCount
57
+ refCascader.value.show()
58
+ }
59
+ watch(() => setting.value.visible, watchVisible, {})
60
+
61
+ const active = computed(() => isActiveLayer(state.value.id))
62
+ const layer = computed(() => computedFloatLayer(state.value.id, active.value))
63
+ const flotaLayerProvider: FlotaLayerProvider = { listener: new Listener(), layer }
64
+ updateFlotaLayerProvider(flotaLayerProvider)
65
+ const wrapperStyle = computed(() => {
66
+ return layer.value === -1 ? '' : `z-index${layer.value};`
67
+ })
68
+
69
+ const visible = (item: any) => {
70
+ return item.show
71
+ }
72
+ const change = (item: Option) => {
73
+ if (state.value.transform === true) return
74
+ refCascader.value?.close()
75
+ setting.value.change(item)
76
+ }
77
+ const cancel = () => {
78
+ if (state.value.transform === true) return
79
+ refCascader.value?.close()
80
+ setting.value.cancel()
81
+ }
82
+
83
+ onMounted(() => window.addEventListener('resize', close))
84
+ onBeforeUnmount(() => window.removeEventListener('resize', close))
85
+
86
+ const autoClose = () => {
87
+ close()
88
+ state.value.enabled = false
89
+ requestAnimationFrame(() => state.value.enabled = true)
90
+
91
+ }
92
+ onMounted(() => window.addEventListener('resize', autoClose))
93
+ onBeforeUnmount(() => window.addEventListener('resize', autoClose))
94
+
95
+ const show = (set: RightMenuSetting) => {
96
+ setting.value = set
97
+ }
98
+ const close = () => {
99
+ setting.value.visible = false
100
+ }
101
+ defineExpose<RightMenuVue>({ show, close })
102
+
103
+ </script>
104
+
105
+ <style lang="less" scoped>
106
+ @import '../theme/global.less';
107
+
108
+ .vyr-right-menu {
109
+ .vyr-font-family;
110
+ width: 100%;
111
+ height: 100%;
112
+ color: var(--vyr-font-color);
113
+ font-size: var(--vyr-font-size);
114
+ position: fixed;
115
+ left: 0;
116
+ top: 0;
117
+ z-index: 99999;
118
+
119
+ :deep(.vyr-options) {
120
+ box-shadow: 0 0 2px var(--vyr-input-border-color);
121
+ }
122
+ }
123
+
124
+ .vyr-right-menu.transform {
125
+ pointer-events: none;
126
+ }
127
+ </style>
@@ -0,0 +1,50 @@
1
+ <template>
2
+ <div class="vyr-row" :style="style">
3
+ <slot></slot>
4
+ </div>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import { computed, ref } from 'vue'
9
+ import { updateRowProvider, useProvider } from './composables/useProvider'
10
+ import { joinMarginStyle, compileMarginStyle } from './composables/useMarginStyle'
11
+ import { rowCommonDefaultProps } from './composables/useDefaultProps'
12
+
13
+ const props = defineProps(rowCommonDefaultProps)
14
+
15
+ const provider = useProvider()
16
+ const curGutter = computed(() => props.gutter === null ? provider.value.row.gutter : props.gutter)
17
+ const rowProvider = ref({ gutter: curGutter, count: 0 })
18
+ updateRowProvider(rowProvider)
19
+
20
+ const style = computed(() => {
21
+ const curMargin = props.margin === null ? provider.value.row.margin : props.margin
22
+ const margin = compileMarginStyle(curMargin)
23
+ const gutter = rowProvider.value.count === 0 ? 0 : -curGutter.value
24
+ margin[1] = gutter
25
+ margin[3] = gutter
26
+ return joinMarginStyle(margin, 'margin')
27
+ })
28
+ </script>
29
+
30
+ <style lang="less" scoped>
31
+ @import '../theme/global.less';
32
+
33
+ .vyr-row {
34
+ .vyr-font-family;
35
+ display: flex;
36
+ flex-wrap: nowrap;
37
+ flex-direction: row;
38
+ box-sizing: border-box;
39
+
40
+ &:before,
41
+ &:after {
42
+ display: table;
43
+ content: "";
44
+ }
45
+
46
+ &:after {
47
+ clear: both;
48
+ }
49
+ }
50
+ </style>
@@ -0,0 +1,99 @@
1
+ <template>
2
+ <div class="vyr-scroll" :style="`${marginStyle}`" @wheel.passive="layout" @mousemove="move" @mouseleave="leave">
3
+ <div class="scroll-wrapper scrollbar" ref="refWrapper">
4
+ <div class="scroll-body" :style="`${paddingStyle}`" ref="refBody">
5
+ <slot></slot>
6
+ </div>
7
+ </div>
8
+ <div class="scroll-bar"
9
+ :style="`display:${state.hide ? 'none' : 'display'};opacity:${opacity};top:${state.top}%;height:${state.height}%;`">
10
+ </div>
11
+ </div>
12
+ </template>
13
+
14
+ <script lang="ts" setup>
15
+ import { ref, onMounted, onBeforeUnmount, Ref, useTemplateRef } from 'vue'
16
+ import { scrollCommonDefaultProps } from './composables/useDefaultProps'
17
+ import { useMarginStyle } from './composables/useMarginStyle'
18
+ import { useScroll } from './composables/useScroll';
19
+ import { observer } from './utils/Scroll';
20
+ import { language } from '../locale'
21
+
22
+ const props = defineProps(scrollCommonDefaultProps)
23
+ const { marginStyle, paddingStyle } = useMarginStyle(props)
24
+
25
+ const emit = defineEmits(['resize'])
26
+ const refWrapper = useTemplateRef('refWrapper')
27
+ const refBody = useTemplateRef('refBody')
28
+ const state = ref({
29
+ hide: true,
30
+ top: 0,
31
+ height: 0,
32
+ })
33
+
34
+ const rect = { wrapper: 0, body: 0, }
35
+ const computeTop = () => {
36
+ if (refWrapper.value) {
37
+ state.value.top = (refWrapper.value.scrollTop / rect.body) * 100
38
+ }
39
+ }
40
+ const computeLayout = () => {
41
+ rect.wrapper = Math.floor(refWrapper.value?.getBoundingClientRect().height ?? 0)
42
+ rect.body = refBody.value?.scrollHeight ?? 0
43
+ state.value.height = (rect.wrapper / rect.body) * 100
44
+ state.value.hide = rect.body - rect.wrapper > 1 ? false : true
45
+ computeTop()
46
+ emit('resize')
47
+ }
48
+
49
+ const listen = () => {
50
+ if (!refBody.value || !refWrapper.value) throw language.get('scroll.listen.error')
51
+ observer.listen(refWrapper.value, computeLayout)
52
+ observer.listen(refBody.value, computeLayout)
53
+ }
54
+ const unlisten = () => {
55
+ observer.unlisten(refWrapper.value as Element)
56
+ observer.unlisten(refBody.value as Element)
57
+ }
58
+ onMounted(listen)
59
+ onBeforeUnmount(unlisten)
60
+
61
+ const { opacity, layout, move, leave } = useScroll(computeTop, refWrapper as Ref<HTMLElement>)
62
+ </script>
63
+
64
+ <style lang="less" scoped>
65
+ @import '../theme/global.less';
66
+
67
+ .vyr-scroll {
68
+ .vyr-font-family;
69
+ width: 100%;
70
+ height: 100%;
71
+ color: var(--vyr-font-color);
72
+ font-size: var(--vyr-font-size);
73
+ position: relative;
74
+ left: 0;
75
+ top: 0;
76
+ box-sizing: border-box;
77
+
78
+ .scroll-wrapper {
79
+ width: 100%;
80
+ height: 100%;
81
+ overflow-x: hidden;
82
+ overflow-y: scroll;
83
+
84
+ .scroll-body {
85
+ width: 100%;
86
+ box-sizing: border-box;
87
+ }
88
+ }
89
+
90
+ .scroll-bar {
91
+ width: 4px;
92
+ border-radius: var(--vyr-radius-size);
93
+ background-color: var(--vyr-scroll-bar-color);
94
+ position: absolute;
95
+ right: 2px;
96
+ transition: opacity var(--vyr-animation-time);
97
+ }
98
+ }
99
+ </style>