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

@lightningtv/solid

Package Overview
Dependencies
Maintainers
0
Versions
92
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lightningtv/solid - npm Package Compare versions

Comparing version 1.0.3 to 1.1.0

34

dist/src/primitives/useFocusManager.d.ts

@@ -0,13 +1,19 @@

import { ElementNode } from '@lightningtv/solid';
import { type Accessor } from 'solid-js';
import { ElementNode } from '@lightningtv/solid';
export type KeyNameOrKeyCode = string | number;
export interface DefaultKeyMap {
Left: string | number | (string | number)[];
Right: string | number | (string | number)[];
Up: string | number | (string | number)[];
Down: string | number | (string | number)[];
Enter: string | number | (string | number)[];
Last: string | number | (string | number)[];
Left: KeyNameOrKeyCode | KeyNameOrKeyCode[];
Right: KeyNameOrKeyCode | KeyNameOrKeyCode[];
Up: KeyNameOrKeyCode | KeyNameOrKeyCode[];
Down: KeyNameOrKeyCode | KeyNameOrKeyCode[];
Enter: KeyNameOrKeyCode | KeyNameOrKeyCode[];
Last: KeyNameOrKeyCode | KeyNameOrKeyCode[];
}
export interface DefaultKeyHoldMap {
EnterHold: KeyNameOrKeyCode | KeyNameOrKeyCode[];
}
export interface KeyMap extends DefaultKeyMap {
}
export interface KeyHoldMap extends DefaultKeyHoldMap {
}
export type KeyHandlerReturn = boolean | void;

