Skip to content

Commit b6ee761

Browse files
committed
support lazy loading components
1 parent 4b038b1 commit b6ee761

File tree

10 files changed

+192
-113
lines changed

10 files changed

+192
-113
lines changed

.eslintrc.cjs

+22-19
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
module.exports = {
2-
root: true,
3-
parser: '@typescript-eslint/parser',
4-
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
5-
plugins: ['svelte3', '@typescript-eslint'],
6-
ignorePatterns: ['*.cjs'],
7-
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
8-
settings: {
9-
'svelte3/typescript': () => require('typescript')
10-
},
11-
parserOptions: {
12-
sourceType: 'module',
13-
ecmaVersion: 2019
14-
},
15-
env: {
16-
browser: true,
17-
es2017: true,
18-
node: true
19-
}
20-
};
2+
root: true,
3+
parser: '@typescript-eslint/parser',
4+
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
5+
plugins: ['svelte3', '@typescript-eslint'],
6+
ignorePatterns: ['*.cjs'],
7+
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
8+
settings: {
9+
'svelte3/typescript': () => require('typescript')
10+
},
11+
rules: {
12+
'@typescript-eslint/no-explicit-any': 'off'
13+
},
14+
parserOptions: {
15+
sourceType: 'module',
16+
ecmaVersion: 2019
17+
},
18+
env: {
19+
browser: true,
20+
es2017: true,
21+
node: true
22+
}
23+
}

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "svelte-modals",
3-
"version": "1.0.4",
3+
"version": "1.1.0-beta.1",
44
"svelte": "index.js",
55
"scripts": {
66
"dev": "svelte-kit dev",
@@ -12,6 +12,7 @@
1212
"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
1313
"format": "prettier --write --plugin-search-dir=. .",
1414
"release": "npm run package && npm publish ./package --access public",
15+
"release:next": "npm run package && npm publish ./package --access public --tag next",
1516
"prepare": "ts-patch install -s"
1617
},
1718
"devDependencies": {

src/lib/Modals.svelte

+39-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
<script>
22
import { modals, exitBeforeEnter, transitioning } from './store'
3+
4+
function isLazyModal(component) {
5+
return typeof component.prototype === 'undefined'
6+
}
7+
8+
async function getComponent(component) {
9+
return component().then((res) => res.default)
10+
}
311
</script>
412

513
{#if $modals.length > 0}
@@ -8,16 +16,36 @@
816

917
<slot>
1018
{#each $modals as modal, i (i)}
11-
<svelte:component
12-
this={modal.component}
13-
isOpen={i === $modals.length - 1 && !$transitioning}
14-
on:introstart={() => {
15-
$exitBeforeEnter = true
16-
}}
17-
on:outroend={() => {
18-
$transitioning = false
19-
}}
20-
{...modal.props || {}}
21-
/>
19+
<!-- lazy modal -->
20+
{#if isLazyModal(modal.component)}
21+
{#await getComponent(modal.component)}
22+
<slot name="loading" />
23+
{:then component}
24+
<svelte:component
25+
this={component}
26+
isOpen={i === $modals.length - 1 && !$transitioning}
27+
{...modal.props}
28+
on:introstart={() => {
29+
$exitBeforeEnter = true
30+
}}
31+
on:outroend={() => {
32+
$transitioning = false
33+
}}
34+
/>
35+
{/await}
36+
{:else}
37+
<!-- normal modal -->
38+
<svelte:component
39+
this={modal.component}
40+
isOpen={i === $modals.length - 1 && !$transitioning}
41+
{...modal.props}
42+
on:introstart={() => {
43+
$exitBeforeEnter = true
44+
}}
45+
on:outroend={() => {
46+
$transitioning = false
47+
}}
48+
/>
49+
{/if}
2250
{/each}
2351
</slot>

src/lib/store.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { SvelteComponent, SvelteComponentTyped } from 'svelte'
1+
import type { SvelteComponentTyped } from 'svelte'
22

33
import { get, writable } from 'svelte/store'
44

@@ -12,9 +12,12 @@ export const transitioning = writable(null)
1212
/**
1313
* A Svelte store containing the current modal stack
1414
*/
15-
export const modals = writable<
16-
Array<{ component: new (...args: any) => SvelteComponent; props?: unknown }>
17-
>([])
15+
export const modals = writable<StoredModal[]>([])
16+
17+
interface StoredModal {
18+
component: SvelteModalComponent<any> | LazySvelteModalComponent<any>
19+
props?: Record<string, unknown>
20+
}
1821

1922
/**
2023
* A Svelte store describing how the current modal came to be active ("push" or "pop").
@@ -59,7 +62,7 @@ export function closeModal(): void {
5962
* Opens a new modal
6063
*/
6164
export function openModal<T>(
62-
component: new (...args: any) => SvelteComponentTyped<T>,
65+
component: SvelteModalComponent<T> | Array<SvelteModalComponent<T>>,
6366
props?: Omit<T, 'isOpen'>,
6467
options?: {
6568
/**
@@ -80,12 +83,17 @@ export function openModal<T>(
8083
exitBeforeEnter.set(false)
8184

8285
if (options?.replace) {
83-
modals.update((prev) => [...prev.slice(0, prev.length - 1), { component, props }])
86+
modals.update(
87+
(prev) => [...prev.slice(0, prev.length - 1), { component, props }] as StoredModal[]
88+
)
8489
} else {
85-
modals.update((prev) => [...prev, { component, props }])
90+
modals.update((prev) => [...prev, { component, props }] as StoredModal[])
8691
}
8792
}
8893

8994
function pop(amount = 1) {
9095
modals.update((prev) => prev.slice(0, Math.max(0, prev.length - amount)))
9196
}
97+
98+
export type SvelteModalComponent<T> = new (...args: any) => SvelteComponentTyped<T>
99+
export type LazySvelteModalComponent<T> = () => Promise<{ default: SvelteModalComponent<T> }>

src/routes/_components/Sidebar.svelte

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
href: '/transitions',
2323
label: 'Transitions'
2424
},
25+
{
26+
href: '/lazy-loading',
27+
label: 'Lazy Loading'
28+
},
2529
{
2630
href: '/examples',
2731
label: 'Examples'

src/routes/api.md

+33
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,39 @@ Renders when any modals are open. The slot is empty by default.
4141
</style>
4242
```
4343

44+
#### `loading`
45+
46+
Rendered when the current modal is being lazy loaded (see [Lazy Loading](/lazy-loading)).
47+
48+
```svelte
49+
<script>
50+
import { Modals, closeModal } from 'svelte-modals'
51+
import Spinner from './Spinner.svelte'
52+
</script>
53+
54+
<Modals>
55+
<div
56+
slot="backdrop"
57+
class="backdrop"
58+
on:click={closeModal}
59+
/>
60+
<div slot="loading">
61+
<Spinner />
62+
</div>
63+
</Modals>
64+
65+
<style>
66+
.backdrop {
67+
position: fixed;
68+
top: 0;
69+
bottom: 0;
70+
right: 0;
71+
left: 0;
72+
background: rgba(0, 0, 0, 0.5);
73+
}
74+
</style>
75+
```
76+
4477
<br />
4578

4679
#### `default`

src/routes/examples/index.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Examples
22

3-
[Basic](https://svelte.dev/repl/2f2d171a9ea7458191904cb2feb5a54c?version=3.38.2)
3+
[Basic](https://svelte.dev/repl/2f2d171a9ea7458191904cb2feb5a54c)
44

5-
[Transitions](https://svelte.dev/repl/5f57abd90c4d4c8b89dd3d632e27dc3b?version=3.38.2)
5+
[Transitions](https://svelte.dev/repl/5f57abd90c4d4c8b89dd3d632e27dc3b)
66

7-
[Random Transitions](https://svelte.dev/repl/2e52b3c0c49b479a8f7be24dedce670a?version=3.38.2)
7+
[Random Transitions](https://svelte.dev/repl/2e52b3c0c49b479a8f7be24dedce670a)
8+
9+
[Async Modals](https://svelte.dev/repl/6c777499c64d412d82a86bd5351a5ca6)

src/routes/index.md

+3-37
Original file line numberDiff line numberDiff line change
@@ -18,46 +18,12 @@ A simple, flexible, zero-dependency modal manager for Svelte.
1818
npm install svelte-modals
1919
```
2020

21-
### SvelteKit / Vite
22-
23-
If you are using SvelteKit or Vite, you will need to add the following to your vite config.
24-
25-
For SvelteKit, add this to your `svelte.config.js` file:
26-
27-
```js
28-
const config = {
29-
kit: {
30-
// ...
31-
32-
// add this
33-
vite: {
34-
optimizeDeps: {
35-
exclude: ['svelte-modals']
36-
}
37-
}
38-
}
39-
}
40-
```
41-
42-
For regular Vite projects, add this to your `vite.config.js`
43-
44-
```js
45-
export default {
46-
// ...
47-
48-
// add this
49-
optimizeDeps: {
50-
exclude: ['svelte-modals']
51-
}
52-
}
53-
```
54-
55-
_This works around a current bug in Vite that will cause modals not to open. See [this issue for more information](https://github.com/sveltejs/vite-plugin-svelte/issues/124)._
56-
5721
### Usage
5822

5923
Add `Modals` somewhere in your app. This is where the modals will render.
6024

25+
(If you're using SvelteKit, `__layout.svelte` would be appropriate)
26+
6127
```svelte
6228
<script>
6329
import { Modals, closeModal } from 'svelte-modals'
@@ -89,7 +55,7 @@ Create your Modal component
8955
<script>
9056
import { closeModal } from 'svelte-modals'
9157
92-
// provided by Modals
58+
// provided by <Modals />
9359
export let isOpen
9460
9561
export let title

src/routes/lazy-loading/index.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<script>
2+
import { Modals, openModal, closeAllModals } from 'svelte-modals'
3+
4+
function handleAsync() {
5+
openModal(() => import('../_AlertModal.svelte'), {
6+
title: "Lazy Modal",
7+
message: "This modal was loaded lazily"
8+
})
9+
}
10+
11+
function handleAsyncSlow() {
12+
openModal(async () => {
13+
await new Promise(resolve => setTimeout(resolve, 1000))
14+
return import('../_AlertModal.svelte')
15+
}, {
16+
title: "Lazy Modal",
17+
message: "This modal was loaded lazily"
18+
})
19+
}
20+
</script>
21+
22+
# Lazy Loading
23+
24+
`openModal` supports lazy loading of your modal components using dynamic imports.
25+
26+
The backdrop will be shown while the component loading.
27+
28+
```js
29+
import { openModal } from 'svelte-modals'
30+
31+
openModal(() => import('./AlertModal.svelte'), {
32+
title: 'Lazy Modal',
33+
message: 'This modal was loaded lazily'
34+
})
35+
```
36+
37+
<button class="mt-6" on:click={handleAsync}>Open Modal</button>

0 commit comments

Comments
 (0)