nexa-ui-kit 0.6.4 → 0.7.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.
@@ -0,0 +1,94 @@
1
+ <script setup>
2
+ import { signal, computed, onMounted, onUnmounted, effect } from 'nexa-framework'
3
+
4
+ const props = defineProps({
5
+ items: { type: Array, default: () => [] },
6
+ itemHeight: { type: Number, default: 48 },
7
+ overscan: { type: Number, default: 3 },
8
+ renderItem: { type: Function, required: true },
9
+ keyFn: { type: Function, default: null }
10
+ })
11
+
12
+ const containerRef = signal(null)
13
+ const scrollTop = signal(0)
14
+ const containerHeight = signal(400)
15
+
16
+ const totalHeight = computed(() => props.items.length * props.itemHeight)
17
+
18
+ const visibleRange = computed(() => {
19
+ const start = Math.max(0, Math.floor(scrollTop.value / props.itemHeight) - props.overscan)
20
+ const visibleCount = Math.ceil(containerHeight.value / props.itemHeight) + props.overscan * 2
21
+ const end = Math.min(props.items.length, start + visibleCount)
22
+ return { start, end }
23
+ })
24
+
25
+ const visibleItems = computed(() => {
26
+ const { start, end } = visibleRange.value
27
+ return props.items.slice(start, end).map((item, i) => ({
28
+ item,
29
+ index: start + i,
30
+ top: (start + i) * props.itemHeight
31
+ }))
32
+ })
33
+
34
+ let resizeObserver = null
35
+
36
+ onMounted(() => {
37
+ const el = containerRef.value
38
+ if (!el) return
39
+ containerHeight.value = el.clientHeight
40
+
41
+ resizeObserver = new ResizeObserver(entries => {
42
+ containerHeight.value = entries[0].contentRect.height
43
+ })
44
+ resizeObserver.observe(el)
45
+ })
46
+
47
+ onUnmounted(() => {
48
+ resizeObserver?.disconnect()
49
+ })
50
+
51
+ const onScroll = (e) => {
52
+ scrollTop.value = e.target.scrollTop
53
+ }
54
+ </script>
55
+
56
+ <template>
57
+ <div
58
+ class="n-virtual-list"
59
+ @scroll="onScroll"
60
+ :ref="el => containerRef.value = el"
61
+ >
62
+ <div
63
+ class="n-virtual-list-spacer"
64
+ :style="{ height: totalHeight.value + 'px', position: 'relative' }"
65
+ >
66
+ <div
67
+ v-for="row in visibleItems.value"
68
+ :key="keyFn ? keyFn(row.item, row.index) : row.index"
69
+ class="n-virtual-list-row"
70
+ :style="{ position: 'absolute', top: row.top + 'px', width: '100%', height: itemHeight + 'px' }"
71
+ >
72
+ {{ renderItem(row.item, row.index) }}
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </template>
77
+
78
+ <style scoped>
79
+ .n-virtual-list {
80
+ overflow-y: auto;
81
+ width: 100%;
82
+ height: 100%;
83
+ -webkit-overflow-scrolling: touch;
84
+ overscroll-behavior: contain;
85
+ }
86
+
87
+ .n-virtual-list-spacer {
88
+ position: relative;
89
+ }
90
+
91
+ .n-virtual-list-row {
92
+ box-sizing: border-box;
93
+ }
94
+ </style>
package/src/index.ts CHANGED
@@ -30,6 +30,9 @@ export { default as NInputNumber } from './components/NInputNumber.nexa'
30
30
  export { default as NMultiSelect } from './components/NMultiSelect.nexa'
31
31
  export { default as NDataTable } from './components/NDataTable.nexa'
32
32
  export { default as NPaginator } from './components/NPaginator.nexa'
33
+ export { default as NVirtualList } from './components/NVirtualList.nexa'
34
+ export { default as NScrollView } from './components/NScrollView.nexa'
35
+ export { default as NImage } from './components/NImage.nexa'
33
36
  export * from './services/ToastService.js'
34
37
  export * from './services/FormValidation.js'
35
38
  export { installTheme } from './styles/theme.js'