@@ -21,2 +27,5 @@ export type KeyHandler = (this: ElementNode, e: KeyboardEvent, target: ElementNode, handlerElm: ElementNode) => KeyHandlerReturn;

};
type KeyHoldMapEventHandlers = {
[K in keyof KeyHoldMap as `on${Capitalize<K>}`]?: KeyHandler;
};
declare module '@lightningtv/solid' {

@@ -27,3 +36,3 @@ /**

*/
interface IntrinsicCommonProps extends KeyMapEventHandlers {
interface IntrinsicCommonProps extends KeyMapEventHandlers, KeyHoldMapEventHandlers {
onFocus?: (currentFocusedElm: ElementNode | undefined, prevFocusedElm: ElementNode | undefined) => void;

@@ -48,4 +57,11 @@ onFocusChanged?: (hasFocus: boolean, currentFocusedElm: ElementNode | undefined, prevFocusedElm: ElementNode | undefined) => void;

}
/**
* holdThreshold is in milliseconds.
*/
export type KeyHoldOptions = {
userKeyHoldMap: Partial<KeyHoldMap>;
holdThreshold?: number;
};
declare const focusPath: Accessor<ElementNode[]>;
export { focusPath };
export declare const useFocusManager: (userKeyMap?: Partial<KeyMap>) => Accessor<ElementNode[]>;
export declare const useFocusManager: (userKeyMap?: Partial<KeyMap>, keyHoldOptions?: KeyHoldOptions) => Accessor<ElementNode[]>;

@@ -1,4 +0,6 @@

import { createEffect, on, createSignal, untrack, } from 'solid-js';
import { activeElement, isFunc, isArray, } from '@lightningtv/solid';
import { createEffect, createSignal, on, onCleanup, untrack } from 'solid-js';
import { makeEventListener } from '@solid-primitives/event-listener';
import { useKeyDownEvent } from '@solid-primitives/keyboard';
import { activeElement, isFunc, isArray, } from '@lightningtv/solid';
import { createSingletonRoot } from '@solid-primitives/rootless';
const keyMapEntries = {

@@ -15,6 +17,28 @@ ArrowLeft: 'Left',

};
const keyHoldMapEntries = {
Enter: 'EnterHold',
};
const DEFAULT_KEY_HOLD_THRESHOLD = 150; // ms
const [focusPath, setFocusPath] = createSignal([]);
export { focusPath };
export const useFocusManager = (userKeyMap) => {
// copy of useKeyDownEvent but for keyup
const useKeyUpEvent = /*#__PURE__*/ createSingletonRoot(() => {
const [event, setEvent] = createSignal(null);
makeEventListener(window, 'keyup', (e) => {
setEvent(e);
setTimeout(() => setEvent(null));
});
return event;
});
export const useFocusManager = (userKeyMap, keyHoldOptions) => {
const keypressEvent = useKeyDownEvent();
const keyupEvent = useKeyUpEvent();
const keyHoldTimeouts = {};
// clear out any leftover timeouts
onCleanup(() => {
for (const [_, timeout] of Object.entries(keyHoldTimeouts)) {
if (timeout)
clearTimeout(timeout);
}
});
if (userKeyMap) {

@@ -33,2 +57,18 @@ // Flatten the userKeyMap to a hash

}
if (keyHoldOptions?.userKeyHoldMap) {
// same as above
for (const [key, value] of Object.entries(keyHoldOptions?.userKeyHoldMap)) {
if (value === undefined || value === null) {
continue;
}
if (isArray(value)) {
for (const v of value) {
keyHoldMapEntries[v] = key;
}
}
else {
keyHoldMapEntries[value] = key;
}
}
}
createEffect(on(activeElement, (currentFocusedElm, prevFocusedElm, prevFocusPath = []) => {

@@ -62,25 +102,12 @@ let current = currentFocusedElm;

}, { defer: true }));
createEffect(() => {
const e = keypressEvent();
if (e) {
// Search keyMap for the value of the pressed key or keyCode if value undefined
const mappedKeyEvent = keyMapEntries[e.key] || keyMapEntries[e.keyCode];
untrack(() => {
const fp = focusPath();
let finalFocusElm = undefined;
for (const elm of fp) {
finalFocusElm = finalFocusElm || elm;
if (mappedKeyEvent) {
const onKeyHandler = elm[`on${mappedKeyEvent}`];
if (isFunc(onKeyHandler)) {
if (onKeyHandler.call(elm, e, elm, finalFocusElm) === true) {
break;
}
}
}
else {
console.log(`Unhandled key event: ${e.key || e.keyCode}`);
}
if (isFunc(elm.onKeyPress)) {
if (elm.onKeyPress.call(elm, e, mappedKeyEvent, elm, finalFocusElm) === true) {
const propagateKeyDown = (e, mappedEvent, isHold = false) => {
untrack(() => {
const fp = focusPath();
let finalFocusElm = undefined;
for (const elm of fp) {
finalFocusElm = finalFocusElm || elm;
if (mappedEvent) {
const onKeyHandler = elm[`on${mappedEvent}`];
if (isFunc(onKeyHandler)) {
if (onKeyHandler.call(elm, e, elm, finalFocusElm) === true) {
break;

@@ -90,5 +117,48 @@ }

}
return false;
});
else {
console.log(`Unhandled key event: ${e.key || e.keyCode}`);
}
const fallbackFunction = isHold ? elm.onKeyPress : elm.onKeyHold;
if (isFunc(fallbackFunction)) {
if (fallbackFunction.call(elm, e, mappedEvent, elm, finalFocusElm) === true) {
break;
}
}
}
return false;
});
};
const keyHoldCallback = (e, mappedKeyHoldEvent) => {
delete keyHoldTimeouts[e.key || e.keyCode];
propagateKeyDown(e, mappedKeyHoldEvent, true);
};
createEffect(() => {
const keypress = keypressEvent();
const keyup = keyupEvent();
if (keypress) {
const key = keypress.key || keypress.keyCode;
const mappedKeyHoldEvent = keyHoldMapEntries[key];
const mappedKeyEvent = keyMapEntries[key];
if (!mappedKeyHoldEvent) {
// just a regular key press
propagateKeyDown(keypress, mappedKeyEvent, false);
}
else {
const delay = keyHoldOptions?.holdThreshold || DEFAULT_KEY_HOLD_THRESHOLD;
if (keyHoldTimeouts[key]) {
// recieved two keydown events without a keyup in between
clearTimeout(keyHoldTimeouts[key]);
}
keyHoldTimeouts[key] = setTimeout(() => keyHoldCallback(keypress, mappedKeyHoldEvent), delay);
}
}
if (keyup) {
const key = keyup.key || keyup.keyCode;
const mappedKeyEvent = keyMapEntries[key];
if (keyHoldTimeouts[key]) {
clearTimeout(keyHoldTimeouts[key]);
delete keyHoldTimeouts[key];
propagateKeyDown(keyup, mappedKeyEvent, false);
}
}
});

@@ -95,0 +165,0 @@ return focusPath;

@@ -6,4 +6,4 @@ import { activeElement, setActiveElement, rootNode, Config, } from '@lightningtv/solid';

import { createEffect } from 'solid-js';
function createKeyboardEvent(key, keyCode) {
return new KeyboardEvent('keydown', {
function createKeyboardEvent(key, keyCode, eventName = 'keydown') {
return new KeyboardEvent(eventName, {
key,

@@ -22,6 +22,6 @@ keyCode,

if (deltaY < 0) {
document.dispatchEvent(createKeyboardEvent('ArrowUp', 38));
document.body.dispatchEvent(createKeyboardEvent('ArrowUp', 38));
}
else if (deltaY > 0) {
document.dispatchEvent(createKeyboardEvent('ArrowDown', 40));
document.body.dispatchEvent(createKeyboardEvent('ArrowDown', 40));
}

@@ -35,2 +35,3 @@ }, 250);

document.dispatchEvent(createKeyboardEvent('Enter', 13));
setTimeout(() => document.body.dispatchEvent(createKeyboardEvent('Enter', 13, 'keyup')), 1);
}

@@ -37,0 +38,0 @@ };

import { type IntrinsicNodeProps, type IntrinsicTextProps } from '@lightningtv/core';
import { type JSXElement, type ValidComponent } from 'solid-js';
import type { RendererMain, RendererMainSettings } from '@lightningjs/renderer';
import { SolidNode } from './types.js';
import type { SolidNode } from './types.js';
export declare const rootNode: import("@lightningtv/core").ElementNode;

@@ -6,0 +6,0 @@ export declare function createRenderer(rendererOptions?: RendererMainSettings | undefined, node?: HTMLElement | string): {

@@ -1,3 +0,3 @@

import { ElementNode, ElementText } from '@lightningtv/core';
import { SolidNode } from './types.js';
import { ElementNode, type ElementText } from '@lightningtv/core';
import type { SolidNode } from './types.js';
declare const _default: {

@@ -4,0 +4,0 @@ createElement(name: string): ElementNode;

@@ -1,3 +0,3 @@

import { ElementNode, Styles, ElementText } from '@lightningtv/core';
import { JSXElement } from 'solid-js';
import { ElementNode, type Styles, type ElementText } from '@lightningtv/core';
import type { JSXElement } from 'solid-js';
import { createRenderer } from 'solid-js/universal';

@@ -4,0 +4,0 @@ export type SolidRendererOptions = Parameters<typeof createRenderer<SolidNode>>[0];

{
"name": "@lightningtv/solid",
"version": "1.0.3",
"version": "1.1.0",
"description": "Lightning Renderer for Solid Universal",

@@ -45,6 +45,7 @@ "type": "module",

"dependencies": {
"@lightningtv/core": "^1.0.3",
"@lightningtv/core": "^1.0.4",
"@solid-primitives/event-listener": "^2.3.3",
"@solid-primitives/keyboard": "^1.2.8",
"@solid-primitives/mouse": "^2.0.19",
"@solid-primitives/rootless": "^1.4.5",
"@solid-primitives/scheduled": "^1.4.3"

@@ -51,0 +52,0 @@ },

import {
createEffect,
on,
createSignal,
untrack,
type Accessor,
} from 'solid-js';
import { useKeyDownEvent } from '@solid-primitives/keyboard';
import {
activeElement,

@@ -15,14 +7,27 @@ ElementNode,

} from '@lightningtv/solid';
import { createEffect, createSignal, on, onCleanup, untrack } from 'solid-js';
import { type Accessor } from 'solid-js';
import { makeEventListener } from '@solid-primitives/event-listener';
import { useKeyDownEvent } from '@solid-primitives/keyboard';
import { createSingletonRoot } from '@solid-primitives/rootless';
export type KeyNameOrKeyCode = string | number;
export interface DefaultKeyMap {
Left: string | number | (string | number)[];
Right: string | number | (string | number)[];
Up: string | number | (string | number)[];
Down: string | number | (string | number)[];
Enter: string | number | (string | number)[];
Last: string | number | (string | number)[];
Left: KeyNameOrKeyCode | KeyNameOrKeyCode[];
Right: KeyNameOrKeyCode | KeyNameOrKeyCode[];
Up: KeyNameOrKeyCode | KeyNameOrKeyCode[];
Down: KeyNameOrKeyCode | KeyNameOrKeyCode[];
Enter: KeyNameOrKeyCode | KeyNameOrKeyCode[];
Last: KeyNameOrKeyCode | KeyNameOrKeyCode[];
}
export interface DefaultKeyHoldMap {
EnterHold: KeyNameOrKeyCode | KeyNameOrKeyCode[];
}
export interface KeyMap extends DefaultKeyMap {}
export interface KeyHoldMap extends DefaultKeyHoldMap {}
export type KeyHandlerReturn = boolean | void;

@@ -43,2 +48,5 @@

};
type KeyHoldMapEventHandlers = {
[K in keyof KeyHoldMap as `on${Capitalize<K>}`]?: KeyHandler;
};

@@ -50,3 +58,5 @@ declare module '@lightningtv/solid' {

*/
interface IntrinsicCommonProps extends KeyMapEventHandlers {
interface IntrinsicCommonProps
extends KeyMapEventHandlers,
KeyHoldMapEventHandlers {
onFocus?: (

@@ -98,3 +108,3 @@ currentFocusedElm: ElementNode | undefined,

const keyMapEntries: Record<string | number, string> = {
const keyMapEntries: Record<KeyNameOrKeyCode, string> = {
ArrowLeft: 'Left',

@@ -111,6 +121,48 @@ ArrowRight: 'Right',

const keyHoldMapEntries: Record<KeyNameOrKeyCode, string> = {
Enter: 'EnterHold',
};
const DEFAULT_KEY_HOLD_THRESHOLD = 150; // ms
/**
* holdThreshold is in milliseconds.
*/
export type KeyHoldOptions = {
userKeyHoldMap: Partial<KeyHoldMap>;
holdThreshold?: number;
};
const [focusPath, setFocusPath] = createSignal<ElementNode[]>([]);
export { focusPath };
export const useFocusManager = (userKeyMap?: Partial<KeyMap>) => {
// copy of useKeyDownEvent but for keyup
const useKeyUpEvent = /*#__PURE__*/ createSingletonRoot<
Accessor<KeyboardEvent | null>
>(() => {
const [event, setEvent] = createSignal<KeyboardEvent | null>(null);
makeEventListener(window, 'keyup', (e) => {
setEvent(e);
setTimeout(() => setEvent(null));
});
return event;
});
export const useFocusManager = (
userKeyMap?: Partial<KeyMap>,
keyHoldOptions?: KeyHoldOptions,
) => {
const keypressEvent = useKeyDownEvent();
const keyupEvent = useKeyUpEvent();
const keyHoldTimeouts: { [key: KeyNameOrKeyCode]: number } = {};
// clear out any leftover timeouts
onCleanup(() => {
for (const [_, timeout] of Object.entries(keyHoldTimeouts)) {
if (timeout) clearTimeout(timeout);
}
});
if (userKeyMap) {

@@ -128,2 +180,17 @@ // Flatten the userKeyMap to a hash

}
if (keyHoldOptions?.userKeyHoldMap) {
// same as above
for (const [key, value] of Object.entries(keyHoldOptions?.userKeyHoldMap)) {
if (value === undefined || value === null) {
continue;
}
if (isArray(value)) {
for (const v of value) {
keyHoldMapEntries[v] = key;
}
} else {
keyHoldMapEntries[value] = key;
}
}
}
createEffect(

@@ -181,42 +248,83 @@ on(

createEffect(() => {
const e = keypressEvent();
if (e) {
// Search keyMap for the value of the pressed key or keyCode if value undefined
const mappedKeyEvent = keyMapEntries[e.key] || keyMapEntries[e.keyCode];
untrack(() => {
const fp = focusPath();
let finalFocusElm: ElementNode | undefined = undefined;
for (const elm of fp) {
finalFocusElm = finalFocusElm || elm;
if (mappedKeyEvent) {
const onKeyHandler =
elm[`on${mappedKeyEvent}` as keyof KeyMapEventHandlers];
if (isFunc(onKeyHandler)) {
if (onKeyHandler.call(elm, e, elm, finalFocusElm) === true) {
break;
}
}
} else {
console.log(`Unhandled key event: ${e.key || e.keyCode}`);
}
if (isFunc(elm.onKeyPress)) {
if (
elm.onKeyPress.call(
elm,
e,
mappedKeyEvent,
elm,
finalFocusElm,
) === true
) {
const propagateKeyDown = (
e: KeyboardEvent,
mappedEvent: string | undefined,
isHold = false,
) => {
untrack(() => {
const fp = focusPath();
let finalFocusElm: ElementNode | undefined = undefined;
for (const elm of fp) {
finalFocusElm = finalFocusElm || elm;
if (mappedEvent) {
const onKeyHandler =
elm[`on${mappedEvent}` as keyof KeyMapEventHandlers];
if (isFunc(onKeyHandler)) {
if (onKeyHandler.call(elm, e, elm, finalFocusElm) === true) {
break;
}
}
} else {
console.log(`Unhandled key event: ${e.key || e.keyCode}`);
}
return false;
});
const fallbackFunction = isHold ? elm.onKeyPress : elm.onKeyHold;
if (isFunc(fallbackFunction)) {
if (
(fallbackFunction as any).call(
elm,
e,
mappedEvent,
elm,
finalFocusElm,
) === true
) {
break;
}
}
}
return false;
});
};
const keyHoldCallback = (
e: KeyboardEvent,
mappedKeyHoldEvent: string | undefined,
) => {
delete keyHoldTimeouts[e.key || e.keyCode];
propagateKeyDown(e, mappedKeyHoldEvent, true);
};
createEffect(() => {
const keypress = keypressEvent();
const keyup = keyupEvent();
if (keypress) {
const key: KeyNameOrKeyCode = keypress.key || keypress.keyCode;
const mappedKeyHoldEvent = keyHoldMapEntries[key];
const mappedKeyEvent = keyMapEntries[key];
if (!mappedKeyHoldEvent) {
// just a regular key press
propagateKeyDown(keypress, mappedKeyEvent, false);
} else {
const delay =
keyHoldOptions?.holdThreshold || DEFAULT_KEY_HOLD_THRESHOLD;
if (keyHoldTimeouts[key]) {
// recieved two keydown events without a keyup in between
clearTimeout(keyHoldTimeouts[key]);
}
keyHoldTimeouts[key] = setTimeout(
() => keyHoldCallback(keypress, mappedKeyHoldEvent),
delay,
);
}
}
if (keyup) {
const key: KeyNameOrKeyCode = keyup.key || keyup.keyCode;
const mappedKeyEvent = keyMapEntries[key];
if (keyHoldTimeouts[key]) {
clearTimeout(keyHoldTimeouts[key]);
delete keyHoldTimeouts[key];
propagateKeyDown(keyup, mappedKeyEvent, false);
}
}
});

@@ -223,0 +331,0 @@

@@ -14,4 +14,8 @@ import type { INode } from '@lightningjs/renderer';

function createKeyboardEvent(key: string, keyCode: number): KeyboardEvent {
return new KeyboardEvent('keydown', {
function createKeyboardEvent(
key: string,
keyCode: number,
eventName: string = 'keydown',
): KeyboardEvent {
return new KeyboardEvent(eventName, {
key,

@@ -31,5 +35,5 @@ keyCode,

if (deltaY < 0) {
document.dispatchEvent(createKeyboardEvent('ArrowUp', 38));
document.body.dispatchEvent(createKeyboardEvent('ArrowUp', 38));
} else if (deltaY > 0) {
document.dispatchEvent(createKeyboardEvent('ArrowDown', 40));
document.body.dispatchEvent(createKeyboardEvent('ArrowDown', 40));
}

@@ -53,2 +57,7 @@ }, 250);

document.dispatchEvent(createKeyboardEvent('Enter', 13));
setTimeout(
() =>
document.body.dispatchEvent(createKeyboardEvent('Enter', 13, 'keyup')),
1,
);
}

@@ -55,0 +64,0 @@ };

@@ -17,3 +17,3 @@ import { createRenderer as solidCreateRenderer } from 'solid-js/universal';

import type { RendererMain, RendererMainSettings } from '@lightningjs/renderer';
import { SolidNode } from './types.js';
import type { SolidNode } from './types.js';

@@ -20,0 +20,0 @@ const solidRenderer = solidCreateRenderer<SolidNode>(nodeOpts);

import { assertTruthy } from '@lightningjs/renderer/utils';
import { ElementNode, NodeType, log, ElementText } from '@lightningtv/core';
import { SolidNode, SolidRendererOptions } from './types.js';
import { ElementNode, NodeType, log, type ElementText } from '@lightningtv/core';
import type { SolidNode, SolidRendererOptions } from './types.js';

@@ -5,0 +5,0 @@ export default {

@@ -1,3 +0,3 @@

import { ElementNode, Styles, ElementText } from '@lightningtv/core';
import { JSXElement } from 'solid-js';
import { ElementNode, type Styles, type ElementText } from '@lightningtv/core';
import type { JSXElement } from 'solid-js';
import { createRenderer } from 'solid-js/universal';

@@ -4,0 +4,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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