New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@zag-js/focus-visible

Package Overview
Dependencies
Maintainers
0
Versions
760
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zag-js/focus-visible - npm Package Compare versions

Comparing version

to
0.0.0-v1-beta-20250220125322

41

dist/index.d.ts
type Modality = "keyboard" | "pointer" | "virtual";
type FocusVisibleCallback = (isFocusVisible: boolean) => void;
declare function trackFocusVisible(fn: FocusVisibleCallback): () => void;
declare function trackInteractionModality(fn: (value: Modality | null) => void): () => void;
declare function setInteractionModality(value: Modality): void;
type RootNode = Document | ShadowRoot | Node;
interface GlobalListenerData {
focus: VoidFunction;
}
declare let listenerMap: Map<Window, GlobalListenerData>;
declare function getInteractionModality(): Modality | null;
declare function setInteractionModality(modality: Modality): void;
interface InteractionModalityChangeDetails {
/** The modality of the interaction that caused the focus to be visible. */
modality: Modality | null;
}
interface InteractionModalityProps {
/** The root element to track focus visibility for. */
root?: RootNode | undefined;
/** Callback to be called when the interaction modality changes. */
onChange: (details: InteractionModalityChangeDetails) => void;
}
declare function trackInteractionModality(props: InteractionModalityProps): VoidFunction;
declare function isFocusVisible(): boolean;
interface FocusVisibleChangeDetails {
/** Whether keyboard focus is visible globally. */
isFocusVisible: boolean;
/** The modality of the interaction that caused the focus to be visible. */
modality: Modality | null;
}
interface FocusVisibleProps {
/** The root element to track focus visibility for. */
root?: RootNode | undefined;
/** Whether the element is a text input. */
isTextInput?: boolean | undefined;
/** Whether the element will be auto focused. */
autoFocus?: boolean | undefined;
/** Callback to be called when the focus visibility changes. */
onChange?: ((details: FocusVisibleChangeDetails) => void) | undefined;
}
declare function trackFocusVisible(props?: FocusVisibleProps): VoidFunction;
export { getInteractionModality, setInteractionModality, trackFocusVisible, trackInteractionModality };
export { type FocusVisibleChangeDetails, type FocusVisibleProps, type InteractionModalityChangeDetails, type InteractionModalityProps, type Modality, getInteractionModality, isFocusVisible, listenerMap, setInteractionModality, trackFocusVisible, trackInteractionModality };

243

dist/index.js

@@ -1,83 +0,62 @@

