Skip to content

Commit

Permalink
Add resize plugin: x-resize (#4304)
Browse files Browse the repository at this point in the history
* implementation

* Add docs
  • Loading branch information
calebporzio committed Jul 15, 2024
1 parent 09cac02 commit 10997a7
Show file tree
Hide file tree
Showing 15 changed files with 248 additions and 7 deletions.
2 changes: 1 addition & 1 deletion packages/docs/src/en/plugins/anchor.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 5
order: 7
title: Anchor
description: Anchor an element's positioning to another element on the page
graph_image: https://alpinejs.dev/social_anchor.jpg
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/plugins/collapse.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 4
order: 6
title: Collapse
description: Collapse and expand elements with robust animations
graph_image: https://alpinejs.dev/social_collapse.jpg
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/plugins/focus.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 3
order: 5
title: Focus
description: Easily manage focus within the page
graph_image: https://alpinejs.dev/social_focus.jpg
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/plugins/morph.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 6
order: 8
title: Morph
description: Morph an element into the provided HTML
graph_image: https://alpinejs.dev/social_morph.jpg
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/en/plugins/persist.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 2
order: 4
title: Persist
description: Easily persist data across page loads using localStorage
graph_image: https://alpinejs.dev/social_persist.jpg
Expand Down
99 changes: 99 additions & 0 deletions packages/docs/src/en/plugins/resize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
order: 3
title: Resize
description: An Alpine convenience wrapper for the Resize Observer API that allows you to easily react when an element is resized.
graph_image: https://alpinejs.dev/social_resize.jpg
---

# Resize Plugin

Alpine's Resize plugin is a convenience wrapper for the [Resize Observer](https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API) that allows you to easily react when an element changes size.

This is useful for: custom size-based animations, intelligent sticky positioning, conditionally adding attributes based on the element's size, etc.

<a name="installation"></a>
## Installation

You can use this plugin by either including it from a `<script>` tag or installing it via NPM:

### Via CDN

You can include the CDN build of this plugin as a `<script>` tag, just make sure to include it BEFORE Alpine's core JS file.

```alpine
<!-- Alpine Plugins -->
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/resize@3.x.x/dist/cdn.min.js"></script>
<!-- Alpine Core -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
```

### Via NPM

You can install Resize from NPM for use inside your bundle like so:

```shell
npm install @alpinejs/resize
```

Then initialize it from your bundle:

```js
import Alpine from 'alpinejs'
import resize from '@alpinejs/resize'

Alpine.plugin(resize)

...
```

<a name="x-resize"></a>
## x-resize

The primary API for using this plugin is `x-resize`. You can add `x-resize` to any element within an Alpine component, and when that element is resized for any reason, the provided expression will execute with two magic properties: `$width` and `$height`.

For example, here's a simple example of using `x-resize` do display the width and height of an element as it changes size.

```alpine
<div
x-data="{ width: 0, height: 0 }"
x-resize="width = $width; height = $height"
>
<p x-text="'Width: ' + width + 'px'"></p>
<p x-text="'Height: ' + height + 'px'"></p>
</div>
```

<!-- START_VERBATIM -->
<div class="demo">
<div x-data="{ width: 0, height: 0 }" x-resize="width = $width; height = $height">
<i>Resize your browser window to see the width and height values change.</i>
<br><br>
<p x-text="'Width: ' + width + 'px'"></p>
<p x-text="'Height: ' + height + 'px'"></p>
</div>
</div>
<!-- END_VERBATIM -->

<a name="modifiers"></a>
## Modifiers

<a name="document"></a>
### .document

It's often useful to observer the entire document's size, rather than a specific element. To do this, you can add the `.document` modifier to `x-resize`:

```alpine
<div x-resize.document="...">
```

<!-- START_VERBATIM -->
<div class="demo">
<div x-data="{ width: 0, height: 0 }" x-resize.document="width = $width; height = $height">
<i>Resize your browser window to see the document width and height values change.</i>
<br><br>
<p x-text="'Width: ' + width + 'px'"></p>
<p x-text="'Height: ' + height + 'px'"></p>
</div>
</div>
<!-- END_VERBATIM -->
2 changes: 1 addition & 1 deletion packages/docs/src/en/plugins/sort.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 6
order: 9
title: Sort
description: Easily re-order elements by dragging them with your mouse
graph_image: https://alpinejs.dev/social_sort.jpg
Expand Down
5 changes: 5 additions & 0 deletions packages/resize/builds/cdn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import resize from '../src/index.js'

document.addEventListener('alpine:init', () => {
window.Alpine.plugin(resize)
})
5 changes: 5 additions & 0 deletions packages/resize/builds/module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import resize from './../src/index.js'

export default resize

export { resize }
17 changes: 17 additions & 0 deletions packages/resize/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@alpinejs/resize",
"version": "3.14.1",
"description": "Trigger JavaScript when an element is resized on the page",
"homepage": "https://alpinejs.dev/plugins/intersect",
"repository": {
"type": "git",
"url": "https://github.com/alpinejs/alpine.git",
"directory": "packages/resize"
},
"author": "Caleb Porzio",
"license": "MIT",
"main": "dist/module.cjs.js",
"module": "dist/module.esm.js",
"unpkg": "dist/cdn.min.js",
"dependencies": {}
}
59 changes: 59 additions & 0 deletions packages/resize/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export default function (Alpine) {
Alpine.directive('resize', Alpine.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => {
let evaluator = evaluateLater(expression)

let evaluate = (width, height) => {
evaluator(() => {}, { scope: { '$width': width, '$height': height }})
}

let off = modifiers.includes('document')
? onDocumentResize(evaluate)
: onElResize(el, evaluate)

cleanup(() => off())
}))
}

