Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

aria-voyager

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

aria-voyager - npm Package Compare versions

Comparing version 0.1.0 to 0.1.1

121

dist/index.d.ts

@@ -11,9 +11,2 @@ interface EmitterOptions<T> {

interface FocusStrategy {
activeItem?: Item;
prevActiveItem?: Item;
activateItem(item: Item): void;
updateItems(): void;
}
type NavigationParameterBag = {

@@ -31,2 +24,78 @@ event: Event;

interface SelectionBehavior {
/**
* Selection behavior:
*
* - `automatic`: active item becomes selected item
* - `manual`: user must select manually with spacebar
*
* @defaultValue `automatic`
*/
singleSelection?: 'automatic' | 'manual';
}
type EventHandler = (...args: unknown[]) => void;
declare class SelectionStrategy implements NavigationPattern {
#private;
private control;
eventListeners: EventNames[];
get selection(): Item[];
private shiftItem?;
private behavior;
constructor(control: Control, behavior?: SelectionBehavior);
dispose(): void;
addListener(event: 'read', handler: EventHandler): void;
removeListener(event: 'read', handler: EventHandler): void;
matches(event: Event): boolean;
prepare(event: Event): void;
handle(bag: NavigationParameterBag): NavigationParameterBag;
select(selection: Item[]): void;
readSelection(): void;
private handleChange;
private handleFocus;
private handlePointer;
private handleKeyboard;
private handleItem;
private handleKeys;
/**
* Handles special keyboard control cases, such as handling the spacebar key
* and cmd/ctrl + a
*/
private handleKeyCombinations;
private deselect;
private selectSingle;
private selectAdd;
private selectAll;
private selectRange;
private selectShift;
private persistSelection;
}
interface FocusStrategy {
activeItem?: Item;
prevActiveItem?: Item;
activateItem(item: Item): void;
updateItems(): void;
}
/**
* @see https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#keyboardnavigationinsidecomponents
*/
declare abstract class AbstractFocusStrategy implements NavigationPattern, FocusStrategy {
protected control: Control;
private selectionStrategy?;
eventListeners: EventNames[];
activeItem?: Item;
prevActiveItem?: Item;
get selection(): Item[];
constructor(control: Control, selectionStrategy?: SelectionStrategy | undefined);
dispose(): void;
readSelectionHandler(): void;
hasFocus(): boolean;
matches(): boolean;
handle(bag: NavigationParameterBag): NavigationParameterBag;
handleFocus(event: FocusEvent): void;
activateSelection(): void;
abstract activateItem(item: Item, forceFocus?: boolean): void;
abstract updateItems(): void;
}
interface UpdateStrategy {

@@ -55,6 +124,5 @@ setControl(control: Control): void;

#private;
protected abstract focusStrategy: FocusStrategy;
protected abstract focusStrategy: AbstractFocusStrategy;
items: Item[];
get enabledItems(): HTMLElement[];
abstract get selection(): Item[];
abstract get activeItem(): Item | undefined;

@@ -83,11 +151,3 @@ abstract get prevActiveItem(): Item | undefined;

declare class ActiveDescendentStrategy implements NavigationPattern, FocusStrategy {
private control;
eventListeners: EventNames[];
activeItem?: Item;
prevActiveItem?: Item;
constructor(control: Control);
matches(): boolean;
handle(bag: NavigationParameterBag): NavigationParameterBag;
handleFocus(): void;
declare class ActiveDescendentStrategy extends AbstractFocusStrategy {
activateItem(item: Item): void;

@@ -108,2 +168,3 @@ updateItems(): void;

constructor(element: HTMLElement, options?: ListboxOptions);
dispose(): void;
readItems(): void;

@@ -117,11 +178,4 @@ readSelection(): void;

*/
declare class RovingTabindexStrategy implements NavigationPattern, FocusStrategy {
private control;
eventListeners: EventNames[];
activeItem?: Item;
prevActiveItem?: Item;
constructor(control: Control);
matches(): boolean;
handle(bag: NavigationParameterBag): NavigationParameterBag;
activateItem(item: Item): void;
declare class RovingTabindexStrategy extends AbstractFocusStrategy {
activateItem(item: Item, forceFocus?: boolean): void;
updateItems(): void;

@@ -144,14 +198,2 @@ }

interface SelectionBehavior {
/**
* Selection behavior:
*
* - `automatic`: active item becomes selected item
* - `manual`: user must select manually with spacebar
*
* @defaultValue `automatic`
*/
singleSelection?: 'automatic' | 'manual';
}
type TablistBehavior = SelectionBehavior;

@@ -170,2 +212,3 @@ interface TablistOptions {

constructor(element: HTMLElement, options?: TablistOptions);
dispose(): void;
readItems(): void;

@@ -172,0 +215,0 @@ readSelection(): void;

@@ -137,2 +137,3 @@ // src/controls/-utils.ts

this.emitter?.dispose?.();
this.focusStrategy.dispose();
const eventNames = new Set(this.navigationPatterns.map((p) => p.eventListeners ?? []).flat());

@@ -167,11 +168,32 @@ for (const eventName of eventNames) {

import { v4 as uuidv4 } from "uuid";
var ActiveDescendentStrategy = class {
constructor(control) {
// src/navigation-patterns/focus-strategy.ts
var AbstractFocusStrategy = class {
constructor(control, selectionStrategy) {
this.control = control;
this.selectionStrategy = selectionStrategy;
this.selectionStrategy?.addListener("read", this.readSelectionHandler.bind(this));
}
eventListeners = ["focusin", "keydown", "pointerup"];
eventListeners = ["focus", "focusin"];
activeItem;
prevActiveItem;
get selection() {
if (this.selectionStrategy) {
return this.selectionStrategy.selection;
}
return [];
}
dispose() {
this.selectionStrategy?.removeListener("read", this.readSelectionHandler.bind(this));
}
readSelectionHandler() {
if (!this.hasFocus() && this.selection.length > 0) {
this.activateSelection();
}
}
hasFocus() {
return this.control.element.contains(document.activeElement) || this.control.element === document.activeElement;
}
matches() {
return this.control.items.length > 0;
return this.control.enabledItems.length > 0;
}

@@ -181,18 +203,29 @@ handle(bag) {

if (event.type === "focusin") {
this.handleFocus();
this.handleFocus(event);
return bag;
}
if (item) {
this.activateItem(item);
this.activateItem(item, event.type === "pointerover");
}
return bag;
}
handleFocus() {
const selectionPresent = this.control.selection.length > 0;
if (selectionPresent) {
this.activateItem(this.control.selection[0]);
} else {
this.activateItem(this.control.items[0]);
handleFocus(event) {
if (this.control.element === event.target) {
const selectionPresent = this.selection.length > 0;
if (selectionPresent) {
this.activateSelection();
} else {
this.activateItem(this.control.enabledItems[0]);
}
} else if (this.control.enabledItems.includes(event.target)) {
this.activateItem(event.target);
}
}
activateSelection() {
this.activateItem(this.selection[0]);
}
};
// src/navigation-patterns/active-descendent-strategy.ts
var ActiveDescendentStrategy = class extends AbstractFocusStrategy {
activateItem(item) {

@@ -412,2 +445,5 @@ if (item === this.activeItem) {

}
#listeners = {
read: /* @__PURE__ */ new Set()
};
eventListeners = ["focusin", "keydown", "keyup", "pointerup", "change"];

@@ -420,2 +456,11 @@ #selection = [];

behavior;
dispose() {
Object.values(this.#listeners).forEach((listeners) => listeners.clear());
}
addListener(event, handler) {
this.#listeners[event].add(handler);
}
removeListener(event, handler) {
this.#listeners[event].delete(handler);
}
matches(event) {

@@ -450,2 +495,5 @@ return this.control.items.length > 0 && this.eventListeners.includes(event.type);

];
for (const listener of this.#listeners.read) {
listener();
}
}

@@ -458,3 +506,3 @@ handleChange() {

const multiple = this.control.options.multiple;
const selectionPresent = this.control.selection.length > 0;
const selectionPresent = this.#selection.length > 0;
if (this.control.capabilities.singleSelection && !multiple && !selectionPresent) {

@@ -468,3 +516,3 @@ this.selectSingle(this.control.items[0]);

} else if (event.metaKey) {
if (this.control.selection.includes(item)) {
if (this.#selection.includes(item)) {
this.deselect(item);

@@ -512,3 +560,3 @@ } else {

if (event.key === " " && this.control.activeItem && this.control.options.multiple) {
if (this.control.selection.includes(this.control.activeItem)) {
if (this.#selection.includes(this.control.activeItem)) {
this.deselect(this.control.activeItem);

@@ -526,4 +574,4 @@ } else {

deselect(item) {
if (this.control.selection.includes(item)) {
const selection = this.control.selection.slice();
if (this.#selection.includes(item)) {
const selection = this.#selection.slice();
selection.splice(selection.indexOf(item), 1);

@@ -538,3 +586,3 @@ this.persistSelection(selection);

selectAdd(item) {
const selection = this.control.options.multiple ? this.control.selection.slice() : [];
const selection = this.control.options.multiple ? this.#selection.slice() : [];
selection.push(item);

@@ -588,3 +636,6 @@ this.shiftItem = item;

#selectionStrategy = new SelectionStrategy(this);
focusStrategy = new ActiveDescendentStrategy(this);
focusStrategy = new ActiveDescendentStrategy(
this,
this.#selectionStrategy
);
get selection() {

@@ -625,2 +676,6 @@ return this.#selectionStrategy.selection;

}
dispose() {
super.dispose();
this.#selectionStrategy.dispose();
}
readItems() {

@@ -774,35 +829,15 @@ this.items = [...this.element.querySelectorAll('[role="option"]')];

// src/navigation-patterns/roving-tabindex-strategy.ts
var RovingTabindexStrategy = class {
constructor(control) {
this.control = control;
}
eventListeners = ["focus", "focusin"];
activeItem;
prevActiveItem;
matches() {
return this.control.enabledItems.length > 0;
}
handle(bag) {
const { event, item } = bag;
if (event.type === "focusin" && !this.activeItem) {
if (this.control.element === event.target) {
this.activateItem(this.control.enabledItems[0]);
} else if (this.control.enabledItems.includes(event.target)) {
this.activateItem(event.target);
var RovingTabindexStrategy = class extends AbstractFocusStrategy {
activateItem(item, forceFocus = false) {
if (item !== this.activeItem) {
item.setAttribute("tabindex", "0");
if (this.activeItem) {
this.prevActiveItem = this.activeItem;
this.prevActiveItem.setAttribute("tabindex", "-1");
}
return bag;
}
if (item) {
this.activateItem(item);
if (this.hasFocus() || forceFocus) {
item.focus();
}
return bag;
}
activateItem(item) {
if (item !== this.activeItem) {
item.setAttribute("tabindex", "0");
this.prevActiveItem = this.activeItem;
this.control.prevActiveItem?.setAttribute("tabindex", "-1");
}
item.focus();
if (item !== this.activeItem) {
this.activeItem = item;

@@ -885,3 +920,3 @@ this.control.emitter?.itemActivated(item);

#selectionStrategy;
focusStrategy = new RovingTabindexStrategy(this);
focusStrategy;
#nextNavigation = new NextNavigation(this, "ArrowRight");

@@ -908,2 +943,3 @@ #prevNavigation = new PreviousNavigation(this, "ArrowLeft");

this.#selectionStrategy = new SelectionStrategy(this, options?.behavior ?? {});
this.focusStrategy = new RovingTabindexStrategy(this, this.#selectionStrategy);
this.registerNavigationPatterns([

@@ -922,2 +958,6 @@ this.#nextNavigation,

}
dispose() {
super.dispose();
this.#selectionStrategy.dispose();
}
readItems() {

@@ -924,0 +964,0 @@ this.items = [...this.element.querySelectorAll('[role="tab"]')];

{
"name": "aria-voyager",
"version": "0.1.0",
"version": "0.1.1",
"description": "A framework agnostic / universal package that implements navigation patterns for various aria roles and features",

@@ -32,3 +32,3 @@ "author": "gossi",

"@swc/cli": "0.5.2",
"@swc/core": "1.10.0",
"@swc/core": "1.10.1",
"@testing-library/dom": "10.4.0",

@@ -44,3 +44,3 @@ "@types/css-modules": "1.0.5",

"eslint": "8.57.1",
"playwright": "^1.49.0",
"playwright": "^1.49.1",
"prettier": "3.4.2",

@@ -51,3 +51,3 @@ "tsup": "8.3.5",

"vitest": "^2.1.8",
"webdriverio": "^9.4.1"
"webdriverio": "^9.4.2"
},

@@ -54,0 +54,0 @@ "engines": {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc