|
3 | 3 | import Item from "./Item.svelte" |
4 | 4 | import {createEventDispatcher, onDestroy, onMount} from "svelte" |
5 | 5 |
|
6 | | - export let dataKey |
7 | | - export let dataSources |
| 6 | + export let key = "id" |
| 7 | + export let data |
8 | 8 | export let keeps = 30 |
9 | 9 | export let estimateSize = 50 |
10 | | - export let direction = "vertical" |
| 10 | + export let isHorizontal = false |
| 11 | + export let start = 0 |
11 | 12 | export let offset = 0 |
12 | 13 | export let pageMode = false |
13 | | - export let topThreshold = 10 |
14 | | - export let bottomThreshold = 10 |
| 14 | + export let topThreshold = 0 |
| 15 | + export let bottomThreshold = 0 |
15 | 16 |
|
16 | 17 | let displayItems = [] |
17 | 18 | let paddingStyle |
18 | | - let isHorizontal = direction === "horizontal" |
19 | 19 | let directionKey = isHorizontal ? "scrollLeft" : "scrollTop" |
20 | 20 | let range = null |
21 | 21 | let virtual = new Virtual({ |
|
31 | 31 | const dispatch = createEventDispatcher() |
32 | 32 |
|
33 | 33 | onMount(() => { |
| 34 | + if (start) { |
| 35 | + scrollToIndex(start) |
| 36 | + } else if (offset) { |
| 37 | + scrollToOffset(offset) |
| 38 | + } |
| 39 | +
|
34 | 40 | if (pageMode) { |
35 | 41 | updatePageModeFront() |
36 | 42 |
|
|
48 | 54 | }) |
49 | 55 |
|
50 | 56 | function getUniqueIdFromDataSources() { |
51 | | - return dataSources.map((dataSource) => dataSource[dataKey]) |
| 57 | + return data.map((dataSource) => dataSource[key]) |
52 | 58 | } |
53 | 59 |
|
54 | | - function onItemResized({id, size}) { |
55 | | - virtual.saveSize(id, size) |
| 60 | + function onItemResized({id, size, type}) { |
| 61 | + if (type === "item") |
| 62 | + virtual.saveSize(id, size) |
| 63 | + else { |
| 64 | + if (id === "header") |
| 65 | + virtual.updateParam("slotHeaderSize", size) |
| 66 | + else if (id === "footer") |
| 67 | + virtual.updateParam("slotFooterSize", size) |
| 68 | +
|
| 69 | + virtual.handleSlotSizeChange() |
| 70 | + } |
56 | 71 | } |
57 | 72 |
|
58 | 73 | function onRangeChanged(range_) { |
59 | 74 | range = range_ |
60 | 75 | paddingStyle = paddingStyle = isHorizontal ? `0px ${range.padBehind}px 0px ${range.padFront}px` : `${range.padFront}px 0px ${range.padBehind}px` |
61 | | - displayItems = dataSources.slice(range.start, range.end + 1) |
| 76 | + displayItems = data.slice(range.start, range.end + 1) |
62 | 77 | } |
63 | 78 |
|
64 | 79 | function onScroll(event) { |
|
75 | 90 | emitEvent(offset, clientSize, scrollSize, event) |
76 | 91 | } |
77 | 92 |
|
78 | | - function getOffset() { |
| 93 | + export function getOffset() { |
79 | 94 | if (pageMode) { |
80 | 95 | return document.documentElement[directionKey] || document.body[directionKey] |
81 | 96 | } else { |
82 | 97 | return root ? Math.ceil(root[directionKey]) : 0 |
83 | 98 | } |
84 | 99 | } |
85 | 100 |
|
86 | | - function getClientSize() { |
| 101 | + export function getClientSize() { |
87 | 102 | const key = isHorizontal ? "clientWidth" : "clientHeight" |
88 | 103 | if (pageMode) { |
89 | 104 | return document.documentElement[key] || document.body[key] |
|
92 | 107 | } |
93 | 108 | } |
94 | 109 |
|
95 | | - function getScrollSize() { |
| 110 | + export function getScrollSize() { |
96 | 111 | const key = isHorizontal ? "scrollWidth" : "scrollHeight" |
97 | 112 | if (pageMode) { |
98 | 113 | return document.documentElement[key] || document.body[key] |
|
101 | 116 | } |
102 | 117 | } |
103 | 118 |
|
104 | | - function updatePageModeFront() { |
| 119 | + export function updatePageModeFront() { |
105 | 120 | if (root) { |
106 | 121 | const rect = root.getBoundingClientRect() |
107 | 122 | const {defaultView} = root.ownerDocument |
|
113 | 128 | function emitEvent(offset, clientSize, scrollSize, event) { |
114 | 129 | dispatch("scroll", {event, range: virtual.getRange()}) |
115 | 130 |
|
116 | | - if (virtual.isFront() && !!dataSources.length && (offset - topThreshold <= 0)) { |
117 | | - dispatch("totop") |
| 131 | + if (virtual.isFront() && !!data.length && (offset - topThreshold <= 0)) { |
| 132 | + dispatch("top") |
118 | 133 | } else if (virtual.isBehind() && (offset + clientSize + bottomThreshold >= scrollSize)) { |
119 | | - dispatch("tobottom") |
| 134 | + dispatch("bottom") |
120 | 135 | } |
121 | 136 | } |
122 | 137 |
|
123 | | - function scrollToOffset(offset) { |
| 138 | + export function scrollToOffset(offset) { |
124 | 139 | if (pageMode) { |
125 | 140 | document.body[directionKey] = offset |
126 | 141 | document.documentElement[directionKey] = offset |
|
129 | 144 | } |
130 | 145 | } |
131 | 146 |
|
132 | | - function scrollToIndex(index) { |
| 147 | + export function scrollToIndex(index) { |
133 | 148 | // scroll to bottom |
134 | | - if (index >= dataSources.length - 1) { |
| 149 | + if (index >= data.length - 1) { |
135 | 150 | scrollToBottom() |
136 | 151 | } else { |
137 | 152 | const offset = virtual.getOffset(index) |
|
158 | 173 | } |
159 | 174 | } |
160 | 175 |
|
161 | | - $: handleDataSourcesChange(dataSources) |
| 176 | + $: scrollToOffset(offset) |
| 177 | + $: scrollToIndex(start) |
| 178 | + $: { |
| 179 | + virtual.updateParam("keeps", keeps) |
| 180 | + virtual.handleSlotSizeChange() |
| 181 | + } |
| 182 | + $: handleDataSourcesChange(data) |
162 | 183 |
|
163 | 184 | async function handleDataSourcesChange(dataSources) { |
164 | 185 | // TODO: fix jump on top added data |
|
170 | 191 | </script> |
171 | 192 |
|
172 | 193 | <div bind:this={root} on:scroll={onScroll} style="overflow-y: auto; height: inherit"> |
| 194 | + {#if $$slots.header} |
| 195 | + <Item on:resize={onItemResized} type="slot" uniqueKey="header"> |
| 196 | + <slot name="header"/> |
| 197 | + </Item> |
| 198 | + {/if} |
173 | 199 | <div style="padding: {paddingStyle}"> |
174 | 200 | {#each displayItems as data} |
175 | | - <Item on:resize={(e) => onItemResized(e.detail)} uniqueKey={data[dataKey]} horizontal={isHorizontal}> |
| 201 | + <Item |
| 202 | + on:resize={(e) => onItemResized(e.detail)} |
| 203 | + uniqueKey={data[key]} |
| 204 | + horizontal={isHorizontal} |
| 205 | + type="item"> |
176 | 206 | <slot {data}/> |
177 | 207 | </Item> |
178 | 208 | {/each} |
179 | 209 | </div> |
| 210 | + {#if $$slots.footer} |
| 211 | + <Item on:resize={onItemResized} type="slot" uniqueKey="footer"> |
| 212 | + <slot name="footer"/> |
| 213 | + </Item> |
| 214 | + {/if} |
180 | 215 | <div bind:this={shepherd} class="shepherd" |
181 | 216 | style="width: {isHorizontal ? '0px' : '100%'};height: {isHorizontal ? '100%' : '0px'}"></div> |
182 | 217 | </div> |
0 commit comments