what-input 5.2.11 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +253 -60
- package/dist/what-input.d.mts +88 -0
- package/dist/what-input.d.ts +88 -0
- package/dist/what-input.js +296 -512
- package/dist/what-input.min.js +17 -2
- package/dist/what-input.min.js.map +7 -1
- package/dist/what-input.mjs +284 -0
- package/package.json +51 -30
- package/.browserslistrc +0 -5
- package/.editorconfig +0 -12
- package/.eslintrc +0 -12
- package/.nvmrc +0 -1
- package/.prettierrc +0 -4
- package/.stylelintrc +0 -9
- package/Gulpfile.js +0 -178
- package/src/images/select-arrow.svg +0 -1
- package/src/markup/index.html +0 -141
- package/src/scripts/what-input.d.ts +0 -15
- package/src/scripts/what-input.js +0 -482
- package/src/styles/_html.scss +0 -49
- package/src/styles/_layout.scss +0 -229
- package/src/styles/_mixins.scss +0 -70
- package/src/styles/_variables.scss +0 -2
- package/src/styles/index.scss +0 -51
package/dist/what-input.min.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* what-input - A global utility for tracking the current input method (mouse, keyboard or touch).
|
|
3
|
-
* @version
|
|
3
|
+
* @version v6.0.0
|
|
4
4
|
* @link https://github.com/ten1seven/what-input
|
|
5
5
|
* @license MIT
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
;(function (root, factory) {
|
|
8
|
+
if (typeof exports === 'object' && typeof module === 'object') {
|
|
9
|
+
module.exports = factory()
|
|
10
|
+
} else if (typeof define === 'function' && define.amd) {
|
|
11
|
+
define('whatInput', [], factory)
|
|
12
|
+
} else {
|
|
13
|
+
root.whatInput = factory()
|
|
14
|
+
}
|
|
15
|
+
})(
|
|
16
|
+
typeof globalThis !== 'undefined' ? globalThis : typeof self !== 'undefined' ? self : this,
|
|
17
|
+
function () {
|
|
18
|
+
var __whatInput
|
|
19
|
+
"use strict";(()=>{var i="data-what",r={input:"what-input",intent:"what-intent"},p=new Set(["initial","mouse","keyboard","touch"]),u={mouse:"mouse",pen:"touch",touch:"touch"},v=["Shift","Control","Alt","Meta","ContextMenu"],y=new Set(["input","select","textarea"]),E=new Set(["checkbox","radio"]),m="[data-whatfreeze]",c={touch:"(pointer: coarse)",mouse:"(pointer: fine)"},l=()=>typeof window<"u"&&typeof document<"u",f=o=>typeof o=="string"&&p.has(o),w=()=>{let o=document.activeElement;for(;o?.shadowRoot?.activeElement;)o=o.shadowRoot.activeElement;return o},g=o=>{if(!o)return!1;let t=o.localName;return t==="input"?!E.has(o.type):y.has(t)||t==="button"&&o.closest("form")!==null||o.isContentEditable===!0||o.closest(m)!==null?!0:o.constructor.formAssociated===!0},b=()=>typeof window.matchMedia=="function"?Object.values(c).map(o=>window.matchMedia(o)):[],d=()=>{if(typeof window.matchMedia!="function")return"unknown";for(let[o,t]of Object.entries(c))if(window.matchMedia(t).matches)return o;return"unknown"},I=o=>{let t=o.composedPath()[0]??o.target;return t instanceof Element?t:null},a=class extends EventTarget{#t={input:"initial",intent:"initial",hint:"unknown"};#n=null;#s=!1;#u=v;#r=[];#o={input:new Set,intent:new Set,hint:new Set};#a=null;#l={x:NaN,y:NaN};constructor(){super(),l()&&this.#p()}ask(t="input"){return this.#t[t]}element(){return this.#n}ignoreKeys(t){this.#u=[...t]}specificKeys(t){this.#r=[...t]}registerOnChange(t,e="input"){this.#o[e].add(t)}unRegisterOnChange(t){for(let e of Object.values(this.#o))e.delete(t)}clearStorage(){try{for(let t of Object.values(r))window.sessionStorage.removeItem(t)}catch{}}destroy(){this.#a?.abort(),this.#a=null;for(let e of Object.values(this.#o))e.clear();if(!l())return;let t=document.documentElement;for(let e of["input","intent","hint","element","classes"])t.removeAttribute(i+e)}#p(){let t=new AbortController,{signal:e}=t;this.#a=t;let n={capture:!0,passive:!0,signal:e};window.addEventListener("pointerdown",this.#v,n),window.addEventListener("keydown",this.#c,n),window.addEventListener("keyup",this.#c,n),window.addEventListener("pointermove",this.#y,n),window.addEventListener("wheel",this.#E,n),window.addEventListener("focusin",this.#f,n),window.addEventListener("focusout",this.#h,n);for(let s of b())s.addEventListener("change",this.#m,{signal:e});document.readyState==="loading"?document.addEventListener("DOMContentLoaded",this.#d,{once:!0,signal:e}):this.#d()}#d=()=>{let{documentElement:t,body:e}=document;if(this.#s=t.dataset.whatpersist!=="false"&&e?.dataset.whatpersist!=="false",this.#s)for(let n of["input","intent"]){let s=this.#b(n);s&&this.#t[n]==="initial"&&(this.#t[n]=s)}this.#t.hint=d(),this.#i("input"),this.#i("intent"),this.#i("hint")};#v=t=>{let e=u[t.pointerType];e&&(this.#e("input",e),this.#e("intent",e))};#y=t=>{let e=u[t.pointerType];if(!e)return;let{x:n,y:s}=this.#l;this.#l={x:t.screenX,y:t.screenY},!(t.screenX===n&&t.screenY===s)&&this.#e("intent",e)};#E=()=>{this.#e("intent","mouse")};#m=()=>{this.#e("hint",d())};#c=t=>{this.#w(t)&&(this.#e("input","keyboard"),g(w())||this.#e("intent","keyboard"))};#f=t=>{let e=I(t);if(!e){this.#h();return}let n=document.documentElement;this.#n=e.localName,n.setAttribute(i+"element",this.#n),e.classList.length?n.setAttribute(i+"classes",Array.from(e.classList).join(",")):n.removeAttribute(i+"classes")};#h=()=>{let t=document.documentElement;this.#n=null,t.removeAttribute(i+"element"),t.removeAttribute(i+"classes")};#w(t){let e=n=>typeof n=="number"?n===t.keyCode:n===t.key||n===t.code;return this.#r.length?this.#r.some(e):!this.#u.some(e)}#e(t,e){this.#t[t]!==e&&(this.#t[t]=e,this.#g(t,e),this.#i(t))}#i(t){let e=this.#t[t];document.documentElement.setAttribute(i+t,e);for(let n of this.#o[t])n(e);this.dispatchEvent(new CustomEvent(t,{detail:e}))}#g(t,e){if(!(t==="hint"||!this.#s))try{window.sessionStorage.setItem(r[t],e)}catch{}}#b(t){try{let e=window.sessionStorage.getItem(r[t]);return f(e)?e:null}catch{return null}}},M=new a,h=M;__whatInput=h;})();
|
|
20
|
+
return __whatInput
|
|
21
|
+
},
|
|
22
|
+
)
|
|
8
23
|
//# sourceMappingURL=what-input.min.js.map
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{"version":3,"sources":["what-input.js"],"names":["root","factory","exports","module","define","amd","this","installedModules","__webpack_require__","m","modules","document","window","ask","element","ignoreKeys","specificKeys","registerOnChange","unRegisterOnChange","docElem","documentElement","currentElement","currentInput","currentIntent","currentTimestamp","Date","now","shouldPersist","formInputs","functionList","ignoreMap","specificMap","inputMap","keydown","keyup","mousedown","mousemove","MSPointerDown","MSPointerMove","pointerdown","pointermove","touchstart","touchend","isScrolling","mousePos","x","y","pointerMap","2","3","4","supportsPassive","opts","Object","defineProperty","get","addEventListener","e","addListeners","options","passive","capture","setPersist","PointerEvent","setInput","setIntent","MSPointerEvent","detectWheel","setElement","clearElement","getAttribute","body","sessionStorage","getItem","doUpdate","event","eventKey","which","value","type","pointerType","ignoreMatch","length","indexOf","specificMatch","shouldUpdate","validateTouch","persistInput","activeElem","activeElement","nodeName","toLowerCase","checkClosest","setAttribute","fireFunctions","detectScrolling","target","classList","toString","replace","removeAttribute","setItem","timestamp","touchIsValid","createElement","undefined","onmousewheel","i","len","fn","call","screenX","screenY","elem","tag","ElementPrototype","Element","prototype","matches","msMatchesSelector","webkitMatchesSelector","closest","parentElement","parentNode","nodeType","Array","opt","arr","eventType","push","position","match","objPos","splice","clearStorage","clear","c","p","moduleId","id","loaded"],"mappings":"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","file":"what-input.min.js","sourcesContent":["/**\n * what-input - A global utility for tracking the current input method (mouse, keyboard or touch).\n * @version v5.2.11\n * @link https://github.com/ten1seven/what-input\n * @license MIT\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"whatInput\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"whatInput\"] = factory();\n\telse\n\t\troot[\"whatInput\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId])\n/******/ \t\t\treturn installedModules[moduleId].exports;\n\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\texports: {},\n/******/ \t\t\tid: moduleId,\n/******/ \t\t\tloaded: false\n/******/ \t\t};\n\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.loaded = true;\n\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n\n\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\n\tmodule.exports = function () {\n\t /*\n\t * bail out if there is no document or window\n\t * (i.e. in a node/non-DOM environment)\n\t *\n\t * Return a stubbed API instead\n\t */\n\t if (typeof document === 'undefined' || typeof window === 'undefined') {\n\t return {\n\t // always return \"initial\" because no interaction will ever be detected\n\t ask: function ask() {\n\t return 'initial';\n\t },\n\n\t // always return null\n\t element: function element() {\n\t return null;\n\t },\n\n\t // no-op\n\t ignoreKeys: function ignoreKeys() {},\n\n\t // no-op\n\t specificKeys: function specificKeys() {},\n\n\t // no-op\n\t registerOnChange: function registerOnChange() {},\n\n\t // no-op\n\t unRegisterOnChange: function unRegisterOnChange() {}\n\t };\n\t }\n\n\t /*\n\t * variables\n\t */\n\n\t // cache document.documentElement\n\t var docElem = document.documentElement;\n\n\t // currently focused dom element\n\t var currentElement = null;\n\n\t // last used input type\n\t var currentInput = 'initial';\n\n\t // last used input intent\n\t var currentIntent = currentInput;\n\n\t // UNIX timestamp of current event\n\t var currentTimestamp = Date.now();\n\n\t // check for a `data-whatpersist` attribute on either the `html` or `body` elements, defaults to `true`\n\t var shouldPersist = 'false';\n\n\t // form input types\n\t var formInputs = ['button', 'input', 'select', 'textarea'];\n\n\t // empty array for holding callback functions\n\t var functionList = [];\n\n\t // list of modifier keys commonly used with the mouse and\n\t // can be safely ignored to prevent false keyboard detection\n\t var ignoreMap = [16, // shift\n\t 17, // control\n\t 18, // alt\n\t 91, // Windows key / left Apple cmd\n\t 93 // Windows menu / right Apple cmd\n\t ];\n\n\t var specificMap = [];\n\n\t // mapping of events to input types\n\t var inputMap = {\n\t keydown: 'keyboard',\n\t keyup: 'keyboard',\n\t mousedown: 'mouse',\n\t mousemove: 'mouse',\n\t MSPointerDown: 'pointer',\n\t MSPointerMove: 'pointer',\n\t pointerdown: 'pointer',\n\t pointermove: 'pointer',\n\t touchstart: 'touch',\n\t touchend: 'touch'\n\n\t // boolean: true if the page is being scrolled\n\t };var isScrolling = false;\n\n\t // store current mouse position\n\t var mousePos = {\n\t x: null,\n\t y: null\n\n\t // map of IE 10 pointer events\n\t };var pointerMap = {\n\t 2: 'touch',\n\t 3: 'touch', // treat pen like touch\n\t 4: 'mouse'\n\n\t // check support for passive event listeners\n\t };var supportsPassive = false;\n\n\t try {\n\t var opts = Object.defineProperty({}, 'passive', {\n\t get: function get() {\n\t supportsPassive = true;\n\t }\n\t });\n\n\t window.addEventListener('test', null, opts);\n\t } catch (e) {}\n\t // fail silently\n\n\n\t /*\n\t * set up\n\t */\n\n\t var setUp = function setUp() {\n\t // add correct mouse wheel event mapping to `inputMap`\n\t inputMap[detectWheel()] = 'mouse';\n\n\t addListeners();\n\t };\n\n\t /*\n\t * events\n\t */\n\n\t var addListeners = function addListeners() {\n\t // `pointermove`, `MSPointerMove`, `mousemove` and mouse wheel event binding\n\t // can only demonstrate potential, but not actual, interaction\n\t // and are treated separately\n\t var options = supportsPassive ? { passive: true, capture: true } : true;\n\n\t document.addEventListener('DOMContentLoaded', setPersist, true);\n\n\t // pointer events (mouse, pen, touch)\n\t if (window.PointerEvent) {\n\t window.addEventListener('pointerdown', setInput, true);\n\t window.addEventListener('pointermove', setIntent, true);\n\t } else if (window.MSPointerEvent) {\n\t window.addEventListener('MSPointerDown', setInput, true);\n\t window.addEventListener('MSPointerMove', setIntent, true);\n\t } else {\n\t // mouse events\n\t window.addEventListener('mousedown', setInput, true);\n\t window.addEventListener('mousemove', setIntent, true);\n\n\t // touch events\n\t if ('ontouchstart' in window) {\n\t window.addEventListener('touchstart', setInput, options);\n\t window.addEventListener('touchend', setInput, true);\n\t }\n\t }\n\n\t // mouse wheel\n\t window.addEventListener(detectWheel(), setIntent, options);\n\n\t // keyboard events\n\t window.addEventListener('keydown', setInput, true);\n\t window.addEventListener('keyup', setInput, true);\n\n\t // focus events\n\t window.addEventListener('focusin', setElement, true);\n\t window.addEventListener('focusout', clearElement, true);\n\t };\n\n\t // checks if input persistence should happen and\n\t // get saved state from session storage if true (defaults to `false`)\n\t var setPersist = function setPersist() {\n\t shouldPersist = !(docElem.getAttribute('data-whatpersist') || document.body.getAttribute('data-whatpersist') === 'false');\n\n\t if (shouldPersist) {\n\t // check for session variables and use if available\n\t try {\n\t if (window.sessionStorage.getItem('what-input')) {\n\t currentInput = window.sessionStorage.getItem('what-input');\n\t }\n\n\t if (window.sessionStorage.getItem('what-intent')) {\n\t currentIntent = window.sessionStorage.getItem('what-intent');\n\t }\n\t } catch (e) {\n\t // fail silently\n\t }\n\t }\n\n\t // always run these so at least `initial` state is set\n\t doUpdate('input');\n\t doUpdate('intent');\n\t };\n\n\t // checks conditions before updating new input\n\t var setInput = function setInput(event) {\n\t var eventKey = event.which;\n\t var value = inputMap[event.type];\n\n\t if (value === 'pointer') {\n\t value = pointerType(event);\n\t }\n\n\t var ignoreMatch = !specificMap.length && ignoreMap.indexOf(eventKey) === -1;\n\n\t var specificMatch = specificMap.length && specificMap.indexOf(eventKey) !== -1;\n\n\t var shouldUpdate = value === 'keyboard' && eventKey && (ignoreMatch || specificMatch) || value === 'mouse' || value === 'touch';\n\n\t // prevent touch detection from being overridden by event execution order\n\t if (validateTouch(value)) {\n\t shouldUpdate = false;\n\t }\n\n\t if (shouldUpdate && currentInput !== value) {\n\t currentInput = value;\n\n\t persistInput('input', currentInput);\n\t doUpdate('input');\n\t }\n\n\t if (shouldUpdate && currentIntent !== value) {\n\t // preserve intent for keyboard interaction with form fields\n\t var activeElem = document.activeElement;\n\t var notFormInput = activeElem && activeElem.nodeName && (formInputs.indexOf(activeElem.nodeName.toLowerCase()) === -1 || activeElem.nodeName.toLowerCase() === 'button' && !checkClosest(activeElem, 'form'));\n\n\t if (notFormInput) {\n\t currentIntent = value;\n\n\t persistInput('intent', currentIntent);\n\t doUpdate('intent');\n\t }\n\t }\n\t };\n\n\t // updates the doc and `inputTypes` array with new input\n\t var doUpdate = function doUpdate(which) {\n\t docElem.setAttribute('data-what' + which, which === 'input' ? currentInput : currentIntent);\n\n\t fireFunctions(which);\n\t };\n\n\t // updates input intent for `mousemove` and `pointermove`\n\t var setIntent = function setIntent(event) {\n\t var value = inputMap[event.type];\n\n\t if (value === 'pointer') {\n\t value = pointerType(event);\n\t }\n\n\t // test to see if `mousemove` happened relative to the screen to detect scrolling versus mousemove\n\t detectScrolling(event);\n\n\t // only execute if scrolling isn't happening\n\t if ((!isScrolling && !validateTouch(value) || isScrolling && event.type === 'wheel' || event.type === 'mousewheel' || event.type === 'DOMMouseScroll') && currentIntent !== value) {\n\t currentIntent = value;\n\n\t persistInput('intent', currentIntent);\n\t doUpdate('intent');\n\t }\n\t };\n\n\t var setElement = function setElement(event) {\n\t if (!event.target.nodeName) {\n\t // If nodeName is undefined, clear the element\n\t // This can happen if click inside an <svg> element.\n\t clearElement();\n\t return;\n\t }\n\n\t currentElement = event.target.nodeName.toLowerCase();\n\t docElem.setAttribute('data-whatelement', currentElement);\n\n\t if (event.target.classList && event.target.classList.length) {\n\t docElem.setAttribute('data-whatclasses', event.target.classList.toString().replace(' ', ','));\n\t }\n\t };\n\n\t var clearElement = function clearElement() {\n\t currentElement = null;\n\n\t docElem.removeAttribute('data-whatelement');\n\t docElem.removeAttribute('data-whatclasses');\n\t };\n\n\t var persistInput = function persistInput(which, value) {\n\t if (shouldPersist) {\n\t try {\n\t window.sessionStorage.setItem('what-' + which, value);\n\t } catch (e) {\n\t // fail silently\n\t }\n\t }\n\t };\n\n\t /*\n\t * utilities\n\t */\n\n\t var pointerType = function pointerType(event) {\n\t if (typeof event.pointerType === 'number') {\n\t return pointerMap[event.pointerType];\n\t } else {\n\t // treat pen like touch\n\t return event.pointerType === 'pen' ? 'touch' : event.pointerType;\n\t }\n\t };\n\n\t // prevent touch detection from being overridden by event execution order\n\t var validateTouch = function validateTouch(value) {\n\t var timestamp = Date.now();\n\n\t var touchIsValid = value === 'mouse' && currentInput === 'touch' && timestamp - currentTimestamp < 200;\n\n\t currentTimestamp = timestamp;\n\n\t return touchIsValid;\n\t };\n\n\t // detect version of mouse wheel event to use\n\t // via https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event\n\t var detectWheel = function detectWheel() {\n\t var wheelType = null;\n\n\t // Modern browsers support \"wheel\"\n\t if ('onwheel' in document.createElement('div')) {\n\t wheelType = 'wheel';\n\t } else {\n\t // Webkit and IE support at least \"mousewheel\"\n\t // or assume that remaining browsers are older Firefox\n\t wheelType = document.onmousewheel !== undefined ? 'mousewheel' : 'DOMMouseScroll';\n\t }\n\n\t return wheelType;\n\t };\n\n\t // runs callback functions\n\t var fireFunctions = function fireFunctions(type) {\n\t for (var i = 0, len = functionList.length; i < len; i++) {\n\t if (functionList[i].type === type) {\n\t functionList[i].fn.call(undefined, type === 'input' ? currentInput : currentIntent);\n\t }\n\t }\n\t };\n\n\t // finds matching element in an object\n\t var objPos = function objPos(match) {\n\t for (var i = 0, len = functionList.length; i < len; i++) {\n\t if (functionList[i].fn === match) {\n\t return i;\n\t }\n\t }\n\t };\n\n\t var detectScrolling = function detectScrolling(event) {\n\t if (mousePos.x !== event.screenX || mousePos.y !== event.screenY) {\n\t isScrolling = false;\n\n\t mousePos.x = event.screenX;\n\t mousePos.y = event.screenY;\n\t } else {\n\t isScrolling = true;\n\t }\n\t };\n\n\t // manual version of `closest()`\n\t var checkClosest = function checkClosest(elem, tag) {\n\t var ElementPrototype = window.Element.prototype;\n\n\t if (!ElementPrototype.matches) {\n\t ElementPrototype.matches = ElementPrototype.msMatchesSelector || ElementPrototype.webkitMatchesSelector;\n\t }\n\n\t if (!ElementPrototype.closest) {\n\t do {\n\t if (elem.matches(tag)) {\n\t return elem;\n\t }\n\n\t elem = elem.parentElement || elem.parentNode;\n\t } while (elem !== null && elem.nodeType === 1);\n\n\t return null;\n\t } else {\n\t return elem.closest(tag);\n\t }\n\t };\n\n\t /*\n\t * init\n\t */\n\n\t // don't start script unless browser cuts the mustard\n\t // (also passes if polyfills are used)\n\t if ('addEventListener' in window && Array.prototype.indexOf) {\n\t setUp();\n\t }\n\n\t /*\n\t * api\n\t */\n\n\t return {\n\t // returns string: the current input type\n\t // opt: 'intent'|'input'\n\t // 'input' (default): returns the same value as the `data-whatinput` attribute\n\t // 'intent': includes `data-whatintent` value if it's different than `data-whatinput`\n\t ask: function ask(opt) {\n\t return opt === 'intent' ? currentIntent : currentInput;\n\t },\n\n\t // returns string: the currently focused element or null\n\t element: function element() {\n\t return currentElement;\n\t },\n\n\t // overwrites ignored keys with provided array\n\t ignoreKeys: function ignoreKeys(arr) {\n\t ignoreMap = arr;\n\t },\n\n\t // overwrites specific char keys to update on\n\t specificKeys: function specificKeys(arr) {\n\t specificMap = arr;\n\t },\n\n\t // attach functions to input and intent \"events\"\n\t // funct: function to fire on change\n\t // eventType: 'input'|'intent'\n\t registerOnChange: function registerOnChange(fn, eventType) {\n\t functionList.push({\n\t fn: fn,\n\t type: eventType || 'input'\n\t });\n\t },\n\n\t unRegisterOnChange: function unRegisterOnChange(fn) {\n\t var position = objPos(fn);\n\n\t if (position || position === 0) {\n\t functionList.splice(position, 1);\n\t }\n\t },\n\n\t clearStorage: function clearStorage() {\n\t window.sessionStorage.clear();\n\t }\n\t };\n\t}();\n\n/***/ })\n/******/ ])\n});\n;"]}
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/what-input.ts", "../scripts/umd-entry.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * What Input?\n *\n * A global utility for tracking the current input method (mouse, keyboard or\n * touch). It writes `data-whatinput`, `data-whatintent`, `data-whatelement`\n * and `data-whatclasses` attributes to the `<html>` element and exposes a\n * small API for scripting against the same information.\n */\n\n/** The input methods What Input can report. */\nexport type InputMethod = 'initial' | 'mouse' | 'keyboard' | 'touch'\n\n/**\n * `input` is the last *direct* interaction (`pointerdown`, `keydown`).\n * `intent` also includes *indirect* signals (`pointermove`, `wheel`) and is\n * preserved while typing into form fields.\n */\nexport type Strategy = 'input' | 'intent'\n\n/**\n * What the device's *primary* pointer suggests before (or regardless of) any\n * interaction, read from the `pointer` interaction media feature. It is a\n * hint only: `data-whatinput` and `data-whatintent` never use it.\n */\nexport type Hint = 'mouse' | 'touch' | 'unknown'\n\n/** A callback registered with `registerOnChange()`. */\nexport type ChangeCallback = (type: InputMethod) => void\n\n/** A callback registered with `registerOnChange(callback, 'hint')`. */\nexport type HintCallback = (hint: Hint) => void\n\ntype Channel = Strategy | 'hint'\n\ntype Callback = ChangeCallback | HintCallback\n\n/**\n * A key to ignore or require. Strings are matched against `KeyboardEvent.key`\n * and `KeyboardEvent.code` (`'Shift'`, `'Tab'`, `'KeyA'`); numbers are matched\n * against the legacy `KeyboardEvent.keyCode` for backwards compatibility.\n */\nexport type KeyMatcher = string | number\n\n/** The events dispatched by the `whatInput` EventTarget. */\nexport interface WhatInputEventMap {\n input: CustomEvent<InputMethod>\n intent: CustomEvent<InputMethod>\n hint: CustomEvent<Hint>\n}\n\nconst ATTRIBUTE_PREFIX = 'data-what'\n\nconst STORAGE_KEYS: Record<Strategy, string> = {\n input: 'what-input',\n intent: 'what-intent',\n}\n\nconst INPUT_METHODS: ReadonlySet<string> = new Set<InputMethod>([\n 'initial',\n 'mouse',\n 'keyboard',\n 'touch',\n])\n\n/** `PointerEvent.pointerType` \u2192 input method. Pens are reported as touch. */\nconst POINTER_TYPES: Readonly<Record<string, InputMethod | undefined>> = {\n mouse: 'mouse',\n pen: 'touch',\n touch: 'touch',\n}\n\n/**\n * Modifier keys that are commonly used together with a pointer (shift-click,\n * cmd-click, ...) and must not flip the input method to `keyboard`.\n */\nconst DEFAULT_IGNORED_KEYS: readonly KeyMatcher[] = [\n 'Shift',\n 'Control',\n 'Alt',\n 'Meta', // Windows key / Apple command\n 'ContextMenu', // Windows menu key\n]\n\n/** Elements where typing is expected and must not change the intent. */\nconst TEXT_ENTRY_ELEMENTS: ReadonlySet<string> = new Set(['input', 'select', 'textarea'])\n\n/** `<input>` types that are toggled, not typed into. */\nconst TOGGLE_INPUT_TYPES: ReadonlySet<string> = new Set(['checkbox', 'radio'])\n\n/** Opt-in attribute: hold the intent while focus is inside the element. */\nconst FREEZE_SELECTOR = '[data-whatfreeze]'\n\n/** Interaction media features describing the device's primary pointer. */\nconst HINT_QUERIES: Readonly<Record<Exclude<Hint, 'unknown'>, string>> = {\n touch: '(pointer: coarse)',\n mouse: '(pointer: fine)',\n}\n\nconst hasDom = (): boolean => typeof window !== 'undefined' && typeof document !== 'undefined'\n\nconst isInputMethod = (value: unknown): value is InputMethod =>\n typeof value === 'string' && INPUT_METHODS.has(value)\n\n/** Follows `activeElement` through open shadow roots. */\nconst deepActiveElement = (): Element | null => {\n let element = document.activeElement\n\n while (element?.shadowRoot?.activeElement) {\n element = element.shadowRoot.activeElement\n }\n\n return element\n}\n\n/** True when keyboard use is for text entry rather than navigation. */\nconst isTextEntry = (element: Element | null): boolean => {\n if (!element) return false\n\n const tag = element.localName\n\n if (tag === 'input') return !TOGGLE_INPUT_TYPES.has((element as HTMLInputElement).type)\n if (TEXT_ENTRY_ELEMENTS.has(tag)) return true\n if (tag === 'button' && element.closest('form') !== null) return true\n if ((element as HTMLElement).isContentEditable === true) return true\n if (element.closest(FREEZE_SELECTOR) !== null) return true\n\n // form-associated custom elements (`static formAssociated = true`)\n return (element.constructor as { formAssociated?: boolean }).formAssociated === true\n}\n\n/** The media query lists behind the hint, when the browser has `matchMedia`. */\nconst hintQueries = (): MediaQueryList[] =>\n typeof window.matchMedia === 'function'\n ? Object.values(HINT_QUERIES).map((query) => window.matchMedia(query))\n : []\n\nconst readHint = (): Hint => {\n if (typeof window.matchMedia !== 'function') return 'unknown'\n\n for (const [hint, query] of Object.entries(HINT_QUERIES) as Array<[Hint, string]>) {\n if (window.matchMedia(query).matches) return hint\n }\n\n return 'unknown'\n}\n\n/** The element that actually received a (possibly retargeted) event. */\nconst eventTarget = (event: Event): Element | null => {\n const target = event.composedPath()[0] ?? event.target\n\n return target instanceof Element ? target : null\n}\n\nclass WhatInput extends EventTarget {\n #state: { input: InputMethod; intent: InputMethod; hint: Hint } = {\n input: 'initial',\n intent: 'initial',\n hint: 'unknown',\n }\n #element: string | null = null\n #persist = false\n #ignoredKeys: readonly KeyMatcher[] = DEFAULT_IGNORED_KEYS\n #specificKeys: readonly KeyMatcher[] = []\n #callbacks: Record<Channel, Set<Callback>> = {\n input: new Set(),\n intent: new Set(),\n hint: new Set(),\n }\n #controller: AbortController | null = null\n #lastPointerPosition = { x: NaN, y: NaN }\n\n constructor() {\n super()\n\n // bail out in non-DOM environments (SSR, workers, tests); the API still\n // answers, it just never observes an interaction\n if (hasDom()) this.#start()\n }\n\n /*\n * api\n */\n\n /**\n * The current input method.\n *\n * - `'input'` (default): the same value as the `data-whatinput` attribute\n * - `'intent'`: the same value as the `data-whatintent` attribute\n * - `'hint'`: the same value as the `data-whathint` attribute\n */\n ask(strategy?: Strategy): InputMethod\n ask(strategy: 'hint'): Hint\n ask(strategy: Channel = 'input'): InputMethod | Hint {\n return this.#state[strategy]\n }\n\n /** The tag name of the currently focused element, or `null`. */\n element(): string | null {\n return this.#element\n }\n\n /**\n * Replaces the list of keys that are ignored (do not switch to `keyboard`)\n * when pressed. Defaults to the modifier keys.\n */\n ignoreKeys(keys: readonly KeyMatcher[]): void {\n this.#ignoredKeys = [...keys]\n }\n\n /**\n * Replaces the list of keys that are the *only* keys that switch to\n * `keyboard`. When set, this overrides `ignoreKeys()`. Pass an empty array\n * to restore the `ignoreKeys()` behavior.\n */\n specificKeys(keys: readonly KeyMatcher[]): void {\n this.#specificKeys = [...keys]\n }\n\n /** Fires `callback` whenever the input (default), intent or hint changes. */\n registerOnChange(callback: ChangeCallback, strategy?: Strategy): void\n registerOnChange(callback: HintCallback, strategy: 'hint'): void\n registerOnChange(callback: Callback, strategy: Channel = 'input'): void {\n this.#callbacks[strategy].add(callback)\n }\n\n /** Removes a callback added with `registerOnChange()`. */\n unRegisterOnChange(callback: Callback): void {\n for (const callbacks of Object.values(this.#callbacks)) {\n callbacks.delete(callback)\n }\n }\n\n /** Removes the persisted input and intent from session storage. */\n clearStorage(): void {\n try {\n for (const key of Object.values(STORAGE_KEYS)) {\n window.sessionStorage.removeItem(key)\n }\n } catch {\n // storage can be unavailable (cookies disabled, sandboxed frames)\n }\n }\n\n /**\n * Stops listening, removes the `data-what*` attributes from `<html>` and\n * drops every registered callback. Useful for tests and app teardown.\n */\n destroy(): void {\n this.#controller?.abort()\n this.#controller = null\n\n for (const callbacks of Object.values(this.#callbacks)) {\n callbacks.clear()\n }\n\n if (!hasDom()) return\n\n const html = document.documentElement\n\n for (const name of ['input', 'intent', 'hint', 'element', 'classes']) {\n html.removeAttribute(ATTRIBUTE_PREFIX + name)\n }\n }\n\n /*\n * set up\n */\n\n #start(): void {\n const controller = new AbortController()\n const { signal } = controller\n\n this.#controller = controller\n\n // capture so the events are seen even when a local listener stops\n // propagation, passive because nothing here ever calls `preventDefault()`\n const options: AddEventListenerOptions = { capture: true, passive: true, signal }\n\n // direct interactions\n window.addEventListener('pointerdown', this.#onPointerDown, options)\n window.addEventListener('keydown', this.#onKey, options)\n window.addEventListener('keyup', this.#onKey, options)\n\n // indirect interactions (potential, not actual, input)\n window.addEventListener('pointermove', this.#onPointerMove, options)\n window.addEventListener('wheel', this.#onWheel, options)\n\n // focus tracking\n window.addEventListener('focusin', this.#onFocusIn, options)\n window.addEventListener('focusout', this.#onFocusOut, options)\n\n // device hint (a keyboard detached from a tablet, a mouse paired with one)\n for (const query of hintQueries()) {\n query.addEventListener('change', this.#onHintChange, { signal })\n }\n\n // `<body data-whatpersist>` can only be read once the body exists\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', this.#init, { once: true, signal })\n } else {\n this.#init()\n }\n }\n\n /** Restores persisted state (if allowed) and publishes the initial state. */\n #init = (): void => {\n const { documentElement, body } = document\n\n this.#persist =\n documentElement.dataset.whatpersist !== 'false' && body?.dataset.whatpersist !== 'false'\n\n if (this.#persist) {\n for (const strategy of ['input', 'intent'] as const) {\n const stored = this.#restore(strategy)\n\n // an interaction that happened before `DOMContentLoaded` wins\n if (stored && this.#state[strategy] === 'initial') {\n this.#state[strategy] = stored\n }\n }\n }\n\n this.#state.hint = readHint()\n\n // always publish so at least the `initial` state is set\n this.#publish('input')\n this.#publish('intent')\n this.#publish('hint')\n }\n\n /*\n * events\n */\n\n #onPointerDown = (event: PointerEvent): void => {\n const value = POINTER_TYPES[event.pointerType]\n\n if (!value) return\n\n this.#set('input', value)\n this.#set('intent', value)\n }\n\n #onPointerMove = (event: PointerEvent): void => {\n const value = POINTER_TYPES[event.pointerType]\n\n if (!value) return\n\n // browsers dispatch synthetic `pointermove` events while the page scrolls\n // under a stationary pointer; those are not evidence of intent\n const { x, y } = this.#lastPointerPosition\n\n this.#lastPointerPosition = { x: event.screenX, y: event.screenY }\n\n if (event.screenX === x && event.screenY === y) return\n\n this.#set('intent', value)\n }\n\n #onWheel = (): void => {\n this.#set('intent', 'mouse')\n }\n\n #onHintChange = (): void => {\n this.#set('hint', readHint())\n }\n\n #onKey = (event: KeyboardEvent): void => {\n if (!this.#isCountedKey(event)) return\n\n this.#set('input', 'keyboard')\n\n // typing into a form field is not a change of intent: a mouse user that\n // fills in a form is still a mouse user\n if (!isTextEntry(deepActiveElement())) {\n this.#set('intent', 'keyboard')\n }\n }\n\n #onFocusIn = (event: FocusEvent): void => {\n const target = eventTarget(event)\n\n if (!target) {\n this.#onFocusOut()\n return\n }\n\n const html = document.documentElement\n\n this.#element = target.localName\n html.setAttribute(ATTRIBUTE_PREFIX + 'element', this.#element)\n\n if (target.classList.length) {\n html.setAttribute(ATTRIBUTE_PREFIX + 'classes', Array.from(target.classList).join(','))\n } else {\n html.removeAttribute(ATTRIBUTE_PREFIX + 'classes')\n }\n }\n\n #onFocusOut = (): void => {\n const html = document.documentElement\n\n this.#element = null\n html.removeAttribute(ATTRIBUTE_PREFIX + 'element')\n html.removeAttribute(ATTRIBUTE_PREFIX + 'classes')\n }\n\n /*\n * state\n */\n\n #isCountedKey(event: KeyboardEvent): boolean {\n const matches = (key: KeyMatcher): boolean =>\n typeof key === 'number' ? key === event.keyCode : key === event.key || key === event.code\n\n return this.#specificKeys.length\n ? this.#specificKeys.some(matches)\n : !this.#ignoredKeys.some(matches)\n }\n\n /** Updates, persists and publishes a value when it actually changed. */\n #set(channel: Strategy, value: InputMethod): void\n #set(channel: 'hint', value: Hint): void\n #set(channel: Channel, value: InputMethod | Hint): void {\n if (this.#state[channel] === value) return\n\n this.#state[channel] = value as never\n this.#store(channel, value)\n this.#publish(channel)\n }\n\n /** Writes the attribute, runs the callbacks and dispatches the event. */\n #publish(channel: Channel): void {\n const value = this.#state[channel]\n\n document.documentElement.setAttribute(ATTRIBUTE_PREFIX + channel, value)\n\n for (const callback of this.#callbacks[channel]) {\n ;(callback as (value: string) => void)(value)\n }\n\n this.dispatchEvent(new CustomEvent(channel, { detail: value }))\n }\n\n #store(channel: Channel, value: InputMethod | Hint): void {\n // the hint is live from the device and never persisted\n if (channel === 'hint' || !this.#persist) return\n\n try {\n window.sessionStorage.setItem(STORAGE_KEYS[channel], value)\n } catch {\n // storage can be unavailable (cookies disabled, sandboxed frames)\n }\n }\n\n #restore(strategy: Strategy): InputMethod | null {\n try {\n const value = window.sessionStorage.getItem(STORAGE_KEYS[strategy])\n\n return isInputMethod(value) ? value : null\n } catch {\n return null\n }\n }\n}\n\n/** Typed listeners for the `input`, `intent` and `hint` events (types only). */\ninterface WhatInput {\n addEventListener<K extends keyof WhatInputEventMap>(\n type: K,\n listener: ((this: WhatInput, event: WhatInputEventMap[K]) => void) | null,\n options?: boolean | AddEventListenerOptions,\n ): void\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | AddEventListenerOptions,\n ): void\n removeEventListener<K extends keyof WhatInputEventMap>(\n type: K,\n listener: ((this: WhatInput, event: WhatInputEventMap[K]) => void) | null,\n options?: boolean | EventListenerOptions,\n ): void\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject | null,\n options?: boolean | EventListenerOptions,\n ): void\n}\n\nexport type { WhatInput }\n\n/** The shared What Input instance. It starts observing as soon as it is imported. */\nconst whatInput = new WhatInput()\n\nexport default whatInput\n", "/*\n * Entry point for the UMD build. The wrapper in build.mjs declares\n * `__whatInput` in the factory function and returns it; assigning to it here\n * avoids esbuild's module-interop boilerplate that `globalName` would emit.\n */\n\nimport whatInput from '../src/what-input.ts'\n\ndeclare let __whatInput: typeof whatInput\n\n__whatInput = whatInput\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["ATTRIBUTE_PREFIX", "STORAGE_KEYS", "INPUT_METHODS", "POINTER_TYPES", "DEFAULT_IGNORED_KEYS", "TEXT_ENTRY_ELEMENTS", "TOGGLE_INPUT_TYPES", "FREEZE_SELECTOR", "HINT_QUERIES", "hasDom", "isInputMethod", "value", "deepActiveElement", "element", "isTextEntry", "tag", "hintQueries", "query", "readHint", "hint", "eventTarget", "event", "target", "WhatInput", "#state", "#element", "#persist", "#ignoredKeys", "#specificKeys", "#callbacks", "#controller", "#lastPointerPosition", "#start", "strategy", "keys", "callback", "callbacks", "key", "html", "name", "controller", "signal", "options", "#onPointerDown", "#onKey", "#onPointerMove", "#onWheel", "#onFocusIn", "#onFocusOut", "#onHintChange", "#init", "documentElement", "body", "stored", "#restore", "#publish", "#set", "x", "y", "#isCountedKey", "matches", "channel", "#store", "whatInput", "what_input_default", "what_input_default"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* what-input - A global utility for tracking the current input method (mouse, keyboard or touch).
|
|
3
|
+
* @version v6.0.0
|
|
4
|
+
* @link https://github.com/ten1seven/what-input
|
|
5
|
+
* @license MIT
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// src/what-input.ts
|
|
9
|
+
var ATTRIBUTE_PREFIX = "data-what";
|
|
10
|
+
var STORAGE_KEYS = {
|
|
11
|
+
input: "what-input",
|
|
12
|
+
intent: "what-intent"
|
|
13
|
+
};
|
|
14
|
+
var INPUT_METHODS = /* @__PURE__ */ new Set([
|
|
15
|
+
"initial",
|
|
16
|
+
"mouse",
|
|
17
|
+
"keyboard",
|
|
18
|
+
"touch"
|
|
19
|
+
]);
|
|
20
|
+
var POINTER_TYPES = {
|
|
21
|
+
mouse: "mouse",
|
|
22
|
+
pen: "touch",
|
|
23
|
+
touch: "touch"
|
|
24
|
+
};
|
|
25
|
+
var DEFAULT_IGNORED_KEYS = [
|
|
26
|
+
"Shift",
|
|
27
|
+
"Control",
|
|
28
|
+
"Alt",
|
|
29
|
+
"Meta",
|
|
30
|
+
// Windows key / Apple command
|
|
31
|
+
"ContextMenu"
|
|
32
|
+
// Windows menu key
|
|
33
|
+
];
|
|
34
|
+
var TEXT_ENTRY_ELEMENTS = /* @__PURE__ */ new Set(["input", "select", "textarea"]);
|
|
35
|
+
var TOGGLE_INPUT_TYPES = /* @__PURE__ */ new Set(["checkbox", "radio"]);
|
|
36
|
+
var FREEZE_SELECTOR = "[data-whatfreeze]";
|
|
37
|
+
var HINT_QUERIES = {
|
|
38
|
+
touch: "(pointer: coarse)",
|
|
39
|
+
mouse: "(pointer: fine)"
|
|
40
|
+
};
|
|
41
|
+
var hasDom = () => typeof window !== "undefined" && typeof document !== "undefined";
|
|
42
|
+
var isInputMethod = (value) => typeof value === "string" && INPUT_METHODS.has(value);
|
|
43
|
+
var deepActiveElement = () => {
|
|
44
|
+
let element = document.activeElement;
|
|
45
|
+
while (element?.shadowRoot?.activeElement) {
|
|
46
|
+
element = element.shadowRoot.activeElement;
|
|
47
|
+
}
|
|
48
|
+
return element;
|
|
49
|
+
};
|
|
50
|
+
var isTextEntry = (element) => {
|
|
51
|
+
if (!element) return false;
|
|
52
|
+
const tag = element.localName;
|
|
53
|
+
if (tag === "input") return !TOGGLE_INPUT_TYPES.has(element.type);
|
|
54
|
+
if (TEXT_ENTRY_ELEMENTS.has(tag)) return true;
|
|
55
|
+
if (tag === "button" && element.closest("form") !== null) return true;
|
|
56
|
+
if (element.isContentEditable === true) return true;
|
|
57
|
+
if (element.closest(FREEZE_SELECTOR) !== null) return true;
|
|
58
|
+
return element.constructor.formAssociated === true;
|
|
59
|
+
};
|
|
60
|
+
var hintQueries = () => typeof window.matchMedia === "function" ? Object.values(HINT_QUERIES).map((query) => window.matchMedia(query)) : [];
|
|
61
|
+
var readHint = () => {
|
|
62
|
+
if (typeof window.matchMedia !== "function") return "unknown";
|
|
63
|
+
for (const [hint, query] of Object.entries(HINT_QUERIES)) {
|
|
64
|
+
if (window.matchMedia(query).matches) return hint;
|
|
65
|
+
}
|
|
66
|
+
return "unknown";
|
|
67
|
+
};
|
|
68
|
+
var eventTarget = (event) => {
|
|
69
|
+
const target = event.composedPath()[0] ?? event.target;
|
|
70
|
+
return target instanceof Element ? target : null;
|
|
71
|
+
};
|
|
72
|
+
var WhatInput = class extends EventTarget {
|
|
73
|
+
#state = {
|
|
74
|
+
input: "initial",
|
|
75
|
+
intent: "initial",
|
|
76
|
+
hint: "unknown"
|
|
77
|
+
};
|
|
78
|
+
#element = null;
|
|
79
|
+
#persist = false;
|
|
80
|
+
#ignoredKeys = DEFAULT_IGNORED_KEYS;
|
|
81
|
+
#specificKeys = [];
|
|
82
|
+
#callbacks = {
|
|
83
|
+
input: /* @__PURE__ */ new Set(),
|
|
84
|
+
intent: /* @__PURE__ */ new Set(),
|
|
85
|
+
hint: /* @__PURE__ */ new Set()
|
|
86
|
+
};
|
|
87
|
+
#controller = null;
|
|
88
|
+
#lastPointerPosition = { x: NaN, y: NaN };
|
|
89
|
+
constructor() {
|
|
90
|
+
super();
|
|
91
|
+
if (hasDom()) this.#start();
|
|
92
|
+
}
|
|
93
|
+
ask(strategy = "input") {
|
|
94
|
+
return this.#state[strategy];
|
|
95
|
+
}
|
|
96
|
+
/** The tag name of the currently focused element, or `null`. */
|
|
97
|
+
element() {
|
|
98
|
+
return this.#element;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Replaces the list of keys that are ignored (do not switch to `keyboard`)
|
|
102
|
+
* when pressed. Defaults to the modifier keys.
|
|
103
|
+
*/
|
|
104
|
+
ignoreKeys(keys) {
|
|
105
|
+
this.#ignoredKeys = [...keys];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Replaces the list of keys that are the *only* keys that switch to
|
|
109
|
+
* `keyboard`. When set, this overrides `ignoreKeys()`. Pass an empty array
|
|
110
|
+
* to restore the `ignoreKeys()` behavior.
|
|
111
|
+
*/
|
|
112
|
+
specificKeys(keys) {
|
|
113
|
+
this.#specificKeys = [...keys];
|
|
114
|
+
}
|
|
115
|
+
registerOnChange(callback, strategy = "input") {
|
|
116
|
+
this.#callbacks[strategy].add(callback);
|
|
117
|
+
}
|
|
118
|
+
/** Removes a callback added with `registerOnChange()`. */
|
|
119
|
+
unRegisterOnChange(callback) {
|
|
120
|
+
for (const callbacks of Object.values(this.#callbacks)) {
|
|
121
|
+
callbacks.delete(callback);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Removes the persisted input and intent from session storage. */
|
|
125
|
+
clearStorage() {
|
|
126
|
+
try {
|
|
127
|
+
for (const key of Object.values(STORAGE_KEYS)) {
|
|
128
|
+
window.sessionStorage.removeItem(key);
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Stops listening, removes the `data-what*` attributes from `<html>` and
|
|
135
|
+
* drops every registered callback. Useful for tests and app teardown.
|
|
136
|
+
*/
|
|
137
|
+
destroy() {
|
|
138
|
+
this.#controller?.abort();
|
|
139
|
+
this.#controller = null;
|
|
140
|
+
for (const callbacks of Object.values(this.#callbacks)) {
|
|
141
|
+
callbacks.clear();
|
|
142
|
+
}
|
|
143
|
+
if (!hasDom()) return;
|
|
144
|
+
const html = document.documentElement;
|
|
145
|
+
for (const name of ["input", "intent", "hint", "element", "classes"]) {
|
|
146
|
+
html.removeAttribute(ATTRIBUTE_PREFIX + name);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/*
|
|
150
|
+
* set up
|
|
151
|
+
*/
|
|
152
|
+
#start() {
|
|
153
|
+
const controller = new AbortController();
|
|
154
|
+
const { signal } = controller;
|
|
155
|
+
this.#controller = controller;
|
|
156
|
+
const options = { capture: true, passive: true, signal };
|
|
157
|
+
window.addEventListener("pointerdown", this.#onPointerDown, options);
|
|
158
|
+
window.addEventListener("keydown", this.#onKey, options);
|
|
159
|
+
window.addEventListener("keyup", this.#onKey, options);
|
|
160
|
+
window.addEventListener("pointermove", this.#onPointerMove, options);
|
|
161
|
+
window.addEventListener("wheel", this.#onWheel, options);
|
|
162
|
+
window.addEventListener("focusin", this.#onFocusIn, options);
|
|
163
|
+
window.addEventListener("focusout", this.#onFocusOut, options);
|
|
164
|
+
for (const query of hintQueries()) {
|
|
165
|
+
query.addEventListener("change", this.#onHintChange, { signal });
|
|
166
|
+
}
|
|
167
|
+
if (document.readyState === "loading") {
|
|
168
|
+
document.addEventListener("DOMContentLoaded", this.#init, { once: true, signal });
|
|
169
|
+
} else {
|
|
170
|
+
this.#init();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** Restores persisted state (if allowed) and publishes the initial state. */
|
|
174
|
+
#init = () => {
|
|
175
|
+
const { documentElement, body } = document;
|
|
176
|
+
this.#persist = documentElement.dataset.whatpersist !== "false" && body?.dataset.whatpersist !== "false";
|
|
177
|
+
if (this.#persist) {
|
|
178
|
+
for (const strategy of ["input", "intent"]) {
|
|
179
|
+
const stored = this.#restore(strategy);
|
|
180
|
+
if (stored && this.#state[strategy] === "initial") {
|
|
181
|
+
this.#state[strategy] = stored;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
this.#state.hint = readHint();
|
|
186
|
+
this.#publish("input");
|
|
187
|
+
this.#publish("intent");
|
|
188
|
+
this.#publish("hint");
|
|
189
|
+
};
|
|
190
|
+
/*
|
|
191
|
+
* events
|
|
192
|
+
*/
|
|
193
|
+
#onPointerDown = (event) => {
|
|
194
|
+
const value = POINTER_TYPES[event.pointerType];
|
|
195
|
+
if (!value) return;
|
|
196
|
+
this.#set("input", value);
|
|
197
|
+
this.#set("intent", value);
|
|
198
|
+
};
|
|
199
|
+
#onPointerMove = (event) => {
|
|
200
|
+
const value = POINTER_TYPES[event.pointerType];
|
|
201
|
+
if (!value) return;
|
|
202
|
+
const { x, y } = this.#lastPointerPosition;
|
|
203
|
+
this.#lastPointerPosition = { x: event.screenX, y: event.screenY };
|
|
204
|
+
if (event.screenX === x && event.screenY === y) return;
|
|
205
|
+
this.#set("intent", value);
|
|
206
|
+
};
|
|
207
|
+
#onWheel = () => {
|
|
208
|
+
this.#set("intent", "mouse");
|
|
209
|
+
};
|
|
210
|
+
#onHintChange = () => {
|
|
211
|
+
this.#set("hint", readHint());
|
|
212
|
+
};
|
|
213
|
+
#onKey = (event) => {
|
|
214
|
+
if (!this.#isCountedKey(event)) return;
|
|
215
|
+
this.#set("input", "keyboard");
|
|
216
|
+
if (!isTextEntry(deepActiveElement())) {
|
|
217
|
+
this.#set("intent", "keyboard");
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
#onFocusIn = (event) => {
|
|
221
|
+
const target = eventTarget(event);
|
|
222
|
+
if (!target) {
|
|
223
|
+
this.#onFocusOut();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const html = document.documentElement;
|
|
227
|
+
this.#element = target.localName;
|
|
228
|
+
html.setAttribute(ATTRIBUTE_PREFIX + "element", this.#element);
|
|
229
|
+
if (target.classList.length) {
|
|
230
|
+
html.setAttribute(ATTRIBUTE_PREFIX + "classes", Array.from(target.classList).join(","));
|
|
231
|
+
} else {
|
|
232
|
+
html.removeAttribute(ATTRIBUTE_PREFIX + "classes");
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
#onFocusOut = () => {
|
|
236
|
+
const html = document.documentElement;
|
|
237
|
+
this.#element = null;
|
|
238
|
+
html.removeAttribute(ATTRIBUTE_PREFIX + "element");
|
|
239
|
+
html.removeAttribute(ATTRIBUTE_PREFIX + "classes");
|
|
240
|
+
};
|
|
241
|
+
/*
|
|
242
|
+
* state
|
|
243
|
+
*/
|
|
244
|
+
#isCountedKey(event) {
|
|
245
|
+
const matches = (key) => typeof key === "number" ? key === event.keyCode : key === event.key || key === event.code;
|
|
246
|
+
return this.#specificKeys.length ? this.#specificKeys.some(matches) : !this.#ignoredKeys.some(matches);
|
|
247
|
+
}
|
|
248
|
+
#set(channel, value) {
|
|
249
|
+
if (this.#state[channel] === value) return;
|
|
250
|
+
this.#state[channel] = value;
|
|
251
|
+
this.#store(channel, value);
|
|
252
|
+
this.#publish(channel);
|
|
253
|
+
}
|
|
254
|
+
/** Writes the attribute, runs the callbacks and dispatches the event. */
|
|
255
|
+
#publish(channel) {
|
|
256
|
+
const value = this.#state[channel];
|
|
257
|
+
document.documentElement.setAttribute(ATTRIBUTE_PREFIX + channel, value);
|
|
258
|
+
for (const callback of this.#callbacks[channel]) {
|
|
259
|
+
;
|
|
260
|
+
callback(value);
|
|
261
|
+
}
|
|
262
|
+
this.dispatchEvent(new CustomEvent(channel, { detail: value }));
|
|
263
|
+
}
|
|
264
|
+
#store(channel, value) {
|
|
265
|
+
if (channel === "hint" || !this.#persist) return;
|
|
266
|
+
try {
|
|
267
|
+
window.sessionStorage.setItem(STORAGE_KEYS[channel], value);
|
|
268
|
+
} catch {
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
#restore(strategy) {
|
|
272
|
+
try {
|
|
273
|
+
const value = window.sessionStorage.getItem(STORAGE_KEYS[strategy]);
|
|
274
|
+
return isInputMethod(value) ? value : null;
|
|
275
|
+
} catch {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
var whatInput = new WhatInput();
|
|
281
|
+
var what_input_default = whatInput;
|
|
282
|
+
export {
|
|
283
|
+
what_input_default as default
|
|
284
|
+
};
|
package/package.json
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "what-input",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "A global utility for tracking the current input method (mouse, keyboard or touch).",
|
|
5
5
|
"main": "dist/what-input.js",
|
|
6
|
-
"
|
|
6
|
+
"module": "dist/what-input.mjs",
|
|
7
|
+
"types": "dist/what-input.d.ts",
|
|
8
|
+
"unpkg": "dist/what-input.min.js",
|
|
9
|
+
"jsdelivr": "dist/what-input.min.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/what-input.d.mts",
|
|
14
|
+
"default": "./dist/what-input.mjs"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/what-input.d.ts",
|
|
18
|
+
"default": "./dist/what-input.js"
|
|
19
|
+
},
|
|
20
|
+
"default": "./dist/what-input.js"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"sideEffects": true,
|
|
7
28
|
"repository": {
|
|
8
29
|
"url": "https://github.com/ten1seven/what-input.git",
|
|
9
30
|
"type": "git"
|
|
@@ -12,42 +33,42 @@
|
|
|
12
33
|
"accessibility",
|
|
13
34
|
"a11y",
|
|
14
35
|
"input",
|
|
15
|
-
"
|
|
36
|
+
"focus-visible",
|
|
37
|
+
"pointer-events",
|
|
38
|
+
"javascript",
|
|
39
|
+
"typescript"
|
|
16
40
|
],
|
|
17
41
|
"author": "Jeremy Fields <jeremy.fields@viget.com>",
|
|
18
42
|
"license": "MIT",
|
|
19
43
|
"bugs": {
|
|
20
44
|
"url": "https://github.com/ten1seven/what-input/issues"
|
|
21
45
|
},
|
|
46
|
+
"homepage": "https://github.com/ten1seven/what-input",
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=20"
|
|
49
|
+
},
|
|
22
50
|
"scripts": {
|
|
23
|
-
"
|
|
51
|
+
"dev": "vite",
|
|
52
|
+
"build": "npm run build:lib && npm run build:demo",
|
|
53
|
+
"build:lib": "node scripts/build.mjs",
|
|
54
|
+
"build:demo": "vite build",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"format": "prettier --write .",
|
|
57
|
+
"lint": "prettier --check . && tsc --noEmit",
|
|
58
|
+
"pretest": "npm run build:lib",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:watch": "vitest",
|
|
61
|
+
"prepublishOnly": "npm run lint && npm test",
|
|
62
|
+
"deploy": "npm run build && gh-pages -d build"
|
|
24
63
|
},
|
|
25
|
-
"homepage": "https://github.com/ten1seven/what-input",
|
|
26
64
|
"devDependencies": {
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"gulp-concat": "^2.6.1",
|
|
36
|
-
"gulp-cssnano": "^2.1.3",
|
|
37
|
-
"gulp-gh-pages": "^0.5.4",
|
|
38
|
-
"gulp-header": "^2.0.9",
|
|
39
|
-
"gulp-load-plugins": "^2.0.1",
|
|
40
|
-
"gulp-notify": "^3.2.0",
|
|
41
|
-
"gulp-plumber": "^1.2.1",
|
|
42
|
-
"gulp-postcss": "^8.0.0",
|
|
43
|
-
"gulp-rename": "^1.4.0",
|
|
44
|
-
"gulp-sass": "^4.0.2",
|
|
45
|
-
"gulp-sass-glob": "^1.1.0",
|
|
46
|
-
"gulp-sourcemaps": "^2.6.5",
|
|
47
|
-
"gulp-standard": "^14.0.0",
|
|
48
|
-
"gulp-uglify": "^3.0.2",
|
|
49
|
-
"stylelint-config-prettier": "^6.0.0",
|
|
50
|
-
"stylelint-config-standard": "^19.0.0",
|
|
51
|
-
"webpack-stream": "3.2.0"
|
|
65
|
+
"@types/node": "^26.6.5",
|
|
66
|
+
"esbuild": "^0.28.2",
|
|
67
|
+
"gh-pages": "^6.3.0",
|
|
68
|
+
"jsdom": "^29.1.1",
|
|
69
|
+
"prettier": "^3.9.10",
|
|
70
|
+
"typescript": "^7.0.2",
|
|
71
|
+
"vite": "^8.3.5",
|
|
72
|
+
"vitest": "^4.1.11"
|
|
52
73
|
}
|
|
53
74
|
}
|
package/.browserslistrc
DELETED
package/.editorconfig
DELETED
package/.eslintrc
DELETED
package/.nvmrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
10.15.0
|
package/.prettierrc
DELETED
package/.stylelintrc
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": ["stylelint-config-standard", "stylelint-config-prettier"],
|
|
3
|
-
"rules": {
|
|
4
|
-
"at-rule-no-unknown": [true, {
|
|
5
|
-
ignoreAtRules: ['include', 'extend', 'mixin', 'function', 'return', 'for', 'if', 'else', 'warn']
|
|
6
|
-
}],
|
|
7
|
-
"no-descending-specificity": null
|
|
8
|
-
}
|
|
9
|
-
}
|