alpinejs / alpine

A rugged, minimal framework for composing JavaScript behavior in your markup.
https://alpinejs.dev
MIT License
27.92k stars 1.22k forks source link

Add resize plugin: `x-resize` #4304

Closed calebporzio closed 1 month ago

calebporzio commented 1 month ago

This PR adds a new Alpine plugin called "Resize" with the following API:

CleanShot 2024-07-15 at 12 22 37@2x

ekwoka commented 1 month ago

Damn, I just made this plugin myself a few days ago.

I think you can safely use a similar strategy for the event delegation on the element as well with a weakmap.

The code I made

import type { PluginCallback } from 'alpinejs'

export const Resize: PluginCallback = (Alpine) => {
  const observerMap = new WeakMap<Element, (width: number) => void>()
  const observer = new ResizeObserver((entries) =>
    entries.forEach((entry) =>
      observerMap.get(entry.target)?.(entry.contentRect.width),
    ),
  )
  Alpine.directive(
    'resize',
    (el, { expression }, { evaluateLater, cleanup }) => {
      let evaluate = evaluateLater(expression)
      observerMap.set(el, (width: number) =>
        // biome-ignore lint/suspicious/noEmptyBlockStatements: Does not need to do anything
        evaluate(() => {}, { scope: { $width: width }, params: [width] }),
      )
      observer.observe(el)
      cleanup(() => {
        observerMap.delete(el)
        observer.unobserve(el)
      })
    },
  )
}

export default Resize

Seems like the contentRect is a better choice than the border box.

Would the border box sizes and height stay consistent if the element is in a flex column?