Svelte Patterns

Passing the Midpoint of an Element around

Recently I needed to pass the midpoint height of a modal element around. The content inside the modal was larger than the element and I wanted to absolutely position a button on the outside of it.

I made use of Stores, bind:clientWidth/Height, and the style tag in this one.

// stores.js
export const modalSize = writable({ w: 0, h: 0 })

As you see the store is pretty simple.

<!-- Modal.svelte -->
<script>
  import { modalSize } from '../../../lib/api/stores'

  let trackSize = false
  let w, h

  onMount(() => {
    if (trackSize) {
      modalSize.update(
        (n) =>
          (n = {
            w,
            h,
          })
      )
    }
  })
</script>

<div
  class="fixed z-50 inset-24 overflow-y-auto"
  aria-labelledby="modal-title"
  role="dialog"
  aria-modal="true"
  bind:clientWidth={w}
  bind:clientHeight={h}
>
  | Modal Content here
</div>

This update could probably be shifted to a method from the store but I’m being lazy.

<!-- PhotoPreviews.svelte -->
<script>
  import { modalSize } from '../../../lib/api/stores'

  $: midpointHeight = $modalSize.h / 2
</script>

{#if preview}
  <Portal>
    <Modal {closeModal} maxWidth="screen-md" fullSize={true} trackSize={true}>
      <div class="flex relative">
        <button
          style="top: {midpointHeight}px"
          class="absolute -left-12"
          on:click={() => previewPhoto(previewIndex - 1)}
          type="button"
        >
          Left
        </button>
        <Photo handle={preview} imgClass="object-scale-down" />
        <button
          style="top: {midpointHeight}px"
          class="absolute -right-12"
          on:click={() => previewPhoto(previewIndex + 1)}
          type="button"
        >
          Right
        </button>
      </div>
    </Modal>
  </Portal>
{/if}

Here’s where the magic happens. Now I can link the buttons position to the Modal container. They’re outside of the normal flow and if I were to position them based on the document they wouldn’t be in the perceived middle.