diff --git a/README.md b/README.md index 372bf51..064a12c 100755 --- a/README.md +++ b/README.md @@ -46,10 +46,19 @@ createApp(App).use(NativeEventVue, options) | Property | Type | Description | | --- | --- | --- | -| `debugLog` | `boolean` or `undefined` | Print additional debugging information to the console. | +| `debugLog` | [`DebugLogLevel`](#debug-log-level) or `undefined` | Print additional debugging information to the console. By default a log level of `Error` is used. | | `nativeEventDirectiveName` | `string` or `undefined` | Optionally specify what to register for the native-event directive. By default this is `native-event` and the directive would be `v-native-event`. | | `propNamePrefix` | `string` or `undefined` | When an event is attached using this library a reference to the the libraries event instance is stored on the attached element using a property prefixed with this value and ending in the event name. This defaults to `native-event-vue-`. | +#### Debug Log Level + +| Level | Description | +| --- | --- | +| `Error` | Only errors are logged. | +| `Info` | Additional debugging information is logged when events are attached and detached. | +| `Verbose` | All additional debugging information is logged including when directive hooks fire and is certain situations in the debouncing logic. | + + ## Usage Native HTML events can be attached and debounced using either the `v-native-event` directive or the `useNativeEvent` composable depending on your situation. @@ -139,6 +148,10 @@ The following debounce behavior modes are available via the `DebounceMode` enum. ## Release Notes +### v1.2.0 + * Add debug logging level + * Additional source documentation + ### v1.1.0 * Add `ImmediateAndTimeout` and `MaximumFrequency` debounce modes. The default mode is now called `Timeout` and acts just as the debounce did previously. diff --git a/e2e/src/main.ts b/e2e/src/main.ts index 0cc6ec8..cd25073 100755 --- a/e2e/src/main.ts +++ b/e2e/src/main.ts @@ -1,6 +1,6 @@ import { createApp } from 'vue' import App from './App.vue' import router from './router' -import { NativeEventVue } from 'native-event-vue' +import { NativeEventVue, DebugLogLevel } from 'native-event-vue' -createApp(App).use(router).use(NativeEventVue, { debugLog: true }).mount('#app') +createApp(App).use(router).use(NativeEventVue, { debugLogLevel: DebugLogLevel.Verbose }).mount('#app') diff --git a/lib/NativeEventVue.ts b/lib/NativeEventVue.ts index e91f2fe..fb4273e 100644 --- a/lib/NativeEventVue.ts +++ b/lib/NativeEventVue.ts @@ -2,15 +2,40 @@ import type { App } from 'vue' import { nativeEventDirective } from './directives/nativeEvent' import { log } from './logger' +export enum DebugLogLevel { + /** + * Only errors are logged. + */ + Error = 0, + /** + * Additional debugging information is logged when events are attached and detached. + */ + Info = 1, + /** + * All additional debugging information is logged including when directive hooks fire and is certain situations in the debouncing logic. + */ + Verbose = 2, +} + export interface NativeEventVueOptions { + /** + * Print additional debugging information to the console. By default a log level of `Error` is used. + */ + debugLogLevel?: DebugLogLevel + /** + * Optionally specify what to register for the native-event directive. By default this is `native-event` and the directive would be `v-native-event`. + */ nativeEventDirectiveName?: string - debugLog?: boolean + /** + * When an event is attached using this library a reference to the the libraries event instance is stored on the attached element using a property + * prefixed with this value and ending in the event name. This defaults to `native-event-vue-`. + */ propNamePrefix?: string } export const nativeEventVueOptions = { nativeEventDirectiveName: 'native-event', - debugLog: false, + debugLogLevel: DebugLogLevel.Error, propNamePrefix: 'native-event-vue-', } as NativeEventVueOptions @@ -23,6 +48,13 @@ export default { }, } +/** + * Resolves the property name used to store the event instance on the element + * based on the configured prefix and the event name. + * @param event The event name. + * @returns The resolved property name. + * @see {@link NativeEventVueOptions.propNamePrefix} + */ export function resolveEventPropNamePrefix(event: string) { return `${nativeEventVueOptions.propNamePrefix}${event}` } diff --git a/lib/composables/useDebounce.ts b/lib/composables/useDebounce.ts index 4463639..c8ea246 100644 --- a/lib/composables/useDebounce.ts +++ b/lib/composables/useDebounce.ts @@ -1,6 +1,7 @@ import { ref } from 'vue' import { log } from '../logger' import { useEnsure } from './useEnsure' +import { DebugLogLevel } from '../NativeEventVue' export type FunctionToDebounce = (...args: any[]) => unknown @@ -71,7 +72,7 @@ export function useDebounce( * If this is the first call, execute immediately */ if (!lastCallTimestamp.value) { - log('useDebounce | first call', { args }) + log('useDebounce | first call', { args }, DebugLogLevel.Verbose) lastArgs.value = args return execute() } @@ -82,7 +83,7 @@ export function useDebounce( */ const elapsed = Date.now() - lastCallTimestamp.value if (!timeoutId.value && elapsed > timeoutMs) { - log('useDebounce | subsequent call within timeout', { args, elapsed }) + log('useDebounce | subsequent call within timeout', { args, elapsed }, DebugLogLevel.Verbose) lastArgs.value = args return execute() } @@ -103,12 +104,12 @@ export function useDebounce( timeoutId.value = window.setTimeout(() => { execute() - log('useDebounce | timeout reached', { args: lastArgs.value, lastCallTimestamp: lastCallTimestamp.value }) + log('useDebounce | timeout reached', { args: lastArgs.value, lastCallTimestamp: lastCallTimestamp.value }, DebugLogLevel.Verbose) }, timeout) - log('useDebounce | timeout reset', { args: lastArgs.value, lastCallTimestamp: lastCallTimestamp.value, timeout }) + log('useDebounce | timeout reset', { args: lastArgs.value, lastCallTimestamp: lastCallTimestamp.value, timeout }, DebugLogLevel.Verbose) } else { - log('useDebounce | maximum frequency mode skip', { args: lastArgs.value, lastCallTimestamp: lastCallTimestamp.value }) + log('useDebounce | maximum frequency mode skip', { args: lastArgs.value, lastCallTimestamp: lastCallTimestamp.value }, DebugLogLevel.Verbose) } } diff --git a/lib/composables/useNativeEvent.ts b/lib/composables/useNativeEvent.ts index a0ed5d5..9ee4cff 100644 --- a/lib/composables/useNativeEvent.ts +++ b/lib/composables/useNativeEvent.ts @@ -6,6 +6,18 @@ import { resolveEventPropNamePrefix } from '../NativeEventVue' export type NativeEvent = { destroy: () => void } | undefined +/** + * Composable to attach an HTML native event to an element. + * @param domEl The DOM element to attach the event listener to. + * @param event The name of the native event (e.g. `resize`). + * @param listener The event handler function to attach. This is the same type as the browser + * API [`addEventListener.listener` parameter](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#the_event_listener_callback). + * @param options Optional. This is the same type as the browser API [`addEventListener.options` parameter](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options). + * @param debounceMs Optionally specify a debounce timeout. + * @param debounceMode Specify the type of desired debounce behavior. Defaults to `Timeout`. + * @param replaceExisting Optionally specify to replace any existing event handler that was attached using `native-event-vue`. Otherwise the new event listener will not be attached. + * @returns {@link NativeEvent} object with a destroy method to remove the event listener. + */ export function useNativeEvent( domEl: HTMLElement, event: string, diff --git a/lib/directives/nativeEvent.ts b/lib/directives/nativeEvent.ts index d470076..80fa3e8 100644 --- a/lib/directives/nativeEvent.ts +++ b/lib/directives/nativeEvent.ts @@ -1,15 +1,34 @@ import type { DirectiveBinding, VNode } from 'vue' import { log } from '../logger' import { useNativeEvent } from '../composables/useNativeEvent' -import { resolveEventPropNamePrefix } from '../NativeEventVue' +import { DebugLogLevel, resolveEventPropNamePrefix } from '../NativeEventVue' import type { DebounceMode } from '../composables/useDebounce' export interface NativeEventOptions { + /** + * The name of the native event (e.g. `resize`). + */ event: string + /** + * The event handler function to attach. This is the same type as the browser + * API [`addEventListener.listener` parameter](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#the_event_listener_callback). + */ listener: EventListenerOrEventListenerObject + /** + * Optional. This is the same type as the browser API [`addEventListener.options` parameter](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options). + */ options?: boolean | AddEventListenerOptions + /** + * Optionally specify a debounce timeout. + */ debounceMs?: number + /** + * Specify the type of desired debounce behavior. Defaults to `Timeout`. + */ debounceMode?: DebounceMode + /** + * Optionally disable/remove the event handler. + */ disabled?: boolean | null | undefined } @@ -25,7 +44,7 @@ export const nativeEventDirective = { } addEventListener(domEl, binding, true) - log('native-event | beforeMount', { domEl, binding: binding.value }) + log('native-event | beforeMount', { domEl, binding: binding.value }, DebugLogLevel.Verbose) }, updated: ( domEl: HTMLElement, @@ -37,7 +56,7 @@ export const nativeEventDirective = { return removeEventListener(domEl, binding) } addEventListener(domEl, binding, false) - log('native-event | updated', { domEl, binding: binding.value }) + log('native-event | updated', { domEl, binding: binding.value }, DebugLogLevel.Verbose) }, beforeUnmount: ( domEl: HTMLElement, @@ -46,7 +65,7 @@ export const nativeEventDirective = { prevVnode: VNode | null, ) => { removeEventListener(domEl, binding) - log('native-event | beforeUnmount', { domEl, binding: binding.value }) + log('native-event | beforeUnmount', { domEl, binding: binding.value }, DebugLogLevel.Verbose) }, } diff --git a/lib/logger.ts b/lib/logger.ts index c7bbbe6..43881b6 100644 --- a/lib/logger.ts +++ b/lib/logger.ts @@ -1,11 +1,14 @@ -import { nativeEventVueOptions } from './NativeEventVue' +import { DebugLogLevel, nativeEventVueOptions } from './NativeEventVue' -function shouldLog(): boolean { - return nativeEventVueOptions.debugLog === true || import.meta.env['VITE_DEBUG_NATIVE_EVENT_VUE'] === 'true' +function shouldLog(logLevel: DebugLogLevel): boolean { + return (nativeEventVueOptions.debugLogLevel ?? DebugLogLevel.Error) >= logLevel || import.meta.env['VITE_DEBUG_NATIVE_EVENT_VUE'] === 'true' } -export function log(eventName: string, data: any) { - if (shouldLog()) { +export function log(eventName: string, data: any, logLevel: DebugLogLevel = DebugLogLevel.Info) { + if (logLevel === DebugLogLevel.Error) { + return logError(eventName, data) + } + if (shouldLog(logLevel)) { console.groupCollapsed(`%c native-event-vue | ${eventName} | ${new Date().toISOString()}`, 'color: orange; font-weight: bold;') console.log({ eventName, ...data }) console.groupEnd() @@ -13,7 +16,7 @@ export function log(eventName: string, data: any) { } export function logError(eventName: string, data: any) { - if (shouldLog()) { + if (shouldLog(DebugLogLevel.Error)) { console.groupCollapsed(`%c ERROR native-event-vue | ${eventName} | ${new Date().toISOString()}`, 'color: red; font-weight: bold;') console.error({ eventName, ...data }) console.groupEnd() diff --git a/lib/main.ts b/lib/main.ts index 1377de5..72d3b31 100755 --- a/lib/main.ts +++ b/lib/main.ts @@ -1,4 +1,4 @@ -import NativeEventVue, { type NativeEventVueOptions, nativeEventVueOptions, resolveEventPropNamePrefix } from './NativeEventVue' +import NativeEventVue, { type NativeEventVueOptions, nativeEventVueOptions, resolveEventPropNamePrefix, DebugLogLevel } from './NativeEventVue' import { useNativeEvent, type NativeEvent } from './composables/useNativeEvent' import { DebounceMode } from './composables/useDebounce' import { type NativeEventOptions } from './directives/nativeEvent' @@ -12,4 +12,5 @@ export { type NativeEvent, type NativeEventOptions, DebounceMode, + DebugLogLevel, } diff --git a/package.json b/package.json index 09057e8..a18e3c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "native-event-vue", - "version": "1.1.0", + "version": "1.2.0", "type": "module", "private": false, "description": "Directives and composables for wiring up and debouncing native HTML events in Vue.",