Intro
So I noticed this post about make a React Modal so i thought I would post my take on a Svelte Modal but with more features and more reusable.
So this for this Modal you can display it and change text and title by just setting the values of a store.
You will also be able to close the modal by clicking outside the modal-box, pressing Escape key or pressing the OK-button.
This is a small variation of a component I created for my submission to the redis hackathon where it was used to display error messages.
Step by step
Start with open a terminal/console
Go to the directory you want the project
Run
npm create vite@latest
Type a project name, in my case svelte-modal
Choose svelte as framework
Choose regular svelte
As instruction says, got to directory and run
npm install
I am lazy and don't like writing a lot of CSS so for this I will use picocss
So now run
npm install @picocss/pico
Start up the dev server with
npm run dev
You should now see this in the browser.
Let's get to code!
Go to the App.svelte file in the src folder. It contains the boilerplate for the page you just open in the browser.
Let's delete it all and replace with:
<script>
</script>
<main>
<button>Show modal</button>
</main>
Let's go to app.css
and just delete all the contents as it's not needed.
Now, lets import pico.css
goto main.js
and add this
import '@picocss/pico/css/pico.min.css';
in the top of the file.
Should now look like this:
import '@picocss/pico/css/pico.min.css';
import './app.css';
import App from './App.svelte';
const app = new App({
target: document.getElementById('app'),
});
export default app;
This modal will make use of a Svelte store. This store will hold the modals message, title and open/close state. By using a store means we will be able to trigger the showing of the modal from both Svelte components and regular Javascript code.
Let's create a new folder named stores
inside the src/lib
folder. Create a new file in the stores
folder named index.js
Put below code in there. If you want can set a default title and message to anything you want.
import { writable } from 'svelte/store';
export const modal = writable({ open: false, title: 'Default title', message: 'Default message' });
Store is now done and ready to be used.
Let's create the actual modal component. I usually create a components folder but for this example we will just put the file in the 'src/lib' folder.
Create a file named Modal.svelte
We put below code in the file, I will explain what it does a bit later.
<script>
import { modal } from '../lib/stores/';
const close = () => ($modal.open = false);
const handle_keydown = (e) => {
if (e.key === 'Escape') return close();
};
</script>
<svelte:window on:keydown|once={handle_keydown} />
<dialog open on:click|self|preventDefault={close}>
<article>
<h3>{$modal.title}</h3>
<p>
{$modal.message}
</p>
<footer>
<button on:click|once={close}>OK</button>
</footer>
</article>
</dialog>
Let's go back to App.svelte
and wire things up so can try things out.
<script>
import Modal from './lib/Modal.svelte';
import { modal } from './lib/stores/';
</script>
<main>
{#if $modal.open == true}
<Modal />
{/if}
<button on:click={() => ($modal.open = true)}>Show modal</button>
</main>
We now got this:
So we import the Modal component and the modal store
We put the Modal component inside a Svelte-if. $modal references the store, so we only show the component if $modal.open
is true.
We now have a modal that will be closed if click outside the modal, if press escape button on keyboard or if pressing the modal ok-button. It can be displayed from anywhere just importing the store and set open to true and in the same way you can set the title and message to display different things, for example error messages.
Now let's dig into the code that makes it work. Seeing to the functionality it provides there is a surprisingly small amount of code.
Here is the Modal.svelte
component again
<script>
import { modal } from '../lib/stores/';
const close = () => ($modal.open = false);
const handle_keydown = (e) => {
if (e.key === 'Escape') return close();
};
</script>
<svelte:window on:keydown|once={handle_keydown} />
<dialog open on:click|self|preventDefault|once={close}>
<article>
<h3>{$modal.title}</h3>
<p>
{$modal.message}
</p>
<footer>
<button on:click|once={close}>OK</button>
</footer>
</article>
</dialog>
Import the store so can use it.
import { modal } from '../lib/stores/';
Function that will close the modal by setting the store.
const close = () => ($modal.open = false);
Handler for keydown that will be shown a bit further down, only call the close() function when escape is pressed.
const handle_keydown = (e) => {
if (e.key === 'Escape') return close();
};
In Svelte we can use the special svelte:window tag here we use it to bind an eventlistener for keydown to the window object, same as you would be able to do in regular javascript.
It uses the special once
DOM modifier. By using once
the listener will delete itself after running once, it can be useful in many situations
<svelte:window on:keydown|once={handle_keydown} />
In pico.css modals are easily created with element with an element inside. These are not special, just regular html-elemnts.
But let's analyze the below code. There is a on:click
listener bound to the <dialog>
element, so the semi-transparent area outside the modal-box. And it runs the close() function making the dialog close.
<dialog open on:click|self|preventDefault|once={close}>
By using self
the on:click will only fire if we explicit click anywhere on the <dialog>
element, i.e. the background. Without this the on:click would also fire if clicked anywhere on the modal-box. Handy feature :)
preventDefault
should not be needed but I put it there cause i wanted to show it. It prevents default behavior and is the same as using the preventDefault() in an eventhandler, this can be handy if you for example want to define an inline function for the on:click handler, like:
on:submit|preventDefault={(e)=>do something simple}
Svelte is filled with smart stuff like this, check the documentation if your curios about other DOM modifiers.
Hope you learned something. This was tiring to write, I will stick to writing devlogs. ;)
Top comments (0)