forked from kaisermann/svelte-loadable
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLoadable.svelte
159 lines (134 loc) · 3.47 KB
/
Loadable.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<script context="module">
export const ALL_LOADERS = new Map()
export const LOADED = new Map()
const STATES = Object.freeze({
INITIALIZED: 0,
LOADING: 1,
SUCCESS: 2,
ERROR: 3,
TIMEOUT: 4,
})
export function findByResolved(resolved) {
for (let [loader, r] of ALL_LOADERS) {
if (r === resolved) return loader
}
return null
}
export function register(loadable) {
const resolved = loadable.resolve()
const loader = findByResolved(resolved)
if (loader) return loader
ALL_LOADERS.set(loadable.loader, resolved)
return loadable.loader
}
export function preloadAll() {
return Promise.all(
Array.from(ALL_LOADERS.keys())
.filter(loader => !LOADED.has(loader))
.map(async loader => load(loader)),
).then(() => {
/** If new loaders have been registered by loaded components, load them next. */
if (ALL_LOADERS.size > LOADED.size) {
return preloadAll()
}
})
}
export async function load(loader) {
const componentModule = await loader()
const component = componentModule.default || componentModule
LOADED.set(loader, component)
return component
}
let loadComponent = load
</script>
<script>
import { onMount, getContext, createEventDispatcher } from 'svelte'
export let delay = 200
export let timeout = null
export let loader = null
export let unloader = false
export let component = null
export let error = null
let load_timer = null
let timeout_timer = null
let state = STATES.INITIALIZED
let componentProps
let slots = $$props.$$slots
$: {
let { delay, timeout, loader, component, error, ...rest } = $$props
componentProps = rest
}
const dispatch = createEventDispatcher()
const capture = getContext('svelte-loadable-capture')
if (typeof capture === 'function' && ALL_LOADERS.has(loader)) {
capture(loader)
}
function clearTimers() {
clearTimeout(load_timer)
clearTimeout(timeout_timer)
}
export async function load() {
clearTimers()
if (typeof loader !== 'function') {
return
}
error = null
component = null
if (delay > 0) {
state = STATES.INITIALIZED
load_timer = setTimeout(() => {
state = STATES.LOADING
}, parseFloat(delay))
} else {
state = STATES.LOADING
}
if (timeout) {
timeout_timer = setTimeout(() => {
state = STATES.TIMEOUT
}, parseFloat(timeout))
}
try {
component = await loadComponent(loader)
state = STATES.SUCCESS
} catch (e) {
state = STATES.ERROR
error = e
if (slots == null || slots.error == null) {
throw e
}
}
clearTimers()
}
if (LOADED.has(loader)) {
state = STATES.SUCCESS
component = LOADED.get(loader)
} else {
onMount(async () => {
await load()
dispatch('load')
if (unloader) {
return () => {
LOADED.delete(loader)
if (typeof unloader === 'function') {
unloader()
}
}
}
})
}
</script>
{#if state === STATES.ERROR}
<slot name="error" {error} />
{:else if state === STATES.TIMEOUT}
<slot name="timeout" />
{:else if state === STATES.LOADING}
<slot name="loading" />
{:else if state === STATES.SUCCESS}
{#if slots && slots.success}
<slot name="success" {component} />
{:else if slots && slots.default}
<slot {component} />
{:else}
<svelte:component this={component} {...componentProps} />
{/if}
{/if}