Let's build a Svelte fullscreen component
Tags: svelte
Published: 2020-01-04 08:48:57

Sometimes you have the need to go fullscreen, especially when building some fun internal dashboards. Because, you know, they are fun to build. Useful too. So why not build one in Svelte for a change? Let's see how easy it is to slap together a fullscreen component with this awesome framework.

Altough, it might not look like much, but you will learn about:

- Svelte's slots
- Exposing sub component's properties to parent
- Adding stylesheets to document's head at runtime
- Using the `onMount` callback
- Svelte's reactivity
- And maybe even some CSS
 
## App.svelte

Replace your App.svelte with the code below. We are importing a fullscreen component that we haven't defined yet. No worries, let the compiler whine for a few minutes. It can wait.


```html
<!-- App.svelte -->

<script>
  import Fullscreen from "./Fullscreen.svelte";
</script>

<style>
  h2 {
    text-align: center;
    font-size: 2rem;
  }
</style>

<Fullscreen let:isFull>
  <div>
    <img
      src="https://media.giphy.com/media/vCKC987OpQAco/giphy.gif"
      alt="Yes you are!" />
    <h2>
      {#if isFull}I am now in fullscreen!{/if}
    </h2>
  </div>
</Fullscreen>

```
If you look at the code you can see that we passing a div as a child component to our fullscreen component. Our fullscreen component is also exposing a property called `isFull` to the parent. We can use it to make decisions if we want to hide something when in fullscreen mode and other useful things. Let's keep things basic.

## Fullscreen.svelte

Here is our fullscreen component. Take a quick look and let's go through it below.

```html
<!-- Fullscreen.svelte -->

<script>
  import { onMount } from "svelte";

  // define initial component state
  let isFull = false;
  let fsContainer = null;

  // boring plain js fullscreen support stuff below
  const noop = () => {};

  const fullscreenSupport = !!(
    document.fullscreenEnabled ||
    document.webkitFullscreenEnabled ||
    document.mozFullScreenEnabled ||
    document.msFullscreenEnabled ||
    false
  );

  const exitFullscreen = (
    document.exitFullscreen ||
    document.mozCancelFullScreen ||
    document.webkitExitFullscreen ||
    document.msExitFullscreen ||
    noop
  ).bind(document);

  const requestFullscreen = () => {
    const requestFS = (
      fsContainer.requestFullscreen ||
      fsContainer.mozRequestFullScreen ||
      fsContainer.webkitRequestFullscreen ||
      fsContainer.msRequestFullscreen ||
      noop
    ).bind(fsContainer);
    requestFS();
  };

  onMount(() => {
    // Add the icon stylesheet dynamically
    const link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = "https://fonts.googleapis.com/icon?family=Material+Icons";
    document.head.appendChild(link);

    // remove the link when component is unmounted
    return () => {
      link.parentNode.removeChild(link);
    };
  });

  // handler for the fullscreen button
  const fsToggle = () => {
    if (!fullscreenSupport) return;

    if (isFull) {
      exitFullscreen();
    } else {
      requestFullscreen(fsContainer);
    }
    isFull = !isFull;
  };

  // the icon name is computed automagically based
  // on the state of the screen
  $: icon = isFull ? "fullscreen_exit" : "fullscreen";
</script>

<style>
  .isFull {
    width: 100vw;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #fff;
  }
  button {
    color: #000;
    position: absolute;
    right: 20px;
    bottom: 20px;
  }
</style>

<div class="fs" class:isFull bind:this={fsContainer}>
  <slot {isFull} />
  {#if fullscreenSupport}
    <button on:click={fsToggle}>
      <i class="material-icons md-36">{icon}</i>
    </button>
  {/if}
</div>

```
At the top we are defining two state variables. `isFull` is a boolean that indicates if component is in fullscreen mode. This is also a variable that we'are exposing in the slot `<slot {isFull} />`, so our parent component can use it if it needs. We are also binding the DOM node of the fullscreen div to the `fsContainer` variable that we use in our code to trigger fullscreen state.


```html
<div class="fs" class:isFull bind:this={fsContainer}>
```
In the code snippet above we are also using Svelte's nifty way of setting a CSS class on the component depending on the variable state. You can see that we defined a `.isFull` class in the style tag. There is also a way to use a pseudo CSS class `:fullscreen` when then component is in fullscreen mode, but for some reason I couldn't get it to work with Svelte's CSS scoping.

## Conclusion

As you see, it's really easy to build custom components in Svelte because it's just vanilla JS with some sugar sprinkled on top. Sometimes it's easier to just copy and paste code and tweak it to your own desire, than to install a package from NPM.