Skip to content

Instantly share code, notes, and snippets.

@msandrini
Created May 14, 2021 09:20
Show Gist options
  • Save msandrini/f87eaeb438d38247ed8960e298c4a0e8 to your computer and use it in GitHub Desktop.
Save msandrini/f87eaeb438d38247ed8960e298c4a0e8 to your computer and use it in GitHub Desktop.
Vue 3 directive with Typescript - focusChangeNotify
import { DirectiveBinding, ObjectDirective } from 'vue'
type FocusableElement = HTMLInputElement | HTMLTextAreaElement
type NotificationCallback = (isNowFocused: boolean) => void
type GenericEventHandler = () => void
interface ExtendedDirective extends ObjectDirective {
handleFocus: GenericEventHandler
handleBlur: GenericEventHandler
}
const focusChangeNotify = {
handleFocus: (() => {}) as GenericEventHandler,
handleBlur: (() => {}) as GenericEventHandler,
mounted(element: FocusableElement, binding: DirectiveBinding) {
const callback = (binding.value as NotificationCallback)
const thisDirective = binding.dir as ExtendedDirective
thisDirective.handleFocus = () => { callback(true) }
thisDirective.handleBlur = () => { callback(false) }
element.addEventListener('focus', thisDirective.handleFocus)
element.addEventListener('blur', thisDirective.handleBlur)
},
beforeUnmount(element: FocusableElement, binding: DirectiveBinding) {
const thisDirective = binding.dir as ExtendedDirective
element.removeEventListener('focus', thisDirective.handleFocus)
element.removeEventListener('blur', thisDirective.handleBlur)
}
}
export default focusChangeNotify
@msandrini
Copy link
Author

In the end, I can just put this on my Vue component, on directives and use as v-notify-focus-change="anyFn" (with anyFn being a function that gets a boolean)

@Blackfaded
Copy link

Thank you for your input!
Based on your solution I came across with this one:

import { DirectiveBinding, ObjectDirective } from 'vue';

interface ExtendedDirective<T = any, V = any> extends ObjectDirective<T, V> {
  pointerdownEvent: V;
  pointerupEvent: V;
}

type CallbackType = () => void;

interface ExtendedHTMLElement extends HTMLElement {
  additionalData: {
    timeout: ReturnType<typeof setTimeout> | null;
  };
}
const pointerdownEvent = (el: ExtendedHTMLElement, binding: DirectiveBinding) => {
  const delay = binding.arg ? parseInt(binding.arg) : 500;
  el.additionalData.timeout = setTimeout(binding.value, delay);
};

const pointerupEvent = (el: ExtendedHTMLElement) => {
  if (el.additionalData.timeout !== null) {
    clearTimeout(el.additionalData.timeout);
  }
};

export const vLongpress: ExtendedDirective<ExtendedHTMLElement, CallbackType> = {
  pointerdownEvent: () => ({}),
  pointerupEvent: () => ({}),
  mounted(el, binding) {
    el.additionalData = {
      timeout: null,
    };
    const dir = binding.dir as ExtendedDirective<ExtendedHTMLElement, CallbackType>;
    dir.pointerdownEvent = () => pointerdownEvent(el, binding);
    dir.pointerupEvent = () => pointerupEvent(el);
    el.addEventListener('pointerdown', dir.pointerdownEvent);
    el.addEventListener('pointerup', dir.pointerupEvent);
    el.addEventListener('pointermove', dir.pointerupEvent);
  },
  beforeUnmount(el, binding) {
    const dir = binding.dir as ExtendedDirective<ExtendedHTMLElement, CallbackType>;
    el.removeEventListener('pointerdown', dir.pointerdownEvent);
    el.removeEventListener('pointerup', dir.pointerupEvent);
    el.removeEventListener('pointermove', dir.pointerupEvent);
  },
};

@LiamKarlMitchell
Copy link

LiamKarlMitchell commented Jun 21, 2022

From the above eslint complains about.
Unsafe argument of type any assigned to a parameter of type () => void

el.additionalData.timeout = setTimeout(binding.value, delay);

Casting it seemed to make it happy.

el.additionalData.timeout = setTimeout(binding.value as () => void, delay);

Or perhaps better

const pointerdownEvent = (el: ExtendedHTMLElement, binding: DirectiveBinding<CallbackType>) => {
  const delay = binding.arg ? parseInt(binding.arg) : 500;
  el.additionalData.timeout = setTimeout(binding.value, delay);
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment