UNPKG

@selenite/commons

Version:

This typescript package provides a set of frequently used utilities, types and svelte actions for building projects with Typescript and Svelte.

133 lines (132 loc) 4.51 kB
/** * Svelte action to add keyboard shortcuts. * * @see {@link shortcut} * @module */ import { isArray } from 'lodash-es'; /** * Returns a listener function that checks if a keyboard shortcut is triggered. * @param node - node the shortcut is bound to * @param params - settings of the shortcut * @returns keyboard listener */ function makeShortcutListener(node, params) { const { shortcuts = [], key, alt, shift, ctrl, action, ignoreElements = ['INPUT', 'TEXTAREA'] } = params; const shortShortcutDef = key || ctrl || alt || shift ? { key, ctrl, alt, shift } : undefined; let triggered = false; return (e) => { if (params.repeats === false && triggered) return; const target = e.target; if (target instanceof HTMLElement) { if (ignoreElements.includes(target?.tagName) || target.contentEditable === 'true') { if (target?.tagName === 'INPUT') { const input = target; if (input.type !== 'checkbox' && input.type !== 'radio') return; } else { return; } } } let triggeredShortcut = undefined; if (shortShortcutDef && isShortcutTriggered(e, shortShortcutDef)) { triggeredShortcut = shortShortcutDef; } else if (typeof shortcuts === 'function') { if (!shortcuts(e)) return; triggeredShortcut = { key: e.key, ctrl: e.ctrlKey, alt: e.altKey, shift: e.shiftKey }; } else { for (const shortcut of isArray(shortcuts) ? shortcuts : [shortcuts]) { if (isShortcutTriggered(e, shortcut)) { triggeredShortcut = shortcut; break; } } } if (!triggeredShortcut) return; // Prevent default behavior if key is defined // We don't want to prevent default behavior if only // modifier keys are defined if (triggeredShortcut.key) e.preventDefault(); console.debug(`shortcut: ${shortcutToString(triggeredShortcut)}`); if (action) action(node, e); if (triggered === false) { if (params.endAction) { function triggerEndAction(e) { console.debug('Trigger end action.'); params.endAction?.(node, e); document.removeEventListener('keyup', triggerEndAction); triggered = false; } document.addEventListener('keyup', triggerEndAction); } triggered = true; } }; } /** * Returns whether a shortcut definition has been triggered. * @param e - keyboard event * @param shortcut - shortcut definition * @returns whether shortcut is triggered */ function isShortcutTriggered(e, shortcut) { return ((shortcut.key === undefined ? true : e.key.toLowerCase() === shortcut.key.toLowerCase()) && e.ctrlKey === !!shortcut.ctrl && e.altKey === !!shortcut.alt && e.shiftKey === !!shortcut.shift); } /** * Converts a keyboard shortcut definition to a string. * @param param0 - Keyboard shortcut definition * @returns */ export function shortcutToString({ ctrl, alt, shift, key }) { const pieces = []; if (ctrl) pieces.push('Ctrl'); if (alt) pieces.push('Alt'); if (shift) pieces.push('Shift'); if (key) pieces.push(key.toUpperCase()); return pieces.join('+'); } /** * Action to add a keyboard shortcut. * * All keyboard listeners are attached to document. * @param node - The element the shortcut is bound to * @param params - Settings for the shortcut * @returns svelte action to add a keyboard shortcut */ export function shortcut(node, params) { let listener = makeShortcutListener(node, params); document.addEventListener('keydown', listener, { capture: true }); return { destroy() { document.removeEventListener('keydown', listener, { capture: true }); }, update(params) { document.removeEventListener('keydown', listener, { capture: true }); listener = makeShortcutListener(node, params); document.addEventListener('keydown', listener, { capture: true }); } }; }