function onElResize(el, callback) {
let observer = new ResizeObserver((entries) => {
let [width, height] = dimensions(entries)

callback(width, height)
})

observer.observe(el)

return () => observer.disconnect()
}

let documentResizeObserver
let documentResizeObserverCallbacks = new Set

function onDocumentResize(callback) {
documentResizeObserverCallbacks.add(callback)

if (! documentResizeObserver) {
documentResizeObserver = new ResizeObserver((entries) => {
let [width, height] = dimensions(entries)

documentResizeObserverCallbacks.forEach(i => i(width, height))
})

documentResizeObserver.observe(document.documentElement)
}

return () => {
documentResizeObserverCallbacks.delete(callback)
}
}

function dimensions(entries) {
let width, height

for (let entry of entries) {
width = entry.borderBoxSize[0].inlineSize
height = entry.borderBoxSize[0].blockSize
}

return [width, height]
}
3 changes: 2 additions & 1 deletion scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ let zlib = require('zlib');
// 'history', - removed because this plugin has been moved to livewire/livewire until it's stable...
// 'navigate', - remove because this plugin has been moved to livewire/livewire until it's stable...
'intersect',
'persist',
'collapse',
'persist',
'resize',
'anchor',
'morph',
'focus',
Expand Down
6 changes: 6 additions & 0 deletions scripts/release.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ function writeNewAlpineVersion() {
writeToPackageDotJson('intersect', 'version', version)
console.log('Bumping @alpinejs/intersect package.json: '+version)

writeToPackageDotJson('resize', 'version', version)
console.log('Bumping @alpinejs/resize package.json: '+version)

writeToPackageDotJson('persist', 'version', version)
console.log('Bumping @alpinejs/persist package.json: '+version)

Expand Down Expand Up @@ -92,6 +95,9 @@ function publish() {
console.log('Publishing @alpinejs/intersect on NPM...');
runFromPackage('intersect', 'npm publish --access public')

console.log('Publishing @alpinejs/resize on NPM...');
runFromPackage('resize', 'npm publish --access public')

console.log('Publishing @alpinejs/persist on NPM...');
runFromPackage('persist', 'npm publish --access public')

Expand Down
48 changes: 48 additions & 0 deletions tests/cypress/integration/plugins/resize.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { haveText, test, html, notHaveText } from '../../utils'

test('can react to the resizing of an element',
[html`
<div x-data="{ width: 0, height: 0 }">
<h1 x-text="width"></h1>
<h2 x-text="height"></h2>
<div x-ref="target" x-resize="width = $width; height = $height" style="width: 100px; height: 100px; background: red">
</div>
<button id="1" x-on:click="$refs.target.style.width = 50 + 'px'">resize width</button>
<button id="2" x-on:click="$refs.target.style.height = 50 + 'px'">resize height</button>
</div>
`],
({ get }) => {
get('h1').should(haveText('100'))
get('h2').should(haveText('100'))
get('button#1').click()
get('h1').should(haveText('50'))
get('h2').should(haveText('100'))
get('button#2').click()
get('h1').should(haveText('50'))
get('h2').should(haveText('50'))
},
)

test('can react to the resizing of the document',
[html`
<div x-data="{ width: 0, height: 0 }">
<h1 x-text="width"></h1>
<h2 x-text="height"></h2>
<div x-ref="target" x-resize.document="width = $width; height = $height" style="width: 100px; height: 100px; background: red">
</div>
`],
({ get }) => {
get('h1').should(notHaveText('0'))
get('h2').should(notHaveText('0'))
get('h1').should(notHaveText('100'))
get('h2').should(notHaveText('100'))

cy.viewport(550, 750)

get('h1').should(haveText('550'))
get('h2').should(haveText('750'))
},
)
1 change: 1 addition & 0 deletions tests/cypress/spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<script src="/../../packages/intersect/dist/cdn.js"></script>
<script src="/../../packages/collapse/dist/cdn.js"></script>
<script src="/../../packages/anchor/dist/cdn.js"></script>
<script src="/../../packages/resize/dist/cdn.js"></script>
<script src="/../../packages/mask/dist/cdn.js"></script>
<script src="/../../packages/sort/dist/cdn.js"></script>
<script src="/../../packages/ui/dist/cdn.js"></script>
Expand Down

0 comments on commit 10997a7

Please sign in to comment.