var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { KEYBORG_FOCUSIN: () => KEYBORG_FOCUSIN, KEYBORG_FOCUSOUT: () => KEYBORG_FOCUSOUT, createKeyborg: () => createKeyborg, disposeKeyborg: () => disposeKeyborg, getLastFocusedProgrammatically: () => getLastFocusedProgrammatically, nativeFocus: () => nativeFocus, version: () => version }); module.exports = __toCommonJS(src_exports); // src/WeakRefInstance.ts var _canUseWeakRef = typeof WeakRef !== "undefined"; var WeakRefInstance = class { constructor(instance) { if (_canUseWeakRef && typeof instance === "object") { this._weakRef = new WeakRef(instance); } else { this._instance = instance; } } /** * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef/deref} */ deref() { var _a, _b; let instance; if (this._weakRef) { instance = (_a = this._weakRef) == null ? void 0 : _a.deref(); if (!instance) { delete this._weakRef; } } else { instance = this._instance; if ((_b = instance == null ? void 0 : instance.isDisposed) == null ? void 0 : _b.call(instance)) { delete this._instance; } } return instance; } }; // src/FocusEvent.ts var KEYBORG_FOCUSIN = "keyborg:focusin"; var KEYBORG_FOCUSOUT = "keyborg:focusout"; function canOverrideNativeFocus(win) { const HTMLElement = win.HTMLElement; const origFocus = HTMLElement.prototype.focus; let isCustomFocusCalled = false; HTMLElement.prototype.focus = function focus() { isCustomFocusCalled = true; }; const btn = win.document.createElement("button"); btn.focus(); HTMLElement.prototype.focus = origFocus; return isCustomFocusCalled; } var _canOverrideNativeFocus = false; function nativeFocus(element) { const focus = element.focus; if (focus.__keyborgNativeFocus) { focus.__keyborgNativeFocus.call(element); } else { element.focus(); } } function setupFocusEvent(win) { const kwin = win; if (!_canOverrideNativeFocus) { _canOverrideNativeFocus = canOverrideNativeFocus(kwin); } const origFocus = kwin.HTMLElement.prototype.focus; if (origFocus.__keyborgNativeFocus) { return; } kwin.HTMLElement.prototype.focus = focus; const shadowTargets = /* @__PURE__ */ new Set(); const focusOutHandler = (e) => { const target = e.target; if (!target) { return; } const event = new CustomEvent(KEYBORG_FOCUSOUT, { cancelable: true, bubbles: true, // Allows the event to bubble past an open shadow root composed: true, detail: { originalEvent: e } }); target.dispatchEvent(event); }; const focusInHandler = (e) => { const target = e.target; if (!target) { return; } let node = e.composedPath()[0]; const currentShadows = /* @__PURE__ */ new Set(); while (node) { if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { currentShadows.add(node); node = node.host; } else { node = node.parentNode; } } for (const shadowRootWeakRef of shadowTargets) { const shadowRoot = shadowRootWeakRef.deref(); if (!shadowRoot || !currentShadows.has(shadowRoot)) { shadowTargets.delete(shadowRootWeakRef); if (shadowRoot) { shadowRoot.removeEventListener("focusin", focusInHandler, true); shadowRoot.removeEventListener("focusout", focusOutHandler, true); } } } onFocusIn(target, e.relatedTarget || void 0); }; const onFocusIn = (target, relatedTarget, originalEvent) => { var _a; const shadowRoot = target.shadowRoot; if (shadowRoot) { for (const shadowRootWeakRef of shadowTargets) { if (shadowRootWeakRef.deref() === shadowRoot) { return; } } shadowRoot.addEventListener("focusin", focusInHandler, true); shadowRoot.addEventListener("focusout", focusOutHandler, true); shadowTargets.add(new WeakRefInstance(shadowRoot)); return; } const details = { relatedTarget, originalEvent }; const event = new CustomEvent(KEYBORG_FOCUSIN, { cancelable: true, bubbles: true, // Allows the event to bubble past an open shadow root composed: true, detail: details }); event.details = details; if (_canOverrideNativeFocus || data.lastFocusedProgrammatically) { details.isFocusedProgrammatically = target === ((_a = data.lastFocusedProgrammatically) == null ? void 0 : _a.deref()); data.lastFocusedProgrammatically = void 0; } target.dispatchEvent(event); }; const data = kwin.__keyborgData = { focusInHandler, focusOutHandler, shadowTargets }; kwin.document.addEventListener( "focusin", kwin.__keyborgData.focusInHandler, true ); kwin.document.addEventListener( "focusout", kwin.__keyborgData.focusOutHandler, true ); function focus() { const keyborgNativeFocusEvent = kwin.__keyborgData; if (keyborgNativeFocusEvent) { keyborgNativeFocusEvent.lastFocusedProgrammatically = new WeakRefInstance( this ); } return origFocus.apply(this, arguments); } let activeElement = kwin.document.activeElement; while (activeElement && activeElement.shadowRoot) { onFocusIn(activeElement); activeElement = activeElement.shadowRoot.activeElement; } focus.__keyborgNativeFocus = origFocus; } function disposeFocusEvent(win) { const kwin = win; const proto = kwin.HTMLElement.prototype; const origFocus = proto.focus.__keyborgNativeFocus; const keyborgNativeFocusEvent = kwin.__keyborgData; if (keyborgNativeFocusEvent) { kwin.document.removeEventListener( "focusin", keyborgNativeFocusEvent.focusInHandler, true ); kwin.document.removeEventListener( "focusout", keyborgNativeFocusEvent.focusOutHandler, true ); for (const shadowRootWeakRef of keyborgNativeFocusEvent.shadowTargets) { const shadowRoot = shadowRootWeakRef.deref(); if (shadowRoot) { shadowRoot.removeEventListener( "focusin", keyborgNativeFocusEvent.focusInHandler, true ); shadowRoot.removeEventListener( "focusout", keyborgNativeFocusEvent.focusOutHandler, true ); } } keyborgNativeFocusEvent.shadowTargets.clear(); delete kwin.__keyborgData; } if (origFocus) { proto.focus = origFocus; } } function getLastFocusedProgrammatically(win) { var _a; const keyborgNativeFocusEvent = win.__keyborgData; return keyborgNativeFocusEvent ? ((_a = keyborgNativeFocusEvent.lastFocusedProgrammatically) == null ? void 0 : _a.deref()) || null : void 0; } // src/Keyborg.ts var _dismissTimeout = 500; var _lastId = 0; var KeyborgCore = class { constructor(win, props) { this._isNavigatingWithKeyboard_DO_NOT_USE = false; this._onFocusIn = (e) => { if (this._isMouseOrTouchUsedTimer) { return; } if (this.isNavigatingWithKeyboard) { return; } const details = e.detail; if (!details.relatedTarget) { return; } if (details.isFocusedProgrammatically || details.isFocusedProgrammatically === void 0) { return; } this.isNavigatingWithKeyboard = true; }; this._onMouseDown = (e) => { if (e.buttons === 0 || e.clientX === 0 && e.clientY === 0 && e.screenX === 0 && e.screenY === 0) { return; } this._onMouseOrTouch(); }; this._onMouseOrTouch = () => { const win = this._win; if (win) { if (this._isMouseOrTouchUsedTimer) { win.clearTimeout(this._isMouseOrTouchUsedTimer); } this._isMouseOrTouchUsedTimer = win.setTimeout(() => { delete this._isMouseOrTouchUsedTimer; }, 1e3); } this.isNavigatingWithKeyboard = false; }; this._onKeyDown = (e) => { const isNavigatingWithKeyboard = this.isNavigatingWithKeyboard; if (isNavigatingWithKeyboard) { if (this._shouldDismissKeyboardNavigation(e)) { this._scheduleDismiss(); } } else { if (this._shouldTriggerKeyboardNavigation(e)) { this.isNavigatingWithKeyboard = true; } } }; this.id = "c" + ++_lastId; this._win = win; const doc = win.document; if (props) { const triggerKeys = props.triggerKeys; const dismissKeys = props.dismissKeys; if (triggerKeys == null ? void 0 : triggerKeys.length) { this._triggerKeys = new Set(triggerKeys); } if (dismissKeys == null ? void 0 : dismissKeys.length) { this._dismissKeys = new Set(dismissKeys); } } doc.addEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); doc.addEventListener("mousedown", this._onMouseDown, true); win.addEventListener("keydown", this._onKeyDown, true); doc.addEventListener("touchstart", this._onMouseOrTouch, true); doc.addEventListener("touchend", this._onMouseOrTouch, true); doc.addEventListener("touchcancel", this._onMouseOrTouch, true); setupFocusEvent(win); } get isNavigatingWithKeyboard() { return this._isNavigatingWithKeyboard_DO_NOT_USE; } set isNavigatingWithKeyboard(val) { if (this._isNavigatingWithKeyboard_DO_NOT_USE !== val) { this._isNavigatingWithKeyboard_DO_NOT_USE = val; this.update(); } } dispose() { const win = this._win; if (win) { if (this._isMouseOrTouchUsedTimer) { win.clearTimeout(this._isMouseOrTouchUsedTimer); this._isMouseOrTouchUsedTimer = void 0; } if (this._dismissTimer) { win.clearTimeout(this._dismissTimer); this._dismissTimer = void 0; } disposeFocusEvent(win); const doc = win.document; doc.removeEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); doc.removeEventListener("mousedown", this._onMouseDown, true); win.removeEventListener("keydown", this._onKeyDown, true); doc.removeEventListener("touchstart", this._onMouseOrTouch, true); doc.removeEventListener("touchend", this._onMouseOrTouch, true); doc.removeEventListener("touchcancel", this._onMouseOrTouch, true); delete this._win; } } isDisposed() { return !!this._win; } /** * Updates all keyborg instances with the keyboard navigation state */ update() { var _a, _b; const keyborgs = (_b = (_a = this._win) == null ? void 0 : _a.__keyborg) == null ? void 0 : _b.refs; if (keyborgs) { for (const id of Object.keys(keyborgs)) { Keyborg.update(keyborgs[id], this.isNavigatingWithKeyboard); } } } /** * @returns whether the keyboard event should trigger keyboard navigation mode */ _shouldTriggerKeyboardNavigation(e) { var _a; if (e.key === "Tab") { return true; } const activeElement = (_a = this._win) == null ? void 0 : _a.document.activeElement; const isTriggerKey = !this._triggerKeys || this._triggerKeys.has(e.keyCode); const isEditable = activeElement && (activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA" || activeElement.isContentEditable); return isTriggerKey && !isEditable; } /** * @returns whether the keyboard event should dismiss keyboard navigation mode */ _shouldDismissKeyboardNavigation(e) { var _a; return (_a = this._dismissKeys) == null ? void 0 : _a.has(e.keyCode); } _scheduleDismiss() { const win = this._win; if (win) { if (this._dismissTimer) { win.clearTimeout(this._dismissTimer); this._dismissTimer = void 0; } const was = win.document.activeElement; this._dismissTimer = win.setTimeout(() => { this._dismissTimer = void 0; const cur = win.document.activeElement; if (was && cur && was === cur) { this.isNavigatingWithKeyboard = false; } }, _dismissTimeout); } } }; var Keyborg = class _Keyborg { constructor(win, props) { this._cb = []; this._id = "k" + ++_lastId; this._win = win; const current = win.__keyborg; if (current) { this._core = current.core; current.refs[this._id] = this; } else { this._core = new KeyborgCore(win, props); win.__keyborg = { core: this._core, refs: { [this._id]: this } }; } } static create(win, props) { return new _Keyborg(win, props); } static dispose(instance) { instance.dispose(); } /** * Updates all subscribed callbacks with the keyboard navigation state */ static update(instance, isNavigatingWithKeyboard) { instance._cb.forEach((callback) => callback(isNavigatingWithKeyboard)); } dispose() { var _a; const current = (_a = this._win) == null ? void 0 : _a.__keyborg; if (current == null ? void 0 : current.refs[this._id]) { delete current.refs[this._id]; if (Object.keys(current.refs).length === 0) { current.core.dispose(); delete this._win.__keyborg; } } else if (process.env.NODE_ENV !== "production") { console.error( `Keyborg instance ${this._id} is being disposed incorrectly.` ); } this._cb = []; delete this._core; delete this._win; } /** * @returns Whether the user is navigating with keyboard */ isNavigatingWithKeyboard() { var _a; return !!((_a = this._core) == null ? void 0 : _a.isNavigatingWithKeyboard); } /** * @param callback - Called when the keyboard navigation state changes */ subscribe(callback) { this._cb.push(callback); } /** * @param callback - Registered with subscribe */ unsubscribe(callback) { const index = this._cb.indexOf(callback); if (index >= 0) { this._cb.splice(index, 1); } } /** * Manually set the keyboard navigtion state */ setVal(isNavigatingWithKeyboard) { if (this._core) { this._core.isNavigatingWithKeyboard = isNavigatingWithKeyboard; } } }; function createKeyborg(win, props) { return Keyborg.create(win, props); } function disposeKeyborg(instance) { Keyborg.dispose(instance); } // src/index.ts var version = "2.6.0"; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { KEYBORG_FOCUSIN, KEYBORG_FOCUSOUT, createKeyborg, disposeKeyborg, getLastFocusedProgrammatically, nativeFocus, version }); /*! * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ //# sourceMappingURL=index.js.map