@v-c/virtual-list 1.0.0 → 1.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.
package/dist/List.cjs CHANGED
@@ -69,9 +69,12 @@ var List_default = /* @__PURE__ */ (0, vue.defineComponent)({
69
69
  inheritAttrs: false,
70
70
  setup(props, { expose, attrs, slots }) {
71
71
  const itemHeight = (0, vue.computed)(() => props.itemHeight);
72
+ const itemKey = (0, vue.toRef)(props, "itemKey");
72
73
  const getKey = (item) => {
73
- if (typeof props.itemKey === "function") return props.itemKey(item);
74
- return item?.[props.itemKey];
74
+ item = (0, vue.toRaw)(item);
75
+ const _itemKey = itemKey.value;
76
+ if (typeof _itemKey === "function") return _itemKey(item);
77
+ return item?.[_itemKey];
75
78
  };
76
79
  const [setInstanceRef, collectHeight, heights, heightUpdatedMark] = require_useHeights.default(getKey, void 0, void 0);
77
80
  const mergedData = (0, vue.computed)(() => props.data || EMPTY_DATA);
@@ -121,6 +124,7 @@ var List_default = /* @__PURE__ */ (0, vue.defineComponent)({
121
124
  fillerOffset.value = void 0;
122
125
  return;
123
126
  }
127
+ const { itemHeight: itemHeight$1, height } = props;
124
128
  if (!inVirtual.value) {
125
129
  scrollHeight.value = fillerInnerRef.value?.offsetHeight || 0;
126
130
  start.value = 0;
@@ -133,23 +137,23 @@ var List_default = /* @__PURE__ */ (0, vue.defineComponent)({
133
137
  let startOffset;
134
138
  let endIndex;
135
139
  const dataLen = mergedData.value.length;
136
- const data = mergedData.value;
140
+ const data = (0, vue.toRaw)(mergedData.value);
137
141
  for (let i = 0; i < dataLen; i += 1) {
138
142
  const item = data[i];
139
143
  const key = getKey(item);
140
144
  const cacheHeight = heights.get(key);
141
- const currentItemBottom = itemTop + (cacheHeight === void 0 ? props.itemHeight : cacheHeight);
145
+ const currentItemBottom = itemTop + (cacheHeight === void 0 ? itemHeight$1 : cacheHeight);
142
146
  if (currentItemBottom >= offsetTop.value && startIndex === void 0) {
143
147
  startIndex = i;
144
148
  startOffset = itemTop;
145
149
  }
146
- if (currentItemBottom > offsetTop.value + props.height && endIndex === void 0) endIndex = i;
150
+ if (currentItemBottom > offsetTop.value + height && endIndex === void 0) endIndex = i;
147
151
  itemTop = currentItemBottom;
148
152
  }
149
153
  if (startIndex === void 0) {
150
154
  startIndex = 0;
151
155
  startOffset = 0;
152
- endIndex = Math.ceil(props.height / props.itemHeight);
156
+ endIndex = Math.ceil(height / itemHeight$1);
153
157
  }
154
158
  if (endIndex === void 0) endIndex = mergedData.value.length - 1;
155
159
  endIndex = Math.min(endIndex + 1, mergedData.value.length - 1);
package/dist/List.js CHANGED
@@ -9,7 +9,7 @@ import useScrollDrag from "./hooks/useScrollDrag.js";
9
9
  import useScrollTo from "./hooks/useScrollTo.js";
10
10
  import ScrollBar_default from "./ScrollBar.js";
11
11
  import { getSpinSize } from "./utils/scrollbarUtil.js";
12
- import { computed, createVNode, defineComponent, isVNode, mergeProps, ref, shallowRef, watch } from "vue";
12
+ import { computed, createVNode, defineComponent, isVNode, mergeProps, ref, shallowRef, toRaw, toRef, watch } from "vue";
13
13
  import ResizeObserver from "@v-c/resize-observer";
14
14
  import { pureAttrs } from "@v-c/util/dist/props-util";
15
15
  function _isSlot(s) {
@@ -66,9 +66,12 @@ var List_default = /* @__PURE__ */ defineComponent({
66
66
  inheritAttrs: false,
67
67
  setup(props, { expose, attrs, slots }) {
68
68
  const itemHeight = computed(() => props.itemHeight);
69
+ const itemKey = toRef(props, "itemKey");
69
70
  const getKey = (item) => {
70
- if (typeof props.itemKey === "function") return props.itemKey(item);
71
- return item?.[props.itemKey];
71
+ item = toRaw(item);
72
+ const _itemKey = itemKey.value;
73
+ if (typeof _itemKey === "function") return _itemKey(item);
74
+ return item?.[_itemKey];
72
75
  };
73
76
  const [setInstanceRef, collectHeight, heights, heightUpdatedMark] = useHeights(getKey, void 0, void 0);
74
77
  const mergedData = computed(() => props.data || EMPTY_DATA);
@@ -118,6 +121,7 @@ var List_default = /* @__PURE__ */ defineComponent({
118
121
  fillerOffset.value = void 0;
119
122
  return;
120
123
  }
124
+ const { itemHeight: itemHeight$1, height } = props;
121
125
  if (!inVirtual.value) {
122
126
  scrollHeight.value = fillerInnerRef.value?.offsetHeight || 0;
123
127
  start.value = 0;
@@ -130,23 +134,23 @@ var List_default = /* @__PURE__ */ defineComponent({
130
134
  let startOffset;
131
135
  let endIndex;
132
136
  const dataLen = mergedData.value.length;
133
- const data = mergedData.value;
137
+ const data = toRaw(mergedData.value);
134
138
  for (let i = 0; i < dataLen; i += 1) {
135
139
  const item = data[i];
136
140
  const key = getKey(item);
137
141
  const cacheHeight = heights.get(key);
138
- const currentItemBottom = itemTop + (cacheHeight === void 0 ? props.itemHeight : cacheHeight);
142
+ const currentItemBottom = itemTop + (cacheHeight === void 0 ? itemHeight$1 : cacheHeight);
139
143
  if (currentItemBottom >= offsetTop.value && startIndex === void 0) {
140
144
  startIndex = i;
141
145
  startOffset = itemTop;
142
146
  }
143
- if (currentItemBottom > offsetTop.value + props.height && endIndex === void 0) endIndex = i;
147
+ if (currentItemBottom > offsetTop.value + height && endIndex === void 0) endIndex = i;
144
148
  itemTop = currentItemBottom;
145
149
  }
146
150
  if (startIndex === void 0) {
147
151
  startIndex = 0;
148
152
  startOffset = 0;
149
- endIndex = Math.ceil(props.height / props.itemHeight);
153
+ endIndex = Math.ceil(height / itemHeight$1);
150
154
  }
151
155
  if (endIndex === void 0) endIndex = mergedData.value.length - 1;
152
156
  endIndex = Math.min(endIndex + 1, mergedData.value.length - 1);
@@ -9,7 +9,7 @@ function parseNumber(value) {
9
9
  function useHeights(getKey, onItemAdd, onItemRemove) {
10
10
  const updatedMark = (0, vue.ref)(0);
11
11
  const instanceRef = (0, vue.ref)(/* @__PURE__ */ new Map());
12
- const heightsRef = (0, vue.reactive)(new require_CacheMap.default());
12
+ const heightsRef = new require_CacheMap.default();
13
13
  const promiseIdRef = (0, vue.ref)(0);
14
14
  function cancelRaf() {
15
15
  promiseIdRef.value += 1;
@@ -1,5 +1,5 @@
1
1
  import CacheMap_default from "../utils/CacheMap.js";
2
- import { onUnmounted, reactive, ref } from "vue";
2
+ import { onUnmounted, ref } from "vue";
3
3
  function parseNumber(value) {
4
4
  const num = parseFloat(value);
5
5
  return isNaN(num) ? 0 : num;
@@ -7,7 +7,7 @@ function parseNumber(value) {
7
7
  function useHeights(getKey, onItemAdd, onItemRemove) {
8
8
  const updatedMark = ref(0);
9
9
  const instanceRef = ref(/* @__PURE__ */ new Map());
10
- const heightsRef = reactive(new CacheMap_default());
10
+ const heightsRef = new CacheMap_default();
11
11
  const promiseIdRef = ref(0);
12
12
  function cancelRaf() {
13
13
  promiseIdRef.value += 1;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@v-c/virtual-list",
3
3
  "type": "module",
4
- "version": "1.0.0",
4
+ "version": "1.0.1",
5
5
  "description": "Vue Virtual List Component",
6
6
  "author": "",
7
7
  "license": "MIT",
@@ -20,6 +20,10 @@
20
20
  "./package.json": "./package.json"
21
21
  },
22
22
  "main": "./dist/index.js",
23
+ "files": [
24
+ "dist",
25
+ "package.json"
26
+ ],
23
27
  "publishConfig": {
24
28
  "access": "public"
25
29
  },
@@ -28,7 +32,7 @@
28
32
  },
29
33
  "dependencies": {
30
34
  "@v-c/resize-observer": "^1.0.0",
31
- "@v-c/util": "^1.0.1"
35
+ "@v-c/util": "^1.0.2"
32
36
  },
33
37
  "scripts": {
34
38
  "build": "vite build",
package/bump.config.ts DELETED
@@ -1,6 +0,0 @@
1
- import { defineConfig } from 'bumpp'
2
-
3
- export default defineConfig({
4
- commit: 'chore(release): @v-c/virtual-list %s',
5
- tag: '@v-c/virtual-list@%s',
6
- })
package/docs/animate.less DELETED
@@ -1,31 +0,0 @@
1
- .motion {
2
- transition: all 0.3s;
3
- }
4
-
5
- .item {
6
- display: inline-block;
7
- box-sizing: border-box;
8
- margin: 0;
9
- padding: 0 16px;
10
- overflow: hidden;
11
- line-height: 31px;
12
- position: relative;
13
-
14
- &:hover {
15
- background: rgba(255, 0, 0, 0.1);
16
- }
17
-
18
- &::after {
19
- content: '';
20
- border-bottom: 1px solid gray;
21
- position: absolute;
22
- bottom: 0;
23
- left: 0;
24
- right: 0;
25
- }
26
-
27
- button {
28
- vertical-align: text-top;
29
- margin-right: 8px;
30
- }
31
- }
package/docs/animate.vue DELETED
@@ -1,159 +0,0 @@
1
- <script setup lang="ts">
2
- import type { ListRef } from '../src'
3
- import { ref } from 'vue'
4
- import VirtualList from '../src'
5
- import './animate.less'
6
-
7
- interface Item {
8
- id: string
9
- uuid: number
10
- }
11
-
12
- let uuid = 0
13
- function genItem(): Item {
14
- const item = {
15
- id: `key_${uuid}`,
16
- uuid,
17
- }
18
- uuid += 1
19
- return item
20
- }
21
-
22
- const originData: Item[] = []
23
- for (let i = 0; i < 1000; i += 1) {
24
- originData.push(genItem())
25
- }
26
-
27
- const data = ref([...originData])
28
- const closeMap = ref<Record<string, boolean>>({})
29
- const animating = ref(false)
30
- const insertIndex = ref<number>()
31
-
32
- const listRef = ref<ListRef>()
33
-
34
- function onClose(id: string) {
35
- closeMap.value = {
36
- ...closeMap.value,
37
- [id]: true,
38
- }
39
- }
40
-
41
- function onLeave(id: string) {
42
- data.value = data.value.filter(item => item.id !== id)
43
- }
44
-
45
- function onAppear(...args: any[]) {
46
- console.log('Appear:', args)
47
- animating.value = false
48
- }
49
-
50
- function lockForAnimation() {
51
- animating.value = true
52
- }
53
-
54
- function onInsertBefore(id: string) {
55
- const index = data.value.findIndex(item => item.id === id)
56
- const newData = [...data.value.slice(0, index), genItem(), ...data.value.slice(index)]
57
- insertIndex.value = index
58
- data.value = newData
59
- lockForAnimation()
60
- }
61
-
62
- function onInsertAfter(id: string) {
63
- const index = data.value.findIndex(item => item.id === id) + 1
64
- const newData = [...data.value.slice(0, index), genItem(), ...data.value.slice(index)]
65
- insertIndex.value = index
66
- data.value = newData
67
- lockForAnimation()
68
- }
69
-
70
- function getCurrentHeight(el: Element) {
71
- const node = el as HTMLElement
72
- node.style.height = `${node.offsetHeight}px`
73
- node.style.opacity = '1'
74
- }
75
-
76
- function getMaxHeight(el: Element) {
77
- const node = el as HTMLElement
78
- node.style.height = `${node.scrollHeight}px`
79
- node.style.opacity = '1'
80
- }
81
-
82
- function getCollapsedHeight(el: Element) {
83
- const node = el as HTMLElement
84
- node.style.height = '0'
85
- node.style.opacity = '0'
86
- }
87
-
88
- function resetHeight(el: Element) {
89
- const node = el as HTMLElement
90
- node.style.height = ''
91
- node.style.opacity = ''
92
- onAppear()
93
- }
94
- </script>
95
-
96
- <template>
97
- <div>
98
- <h2>Animate</h2>
99
- <p>Current: {{ data.length }} records</p>
100
-
101
- <VirtualList
102
- ref="listRef"
103
- :data="data"
104
- data-id="list"
105
- :height="200"
106
- :item-height="20"
107
- item-key="id"
108
- :style="{
109
- border: '1px solid red',
110
- boxSizing: 'border-box',
111
- }"
112
- >
113
- <template #default="{ item, index }">
114
- <Transition
115
- name="motion"
116
- :appear="animating && insertIndex === index"
117
- @before-enter="getCollapsedHeight"
118
- @enter="getMaxHeight"
119
- @after-enter="resetHeight"
120
- @before-leave="getCurrentHeight"
121
- @leave="getCollapsedHeight"
122
- @after-leave="() => onLeave(item.id)"
123
- >
124
- <div
125
- v-if="!(closeMap as any)[item.id]"
126
- class="item"
127
- :data-id="item.id"
128
- >
129
- <div :style="{ height: item.uuid % 2 ? '100px' : undefined }">
130
- <button type="button" @click="onClose(item.id)">
131
- Close
132
- </button>
133
- <button type="button" @click="onInsertBefore(item.id)">
134
- Insert Before
135
- </button>
136
- <button type="button" @click="onInsertAfter(item.id)">
137
- Insert After
138
- </button>
139
- {{ item.id }}
140
- </div>
141
- </div>
142
- </Transition>
143
- </template>
144
- </VirtualList>
145
- </div>
146
- </template>
147
-
148
- <style scoped>
149
- .motion-enter-active,
150
- .motion-leave-active {
151
- transition: all 0.3s;
152
- }
153
-
154
- .motion-enter-from,
155
- .motion-leave-to {
156
- height: 0;
157
- opacity: 0;
158
- }
159
- </style>
package/docs/basic.vue DELETED
@@ -1,176 +0,0 @@
1
- <script setup lang="ts">
2
- import type { ListRef } from '../src'
3
- import { ref } from 'vue'
4
- import VirtualList from '../src'
5
-
6
- interface Item {
7
- id: number
8
- }
9
-
10
- const data: Item[] = []
11
- for (let i = 0; i < 1000; i += 1) {
12
- data.push({ id: i })
13
- }
14
-
15
- const listRef = ref<ListRef>()
16
-
17
- function scrollToShowBar() {
18
- listRef.value?.scrollTo(null)
19
- }
20
-
21
- function scrollTo100px() {
22
- listRef.value?.scrollTo(500)
23
- }
24
-
25
- function scrollToLarge() {
26
- listRef.value?.scrollTo({
27
- index: 99999999,
28
- align: 'top',
29
- })
30
- }
31
-
32
- function scrollTo50Top() {
33
- listRef.value?.scrollTo({
34
- index: 50,
35
- align: 'top',
36
- })
37
- }
38
-
39
- function scrollTo50Bottom() {
40
- listRef.value?.scrollTo({
41
- index: 50,
42
- align: 'bottom',
43
- })
44
- }
45
-
46
- function scrollTo50Auto() {
47
- listRef.value?.scrollTo({
48
- index: 50,
49
- align: 'auto',
50
- })
51
- }
52
-
53
- function scrollTo50TopOffset() {
54
- listRef.value?.scrollTo({
55
- index: 50,
56
- align: 'top',
57
- offset: 15,
58
- })
59
- }
60
-
61
- function scrollTo50BottomOffset() {
62
- listRef.value?.scrollTo({
63
- index: 50,
64
- align: 'bottom',
65
- offset: 15,
66
- })
67
- }
68
-
69
- function scrollToKey50() {
70
- listRef.value?.scrollTo({
71
- key: 50,
72
- align: 'auto',
73
- })
74
- }
75
-
76
- function scrollToLast() {
77
- listRef.value?.scrollTo({
78
- index: data.length - 2,
79
- align: 'top',
80
- })
81
- }
82
-
83
- function scrollToFirst() {
84
- listRef.value?.scrollTo({
85
- index: 0,
86
- align: 'bottom',
87
- })
88
- }
89
-
90
- const visible = ref(true)
91
-
92
- function onScroll(e: Event) {
93
- const target = e.currentTarget as HTMLElement
94
- console.log('scroll:', target.scrollTop)
95
- }
96
- </script>
97
-
98
- <template>
99
- <div style="height: 200vh">
100
- <h2>Basic</h2>
101
- <div style="margin-bottom: 16px">
102
- <button type="button" style="margin: 4px" @click="scrollToShowBar">
103
- Show scroll bar
104
- </button>
105
- <button type="button" style="margin: 4px" @click="scrollTo100px">
106
- Scroll To 100px
107
- </button>
108
- <button type="button" style="margin: 4px" @click="scrollToLarge">
109
- Scroll To 99999999 (top)
110
- </button>
111
- <button type="button" style="margin: 4px" @click="scrollTo50Top">
112
- Scroll To 50 (top)
113
- </button>
114
- <button type="button" style="margin: 4px" @click="scrollTo50Bottom">
115
- Scroll To 50 (bottom)
116
- </button>
117
- <button type="button" style="margin: 4px" @click="scrollTo50Auto">
118
- Scroll To 50 (auto)
119
- </button>
120
- <br>
121
- <button type="button" style="margin: 4px" @click="scrollTo50TopOffset">
122
- Scroll To 50 (top) + 15 offset
123
- </button>
124
- <button type="button" style="margin: 4px" @click="scrollTo50BottomOffset">
125
- Scroll To 50 (bottom) + 15 offset
126
- </button>
127
- <button type="button" style="margin: 4px" @click="scrollToKey50">
128
- Scroll To key 50 (auto)
129
- </button>
130
- <button type="button" style="margin: 4px" @click="scrollToLast">
131
- Scroll To Last (top)
132
- </button>
133
- <button type="button" style="margin: 4px" @click="scrollToFirst">
134
- Scroll To First (bottom)
135
- </button>
136
- <button type="button" style="margin: 4px" @click="visible = !visible">
137
- Toggle visible
138
- </button>
139
- </div>
140
-
141
- <VirtualList
142
- ref="listRef"
143
- :data="data"
144
- :height="200"
145
- :item-height="20"
146
- item-key="id"
147
- :style="{
148
- border: '1px solid red',
149
- boxSizing: 'border-box',
150
- display: visible ? '' : 'none',
151
- }"
152
- @scroll="onScroll"
153
- >
154
- <template #default="{ item }">
155
- <span
156
- :style="{
157
- height: `${30 + (item.id % 2 ? 0 : 10)}px`,
158
- lineHeight: '30px',
159
- display: 'inline-block',
160
- padding: '0 8px',
161
- }"
162
- @click="() => console.log('Click:', item.id)"
163
- >
164
- {{ item.id }}
165
- </span>
166
- </template>
167
- </VirtualList>
168
- </div>
169
- </template>
170
-
171
- <style scoped>
172
- button {
173
- padding: 4px 8px;
174
- cursor: pointer;
175
- }
176
- </style>
package/docs/height.vue DELETED
@@ -1,48 +0,0 @@
1
- <script setup lang="ts">
2
- import VirtualList from '../src'
3
-
4
- interface Item {
5
- id: number
6
- height: number
7
- }
8
-
9
- const data: Item[] = []
10
- for (let i = 0; i < 100; i += 1) {
11
- data.push({
12
- id: i,
13
- height: 30 + (i % 2 ? 70 : 0),
14
- })
15
- }
16
- </script>
17
-
18
- <template>
19
- <div>
20
- <h2>Dynamic Height</h2>
21
-
22
- <VirtualList
23
- :data="data"
24
- :height="500"
25
- :item-height="30"
26
- item-key="id"
27
- :style="{
28
- border: '1px solid red',
29
- boxSizing: 'border-box',
30
- }"
31
- >
32
- <template #default="{ item }">
33
- <span
34
- :style="{
35
- border: '1px solid gray',
36
- padding: '0 16px',
37
- height: `${item.height}px`,
38
- lineHeight: '30px',
39
- boxSizing: 'border-box',
40
- display: 'inline-block',
41
- }"
42
- >
43
- {{ item.id }}
44
- </span>
45
- </template>
46
- </VirtualList>
47
- </div>
48
- </template>
package/docs/nest.vue DELETED
@@ -1,60 +0,0 @@
1
- <script setup lang="ts">
2
- import VirtualList from '../src'
3
-
4
- interface Item {
5
- id: number
6
- }
7
-
8
- const data: Item[] = []
9
- for (let i = 0; i < 100; i += 1) {
10
- data.push({ id: i })
11
- }
12
-
13
- function onScroll(e: Event) {
14
- // console.log('scroll:', (e.currentTarget as HTMLElement).scrollTop)
15
- }
16
- </script>
17
-
18
- <template>
19
- <div>
20
- <h2>Nested List</h2>
21
- <VirtualList
22
- :data="data"
23
- :height="800"
24
- :item-height="20"
25
- item-key="id"
26
- :style="{
27
- border: '1px solid red',
28
- boxSizing: 'border-box',
29
- }"
30
- @scroll="onScroll"
31
- >
32
- <template #default="{ item }">
33
- <div :style="{ padding: '20px', background: 'yellow' }">
34
- <VirtualList
35
- :data="data"
36
- :height="200"
37
- :item-height="20"
38
- item-key="id"
39
- :style="{
40
- border: '1px solid blue',
41
- boxSizing: 'border-box',
42
- background: 'white',
43
- }"
44
- >
45
- <template #default="{ index }">
46
- <div
47
- :style="{
48
- height: '20px',
49
- border: '1px solid cyan',
50
- }"
51
- >
52
- {{ item.id }}-{{ index }}
53
- </div>
54
- </template>
55
- </VirtualList>
56
- </div>
57
- </template>
58
- </VirtualList>
59
- </div>
60
- </template>