@solid-primitives/resize-observer
Advanced tools
Comparing version 1.1.0 to 2.0.0
@@ -1,18 +0,96 @@ | ||
declare type ResizeHandler = (size: { | ||
import { Accessor } from 'solid-js'; | ||
import { MaybeAccessor, Many } from '@solid-primitives/utils'; | ||
declare type ResizeHandler = (rect: DOMRectReadOnly, element: Element, entry: ResizeObserverEntry) => void; | ||
/** | ||
* Instantiate a new ResizeObserver that automatically get's disposed on cleanup. | ||
* | ||
* @param callback handler called once element size changes | ||
* @param options ResizeObserver options | ||
* @returns `observe` and `unobserve` functions | ||
*/ | ||
declare function makeResizeObserver<T extends Element>(callback: ResizeObserverCallback, options?: ResizeObserverOptions): { | ||
observe: (ref: T) => void; | ||
unobserve: (ref: T) => void; | ||
}; | ||
/** | ||
* Create resize observer instance, listening for changes to size of the reactive {@link targets} array. | ||
* | ||
* @param targets Elements to be observed. Can be a reactive signal or store top-level array. | ||
* @param onResize - Function handler to trigger on element resize | ||
* | ||
* @example | ||
* ```tsx | ||
* let ref | ||
* createResizeObserver(() => ref, ({ width, height }, el) => { | ||
* if (el === ref) console.log(width, height) | ||
* }); | ||
* <div ref={ref}/> | ||
* ``` | ||
*/ | ||
declare function createResizeObserver(targets: MaybeAccessor<Many<Element>>, onResize: ResizeHandler, options?: ResizeObserverOptions): void; | ||
/** | ||
* @returns object with width and height dimensions of window, page and screen. | ||
*/ | ||
declare function getWindowSize(): { | ||
width: number; | ||
height: number; | ||
}, ref: Element) => void; | ||
}; | ||
/** | ||
* Create resize observer is a helper primitive for binding resize events. | ||
* Creates a reactive store-like object of current width and height dimensions of window, page and screen. | ||
* @example | ||
* const size = createWindowSize(); | ||
* createEffect(() => { | ||
* console.log(size.width, size.height) | ||
* }) | ||
*/ | ||
declare function createWindowSize(): { | ||
readonly width: number; | ||
readonly height: number; | ||
}; | ||
/** | ||
* Returns a reactive store-like object of current width and height dimensions of window, page and screen. | ||
* | ||
* @param opts.refs - Either an `Element`, an array of `Element`s, or a signal returning one of these. | ||
* @param opts.onResize - Function handler to trigger on resize | ||
* @return A callback that can be used to add refs to observe resizing | ||
* This is a [shared root](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSharedRoot) primitive. | ||
* | ||
* @example | ||
* const size = useWindowSize(); | ||
* createEffect(() => { | ||
* console.log(size.width, size.height) | ||
* }) | ||
*/ | ||
declare function createResizeObserver<T extends Element>(opts: { | ||
onResize: ResizeHandler; | ||
refs?: T | T[] | (() => T | T[]); | ||
}): (arg: T) => void; | ||
declare const useWindowSize: typeof createWindowSize; | ||
/** | ||
* @param target html element | ||
* @returns object with width and height dimensions of provided {@link target} element. | ||
*/ | ||
declare function getElementSize(target: Element | false | undefined | null): { | ||
width: number; | ||
height: number; | ||
} | { | ||
width: null; | ||
height: null; | ||
}; | ||
/** | ||
* Creates a reactive store-like object of current width and height dimensions of {@link target} element. | ||
* @param target html element to track the size of. Can be a reactive signal. | ||
* @returns `{ width: number, height: number }` | ||
* @example | ||
* const size = createElementSize(document.body); | ||
* createEffect(() => { | ||
* console.log(size.width, size.height) | ||
* }) | ||
*/ | ||
declare function createElementSize(target: MaybeAccessor<Element>): { | ||
readonly width: number; | ||
readonly height: number; | ||
}; | ||
declare function createElementSize(target: Accessor<Element | false | undefined | null>): { | ||
readonly width: number; | ||
readonly height: number; | ||
} | { | ||
readonly width: null; | ||
readonly height: null; | ||
}; | ||
export { createResizeObserver as default }; | ||
export { ResizeHandler, createElementSize, createResizeObserver, createWindowSize, getElementSize, getWindowSize, makeResizeObserver, useWindowSize }; |
// src/index.ts | ||
import { createEffect, createSignal, onCleanup } from "solid-js"; | ||
function createResizeObserver(opts) { | ||
const [otherRefs, setOtherRefs] = createSignal([]); | ||
const refCallback = (e) => setOtherRefs((l) => l.concat(e)); | ||
const previousMap = /* @__PURE__ */ new Map(); | ||
const resizeObserver = new ResizeObserver((entries) => { | ||
if (!Array.isArray(entries)) { | ||
return; | ||
} | ||
import { createEffect, onCleanup, on, $PROXY, $TRACK, onMount } from "solid-js"; | ||
import { | ||
asArray, | ||
handleDiffArray, | ||
createStaticStore, | ||
access | ||
} from "@solid-primitives/utils"; | ||
import { createSharedRoot } from "@solid-primitives/rootless"; | ||
import { makeEventListener } from "@solid-primitives/event-listener"; | ||
function makeResizeObserver(callback, options) { | ||
const resizeObserver = new ResizeObserver(callback); | ||
onCleanup(resizeObserver.disconnect.bind(resizeObserver)); | ||
return { | ||
observe: (ref) => resizeObserver.observe(ref, options), | ||
unobserve: resizeObserver.unobserve.bind(resizeObserver) | ||
}; | ||
} | ||
function createResizeObserver(targets, onResize, options) { | ||
const previousMap = /* @__PURE__ */ new WeakMap(); | ||
const { observe, unobserve } = makeResizeObserver(handleObserverCallback, options); | ||
function handleObserverCallback(entries) { | ||
for (const entry of entries) { | ||
const newWidth = Math.round(entry.contentRect.width); | ||
const newHeight = Math.round(entry.contentRect.height); | ||
const previous = previousMap.get(entry.target); | ||
if (!previous || previous.width !== newWidth || previous.height !== newHeight) { | ||
const newSize = { width: newWidth, height: newHeight }; | ||
opts.onResize(newSize, entry.target); | ||
previousMap.set(entry.target, { width: newWidth, height: newHeight }); | ||
const { contentRect, target } = entry; | ||
const width = Math.round(contentRect.width); | ||
const height = Math.round(contentRect.height); | ||
const previous = previousMap.get(target); | ||
if (!previous || previous.width !== width || previous.height !== height) { | ||
onResize(contentRect, entry.target, entry); | ||
previousMap.set(target, { width, height }); | ||
} | ||
} | ||
}); | ||
createEffect((oldRefs) => { | ||
let refs = []; | ||
if (opts.refs) { | ||
const optsRefs = typeof opts.refs === "function" ? opts.refs() : opts.refs; | ||
if (Array.isArray(optsRefs)) | ||
refs = refs.concat(optsRefs); | ||
else | ||
refs.push(optsRefs); | ||
} | ||
refs = refs.concat(otherRefs()); | ||
oldRefs = oldRefs || []; | ||
oldRefs.forEach((oldRef) => { | ||
if (!(oldRef in refs)) { | ||
resizeObserver.unobserve(oldRef); | ||
previousMap.delete(oldRef); | ||
} | ||
}); | ||
refs.forEach((ref) => { | ||
if (!(ref in oldRefs)) { | ||
resizeObserver.observe(ref); | ||
} | ||
}); | ||
return refs; | ||
}); | ||
onCleanup(() => resizeObserver.disconnect()); | ||
return refCallback; | ||
} | ||
let refs; | ||
if (typeof targets === "function") | ||
refs = () => asArray(targets()).slice(); | ||
else if (Array.isArray(targets) && $PROXY in targets) | ||
refs = () => { | ||
targets[$TRACK]; | ||
return targets.slice(); | ||
}; | ||
else { | ||
asArray(targets).forEach(observe); | ||
return; | ||
} | ||
createEffect(on(refs, (current, prev = []) => handleDiffArray(current, prev, observe, unobserve))); | ||
} | ||
var src_default = createResizeObserver; | ||
function getWindowSize() { | ||
return { | ||
width: window.innerWidth, | ||
height: window.innerHeight | ||
}; | ||
} | ||
function createWindowSize() { | ||
const [size, setSize] = createStaticStore(getWindowSize()); | ||
const updateSize = () => setSize(getWindowSize()); | ||
makeEventListener(window, "resize", updateSize); | ||
return size; | ||
} | ||
var useWindowSize = createSharedRoot(createWindowSize); | ||
function getElementSize(target) { | ||
if (!target) | ||
return { | ||
width: null, | ||
height: null | ||
}; | ||
const { width, height } = target.getBoundingClientRect(); | ||
return { width, height }; | ||
} | ||
function createElementSize(target) { | ||
const [size, setSize] = createStaticStore(getElementSize(access(target))); | ||
if (typeof target === "function") | ||
onMount(() => setSize(getElementSize(target()))); | ||
const updateSize = (e) => setSize({ width: e.width, height: e.height }); | ||
createResizeObserver(typeof target === "function" ? () => target() || [] : target, updateSize); | ||
return size; | ||
} | ||
export { | ||
src_default as default | ||
createElementSize, | ||
createResizeObserver, | ||
createWindowSize, | ||
getElementSize, | ||
getWindowSize, | ||
makeResizeObserver, | ||
useWindowSize | ||
}; |
// src/server.ts | ||
var createResizeObserver = (opts) => { | ||
return () => { | ||
}; | ||
}; | ||
var server_default = createResizeObserver; | ||
import { noop } from "@solid-primitives/utils"; | ||
var makeResizeObserver = () => ({ | ||
observe: noop, | ||
unobserve: noop | ||
}); | ||
var createResizeObserver = noop; | ||
var getWindowSize = () => ({ | ||
height: 0, | ||
width: 0 | ||
}); | ||
var createWindowSize = getWindowSize; | ||
var useWindowSize = getWindowSize; | ||
var getElementSize = () => ({ | ||
height: 0, | ||
width: 0 | ||
}); | ||
var createElementSize = () => ({ | ||
height: 0, | ||
width: 0 | ||
}); | ||
export { | ||
server_default as default | ||
createElementSize, | ||
createResizeObserver, | ||
createWindowSize, | ||
getElementSize, | ||
getWindowSize, | ||
makeResizeObserver, | ||
useWindowSize | ||
}; |
{ | ||
"name": "@solid-primitives/resize-observer", | ||
"version": "1.1.0", | ||
"description": "Primitive to observer browser resizes", | ||
"version": "2.0.0", | ||
"description": "Reactive primitives for observing resizing of HTML elements.", | ||
"author": "Moshe Udimar", | ||
"contributors": [ | ||
"Damian Tarnawski <gthetarnav@gmail.com>" | ||
], | ||
"license": "MIT", | ||
@@ -16,3 +19,5 @@ "homepage": "https://github.com/solidjs-community/solid-primitives/tree/main/packages/resize-observer", | ||
"list": [ | ||
"createResizeObserver" | ||
"createResizeObserver", | ||
"createWindowSize", | ||
"createElementSize" | ||
], | ||
@@ -38,5 +43,2 @@ "category": "Display & Media" | ||
}, | ||
"scripts": { | ||
"build": "tsup" | ||
}, | ||
"keywords": [ | ||
@@ -48,18 +50,30 @@ "resize", | ||
], | ||
"scripts": { | ||
"start": "vite serve dev --host", | ||
"dev": "npm run start", | ||
"build": "tsup", | ||
"test": "uvu -r solid-register", | ||
"test:watch": "watchlist src test -- npm test" | ||
}, | ||
"devDependencies": { | ||
"@types/jest": "^27.0.0", | ||
"prettier": "^2.0.5", | ||
"solid-testing-library": "^0.2.0", | ||
"tslib": "^2.0.1", | ||
"tsup": "^5.10.1", | ||
"typescript": "^4.0.2" | ||
"jsdom": "^19.0.0", | ||
"prettier": "^2.6.2", | ||
"solid-register": "^0.2.5", | ||
"tslib": "^2.4.0", | ||
"tsup": "^5.12.8", | ||
"typescript": "^4.6.4", | ||
"unocss": "0.34.0", | ||
"uvu": "^0.5.3", | ||
"vite": "2.9.9", | ||
"vite-plugin-solid": "2.2.6", | ||
"watchlist": "^0.3.1" | ||
}, | ||
"dependencies": { | ||
"@solid-primitives/event-listener": "^2.1.0", | ||
"@solid-primitives/utils": "^2.1.0", | ||
"@solid-primitives/rootless": "^1.1.0" | ||
}, | ||
"peerDependencies": { | ||
"solid-js": "^1.3.0" | ||
}, | ||
"jest": { | ||
"setupFiles": [ | ||
"./test/setup.ts" | ||
] | ||
"solid-js": "^1.4.0" | ||
} | ||
} |
157
README.md
@@ -12,4 +12,9 @@ <p> | ||
Provides a reactive resize observer wrapper. | ||
Reactive primitives for observing resizing of HTML elements. | ||
- [`makeResizeObserver`](#makeResizeObserver) — Instantiate a new ResizeObserver that automatically get's disposed on cleanup. | ||
- [`createResizeObserver`](#createResizeObserver) — Create resize observer instance, listening for changes to size of reactive element targets array. | ||
- [`createWindowSize`](#createWindowSize) — Creates a reactive store-like object of current width and height dimensions of window, page and screen. | ||
- [`createElementSize`](#createElementSize) — Creates a reactive store-like object of current width and height dimensions of html element. | ||
## Installation | ||
@@ -23,12 +28,146 @@ | ||
## How to use it | ||
## `makeResizeObserver` | ||
### createResizeObserver | ||
Instantiate a new ResizeObserver that automatically get's disposed on cleanup. | ||
Main resize observer primitive. | ||
### How to use it | ||
`makeResizeObserver` returns `observe` and `unobserve` functions for managing targets. | ||
```ts | ||
const refCallback = createResizeObserver(() => console.log("resized")); | ||
import { makeResizeObserver } from "@solid-primitives/resize-observer"; | ||
const { observe, unobserve } = makeResizeObserver(handleObserverCallback, { box: "content-box" }); | ||
observe(document.body); | ||
observe(ref); | ||
function handleObserverCallback(entries: ResizeObserverEntry[]) { | ||
for (const entry of entries) { | ||
console.log(entry.contentRect.width); | ||
} | ||
} | ||
``` | ||
#### Disposing | ||
`makeResizeObserver` will dispose itself with it's parent reactive owner. | ||
To dispose early, wrap the primitive with a [`createRoot`](https://www.solidjs.com/docs/latest/api#createroot). | ||
```ts | ||
const { dispose } = createRoot(dispose => { | ||
const { observe, unobserve } = makeResizeObserver(handleObserverCallback); | ||
return { dispose, observe, unobserve }; | ||
}); | ||
// dispose early | ||
dispose(); | ||
``` | ||
## `createResizeObserver` | ||
Create resize observer instance, listening for changes to size of reactive element targets array. | ||
Disposes automatically itself with it's parent reactive owner. | ||
### How to use it | ||
```tsx | ||
import { createResizeObserver } from "@solid-primitives/resize-observer"; | ||
let ref!: HTMLDivElement; | ||
// can in onMount if the target variable isn't yet populated | ||
onMount(() => { | ||
createResizeObserver(ref, ({ width, height }, el) => { | ||
if (el === ref) console.log(width, height); | ||
}); | ||
}); | ||
<div ref={ref} />; | ||
``` | ||
#### Reactive targets | ||
The `targets` argument can be a reactive signal or top-level store array. | ||
```ts | ||
const [targets, setTargets] = createSignal([document.body]); | ||
createResizeObserver(targets, ({ width, height }, el) => {}); | ||
// updating the signal will unobserve removed elements and observe added ones | ||
setTargets(p => [...p, element]); | ||
// createResizeObserver supports top-lever store arrays too | ||
const [targets, setTargets] = createStore([document.body]); | ||
createResizeObserver(targets, ({ width, height }, el) => {}); | ||
setTargets(targets.length, element); | ||
``` | ||
## `createWindowSize` | ||
Creates a reactive store-like object of current width and height dimensions of window, page and screen. | ||
### How to use it | ||
```ts | ||
import { createWindowSize } from "@solid-primitives/resize-observer"; | ||
const size = createWindowSize(); | ||
createEffect(() => { | ||
size.width; // => number | ||
size.height; // => number | ||
}); | ||
``` | ||
### `useWindowSize` | ||
`useWindowSize` is a [shared root](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSharedRoot) primitive. It is providing the same reactive object as `createWindowSize`, but the object instance, signals and event-listeners are shared between dependents, making it more optimized to use in multiple places at once. | ||
```ts | ||
import { useWindowSize } from "@solid-primitives/resize-observer"; | ||
const size = useWindowSize(); | ||
createEffect(() => { | ||
size.width; // => number | ||
size.height; // => number | ||
}); | ||
``` | ||
### Media Queries | ||
**The `createWindowSize` isn't meant to be used for creating media queries.** | ||
If you want a reactive interface for media-queries, please checkout [the media package](https://github.com/solidjs-community/solid-primitives/tree/main/packages/media#readme). | ||
## `createElementSize` | ||
Creates a reactive store-like object of current width and height dimensions of html element. | ||
### How to use it | ||
`createElementSize` needs to be provided a target. It can be an HTML element, or a reactive signal returning one. Target also takes falsy values to disable tracking. | ||
```tsx | ||
import { createElementSize } from "@solid-primitives/resize-observer"; | ||
const size = createElementSize(document.body); | ||
createEffect(() => { | ||
size.width; // => number | ||
size.height; // => number | ||
}); | ||
// reactive target | ||
const [target, setTarget] = createSignal<HTMLElement>(); | ||
const size = createElementSize(target); | ||
createEffect(() => { | ||
size.width; // => number | null | ||
size.height; // => number | null | ||
}); | ||
<div ref={setTarget} />; | ||
``` | ||
## Changelog | ||
@@ -49,4 +188,10 @@ | ||
Patched HTMLElement to Element to resolve type error on buildd. Updated to Solid 1.3. | ||
Patched HTMLElement to Element to resolve type error on build. Updated to Solid 1.3. | ||
2.0.0 | ||
Refactored `createResizeObserver` API. | ||
Added `makeResizeObserver`, `createWindowSize`, `useWindowSize` and `createElementSize` | ||
</details> | ||
@@ -53,0 +198,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
22330
379
199
4
11
1
+ Added@solid-primitives/event-listener@2.3.3(transitive)
+ Added@solid-primitives/rootless@1.4.5(transitive)
+ Added@solid-primitives/utils@2.2.16.2.3(transitive)