Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add resize plugin: x-resize #4304

Merged
merged 2 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/[email protected]/dist/cdn.min.js"></script>

<!-- Alpine Core -->
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/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
Loading