Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.

Commit 9247017

Browse files
committed
feat: Support virtual scroll
1 parent 37feb85 commit 9247017

File tree

16 files changed

+226
-169
lines changed

16 files changed

+226
-169
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module.exports = {
1414
rules: {
1515
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
1616
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
17+
'no-unused-expressions': 'off',
1718
'@typescript-eslint/ban-ts-ignore': 'off'
1819
},
1920
overrides: [

dev/App.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
:language="language"
3636
:prev="prev"
3737
:current="current"
38-
:virtual-scroll="{ height: 500, lineMinHeight: 24 }"
38+
:input-delay="0"
39+
:virtual-scroll="{ height: 500, lineMinHeight: 24, scrollDelay: 250 }"
3940
/>
4041
</section>
4142
</div>

jest.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@ module.exports = {
22
preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel',
33
transform: {
44
'^.+\\.vue$': 'vue-jest'
5-
}
5+
},
6+
transformIgnorePatterns: [
7+
'node_modules/(?!lodash-es/.*)'
8+
]
69
}

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"rollup-plugin-vue": "^6.0.0",
8282
"sass": "^1.26.5",
8383
"sass-loader": "^8.0.2",
84-
"typescript": "~3.9.3",
84+
"typescript": "^3.9.6",
8585
"vite": "^2.0.0-beta.29",
8686
"vue": "^3.0.5",
8787
"vue-jest": "^5.0.0-0"

rollup.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const options = {
2525
resolve(),
2626
commonjs(),
2727
scss({
28-
processor: css => postcss([autoprefixer()])
28+
processor: () => postcss([autoprefixer()])
2929
}),
3030
css({ output: 'dist/index.css' })
3131
]

src/Code.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default defineComponent({
2222
const highlightCode = ref('')
2323
2424
onMounted(() => {
25-
watch(() => props.code, () => {
25+
watch([() => props.language, () => props.code], () => {
2626
setHighlightCode({
2727
highlightCode,
2828
language: props.language,

src/Diff.vue

Lines changed: 66 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,42 @@
66
<div
77
ref="viewer"
88
class="vue-diff-viewer"
9-
:style="{ height: viewerHeight ? viewerHeight + 'px' : undefined }"
9+
:style="{ height: scrollOptions ? scrollOptions.height + 'px' : undefined }"
1010
>
1111
<div
1212
class="vue-diff-viewer-inner"
13-
:style="{ minHeight: innerMinHeight ? innerMinHeight + 'px' : undefined }"
13+
:style="{ minHeight: minHeight ? minHeight + 'px' : undefined }"
1414
>
1515
<Line
16+
v-for="(data, index) in visible"
1617
:key="index"
17-
v-for="(data, index) in visibleData"
18-
:index="index"
1918
:mode="mode"
2019
:language="language"
21-
:data="data"
22-
:virtualScroll="virtualScroll"
23-
@set-line-height="setLineHeight"
20+
:meta="meta[data.index]"
21+
:render="render[data.index]"
22+
:virtualScroll="scrollOptions"
23+
@setLineHeight="setLineHeight"
2424
/>
2525
</div>
2626
</div>
2727
</div>
2828
</template>
2929

3030
<script lang="ts">
31-
import { defineComponent, PropType, ref, watch, onMounted, onBeforeUnmount, computed, readonly, toRaw } from 'vue'
32-
import throttle from 'lodash-es/throttle'
33-
import { renderLines, renderWords } from './utils'
31+
import {
32+
computed,
33+
defineComponent,
34+
onMounted,
35+
ref,
36+
watch
37+
} from 'vue'
38+
import debounce from 'lodash-es/debounce'
39+
import { useVirtualScroll } from './hooks'
40+
import { renderLines } from './utils'
3441
import Line from './Line.vue'
3542
36-
import type { Mode, Theme, Lines } from './utils'
37-
38-
interface VirtualScroll {
39-
height?: number;
40-
lineMinHeight?: number;
41-
}
42-
43-
interface Data {
44-
key: number;
45-
render: Lines;
46-
top?: number;
47-
height?: number;
48-
}
43+
import type { PropType } from 'vue'
44+
import type { Meta, VirtualScroll, Mode, Theme, Lines } from './types'
4945
5046
export default defineComponent({
5147
components: {
@@ -72,101 +68,71 @@ export default defineComponent({
7268
type: String,
7369
default: ''
7470
},
71+
inputDelay: {
72+
type: Number,
73+
default: 0
74+
},
7575
virtualScroll: {
76-
type: false || Object as PropType<VirtualScroll>,
76+
type: [Boolean, Object] as PropType<boolean|VirtualScroll>,
7777
default: false
7878
}
7979
},
8080
setup (props) {
8181
const viewer = ref<null|HTMLElement>(null)
82-
const source = ref<Array<Data>>([])
83-
const visibleData = ref<Array<Data>>([])
84-
const heightList = ref<Array<number>>([])
85-
86-
const viewerHeight = computed(() => {
87-
if (!props.virtualScroll) return false
88-
return props.virtualScroll.height || 500
89-
})
90-
91-
const innerMinHeight = computed(() => {
92-
if (!props.virtualScroll) return false
93-
return heightList.value.reduce((acc, curr) => acc + curr, 0)
82+
const meta = ref<Array<Meta>>([])
83+
const render = ref<Array<Lines>>([])
84+
const visible = computed(() => meta.value.filter(item => item.visible))
85+
const { minHeight, scrollOptions, setMeta } = useVirtualScroll({
86+
meta,
87+
viewer,
88+
virtualScroll: props.virtualScroll,
89+
render
9490
})
9591
96-
const getVisibleData = () => {
97-
if (!viewer.value || !viewerHeight.value) return []
98-
99-
const result: Data[] = []
100-
const start = viewer.value.scrollTop - viewerHeight.value
101-
const end = viewer.value.scrollTop + viewerHeight.value * 2
102-
103-
heightList.value.reduce((acc, curr, index: number) => {
104-
if (acc >= start && acc <= end) {
105-
result.push({
106-
key: index,
107-
render: source.value[index].render,
108-
top: acc,
109-
height: 24
110-
})
92+
const setData = () => {
93+
render.value = renderLines(props.mode, props.prev, props.current)
94+
meta.value.splice(render.value.length)
95+
render.value.map((v, index: number) => {
96+
const item = meta.value[index]
97+
meta.value[index] = {
98+
index,
99+
visible: item?.visible || !props.virtualScroll,
100+
top: item?.top || undefined,
101+
height: item?.height || 24
111102
}
112-
113-
return acc + curr
114-
}, 0)
115-
116-
return result
117-
}
118-
119-
const handleScroll = () => {
120-
visibleData.value = getVisibleData()
121-
}
122-
123-
const setLineHeight = (index: number, height: number) => {
124-
const item = source.value.find(item => item.key === index)
125-
if (item) {
126-
item.height = height
127-
}
103+
})
104+
setMeta()
128105
}
129106
130107
onMounted(() => {
131-
if (!viewer.value) return
132-
viewer.value.addEventListener('scroll', throttle(handleScroll, 500))
133-
134-
watch([
135-
() => props.mode,
136-
() => props.prev,
137-
() => props.current
138-
], () => {
139-
source.value = renderLines(props.mode, props.prev, props.current).map((render, index) => {
140-
return {
141-
key: index,
142-
render,
143-
height: 24
144-
}
145-
})
146-
147-
if (!props.virtualScroll) {
148-
if (source.value.length > 500) {
149-
console.warn('If there are many lines, please use virtualScroll property.')
150-
}
151-
152-
visibleData.value = source.value
153-
} else {
154-
visibleData.value = getVisibleData()
108+
watch(
109+
[
110+
() => props.mode,
111+
() => props.prev,
112+
() => props.current
113+
],
114+
debounce(setData, props.inputDelay),
115+
{
116+
immediate: true,
117+
deep: true
155118
}
156-
}, { immediate: true })
119+
)
157120
})
158121
159-
onBeforeUnmount(() => {
160-
if (!viewer.value) return
161-
viewer.value.removeEventListener('scroll', throttle(handleScroll, 500))
162-
})
122+
const setLineHeight = (index: number, height: number) => {
123+
if (meta.value[index]) {
124+
meta.value[index].height = height
125+
}
126+
}
163127
164128
return {
165-
visibleData,
129+
meta,
130+
minHeight,
131+
render,
132+
scrollOptions,
133+
setLineHeight,
166134
viewer,
167-
viewerHeight,
168-
innerMinHeight,
169-
setLineHeight
135+
visible
170136
}
171137
}
172138
})

0 commit comments

Comments
 (0)