@solid-primitives/event-listener
Advanced tools
Comparing version 1.6.1 to 2.0.0
@@ -1,4 +0,3 @@ | ||
import { MaybeAccessor, Many } from '@solid-primitives/utils'; | ||
import { JSX, Accessor, Component } from 'solid-js'; | ||
import { Store } from 'solid-js/store'; | ||
import { MaybeAccessor, Many, Directive } from '@solid-primitives/utils'; | ||
import { JSX, Accessor } from 'solid-js'; | ||
@@ -13,3 +12,2 @@ declare type EventListenerOptions = boolean | AddEventListenerOptions; | ||
]; | ||
declare type EventListenerMapDirectiveProps = [handlerMap: Record<string, (e: any) => void>, options?: EventListenerOptions] | Record<string, (e: any) => void>; | ||
declare module "solid-js" { | ||
@@ -19,3 +17,2 @@ namespace JSX { | ||
eventListener: EventListenerDirectiveProps; | ||
eventListenerMap: EventListenerMapDirectiveProps; | ||
} | ||
@@ -26,24 +23,35 @@ } | ||
declare type EventListenerSignalReturns<Event> = [ | ||
lastEvent: Accessor<Event | undefined>, | ||
clear: VoidFunction | ||
]; | ||
/** | ||
* Creates an event listener, that will be automatically disposed on cleanup. | ||
* | ||
* @param target - ref to HTMLElement, EventTarget or Array thereof | ||
* @param target - ref to HTMLElement, EventTarget | ||
* @param type - name of the handled event | ||
* @param handler - event handler | ||
* @param options - addEventListener options | ||
* | ||
* @returns Function clearing all event listeners form targets | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener | ||
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#makeEventListener | ||
* @example | ||
* const clear = makeEventListener(element, 'click', e => { ... }, { passive: true }) | ||
* // remove listener (will also happen on cleanup) | ||
* clear() | ||
*/ | ||
declare function makeEventListener<Target extends TargetWithEventMap, EventMap extends EventMapOf<Target>, EventType extends keyof EventMap>(target: Target, type: EventType, handler: (event: EventMap[EventType]) => void, options?: EventListenerOptions): VoidFunction; | ||
declare function makeEventListener<EventMap extends Record<string, Event>, EventType extends keyof EventMap = keyof EventMap>(target: EventTarget, type: EventType, handler: (event: EventMap[EventType]) => void, options?: EventListenerOptions): VoidFunction; | ||
/** | ||
* Creates a reactive event listener, that will be automatically disposed on cleanup, | ||
* and can take reactive arguments to attach listeners to new targets once changed. | ||
* @param target - ref to HTMLElement, EventTarget or Array thereof | ||
* @param type - name of the handled event | ||
* @param handler - event handler | ||
* @param options - addEventListener options | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener | ||
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#createEventListener | ||
* | ||
* @example | ||
* const clear = createEventListener(element, 'click', e => { ... }, { passive: true }) | ||
* const [targets, setTargets] = createSignal([element]) | ||
* createEventListener(targets, 'click', e => { ... }, { passive: true }) | ||
* setTargets([]) // <- removes listeners from previous target | ||
* setTargets([element, button]) // <- adds listeners to new targets | ||
*/ | ||
declare function createEventListener<Target extends TargetWithEventMap, EventMap extends EventMapOf<Target>, EventType extends keyof EventMap>(target: MaybeAccessor<Many<Target>>, type: MaybeAccessor<Many<EventType>>, handler: (event: EventMap[EventType]) => void, options?: EventListenerOptions): VoidFunction; | ||
declare function createEventListener<EventMap extends Record<string, Event>, EventType extends keyof EventMap = keyof EventMap>(target: MaybeAccessor<Many<EventTarget>>, type: MaybeAccessor<Many<EventType>>, handler: (event: EventMap[EventType]) => void, options?: EventListenerOptions): VoidFunction; | ||
declare function createEventListener<Target extends TargetWithEventMap, EventMap extends EventMapOf<Target>, EventType extends keyof EventMap>(target: MaybeAccessor<Many<Target>>, type: MaybeAccessor<Many<EventType>>, handler: (event: EventMap[EventType]) => void, options?: EventListenerOptions): void; | ||
declare function createEventListener<EventMap extends Record<string, Event>, EventType extends keyof EventMap = keyof EventMap>(target: MaybeAccessor<Many<EventTarget>>, type: MaybeAccessor<Many<EventType>>, handler: (event: EventMap[EventType]) => void, options?: EventListenerOptions): void; | ||
/** | ||
@@ -68,4 +76,4 @@ * Provides an reactive signal of last captured event. | ||
*/ | ||
declare function createEventSignal<Target extends TargetWithEventMap, EventMap extends EventMapOf<Target>, EventType extends keyof EventMap>(target: MaybeAccessor<Many<Target>>, type: MaybeAccessor<Many<EventType>>, options?: EventListenerOptions): EventListenerSignalReturns<EventMap[EventType]>; | ||
declare function createEventSignal<EventMap extends Record<string, Event>, EventType extends keyof EventMap = keyof EventMap>(target: MaybeAccessor<Many<EventTarget>>, type: MaybeAccessor<Many<EventType>>, options?: EventListenerOptions): EventListenerSignalReturns<EventMap[EventType]>; | ||
declare function createEventSignal<Target extends TargetWithEventMap, EventMap extends EventMapOf<Target>, EventType extends keyof EventMap>(target: MaybeAccessor<Many<Target>>, type: MaybeAccessor<Many<EventType>>, options?: EventListenerOptions): Accessor<EventMap[EventType]>; | ||
declare function createEventSignal<EventMap extends Record<string, Event>, EventType extends keyof EventMap = keyof EventMap>(target: MaybeAccessor<Many<EventTarget>>, type: MaybeAccessor<Many<EventType>>, options?: EventListenerOptions): Accessor<EventMap[EventType]>; | ||
/** | ||
@@ -79,3 +87,3 @@ * Directive Usage. Creates an event listener, that will be automatically disposed on cleanup. | ||
*/ | ||
declare function eventListener(target: Element, props: Accessor<EventListenerDirectiveProps>): void; | ||
declare const eventListener: Directive<EventListenerDirectiveProps>; | ||
@@ -85,3 +93,2 @@ declare type EventHandlersMap<EventMap> = { | ||
}; | ||
declare type EventListenerStoreReturns<E> = [lastEvents: Store<Partial<E>>, clear: VoidFunction]; | ||
/** | ||
@@ -94,4 +101,2 @@ * A helpful primitive that listens to a map of events. Handle them by individual callbacks. | ||
* | ||
* @returns Function clearing all event listeners form targets | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener | ||
@@ -101,3 +106,3 @@ * @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#createEventListenerMap | ||
* @example | ||
* const clear = createEventListenerMap(element, { | ||
* createEventListenerMap(element, { | ||
* mousemove: mouseHandler, | ||
@@ -108,46 +113,11 @@ * mouseenter: e => {}, | ||
*/ | ||
declare function createEventListenerMap<EventMap extends Record<string, Event>, UsedEvents extends keyof EventMap = keyof EventMap>(target: MaybeAccessor<Many<EventTarget>>, handlersMap: Partial<Pick<EventHandlersMap<EventMap>, UsedEvents>>, options?: EventListenerOptions): VoidFunction; | ||
declare function createEventListenerMap<Target extends TargetWithEventMap, EventMap extends EventMapOf<Target>, HandlersMap extends Partial<EventHandlersMap<EventMap>>>(target: MaybeAccessor<Many<Target>>, handlersMap: HandlersMap, options?: EventListenerOptions): VoidFunction; | ||
/** | ||
* A helpful primitive that listens to target events and provides a reactive store with the latest captured events. | ||
* | ||
* @param target accessor or variable of multiple or single event targets | ||
* @param options e.g. `{ passive: true }` *(can be omitted)* | ||
* @param eventNames names of events you want to listen to, e.g. `"mousemove", "touchend", "click"` | ||
* | ||
* @returns reactive store with the latest captured events & clear function | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener | ||
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#createEventStore | ||
* | ||
* @example | ||
* const [lastEvents, clear] = createEventStore(el, "mousemove", "touchend", "click"); | ||
* | ||
* createEffect(() => { | ||
* console.log(lastEvents.mousemove.x) | ||
* }) | ||
*/ | ||
declare function createEventStore<Target extends TargetWithEventMap, EventMap extends EventMapOf<Target>, UsedEvents extends keyof EventMap>(target: MaybeAccessor<Many<Target>>, ...eventNames: UsedEvents[]): EventListenerStoreReturns<Pick<EventMap, UsedEvents>>; | ||
declare function createEventStore<Target extends TargetWithEventMap, EventMap extends EventMapOf<Target>, UsedEvents extends keyof EventMap>(target: MaybeAccessor<Many<Target>>, options: EventListenerOptions, ...eventNames: UsedEvents[]): EventListenerStoreReturns<Pick<EventMap, UsedEvents>>; | ||
declare function createEventStore<EventMap extends Record<string, Event>, UsedEvents extends keyof EventMap = keyof EventMap>(target: MaybeAccessor<Many<EventTarget>>, ...eventNames: UsedEvents[]): EventListenerStoreReturns<Pick<EventMap, UsedEvents>>; | ||
declare function createEventStore<EventMap extends Record<string, Event>, UsedEvents extends keyof EventMap = keyof EventMap>(target: MaybeAccessor<Many<EventTarget>>, options: EventListenerOptions, ...eventNames: UsedEvents[]): EventListenerStoreReturns<Pick<EventMap, UsedEvents>>; | ||
/** | ||
* Directive Usage. A helpful primitive that listens to provided events. Handle them by callbacks. | ||
* | ||
* @param props [handlerMap, options] | handlerMap | ||
* - **handlerMap**: e.g. `{ mousemove: e => {}, click: e => {} }` | ||
* - **options** e.g. `{ passive: true }` | ||
* | ||
* @example | ||
* <div use:eventListenerMap={{ | ||
* mousemove: e => {}, | ||
* click: clickHandler, | ||
* touchstart: () => {} | ||
* }}></div> | ||
*/ | ||
declare function eventListenerMap(target: Element, getProps: Accessor<EventListenerMapDirectiveProps>): void; | ||
declare function createEventListenerMap<EventMap extends Record<string, Event>, UsedEvents extends keyof EventMap = keyof EventMap>(target: MaybeAccessor<Many<EventTarget>>, handlersMap: Partial<Pick<EventHandlersMap<EventMap>, UsedEvents>>, options?: EventListenerOptions): void; | ||
declare function createEventListenerMap<Target extends TargetWithEventMap, EventMap extends EventMapOf<Target>, HandlersMap extends Partial<EventHandlersMap<EventMap>>>(target: MaybeAccessor<Many<Target>>, handlersMap: HandlersMap, options?: EventListenerOptions): void; | ||
declare type EventAttributes<T> = { | ||
[K in keyof JSX.DOMAttributes<T>]: `${K}` extends `on${string}` ? JSX.DOMAttributes<T>[K] : never; | ||
declare type WindowEventProps = { | ||
[K in keyof WindowEventMap as `on${Capitalize<K>}` | `on${K}`]?: (event: WindowEventMap[K]) => void; | ||
}; | ||
declare type DocumentEventProps = { | ||
[K in keyof DocumentEventMap as `on${Capitalize<K>}` | `on${K}`]?: (event: DocumentEventMap[K]) => void; | ||
}; | ||
/** | ||
@@ -161,3 +131,3 @@ * Listen to the `window` DOM Events, using a component. | ||
*/ | ||
declare const WindowEventListener: Component<EventAttributes<null>>; | ||
declare function WindowEventListener(props: WindowEventProps): undefined; | ||
/** | ||
@@ -171,28 +141,22 @@ * Listen to the `document` DOM Events, using a component. | ||
*/ | ||
declare const DocumentEventListener: Component<EventAttributes<null>>; | ||
declare function DocumentEventListener(props: DocumentEventProps): undefined; | ||
declare type EventListenerBus<EventMap extends Record<string, any>> = Readonly<{ | ||
[K in `on${keyof EventMap extends string ? keyof EventMap : never}`]: (handler: (event: EventMap[K extends `on${infer T}` ? T : never]) => void) => VoidFunction; | ||
} & { | ||
on: <T extends keyof EventMap>(type: MaybeAccessor<Many<T>>, handler: (event: EventMap[T]) => void) => VoidFunction; | ||
}>; | ||
declare type EventListenerStackOn<EventMap extends Record<string, any>> = { | ||
<T extends keyof EventMap>(type: T, handler: (event: EventMap[T]) => void, options?: EventListenerOptions): VoidFunction; | ||
}; | ||
/** | ||
* Dynamically add and remove event listeners to an event target. The listeners will be automatically removed on cleanup. | ||
* @param target the event target, could be a `window`, `document`, `HTMLElement` or `MediaQueryList`. *Defaults to `window`* | ||
* @param options event listener options, such as `passive` or `capture` | ||
* @returns a Proxy object, which lets you create event listeners by calling appropriate property | ||
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener#createEventListenerBus | ||
* Creates a stack of event listeners, that will be automatically disposed on cleanup. | ||
* @param target - ref to HTMLElement, EventTarget | ||
* @param options - addEventListener options | ||
* @returns Function clearing all event listeners form targets | ||
* @example | ||
* const bus = createEventListenerBus(document.body); | ||
* bus.onpointerenter(e => {...}); | ||
* // listeners return a function that removes them | ||
* const clear = bus.onpointermove(e => {...}); | ||
* clear(); | ||
* // or pass event type as an argument | ||
* const unsub = bus.on('click', e => {...}) | ||
* const [listen, clear] = makeEventListenerStack(target, { passive: true }); | ||
* listen("mousemove", handleMouse); | ||
* listen("dragover", handleMouse); | ||
* // remove listener (will also happen on cleanup) | ||
* clear() | ||
*/ | ||
declare function createEventListenerBus(target?: undefined, options?: EventListenerOptions): EventListenerBus<WindowEventMap>; | ||
declare function createEventListenerBus<Target extends TargetWithEventMap>(target: MaybeAccessor<Many<Target>>, options?: EventListenerOptions): EventListenerBus<EventMapOf<Target>>; | ||
declare function createEventListenerBus<EventMap extends Record<string, Event>>(target?: MaybeAccessor<Many<EventTarget>>, options?: EventListenerOptions): EventListenerBus<EventMap>; | ||
declare function makeEventListenerStack<Target extends TargetWithEventMap, EventMap extends EventMapOf<Target>>(target: Target, options?: EventListenerOptions): [listen: EventListenerStackOn<EventMap>, clear: VoidFunction]; | ||
declare function makeEventListenerStack<EventMap extends Record<string, Event>>(target: EventTarget, options?: EventListenerOptions): [listen: EventListenerStackOn<EventMap>, clear: VoidFunction]; | ||
export { DocumentEventListener, E, EventHandlersMap, EventListenerBus, EventListenerDirectiveProps, EventListenerMapDirectiveProps, EventListenerOptions, EventListenerSignalReturns, EventListenerStoreReturns, EventMapOf, TargetWithEventMap, WindowEventListener, createEventListener, createEventListenerBus, createEventListenerMap, createEventSignal, createEventStore, eventListener, eventListenerMap }; | ||
export { DocumentEventListener, DocumentEventProps, E, EventHandlersMap, EventListenerDirectiveProps, EventListenerOptions, EventListenerStackOn, EventMapOf, TargetWithEventMap, WindowEventListener, WindowEventProps, createEventListener, createEventListenerMap, createEventSignal, eventListener, makeEventListener, makeEventListenerStack }; |
// src/eventListener.ts | ||
import { | ||
createCallbackStack, | ||
noop, | ||
isServer, | ||
access, | ||
asArray | ||
} from "@solid-primitives/utils"; | ||
import { isServer, access, asArray } from "@solid-primitives/utils"; | ||
import { createEffect, onCleanup, createRenderEffect, createSignal } from "solid-js"; | ||
function makeEventListener(target, type, handler, options) { | ||
target.addEventListener(type, handler, options); | ||
return onCleanup(target.removeEventListener.bind(target, type, handler, options)); | ||
} | ||
function createEventListener(targets, type, handler, options) { | ||
if (isServer) | ||
return noop; | ||
const cleanup = createCallbackStack(); | ||
return; | ||
const attachListeners = () => { | ||
cleanup.execute(); | ||
asArray(access(type)).forEach((type2) => { | ||
asArray(access(targets)).forEach((el) => { | ||
if (!el) | ||
return; | ||
el.addEventListener(type2, handler, options); | ||
cleanup.push(() => el.removeEventListener(type2, handler, options)); | ||
}); | ||
asArray(access(targets)).forEach((el) => { | ||
if (el) | ||
asArray(access(type)).forEach((type2) => makeEventListener(el, type2, handler, options)); | ||
}); | ||
@@ -29,111 +21,56 @@ }; | ||
createRenderEffect(attachListeners); | ||
onCleanup(cleanup.execute); | ||
return cleanup.execute; | ||
} | ||
function createEventSignal(target, type, options) { | ||
const [lastEvent, setLastEvent] = createSignal(); | ||
const clear = createEventListener(target, type, setLastEvent, options); | ||
return [lastEvent, clear]; | ||
createEventListener(target, type, setLastEvent, options); | ||
return lastEvent; | ||
} | ||
function eventListener(target, props) { | ||
const toCleanup = createCallbackStack(); | ||
var eventListener = (target, props) => { | ||
createEffect(() => { | ||
toCleanup.execute(); | ||
const [type, handler, options] = props(); | ||
target.addEventListener(type, handler, options); | ||
toCleanup.push(() => target.removeEventListener(type, handler, options)); | ||
makeEventListener(target, type, handler, options); | ||
}); | ||
onCleanup(toCleanup.execute); | ||
} | ||
}; | ||
// src/eventListenerMap.ts | ||
import { | ||
createCallbackStack as createCallbackStack2, | ||
forEachEntry, | ||
access as access2 | ||
} from "@solid-primitives/utils"; | ||
import { createEffect as createEffect2, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js"; | ||
import { forEachEntry } from "@solid-primitives/utils"; | ||
function createEventListenerMap(targets, handlersMap, options) { | ||
const { push, execute } = createCallbackStack2(); | ||
forEachEntry(access2(handlersMap), (eventName, handler) => { | ||
push(createEventListener(targets, eventName, (e) => handler == null ? void 0 : handler(e), options)); | ||
forEachEntry(handlersMap, (eventName, handler) => { | ||
if (handler) | ||
createEventListener(targets, eventName, handler, options); | ||
}); | ||
return execute; | ||
} | ||
function createEventStore(targets, ...rest) { | ||
let options = void 0; | ||
let names; | ||
if (typeof rest[0] === "string") | ||
names = rest; | ||
else { | ||
const [_options, ..._events] = rest; | ||
options = _options; | ||
names = _events; | ||
} | ||
const store = {}; | ||
const { push, execute } = createCallbackStack2(); | ||
names.forEach((type) => { | ||
const [accessor, setter] = createSignal2(); | ||
Object.defineProperty(store, type, { get: accessor, set: setter, enumerable: true }); | ||
push(createEventListener(targets, type, setter, options)); | ||
}); | ||
return [store, execute]; | ||
} | ||
function eventListenerMap(target, getProps) { | ||
const toCleanup = createCallbackStack2(); | ||
createEffect2(() => { | ||
toCleanup.execute(); | ||
let handlersMap; | ||
let options; | ||
const props = getProps(); | ||
if (Array.isArray(props)) { | ||
handlersMap = props[0]; | ||
options = props[1]; | ||
} else | ||
handlersMap = props; | ||
forEachEntry(handlersMap, (type, handler) => { | ||
target.addEventListener(type, handler, options); | ||
toCleanup.push(() => target.removeEventListener(type, handler, options)); | ||
}); | ||
}); | ||
onCleanup2(toCleanup.execute); | ||
} | ||
// src/components.ts | ||
import { isClient } from "@solid-primitives/utils"; | ||
var forEachEventAttr = (props, fn) => { | ||
Object.keys(props).forEach((attr) => { | ||
if (!attr.startsWith("on")) | ||
return; | ||
const eventName = attr.substring(2).toLowerCase(); | ||
fn(eventName, attr); | ||
import { isClient, keys } from "@solid-primitives/utils"; | ||
var attachPropListeners = (target, props) => { | ||
keys(props).forEach((attr) => { | ||
if (attr.startsWith("on") && typeof props[attr] === "function") | ||
makeEventListener(target, attr.substring(2).toLowerCase(), props[attr]); | ||
}); | ||
}; | ||
var WindowEventListener = (props) => { | ||
function WindowEventListener(props) { | ||
if (isClient) | ||
forEachEventAttr(props, (type, attr) => { | ||
createEventListener(window, type, (e) => props[attr](e)); | ||
}); | ||
attachPropListeners(window, props); | ||
return void 0; | ||
}; | ||
var DocumentEventListener = (props) => { | ||
} | ||
function DocumentEventListener(props) { | ||
if (isClient) | ||
forEachEventAttr(props, (eventName, attr) => { | ||
createEventListener(document, eventName, (e) => props[attr](e)); | ||
}); | ||
attachPropListeners(document, props); | ||
return void 0; | ||
}; | ||
} | ||
// src/bus.ts | ||
import { createProxy, isClient as isClient2 } from "@solid-primitives/utils"; | ||
import { runWithSubRoot } from "@solid-primitives/rootless"; | ||
import { getOwner } from "solid-js"; | ||
function createEventListenerBus(target = isClient2 ? window : [], options = {}) { | ||
const owner = getOwner(); | ||
const addListener = (type, handler) => runWithSubRoot(() => { | ||
createEventListener(target, type, handler, options); | ||
}, owner, getOwner()); | ||
return createProxy({ | ||
get: (key) => (...args) => args.length === 2 ? addListener(...args) : addListener(key.substring(2), args[0]) | ||
}); | ||
// src/eventListenerStack.ts | ||
import { createCallbackStack } from "@solid-primitives/utils"; | ||
import { onCleanup as onCleanup2 } from "solid-js"; | ||
function makeEventListenerStack(target, options) { | ||
const { push, execute } = createCallbackStack(); | ||
return [ | ||
(type, handler, overwriteOptions) => { | ||
const clear = makeEventListener(target, type, handler, overwriteOptions != null ? overwriteOptions : options); | ||
push(clear); | ||
return clear; | ||
}, | ||
onCleanup2(execute) | ||
]; | ||
} | ||
@@ -144,8 +81,7 @@ export { | ||
createEventListener, | ||
createEventListenerBus, | ||
createEventListenerMap, | ||
createEventSignal, | ||
createEventStore, | ||
eventListener, | ||
eventListenerMap | ||
makeEventListener, | ||
makeEventListenerStack | ||
}; |
{ | ||
"name": "@solid-primitives/event-listener", | ||
"version": "1.6.1", | ||
"description": "Primitive to manage creating event listeners.", | ||
"version": "2.0.0", | ||
"description": "SolidJS Primitives to manage creating event listeners.", | ||
"author": "David Di Biase <dave.dibiase@gmail.com>", | ||
"contributors": [ | ||
"Damian Tarnawski <gthetarnav@gmail.com>" | ||
], | ||
"license": "MIT", | ||
@@ -19,4 +22,2 @@ "homepage": "https://github.com/solidjs-community/solid-primitives/tree/main/packages/event-listener", | ||
"createEventListenerMap", | ||
"createEventStore", | ||
"createEventListenerBus", | ||
"WindowEventListener", | ||
@@ -37,6 +38,7 @@ "DocumentEventListener" | ||
"scripts": { | ||
"start": "vite -r ./dev/ -c ./dev/vite.config.ts", | ||
"start": "vite serve dev --host", | ||
"dev": "yarn start", | ||
"build": "tsup", | ||
"test": "uvu -r solid-register", | ||
"watch-test": "watchlist src test -- npm test" | ||
"test:watch": "watchlist src test -- npm test" | ||
}, | ||
@@ -51,10 +53,10 @@ "keywords": [ | ||
"jsdom": "^19.0.0", | ||
"prettier": "^2.5.0", | ||
"solid-register": "^0.1.5", | ||
"prettier": "^2.6.2", | ||
"solid-register": "^0.2.3", | ||
"tslib": "^2.3.1", | ||
"tsup": "^5.10.0", | ||
"unocss": "0.24.3", | ||
"uvu": "^0.5.2", | ||
"vite": "2.8.1", | ||
"vite-plugin-solid": "2.2.5", | ||
"tsup": "^5.12.6", | ||
"unocss": "0.31.12", | ||
"uvu": "^0.5.3", | ||
"vite": "2.9.5", | ||
"vite-plugin-solid": "2.2.6", | ||
"watchlist": "^0.3.1" | ||
@@ -66,5 +68,4 @@ }, | ||
"dependencies": { | ||
"@solid-primitives/rootless": "^0.1.0", | ||
"@solid-primitives/utils": "^1.2.0" | ||
} | ||
} |
274
README.md
@@ -14,9 +14,17 @@ <p> | ||
- [`createEventListener`](#createEventListener) - Very basic and straightforward primitive that handles multiple elements according to a single event binding. | ||
- [`createEventSignal`](#createEventListener) - Like `createEventListener`, but events are handled with the returned signal, instead of with a callback. | ||
##### Non-reactive primitives: | ||
- [`makeEventListener`](#makeEventListener) - Non-reactive primitive for adding event listeners that get's removed onCleanup. | ||
- [`makeEventListenerStack`](#makeEventListenerStack) - Creates a stack of event listeners, that will be automatically disposed on cleanup. | ||
##### Reactive primitives: | ||
- [`createEventListener`](#createEventListener) - Reactive version of [`makeEventListener`](#makeEventListener), that takes signal arguments to apply new listeners once changed. | ||
- [`createEventSignal`](#createEventListener) - Like [`createEventListener`](#createEventListener), but captured events are stored in a returned signal. | ||
- [`createEventListenerMap`](#createEventListenerMap) - A helpful primitive that listens to a map of events. Handle them by individual callbacks. | ||
- [`createEventStore`](#createEventStore) - Similar to `createEventListenerMap`, but provides a reactive store with the latest captured events. | ||
- [`createEventListenerBus`](#createEventListenerBus) - Dynamically add and remove event listeners to an event target by calling appropriate property. | ||
##### Component global listeners: | ||
- [`WindowEventListener`](#WindowEventListener) - Listen to the `window` DOM Events, using a component. | ||
- [`DocumentEventListener`](#DocumentEventListener) - The same as [`WindowEventListener`](#WindowEventListener), but listens to `document` events. | ||
- [`DocumentEventListener`](#DocumentEventListener) - Listen to the `document` DOM Events, using a component. | ||
@@ -31,12 +39,14 @@ ## Installation | ||
## `createEventListener` | ||
## `makeEventListener` | ||
Can be used to listen to DOM or Custom Events on window, document, list of HTML elements or any EventTarget. The target prop can be reactive. | ||
Can be used to listen to DOM or Custom Events on window, document, or any EventTarget. | ||
Event listener is automatically removed on root cleanup. The clear() function is also returned for calling it early. | ||
### How to use it | ||
```ts | ||
import { createEventListener } from "@solid-primitives/event-listener"; | ||
```tsx | ||
import { makeEventListener } from "@solid-primitives/event-listener"; | ||
const clear = createEventListener( | ||
const clear = makeEventListener( | ||
document.getElementById("myButton"), | ||
@@ -51,13 +61,69 @@ "mousemove", | ||
// when listening to element refs, call it inside onMount | ||
let ref!: HTMLDivElement | ||
onMount(() => { | ||
makeEventListener(ref, "click", e => {...}, { passive: true }); | ||
}); | ||
<div ref={ref} />; | ||
``` | ||
#### Custom events | ||
```ts | ||
// you can provide your own event map type as well: | ||
// fill both type generics for the best type support | ||
makeEventListener<{ myCustomEvent: MyEvent; other: Event }, "myCustomEvent">( | ||
window, | ||
"myCustomEvent", | ||
() => console.log("yup!") | ||
); | ||
// just don't use interfaces as EventMaps! (write them using `type` keyword) | ||
``` | ||
## `makeEventListenerStack` | ||
Creates a stack of event listeners, that will be automatically disposed on cleanup. | ||
### How to use it | ||
```ts | ||
import { makeEventListenerStack } from "@solid-primitives/event-listener"; | ||
const [listen, clear] = makeEventListenerStack(target, { passive: true }); | ||
listen("mousemove", handleMouse); | ||
listen("dragover", handleMouse); | ||
// remove listener (will also happen on cleanup) | ||
clear(); | ||
``` | ||
## `createEventListener` | ||
Reactive version of [`makeEventListener`](#makeEventListener), that can take signal `target` and `type` arguments to apply new listeners once changed. | ||
### How to use it | ||
```tsx | ||
import { createEventListener } from "@solid-primitives/event-listener"; | ||
createEventListener( | ||
document.getElementById("myButton"), | ||
"mousemove", | ||
e => console.log("x:", e.pageX, "y:", e.pageY), | ||
{ passive: true } | ||
); | ||
// target element and event name can be reactive signals | ||
const [ref, setRef] = createSignal<HTMLElement>(); | ||
const [name, setName] = createSignal("mousemove"); | ||
createEventListener(ref, name, e => {}, { passive: true }); | ||
const [type, setType] = createSignal("mousemove"); | ||
createEventListener(ref, type, e => {...}); | ||
// also runs in createEffect so refs are already bound | ||
// (but variable refs need to be wrapped in functions) | ||
// when using ref as a target, pass it in a function – function will be executed after mount | ||
// or wrap the whole primitive in onMount | ||
let ref; | ||
createEventListener(() => ref, "mousemove", e => {}); | ||
return <div ref={ref}/>; | ||
<div ref={ref} />; | ||
``` | ||
@@ -78,2 +144,27 @@ | ||
#### Removing event listeners manually | ||
Since version `@2.0.0` `createEventListener` and other reactive primitives aren't returning a `clear()` function, because of it's flawed behavior [described in this issue](https://github.com/solidjs-community/solid-primitives/issues/103). | ||
Although there are still ways to remove attached event listeners: | ||
1. Changing reactive `target` or `type` arguments to an empty array. | ||
```ts | ||
const [type, setType] = createSignal<"click" | []>("click"); | ||
createEventListener(window, type, e => {...}); | ||
// remove listener: | ||
setType([]); | ||
``` | ||
2. Wrapping usage of `createEventListener` primitive in Solid's `createRoot` or `createBranch` | `createDisposable` from ["@solid-primitives/rootless"](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createDisposable). | ||
```ts | ||
import { createDisposable } from "@solid-primitives/rootless"; | ||
const clear = createDisposable(() => createEventListener(element, "click", e => {...})); | ||
// remove listener: | ||
clear(); | ||
``` | ||
#### Listening to multiple events | ||
@@ -111,3 +202,3 @@ | ||
// all arguments can be reactive signals | ||
const [lastEvent, clear] = createEventSignal(el, "mousemove", { passive: true }); | ||
const lastEvent = createEventSignal(el, "mousemove", { passive: true }); | ||
@@ -117,5 +208,2 @@ createEffect(() => { | ||
}); | ||
// to clear all the event listeners | ||
clear(); | ||
``` | ||
@@ -132,3 +220,3 @@ | ||
const clear = createEventListenerMap(element, { | ||
createEventListenerMap(element, { | ||
mousemove: mouseHandler, | ||
@@ -139,5 +227,2 @@ mouseenter: e => {}, | ||
// to clear all the event listeners | ||
clear(); | ||
// both target can be reactive: | ||
@@ -169,133 +254,2 @@ const [target, setTarget] = createSignal(document.getElementById("abc")); | ||
### Directive usage | ||
```tsx | ||
import { eventListenerMap } from "@solid-primitives/event-listener"; | ||
// prevent tree-shaking: | ||
eventListenerMap; | ||
<div | ||
use:eventListenerMap={{ | ||
mousemove: e => {}, | ||
click: clickHandler, | ||
touchstart: () => {}, | ||
myCustomEvent: e => {} | ||
}} | ||
></div>; | ||
``` | ||
## `createEventStore` | ||
Similar to [`createEventListenerMap`](#createEventListenerMap), but provides a reactive store with the latest captured events. | ||
### How to use it | ||
```ts | ||
const [lastEvents, clear] = createEventStore(el, "mousemove", "touchend", "click"); | ||
createEffect(() => { | ||
console.log(lastEvents?.mousemove.x); | ||
}); | ||
// to clear all the event listeners | ||
clear() | ||
// both target and options args can be reactive: | ||
const [target, setTarget] = createSignal(document.getElementById("abc")); | ||
const [lastEvents] = createEventStore(target, "mousemove", "touchmove"); | ||
// createEventStore can be used to listen to custom events | ||
// fill both type generics for the best type support | ||
const [lastEvents] = createEventStore< | ||
{ | ||
myEvent: MyEvent; | ||
custom: Event; | ||
unused: Event; | ||
}, | ||
"myEvent" | "custom" | ||
>(target, "myEvent", "custom"); | ||
// DON'T DO THIS: | ||
const [{ mousemove }] = createEventStore(target, "mousemove", ...); | ||
// the store cannot be destructured | ||
``` | ||
## `createEventListenerBus` | ||
###### Added in `@1.5.0` | ||
Dynamically add and remove event listeners to an event target by calling appropriate property. The listeners will be automatically removed on cleanup. | ||
### How to use it | ||
#### Import | ||
```ts | ||
import { createEventListenerBus } from "@solid-primitives/event-listener"; | ||
``` | ||
#### Basic usage | ||
`createEventListenerBus` takes two arguments: | ||
- `target` - the event target, could be a `window`, `document`, `HTMLElement` or `MediaQueryList`. _Defaults to `window`_ | ||
- `options` - event listener options, such as `passive` or `capture` | ||
```ts | ||
const bus = createEventListenerBus(document.body); | ||
bus.onpointerenter(e => {...}); | ||
// listeners return a function that removes them | ||
const clear = bus.onpointermove(e => {...}); | ||
clear(); | ||
``` | ||
#### Reactive target | ||
Target argument could be an **array**, and a **reactive signal**. | ||
```ts | ||
const [target, setTarget] = createSignal([document.body]); | ||
const bus = createEventListenerBus(target); | ||
setTarget(p => [...p, window]); // will change the targets of all active listeners. | ||
``` | ||
#### generic on() | ||
Using `bus.on(type, handler)` gives you more options in passing event type. | ||
```ts | ||
bus.on("click", e => {}); | ||
bus.on(["mousemove", "mousedown"], e => {}); | ||
const [types, setTypes] = createsignal(["focus", "blur"]); | ||
bus.on(types, e => {}); | ||
``` | ||
#### Custom Events | ||
The `createEventListenerBus` can be used to listen to Custom Events. | ||
```ts | ||
const bus = createEventListenerBus<{ | ||
foo: SomeEvent; | ||
bar: MyEvent; | ||
}>(); | ||
bus.onfoo(e => {}); | ||
bus.onbar(e => {}); | ||
``` | ||
#### Reactive Roots | ||
The EventListenerBus is designed in a way to let you add event listeners outside of reactive roots/ in different root then the one primitive was used in. | ||
```ts | ||
const bus = createRoot(dispose => { | ||
return createEventListenerBus(); | ||
}); | ||
// listeners can be added outside of the original root setup function. | ||
createRoot(dispose => { | ||
bus.onclick(e => {}); | ||
}); | ||
``` | ||
## `WindowEventListener` | ||
@@ -383,2 +337,12 @@ | ||
2.0.0 | ||
[PR#113](https://github.com/solidjs-community/solid-primitives/pull/113) | ||
Remove `createEventListenerBus`, `createEventListenerStore` & `eventListenerMap` | ||
Add `makeEventListener` and `makeEventListenerStack` | ||
Remove clear() functions from reactive primitives. | ||
</details> |
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
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
2
28097
345
339
- Removed@solid-primitives/rootless@^0.1.0
- Removed@solid-primitives/rootless@0.1.0(transitive)