"use strict";
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);
'use strict';
var domQuery = require('@zag-js/dom-query');
// src/index.ts
var src_exports = {};
__export(src_exports, {
getInteractionModality: () => getInteractionModality,
setInteractionModality: () => setInteractionModality,
trackFocusVisible: () => trackFocusVisible,
trackInteractionModality: () => trackInteractionModality
});
module.exports = __toCommonJS(src_exports);
var import_dom_query = require("@zag-js/dom-query");
var hasSetup = false;
var modality = null;
function isVirtualClick(event) {
if (event.mozInputSource === 0 && event.isTrusted) return true;
return event.detail === 0 && !event.pointerType;
}
function isValidKey(e) {
return !(e.metaKey || !domQuery.isMac() && e.altKey || e.ctrlKey || e.key === "Control" || e.key === "Shift" || e.key === "Meta");
}
var nonTextInputTypes = /* @__PURE__ */ new Set(["checkbox", "radio", "range", "color", "file", "image", "button", "submit", "reset"]);
function isKeyboardFocusEvent(isTextInput, modality, e) {
const target = e ? domQuery.getEventTarget(e) : null;
const win = domQuery.getWindow(target);
isTextInput = isTextInput || target instanceof win.HTMLInputElement && !nonTextInputTypes.has(target?.type) || target instanceof win.HTMLTextAreaElement || target instanceof win.HTMLElement && target.isContentEditable;
return !(isTextInput && modality === "keyboard" && e instanceof win.KeyboardEvent && !Reflect.has(FOCUS_VISIBLE_INPUT_KEYS, e.key));
}
var currentModality = null;
var changeHandlers = /* @__PURE__ */ new Set();
var listenerMap = /* @__PURE__ */ new Map();
var hasEventBeforeFocus = false;
var hasBlurredWindowRecently = false;
var handlers = /* @__PURE__ */ new Set();
function trigger(modality2, event) {
handlers.forEach((handler) => handler(modality2, event));
var FOCUS_VISIBLE_INPUT_KEYS = {
Tab: true,
Escape: true
};
function triggerChangeHandlers(modality, e) {
for (let handler of changeHandlers) {
handler(modality, e);
}
}
var isMac = typeof window !== "undefined" && window.navigator != null ? /^Mac/.test(window.navigator.platform) : false;
function isValidKey(e) {
return !(e.metaKey || !isMac && e.altKey || e.ctrlKey || e.key === "Control" || e.key === "Shift" || e.key === "Meta");
}
function onKeyboardEvent(event) {
function handleKeyboardEvent(e) {
hasEventBeforeFocus = true;
if (isValidKey(event)) {
modality = "keyboard";
trigger("keyboard", event);
if (isValidKey(e)) {
currentModality = "keyboard";
triggerChangeHandlers("keyboard", e);
}
}
function onPointerEvent(event) {
modality = "pointer";
if (event.type === "mousedown" || event.type === "pointerdown") {
function handlePointerEvent(e) {
currentModality = "pointer";
if (e.type === "mousedown" || e.type === "pointerdown") {
hasEventBeforeFocus = true;
const target = event.composedPath ? event.composedPath()[0] : event.target;
let matches = false;
try {
matches = target.matches(":focus-visible");
} catch {
}
if (matches) return;
trigger("pointer", event);
triggerChangeHandlers("pointer", e);
}
}
function isVirtualClick(event) {
if (event.mozInputSource === 0 && event.isTrusted) return true;
return event.detail === 0 && !event.pointerType;
}
function onClickEvent(e) {
function handleClickEvent(e) {
if (isVirtualClick(e)) {
hasEventBeforeFocus = true;
modality = "virtual";
currentModality = "virtual";
}
}
function onWindowFocus(event) {
if (event.target === window || event.target === document) {
function handleFocusEvent(e) {
const target = domQuery.getEventTarget(e);
if (target === domQuery.getWindow(target) || target === domQuery.getDocument(target)) {
return;
}
if (event.target instanceof Element && event.target.hasAttribute("tabindex")) {
return;
}
if (!hasEventBeforeFocus && !hasBlurredWindowRecently) {
modality = "virtual";
trigger("virtual", event);
currentModality = "virtual";
triggerChangeHandlers("virtual", e);
}

@@ -87,66 +66,104 @@ hasEventBeforeFocus = false;

}
function onWindowBlur() {
function handleWindowBlur() {
hasEventBeforeFocus = false;
hasBlurredWindowRecently = true;
}
function isFocusVisible() {
return modality !== "pointer";
}
function setupGlobalFocusEvents() {
if (!(0, import_dom_query.isDom)() || hasSetup) {
function setupGlobalFocusEvents(root) {
if (typeof window === "undefined" || listenerMap.get(domQuery.getWindow(root))) {
return;
}
const { focus } = HTMLElement.prototype;
HTMLElement.prototype.focus = function focusElement(...args) {
const win = domQuery.getWindow(root);
const doc = domQuery.getDocument(root);
let focus = win.HTMLElement.prototype.focus;
win.HTMLElement.prototype.focus = function() {
currentModality = "virtual";
triggerChangeHandlers("virtual", null);
hasEventBeforeFocus = true;
focus.apply(this, args);
focus.apply(this, arguments);
};
document.addEventListener("keydown", onKeyboardEvent, true);
document.addEventListener("keyup", onKeyboardEvent, true);
document.addEventListener("click", onClickEvent, true);
window.addEventListener("focus", onWindowFocus, true);
window.addEventListener("blur", onWindowBlur, false);
if (typeof PointerEvent !== "undefined") {
document.addEventListener("pointerdown", onPointerEvent, true);
document.addEventListener("pointermove", onPointerEvent, true);
document.addEventListener("pointerup", onPointerEvent, true);
doc.addEventListener("keydown", handleKeyboardEvent, true);
doc.addEventListener("keyup", handleKeyboardEvent, true);
doc.addEventListener("click", handleClickEvent, true);
win.addEventListener("focus", handleFocusEvent, true);
win.addEventListener("blur", handleWindowBlur, false);
if (typeof win.PointerEvent !== "undefined") {
doc.addEventListener("pointerdown", handlePointerEvent, true);
doc.addEventListener("pointermove", handlePointerEvent, true);
doc.addEventListener("pointerup", handlePointerEvent, true);
} else {
document.addEventListener("mousedown", onPointerEvent, true);
document.addEventListener("mousemove", onPointerEvent, true);
document.addEventListener("mouseup", onPointerEvent, true);
doc.addEventListener("mousedown", handlePointerEvent, true);
doc.addEventListener("mousemove", handlePointerEvent, true);
doc.addEventListener("mouseup", handlePointerEvent, true);
}
hasSetup = true;
win.addEventListener(
"beforeunload",
() => {
tearDownWindowFocusTracking(root);
},
{ once: true }
);
listenerMap.set(win, { focus });
}
function trackFocusVisible(fn) {
setupGlobalFocusEvents();
fn(isFocusVisible());
const handler = () => fn(isFocusVisible());
handlers.add(handler);
var tearDownWindowFocusTracking = (root, loadListener) => {
const win = domQuery.getWindow(root);
const doc = domQuery.getDocument(root);
if (!listenerMap.has(win)) {
return;
}
win.HTMLElement.prototype.focus = listenerMap.get(win).focus;
doc.removeEventListener("keydown", handleKeyboardEvent, true);
doc.removeEventListener("keyup", handleKeyboardEvent, true);
doc.removeEventListener("click", handleClickEvent, true);
win.removeEventListener("focus", handleFocusEvent, true);
win.removeEventListener("blur", handleWindowBlur, false);
if (typeof win.PointerEvent !== "undefined") {
doc.removeEventListener("pointerdown", handlePointerEvent, true);
doc.removeEventListener("pointermove", handlePointerEvent, true);
doc.removeEventListener("pointerup", handlePointerEvent, true);
} else {
doc.removeEventListener("mousedown", handlePointerEvent, true);
doc.removeEventListener("mousemove", handlePointerEvent, true);
doc.removeEventListener("mouseup", handlePointerEvent, true);
}
listenerMap.delete(win);
};
function getInteractionModality() {
return currentModality;
}
function setInteractionModality(modality) {
currentModality = modality;
triggerChangeHandlers(modality, null);
}
function trackInteractionModality(props) {
const { onChange, root } = props;
setupGlobalFocusEvents(root);
onChange({ modality: currentModality });
const handler = () => onChange({ modality: currentModality });
changeHandlers.add(handler);
return () => {
handlers.delete(handler);
changeHandlers.delete(handler);
};
}
function trackInteractionModality(fn) {
setupGlobalFocusEvents();
fn(modality);
const handler = () => fn(modality);
handlers.add(handler);
function isFocusVisible() {
return currentModality === "keyboard";
}
function trackFocusVisible(props = {}) {
const { isTextInput, autoFocus, onChange, root } = props;
setupGlobalFocusEvents(root);
onChange?.({ isFocusVisible: autoFocus || isFocusVisible(), modality: currentModality });
const handler = (modality, e) => {
if (!isKeyboardFocusEvent(!!isTextInput, modality, e)) return;
onChange?.({ isFocusVisible: isFocusVisible(), modality });
};
changeHandlers.add(handler);
return () => {
handlers.delete(handler);
changeHandlers.delete(handler);
};
}
function setInteractionModality(value) {
modality = value;
trigger(value, null);
}
function getInteractionModality() {
return modality;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
getInteractionModality,
setInteractionModality,
trackFocusVisible,
trackInteractionModality
});
//# sourceMappingURL=index.js.map
exports.getInteractionModality = getInteractionModality;
exports.isFocusVisible = isFocusVisible;
exports.listenerMap = listenerMap;
exports.setInteractionModality = setInteractionModality;
exports.trackFocusVisible = trackFocusVisible;
exports.trackInteractionModality = trackInteractionModality;
{
"name": "@zag-js/focus-visible",
"version": "0.0.0-dev-20240723090825",
"version": "0.0.0-v1-beta-20250220125322",
"description": "Focus visible polyfill utility based on WICG",

@@ -17,4 +17,3 @@ "keywords": [

"files": [
"dist",
"src"
"dist"
],

@@ -30,3 +29,3 @@ "publishConfig": {

"dependencies": {
"@zag-js/dom-query": "0.0.0-dev-20240723090825"
"@zag-js/dom-query": "0.0.0-v1-beta-20250220125322"
},

@@ -48,4 +47,4 @@ "devDependencies": {

"build": "tsup",
"test": "jest --config ../../../jest.config.js --rootDir tests",
"lint": "eslint src --ext .ts,.tsx",
"test": "vitest",
"lint": "eslint src",
"test-ci": "pnpm test --ci --runInBand -u",

@@ -52,0 +51,0 @@ "test-watch": "pnpm test --watchAll"

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet