Giter VIP home page Giter VIP logo

svelte-simple-modal's Introduction

svelte-simple-modal

A simple, small, and content-agnostic modal for Svelte.


NPM Version Build Status File Size Code Style Prettier Demo

simple-modal

Live demo: https://svelte.dev/repl/033e824fad0a4e34907666e7196caec4?version=3.20.1

Works with: Svelte >=v3.30 and v4!

Table of Contents

Install

npm install --save svelte-simple-modal

Rollup Setup

Make sure that the main application's version of svelte is used for bundling by setting rollup-plugin-node-resolve's dedupe option as follows:

import resolve from 'rollup-plugin-node-resolve';

export default {
  plugins: [
    resolve({
      dedupe: ['svelte', 'svelte/transition', 'svelte/internal'], // important!
    }),
  ],
};

Usage

Import the Modal component into your main app component (e.g., App.svelte).

The modal is exposing two context functions:

  • open() opens a component as a modal.
  • close() simply closes the modal.
<!-- App.svelte -->
<script>
  import Content from './Content.svelte';
  import Modal from 'svelte-simple-modal';
</script>

<Modal><Content /></Modal>


<!-- Content.svelte -->
<script>
  import { getContext } from 'svelte';
  import Popup from './Popup.svelte';
  const { open } = getContext('simple-modal');
  const showSurprise = () => open(Popup, { message: "It's a modal!" });
</script>

<p><button on:click={showSurprise}>Show me a surprise!</button></p>


<!-- Popup.svelte -->
<script>
  export let message = 'Hi';
</script>

<p>๐ŸŽ‰ {message} ๐Ÿพ</p>

Demo: https://svelte.dev/repl/52e0ade6d42546d8892faf8528c70d30

Usage with TypeScript

You can use the Context type exported by the package to validate the getContext function:

import type { Context } from 'svelte-simple-modal';
const { open } = getContext<Context>('simple-modal');
// ...

Usage With a Svelte Store

Alternatively, you can use a Svelte store to show/hide a component as a modal.

<!-- App.svelte -->
<script>
  import { writable } from 'svelte/store';
  import Modal, { bind } from 'svelte-simple-modal';
  import Popup from './Popup.svelte';
  const modal = writable(null);
  const showModal = () => modal.set(bind(Popup, { message: "It's a modal!" }));
</script>

<Modal show={$modal}>
  <button on:click={showModal}>Show modal</button>
</Modal>

Demo: https://svelte.dev/repl/aec0c7d9f5084e7daa64f6d0c8ef0209

The <Popup /> component is the same as in the example above.

To hide the modal programmatically, simply unset the store. E.g., modal.set(null).

Styling

The modal comes pre-styled for convenience but you can easily extent or replace the styling using either custom CSS classes or explicit CSS styles.

Custom CSS classes can be applied via the classBg, classWindow, classWindowWrap, classContent, and classCloseButton properties. For instance, you could customize the styling with TailwindCSS as follows:

<Modal
  show={$modal}
  unstyled={true}
  classBg="fixed top-0 left-0 w-screen h-screen flex flex-col justify-center bg-orange-100/[.9]"
  classWindowWrap="relative m-2 max-h-full"
  classWindow="relative w-40 max-w-full max-h-full my-2 mx-auto text-orange-200 rounded shadow-md bg-indigo-900"
  classContent="relative p-2 overflow-auto"
  closeButton={false}
>
  <button on:click={showModal}>Show modal</button>
</Modal>

Demo: https://svelte.dev/repl/f2a988ddbd5644f18d7cd4a4a8277993

Note: to take full control over the modal styles with CSS classes you have to reset existing styles via unstyled={true} as internal CSS classes are always applied last due to Svelte's class scoping.

Alternatively, you can also apply CSS styles directly via the styleBg, styleWindow, styleWindowWrap, styleContent, and styleCloseButton properties. For instance:

<Modal
  show={$modal}
  styleBg={{ backgroundColor: 'rgba(255, 255, 255, 0.85)' }}
  styleWindow={{ boxShadow: '0 2px 5px 0 rgba(0, 0, 0, 0.15)' }}
>
  <button on:click={showModal}>Show modal</button>
</Modal>

Demo: https://svelte.dev/repl/50df1c694b3243c1bd524b27f86eec51

Server-Side Rendering

With SvelteKit you can enable SSR using the browser environmental variable as follows:

<script>
  import Modal from 'svelte-simple-modal';
  import { browser } from '$app/env';
</script>

{#if browser}
  <Modal>
    <Content />
  </Modal>
{/if}

Alternatively, you can enable SSR by dynamically importing svelte-simple-modal in onMount() as follows:

import { onMount } from 'svelte';

onMount(async () => {
  const svelteSimpleModal = await import('svelte-simple-modal');
  Modal = svelteSimpleModal.default;
});

Accessibility

The library applies the following WAI-ARIA guidelines for modal dialogs automtically:

  • aria-modal="true" and role="dialog" are applied automatically
  • The tab focus is trapped in the modal
  • The modal is closed on pressing the esc key

To further improve the accessibility you'll have to either provide a label via ariaLabel or reference a title element via ariaLabelledBy. The ariaLabel is automatically omitted when ariaLabelledBy is specified.

API

Component API

The <Modal /> component accepts the following optional properties:

Property Type Default Description
show Component | null null A Svelte component to show as the modal. See Store API for details.
id string | null null Element ID to be assigned to the modal's root DOM element.
ariaLabel string | null null Accessibility label of the modal. See W3C WAI-ARIA for details.
ariaLabelledBy string | null null Element ID holding the accessibility label of the modal. See W3C WAI-ARIA for details.
closeButton Component | boolean true If true a button for closing the modal is rendered. You can also pass in a custom Svelte component to have full control over the styling.
closeOnEsc boolean true If true the modal will close when pressing the escape key.
closeOnOuterClick boolean true If true the modal will close when clicking outside the modal window.
transitionBg function svelte.fade Transition function for the background.
transitionBgProps BlurParams | FadeParams | FlyParams | SlideParams {} Properties of the transition function for the background.
transitionWindow function svelte.fade Transition function for the window.
transitionWindowProps BlurParams | FadeParams | FlyParams | SlideParams {} Properties of the transition function for the window.
classBg string | null null Class name for the background element.
classWindowWrap string | null null Class name for the modal window wrapper element.
classWindow string | null null Class name for the modal window element.
classContent string | null null Class name for the modal content element.
classCloseButton string | null null Class name for the built-in close button.
styleBg Record<string, string | number> {} Style properties of the background.
styleWindowWrap Record<string, string | number> {} Style properties of the modal window wrapper element.
styleWindow Record<string, string | number> {} Style properties of the modal window.
styleContent Record<string, string | number> {} Style properties of the modal content.
styleCloseButton Record<string, string | number> {} Style properties of the built-in close button.
unstyled boolean false When true, the default styles are not applied to the modal elements.
disableFocusTrap boolean false If true elements outside the modal can be in focus. This can be useful in certain edge cases.
isTabbable (node: Element): boolean internal function A function to determine if an HTML element is tabbable.
key string "simple-modal" The context key that is used to expose open() and close(). Adjust to avoid clashes with other contexts.
setContext function svelte.setContent You can normally ingore this property when you have configured Rollup properly. If you want to bundle simple-modal with its own version of Svelte you have to pass setContext() from your main app to simple-modal using this parameter.

Component Events

The <Modal /> component dispatches the following events:

  • open: dispatched when the modal window starts to open.
  • opened: dispatched when the modal window opened.
  • close: dispatched when the modal window starts to close.
  • closed: dispatched when the modal window closed.

Alternatively, you can listen to those events via callbacks passed to open() and close().

Context API

Svelte Simple Modal uses Svelte's context API to expose the open() and close() methods. You can get these methods as follows:

const { open, close } = getContext('simple-modal');

# open(Component, props = {}, options = {}, callbacks = {})

Opens the modal with <Component {props}> rendered as the content. options can be used to adjust the modal behavior once for the modal that is about to be opened. The options allows to customize all parameters except key and setContext:

{
  closeButton: false,
  closeOnEsc: false,
  closeOnOuterClick: false,
  transitionBg: fade,
  transitionBgProps: {
    duration: 5000
  },
  transitionWindow: fly,
  transitionWindowProps: {
    y: 100,
    duration: 250
  },
  styleBg: { backgroundImage: 'http://example.com/my-background.jpg' },
  styleWindow: { fontSize: '20em' },
  styleContent: { color: 'yellow' },
  styleCloseButton: { width: '3rem', height: '3rem' },
  disableFocusTrap: true
}

# close(callbacks = {})

Closes the modal. Similar to open(), this method supports adding callbacks for the closing transition:

{
  onClose: () => { /* modal window starts to close */ },
  onClosed: () => { /* modal window closed */ },
}

Callbacks are triggered at the beginning and end of the opening and closing transition. The following callbacks are supported:

{
  onOpen: () => { /* modal window starts to open */ },
  onOpened: () => { /* modal window opened */ },
  onClose: () => { /* modal window starts to close */ },
  onClosed: () => { /* modal window closed */ },
}

Store API

You can also use Svelte stores to open a modal using the <Modal />'s show property as follows:

<!-- App.svelte -->
<script>
  import { writable } from 'svelte/store';
  import Modal from 'svelte-simple-modal';
  import Popup from './Popup.svelte';
  const modal = writable(null);
  const showModal = () => modal.set(Popup);
</script>

<Modal show={$modal}>
  <button on:click={showModal}>Show modal</button>
</Modal>

<!-- Popup.svelte -->
<p>๐ŸŽ‰ Hi ๐Ÿพ</p>

Demo: https://svelte.dev/repl/6f55b643195646408e780ceeb779ac2a

An added benefit of using stores is that the component opening the modal does not have to be a parent of <Modal />. For instance, in the example above, App.svelte is toggling Popup.svelte as a modal even though App.svelte is not a child of <Modal />.

Bind Props to a Component Shown as a Modal

Sometimes you want to pass properties to the component shown as a modal. To accomplish this, you can use our bind() helper function as follows:

<script>
  import { bind } from 'svelte-simple-modal';
  import Popup from './Popup.svelte';
  import { modal } from './App.svelte';

  modal.set(bind(Popup, { name: 'custom name' }));
</script>

If you've worked with React/JSX then think of const c = bind(Component, props) as the equivalent of const c = <Component ...props />.

FAQ

Custom Close Button

This feature requires Svelte >=v3.19!

Unfortunately, it's not possible to adjust all styles of the built-in close button via the styleCloseButton option. If you need full control you can implement your own Svelte component and use that as the close button. To do so pass your component via the closeButton option to <Modal /> or open().

For example, your close button might look as follows:

<!-- CloseButton.svelte -->
<script>
  // This property is used by Modal.svelte to pass down the close function
  export let onClose;
</script>

<button on:click={onClose}>Custom Close Button</button>

<style>
  /* Customize to your liking */
  button {
    position: absolute;
    top: -3rem;
    right: 0;
  }
</style>

Now you can set it as the default close button by passing it to <Modal /> as follows:

<!-- App.svelte -->
<script>
  import Content from './Content.svelte';
  import CloseButton from './CloseButton.svelte';
  import Modal from 'svelte-simple-modal';
</script>

<Modal closeButton={CloseButton}>
  <Content />
</Modal>

Or you can pass CloseButton to open() as shown below:

<!-- Content.svelte -->
<script>
  import { getContext } from 'svelte';
  import Surprise from './Surprise.svelte';
  import CloseButton from './CloseButton.svelte';

  const { open } = getContext('simple-modal');

  const showSurprise = () => {
    open(Surprise, { message: "It's a modal!" }, { closeButton: CloseButton });
  };
</script>

<p><button on:click={showSurprise}>Show me a surprise!</button></p>

License

MIT

svelte-simple-modal's People

Contributors

ajmeese7 avatar andrew-mc avatar daniel17903 avatar daphnechiu avatar dependabot[bot] avatar flekschas avatar fluffydiscord avatar glench avatar idchef avatar leodog896 avatar leovoon avatar orcuntuna avatar truongvan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

svelte-simple-modal's Issues

Conflict with "const fs = require('fs');"

Hi There

Sorry to bother you again. I know now why the modal wouldn't work for me previously.

I realize that it stops working as soon as I add const fs = require('fs'); inside the script tags.

The problem is I need to read and parse a local file for my app to function the way I need.

As soon as I remove const fs = require('fs'); , the modal works as normal.

Maybe you want to look into it.

Alternative to context API?

Hi, is it possible to expose an alternative to the context API (a store or prop or similar) ?

The reason is - as I understand it the context API forces a structure like this
<Modal> <ModalWrapper> <ModalContent> </ModalWrapper> </Modal>

So if you want to invoke the component from App.svelte, the context is undefined. So App.svelte can never directly open the Modal - if my understanding is correct.

This seems restrictive just to obtain a reference - but I'm no Svelte expert so I could be wrong.

Thanks for your work on this

Can't get it to work in sapper

Hi there.

I just can't get the popup to appear. Here's what I've done in SAPPER

  • npm install -D svelte-simple-modal

  • created and copy pasted the Content.svelte and Surprise.svelte in the components folder, same as in the README

  • in routes/index.svelte,

<script>
import Content from '../components/Content.svelte';
import Modal from 'svelte-simple-modal';
</script>
<Modal>
  <Content />
</Modal>
  • and finally set the dedupe option of rollup-plugin-node-resolve

The button is there but when I click it, the popup doesn't show up.

Do you think I missed something in my configuration ? I also tried with moving Content.svelte and Surprise.svelte in the routes folder, with no success.

Thanks in advance if you can help me on this

Type definition for `bind` seems incorrect

image

Following the readme's example, I tried using the bind function, however I get this warning. Further digging seems to lead me to believe that the source of problem comes from the type definition for the bind function to be incorrect.

Cloudflare Adapter Exception

__layout.svelte-7b3f1eda.js:formatted:751 Uncaught (in promise) TypeError: Cannot destructure property 'open' of 'C(...)' as it is undefined.
    at ns (__layout.svelte-7b3f1eda.js:formatted:751)
    at Pt (vendor-f4e5e978.js:1)
    at new is (__layout.svelte-7b3f1eda.js:formatted:778)
    at Array.ds (__layout.svelte-7b3f1eda.js:formatted:806)
    at vs (__layout.svelte-7b3f1eda.js:formatted:1144)
    at Pt (vendor-f4e5e978.js:1)
    at new default (__layout.svelte-7b3f1eda.js:formatted:1460)
    at M (start-a2f5bb3a.js:1)
    at Pt (vendor-f4e5e978.js:1)
    at new z (start-a2f5bb3a.js:1)

Hello, it appears the library does not run correctly when using the cloudflare-worker adapter. Unfortunately, svelte-simple-modal does not properly register its context in this scenario, and as a result attempting to use getContext('simple-modal') throws an exception. Any help would be much appreciated.

'closeButton: false' not working when not using context API

Is it possible to apply styling to the window, or even setting closeButton: false for that matter.

This is my code:

modal.set(
     bind(WarningModal, {
                title: "Heres a title",
                message: "Heres a message",
            }, {
                closeButton: false
            }
        )
    )

This code will show a modal with the props passed, but does not remove the closeButton or apply any windowStyling.

Is this feature only available to those using the contextAPI?

Warning

(!) Plugin svelte: will not be reactive if Component changes. Use <svelte:component this={Component}/> if you want this reactivity.
node_modules\svelte-simple-modal\src\Modal.svelte
217: {/if}
218:


219: <Component {...props}/>
^
220:

221:

Is it safe to ignore?

Modal Target

Hi ,
how to pass component to Modal without doing it in html tags i wanna set different target separately
example of what am doing

----------------- this will set target to both components at once ------------------------
new ModalCompo({
target: Target,
props: {
Name: Name,
},
});

------------ModalCompo Script------------

<script> import Modal from "svelte-simple-modal"; import Popup from "./Content.svelte"; export let Name = ""; </script>

what i want to do ====>

const popupmeesage = new Popup({
target: Target,
props: {
Name: Name,
},
});

const modal = new Modal({
target: document.body,
content: popupmeesage
});


Thank you

Missing type definition for bind

The typescript definition is missing for the bind function that can be imported using import { bind } from 'svelte-simple-modal';

This worked in: 1.0.3
Throws error in: 1.1.1

Error:

Module '"svelte-simple-modal"' has no exported member 'bind'. Did you mean to use 'import bind from "svelte-simple-modal"' instead?

I can't say exactly the version that is started to fail with but it did work with 1.0.3.

Workaround: downgrade to 1.0.3

Kb size different on bundlephobia

Any idea why the kb size in the repo is different from the size shown on bundlephobia ?

Do you know which is more accurate? Does it change significantly after Svelte's build step too?

Error when using with Webpack

I have the following error:

TypeError: Cannot destructure property 'open' of '(0 , svelte__WEBPACK_IMPORTED_MODULE_1__.getContext)(...)' as it is undefined.

My use case is as such:

--- App.svelte ---

<script lang="ts">
   import {getContext, onMount} from "svelte";
    const {open} = getContext('simple-modal');
    import Popup from "src/components/Popup.svelte";
    import Modal from 'svelte-simple-modal';

    const showSurprise = () => open(Popup, {message: "It's a modal!"});
</script>

<Modal>
    <div>App Content</div>
</Modal>

Cannot destructure property 'open' of 'getContext(...)' as it is undefined

I am using sapper and I've installed this library using npm i -D svelte-simple-modal. I'm following the tutorial in README.md but I'm stuck at this part of the code:

const { open } = getContext("simple-modal");

It's throwing an Internal Server Error (500):

Cannot destructure property 'open' of 'getContext(...)' as it is undefined.

TypeError: Cannot destructure property 'open' of 'getContext(...)' as it is undefined.
    at D:\GitHub\pejabatku-frontend\src\routes\documents\[id].svelte:47:14
    at Object.$$render (D:\GitHub\pejabatku-frontend\node_modules\svelte\internal\index.mjs:1341:23)
    at Object.default (D:\GitHub\pejabatku-frontend\src\node_modules\@sapper\internal\App.svelte:24:50)
    at D:\GitHub\pejabatku-frontend\__sapper__\dev\server\server-c9687f2a.js:761:34
    at Object.$$render (D:\GitHub\pejabatku-frontend\node_modules\svelte\internal\index.mjs:1341:23)
    at D:\GitHub\pejabatku-frontend\src\node_modules\@sapper\internal\App.svelte:20:18
    at $$render (D:\GitHub\pejabatku-frontend\node_modules\svelte\internal\index.mjs:1341:23)
    at Object.render (D:\GitHub\pejabatku-frontend\node_modules\svelte\internal\index.mjs:1349:33)
    at D:\GitHub\pejabatku-frontend\src\node_modules\@sapper\server.mjs:4687:54
    at Generator.next (<anonymous>)

where pejabatku-frontend is my project name.

Sapper version: 0.28.0
Svelte version: 3.29.4

Can I get a pointer on how to fix it?

Improper packaging

The latest version of svelte-kit warns that simple modal is improperly packaged some one might look into this.

image

Use with Sveltekit navigation: potential issue (or confusion)

Elegant & useful component, thanks!

If I use it as a navigation dialog, I come up against a few issues (or confusion, I'm not sure).

If I use the modal in a __layout file, the modal never closes with a route change. Perhaps this is correct behaviour on the part of Sveltekit for components within a __layout. If so, does anyone know how I might destroy the component on route change? Can SvK's { navigating } from '$app/stores' be used to close() it?

Kapture.2021-12-10.at.22.18.20.mp4

To mitigate this, I can use the modal in a component that wraps every page (which is what the __layout is really for, but I guess I get the component destroyed for free each page change). However, when I do this, the page never scrolls back to the window top (as SvK is supposed to do, noted here). Again, this might be a SvK thing, but it is directly related to the working of this Modal.

Kapture.2021-12-10.at.22.19.07.mp4

Accessibility: keyboard focus improvements

For modals, the WAI-ARIA Authoring Practices recommend:

  1. When the modal is opened, focus is moved to an element inside the modal (e.g. the close button)
  2. When the modal is closed, focus is returned to the element that triggered the modal
  3. Tabbing when the modal is open only focuses elements within the modal (focus trapping)

With the existing demo, when I open one of the modals I have to tab back through the page to reach the close button, which would be confusing for those who primarily navigate using the keyboard. Users of assistive technology also wouldn't know what the button did and that there's new content to interact with.

Implementing these recommendations would make the modal much more accessible to keyboard users.

Safari iPhone chin covering the bottom

Hello ๐Ÿ‘‹

When the modal has a long content the bottom is covered by the new iOS Safari chin (now iPhones display the URL at the bottom).

There is two pictures, one is a regular one, and the second is when you scroll down a little, the chin desapears, but a weird block still covers the bottom only when you open the modal.

I don't really now if this is a Safari bug or not, I just wanted to leave it recorded.

IMG_1277
IMG_1278

Cannot focus select with tabulation using a select library

Hello, I am trying to use this modal library along with another library, Svelte-Select.

When I use a Select in the opened modal, I'm not able to get focus on the select. I also cannot continue to tabulate through other inputs afterward.

I created a REPL with my exact versions:
https://svelte.dev/repl/7423b7a7c40c4e0e90cf40c919739d59?version=3.12.1

In the modal, click on the first input, and try to access others using only tabulation.

Any thoughts on what could be the cause of this issue ?

Usage in SvelteKit

Hey, thanks for maintaining this package!

I got it running in SvelteKit and thought I might save others some time by posting my solution here.

There is no need to import the package with onMount but use the browser variable instead:

routes/index.svelte

<script>
  import Modal from 'svelte-simple-modal';
  import { browser } from '$app/env';
</script>

{#if browser}
<Modal>
  <Content />
</Modal>
{/if}

Content.svelte and Surprise.svelte can remain as stated in the documentation.

But because I want Content.svelte to be rendered by SSR, I have a slightly modified version of your non-getContext suggestion:

routes/index.svelte

<script>
  import { writable } from 'svelte/store';
  import { browser } from '$app/env';
  import Modal from 'svelte-simple-modal';
  import Popup from '$lib/Modal/Popup.svelte';
  const modal = writable(null);

  function showModal() {
    modal.set(Popup);
  }
</script>

<Content on:showSurprise={showModal} />
{#if browser}
<Modal show={$modal}/>
{/if}

Content.svelte

<script>
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();
  function showSurprise() {
    dispatch('showSurprise');
  }
</script>

<button on:click={() => showSurprise()}>

This alllows me to dispatch showSurprise without knowledge about the modal details.

All the best and thanks again.

100vh height gets cut off on android browsers

The modal is too tall on android browsers and gets cut off on the bottom toolbar. Restyling the window and bg from 100vh to 100% units solved the issue for me. Perhaps that should be the default behavior?

Modal as photo gallery

Hello, I am trying to use svelte-simple-modal in order to create a photo gallery type page. Clicking on a particular thumbnail would render a larger version of that photo within the modal. Thumbnails are in regular HTML.

Should this be possible (without creating a separate component for each photograph), by passing the img src attribute into the Modal contents dynamically and rendering a larger version of the photograph in HTML?. Any pointers welcome.

PS. I can achieve this via pure Javascript, but I am currently learning Svelte + components.

Can't override closeButton

Hello! I've tried overriding closeButton on a project I'm working. In the debugger, I see it get passed in, but the default close button is still shown. I created a repo (forked from https://github.com/mvolkmann/svelte-ssr) to illustrate the issue; looks like neither client nor server code is working properly (and also not sure why there'd be a difference to begin with). You can see the issue here: https://github.com/bllfrnch/svelte-simple-modal-close-button

Follow instructions for building on client or server, and note the modal that's displayed on clicking the button still shows the default close button.

Thanks for all your hard work!

Modal goes above (not on top of) everything in production build

Hi ! Firstly, thank you for your work, this library is a lifesaver.

I happened to come across a weird issue where the opened modal goes above the rest of the page (instead of on top of it).
That happens ONLY after npm run build (and not npm run dev)

Here's what happens in production (modal framed in red) along with the relevant code :

image

image

image

Here's what's expected (this is with npm run dev):

image

I honestly don't know why this happens.

For additional infos, I did setup my rollup config as recommended in the README.md, and here's my package.json file.

{
    "name": "friday",
    "version": "0.1.0",
    "private": true,
    "scripts": {
        "build": "rollup -c",
        "dev": "rollup -c -w",
        "start": "sirv public --no-clear"
    },
    "devDependencies": {
        "@fullcalendar/daygrid": "^5.10.1",
        "@fullcalendar/rrule": "^5.10.1",
        "@fullhuman/postcss-purgecss": "^4.0.3",
        "@rollup/plugin-commonjs": "^17.0.0",
        "@rollup/plugin-node-resolve": "^11.0.0",
        "@rollup/plugin-replace": "^3.0.1",
        "postcss": "^8.3.11",
        "postcss-load-config": "^3.1.0",
        "rollup": "^2.3.4",
        "rollup-plugin-css-only": "^3.1.0",
        "rollup-plugin-livereload": "^2.0.0",
        "rollup-plugin-svelte": "^7.0.0",
        "rollup-plugin-terser": "^7.0.0",
        "svelte": "^3.0.0",
        "svelte-fullcalendar": "^1.1.1",
        "svelte-preprocess": "^4.9.8",
        "svelte-simple-modal": "^1.2.0",
        "svelte-spa-router": "^3.2.0",
        "tailwindcss": "^2.2.19",
        "tippy.js": "^6.3.7",
        "@fullcalendar/list": "^5.10.1"
    },
    "dependencies": {
        "build": "^0.1.4",
        "sirv-cli": "^1.0.0"
    }
}

And rollup config:

import svelte from 'rollup-plugin-svelte';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload';
import {terser} from 'rollup-plugin-terser';
import css from 'rollup-plugin-css-only';
import sveltePreprocess from 'svelte-preprocess';
import replace from '@rollup/plugin-replace';

const production = !process.env.ROLLUP_WATCH;

const onwarn = (warning, onwarn) =>
    (warning.code === 'CIRCULAR_DEPENDENCY' && /[/\\]rrule[/\\]/.test(warning.message)) ||
    onwarn(warning);

function serve() {
    let server;

    function toExit() {
        if (server) server.kill(0);
    }

    return {
        writeBundle() {
            if (server) return;
            server = require('child_process').spawn(
                'npm',
                ['run', 'start', '--', '--dev'],
                {
                    stdio: ['ignore', 'inherit', 'inherit'],
                    shell: true,
                }
            );

            process.on('SIGTERM', toExit);
            process.on('exit', toExit);
        },
    };
}

export default {
    input: 'src/main.js',
    output: {
        sourcemap: true,
        format: 'iife',
        name: 'app',
        file: 'public/build/bundle.js',
    },
    plugins: [
        svelte({
            compilerOptions: {
                // enable run-time checks when not in production
                dev: !production,
            },
            preprocess: sveltePreprocess({
                postcss: true,
            }),
        }),

        replace({
            'process.env.NODE_ENV': JSON.stringify(production ? 'production' : 'development'),
            'process.env.GMAP_KEY': JSON.stringify('AIzaSyDxdrOS-gFaZsIeUm5L0rcN4aLHdJPBaP8'),
            preventAssignment: true,
        }),

        // we'll extract any component CSS out into
        // a separate file - better for performance
        css({output: 'bundle.css'}),

        // If you have external dependencies installed from
        // npm, you'll most likely need these plugins. In
        // some cases you'll need additional configuration -
        // consult the documentation for details:
        // https://github.com/rollup/plugins/tree/master/packages/commonjs
        resolve({
            browser: true,
            dedupe: ['svelte', 'svelte/transition', 'svelte/internal'],
        }),
        commonjs(),

        // In dev mode, call `npm run start` once
        // the bundle has been generated
        !production && serve(),

        // Watch the `public` directory and refresh the
        // browser on changes when not in production
        !production && livereload('public'),

        // If we're building for production (npm run build
        // instead of npm run dev), minify
        production && terser(),
    ],
    watch: {
        clearScreen: false,
    },
    onwarn,
};

Thank you in advance

Sapper throws 500 document is not defined

Hi, I am trying to use this component in a sapper project. But it throws the following error:

500
document is not defined

ReferenceError: document is not defined
    at enableScroll (D:\PROJECTS\repos\AriseWebsite\node_modules\svelte-simple-modal\src\Modal.svelte:184:12)
    at close (D:\PROJECTS\repos\AriseWebsite\node_modules\svelte-simple-modal\src\Modal.svelte:132:16)
    at D:\PROJECTS\repos\AriseWebsite\node_modules\svelte-simple-modal\src\Modal.svelte:196:11
    at Object.$$render (D:\PROJECTS\repos\AriseWebsite\node_modules\svelte\internal\index.mjs:1366:23)
    at D:\PROJECTS\repos\AriseWebsite\__sapper__\dev\server\server.js:2246:38
    at Object.$$render (D:\PROJECTS\repos\AriseWebsite\node_modules\svelte\internal\index.mjs:1366:23)
    at Object.default (D:\PROJECTS\repos\AriseWebsite\src\node_modules\@sapper\internal\App.svelte:24:50)
    at D:\PROJECTS\repos\AriseWebsite\__sapper__\dev\server\server.js:2475:66
    at Object.$$render (D:\PROJECTS\repos\AriseWebsite\node_modules\svelte\internal\index.mjs:1366:23)
    at D:\PROJECTS\repos\AriseWebsite\src\node_modules\@sapper\internal\App.svelte:20:18

Make not only CSS attributes be changeable from the API but also CSS classes

When using styling libraries like e.g. TailwindCSS you rather want to assign a css class like bg-thm-primary to an element like the page overlay in the background of the modal instead of setting the rgb value itself.

Example:
instead of

open(
    ConfirmationModal,
    {},
    {
        styleBg: {
            background: "rgba(98, 69, 180, 0.4)",
        },
        styleWindow: {
            borderRadius: "1rem",
        },
    }
);

I'd like to write

open(
    ConfirmationModal,
    {},
    {
        styleBg: "bg-thm-primary",
        styleWindow: "rounded-xl",
    }
);

Custom close button doesn't work

It can not get the custom close button to work.
The reason seems to be, that isSvelteComponent is not true for my component.
Given the following code

import CloseButton from './CloseButton.svelte'
console.log(CloseButton)

my local svelte version prints Proxy<CloseButton>, whereas the version in the REPL says CloseButton.

Do you have an idea of how to resolve this issue?

Inconsistency between even name and callback handler prop

Thanks to #33 I found out about an inconsistency between the event fired when the modal starts to open and the callback props for assigning a one-time callback function in open(Component, props, options, callback)

The dispatched event is called opening and closing while the callback props are called onOpen and onClose.

For consistency, both should have the same name.

I tend to rename opening and closing to open and close (that's documented already also)

Using props in Sveltekit

I am having trouble using props in Sveltekit

modal.set(bind(Popup, { name: 'custom name' })); is not working.
I get error Uncaught TypeError: class constructors must be invoked with 'new'.

It works with the code below:

`const modal = writable(null);

function showModal() {
    modal.set(Popup);
}`

Error close button send me to new page

if i create new object from modal the close button will not work as expected instead it open new page !
Note : closeOnOuterClick work perfectly ! aren't close button and click outer have same close function !
----------------Main Script----------------
new ModalCompo({
target: Target,
props: {
Name: Name,
},
});

----------------ModalCompo Script----------------

<script> import Modal from "svelte-simple-modal"; import Popup from "./Content.svelte"; export let Name = ""; </script>

Undesirable scroll on page below the modal

First of all thank you very much for this well designed Modal component. I've been able to use it very easily to implement the โ€œroute as modalโ€ pattern that I have found much more difficult to work with using React librairies.

When a modal is opened the page below the modal keeps the โ€œscroll focusโ€ (on both desktop using the mouse or on mobile). This is undesirable espacilly if there are some elements to interact with within the modal (a slider in my case).

Overriding styles of the window

Good evening,

When attempting to use svelte-simple-modal with my project, I tired to use the styleWindow option to specify a width for the modal. Unfortunately it appears that styles in styleWindow which overwrite the default styling are not applied. Perhaps this is my ignorance on how to properly use the library.

Thanks in advance,
Drew.

Using nested components

I'm encountering a TypeError: outros is undefined when using a nested component.

App.svelte

...
<Modal>
    <Delivery />
<Modal />

Delivery.svelte

...
const { open } = getContext('simple-modal');

function showCardPickerModal() {
    open(CardPicker);
}

CardPicker.svelte

<script>
import Card from './Card.svelte
<script />

<Card />

Card.svelte

<h1>Some text</h1>

The modal opens, however when using the close button or clicking the background the error appears in the browser console window. All works as expected when I don't use a nested component within the CardPicker component. Is this a limitation of Svelte or svelte-simple-modal?

Using latest Svelte and svelte-simple-modal version.

A11y Attributes

Is there interest for adopting a11y strategies to make this smoother with a screen reader? Or were there already considerations for this I've missed? Perhaps adopting some of the strategies used in MicroModal (repo).

I'd be happy to contribute to this if you're interested @flekschas.

Problem understanding how to use the component

Hi,

I've been looking at your example, and the REPL (https://svelte.dev/repl/033e824fad0a4e34907666e7196caec4?version=3.20.1), but something is not clear for me in the structure <Modal><Content /></Modal>. At first, I thought that <Content /> would simply be the content of the modal, but it seems that <Modal> is a wrapper for the whole app.

For instance, in the REPL, it seems very weird to me that the links to open the various exemples ("popup", "popup with long text", etc.), and the logic to control them, are all located within this top-level <Modal>.

Sorry if I don't sound very clear, but I don't know where to start to put a modal in my existing project. I would expect to add the <Modal> component beside the rest of my elements/components, not around them.

Allow using the modal exclusively within an element other than the HTML body

Currently, svelte-simple-modal assume that it's used globally. I.e., it overlays the body element. However, sometimes it can be useful to just use it within another (scrollable) container.

To support this we could traverse the DOM tree and find the first parent that is scrollable using for instance:

const isScrollable = (element) =>
  element.scrollHeight > element.clientHeight && window.getComputedStyle(element).overflowY.indexOf('hidden') === -1;

const getScrollableParent = (element) =>
  !element || element === document.body
    ? document.body
    : (isScrollable(element) ? element : getScrollableParent(element.parentNode));

Sapper returns 500 error

I'm currently using this component on a production build, and it returns a 500 page for a few seconds with the current log:

Error: <Modal> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules
    at validate_component (/home/phr3nzy/syodigital/sapper-frontend/__sapper__/dev/server/server.js:173:15)
    at /home/phr3nzy/syodigital/sapper-frontend/__sapper__/dev/server/server.js:241:5
    at Object.$$render (/home/phr3nzy/syodigital/sapper-frontend/__sapper__/dev/server/server.js:191:22)
    at /home/phr3nzy/syodigital/sapper-frontend/__sapper__/dev/server/server.js:395:36
    at Object.$$render (/home/phr3nzy/syodigital/sapper-frontend/__sapper__/dev/server/server.js:191:22)
    at Object.default (/home/phr3nzy/syodigital/sapper-frontend/__sapper__/dev/server/server.js:1283:86)
    at /home/phr3nzy/syodigital/sapper-frontend/__sapper__/dev/server/server.js:1108:37
    at Object.$$render (/home/phr3nzy/syodigital/sapper-frontend/__sapper__/dev/server/server.js:191:22)
    at /home/phr3nzy/syodigital/sapper-frontend/__sapper__/dev/server/server.js:1279:40
    at $$render (/home/phr3nzy/syodigital/sapper-frontend/__sapper__/dev/server/server.js:191:22)

It just shows it at the beginning of the page render and disappears, but when I build using sapper export it displays that the index.html page returns a 500 error.

It works perfectly, but that error is inconvenient to users coming to the site. Thanks

PS: I am using the dedup fix. Same result.

Resolve solution for Webpack

Thanks so much for the library. It saved me a lot of time.
For some reasons I have to use Webpack, not Rollup. And I have the problem of duplicates while building.
Would you know what would be a solution for Webpack instead of rollup-plugin-node-resolve?
Thanks

Is the wrapping div necessary?

This is a great component, but wrapping the entire app in extra divs messes up my css layout. Is there any reason it's needed? I deleted the div for my app, and it seems to work fine.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.