@lightningtv/solid 3.0.0-2 → 3.0.0-21
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/LICENSE +1 -1
- package/README.md +6 -0
- package/dist/src/activeElement.d.ts +1 -1
- package/dist/src/core/animation.d.ts +35 -0
- package/dist/src/core/animation.js +120 -0
- package/dist/src/core/animation.js.map +1 -0
- package/dist/src/core/config.d.ts +47 -0
- package/dist/src/core/config.js +23 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/domRenderer.d.ts +117 -0
- package/dist/src/core/domRenderer.js +1160 -0
- package/dist/src/core/domRenderer.js.map +1 -0
- package/dist/src/core/elementNode.d.ts +209 -0
- package/dist/src/core/elementNode.js +829 -0
- package/dist/src/core/elementNode.js.map +1 -0
- package/dist/src/core/flex.d.ts +2 -0
- package/dist/src/core/flex.js +243 -0
- package/dist/src/core/flex.js.map +1 -0
- package/dist/src/core/focusKeyTypes.d.ts +42 -0
- package/dist/src/core/focusKeyTypes.js +2 -0
- package/dist/src/core/focusKeyTypes.js.map +1 -0
- package/dist/src/core/focusManager.d.ts +13 -0
- package/dist/src/core/focusManager.js +269 -0
- package/dist/src/core/focusManager.js.map +1 -0
- package/dist/src/core/index.d.ts +12 -0
- package/dist/src/core/index.js +12 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/core/intrinsicTypes.d.ts +90 -0
- package/dist/src/core/intrinsicTypes.js +2 -0
- package/dist/src/core/intrinsicTypes.js.map +1 -0
- package/dist/src/core/lightningInit.d.ts +89 -0
- package/dist/src/core/lightningInit.js +26 -0
- package/dist/src/core/lightningInit.js.map +1 -0
- package/dist/src/core/nodeTypes.d.ts +6 -0
- package/dist/src/core/nodeTypes.js +6 -0
- package/dist/src/core/nodeTypes.js.map +1 -0
- package/dist/src/core/shaders.d.ts +51 -0
- package/dist/src/core/shaders.js +446 -0
- package/dist/src/core/shaders.js.map +1 -0
- package/dist/src/core/states.d.ts +12 -0
- package/dist/src/core/states.js +84 -0
- package/dist/src/core/states.js.map +1 -0
- package/dist/src/core/timings.d.ts +36 -0
- package/dist/src/core/timings.js +199 -0
- package/dist/src/core/timings.js.map +1 -0
- package/dist/src/core/utils.d.ts +39 -0
- package/dist/src/core/utils.js +164 -0
- package/dist/src/core/utils.js.map +1 -0
- package/dist/src/devtools/index.d.ts +1 -1
- package/dist/src/devtools/index.js +1 -1
- package/dist/src/devtools/index.js.map +1 -1
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/jsx-runtime.d.ts +1 -3
- package/dist/src/primitives/Column.jsx +9 -10
- package/dist/src/primitives/Column.jsx.map +1 -1
- package/dist/src/primitives/FPSCounter.jsx +14 -1
- package/dist/src/primitives/FPSCounter.jsx.map +1 -1
- package/dist/src/primitives/Grid.d.ts +15 -6
- package/dist/src/primitives/Grid.jsx +35 -22
- package/dist/src/primitives/Grid.jsx.map +1 -1
- package/dist/src/primitives/Image.d.ts +8 -0
- package/dist/src/primitives/Image.jsx +24 -0
- package/dist/src/primitives/Image.jsx.map +1 -0
- package/dist/src/primitives/KeepAlive.d.ts +30 -0
- package/dist/src/primitives/KeepAlive.jsx +77 -0
- package/dist/src/primitives/KeepAlive.jsx.map +1 -0
- package/dist/src/primitives/Lazy.d.ts +8 -7
- package/dist/src/primitives/Lazy.jsx +52 -23
- package/dist/src/primitives/Lazy.jsx.map +1 -1
- package/dist/src/primitives/Marquee.d.ts +64 -0
- package/dist/src/primitives/Marquee.jsx +86 -0
- package/dist/src/primitives/Marquee.jsx.map +1 -0
- package/dist/src/primitives/Preserve.d.ts +4 -0
- package/dist/src/primitives/Preserve.jsx +11 -0
- package/dist/src/primitives/Preserve.jsx.map +1 -0
- package/dist/src/primitives/Row.jsx +9 -10
- package/dist/src/primitives/Row.jsx.map +1 -1
- package/dist/src/primitives/Suspense.d.ts +22 -0
- package/dist/src/primitives/Suspense.jsx +33 -0
- package/dist/src/primitives/Suspense.jsx.map +1 -0
- package/dist/src/primitives/Virtual.d.ts +18 -0
- package/dist/src/primitives/Virtual.jsx +434 -0
- package/dist/src/primitives/Virtual.jsx.map +1 -0
- package/dist/src/primitives/VirtualGrid.d.ts +13 -0
- package/dist/src/primitives/VirtualGrid.jsx +160 -0
- package/dist/src/primitives/VirtualGrid.jsx.map +1 -0
- package/dist/src/primitives/VirtualList.d.ts +11 -0
- package/dist/src/primitives/VirtualList.jsx +96 -0
- package/dist/src/primitives/VirtualList.jsx.map +1 -0
- package/dist/src/primitives/VirtualRow.d.ts +13 -0
- package/dist/src/primitives/VirtualRow.jsx +97 -0
- package/dist/src/primitives/VirtualRow.jsx.map +1 -0
- package/dist/src/primitives/Visible.d.ts +0 -1
- package/dist/src/primitives/Visible.jsx +1 -1
- package/dist/src/primitives/Visible.jsx.map +1 -1
- package/dist/src/primitives/announcer/announcer.d.ts +2 -0
- package/dist/src/primitives/announcer/announcer.js +7 -5
- package/dist/src/primitives/announcer/announcer.js.map +1 -1
- package/dist/src/primitives/announcer/index.d.ts +5 -1
- package/dist/src/primitives/announcer/index.js +8 -2
- package/dist/src/primitives/announcer/index.js.map +1 -1
- package/dist/src/primitives/announcer/speech.d.ts +2 -2
- package/dist/src/primitives/announcer/speech.js +157 -28
- package/dist/src/primitives/announcer/speech.js.map +1 -1
- package/dist/src/primitives/createFocusStack.d.ts +4 -4
- package/dist/src/primitives/createFocusStack.jsx +15 -6
- package/dist/src/primitives/createFocusStack.jsx.map +1 -1
- package/dist/src/primitives/createTag.d.ts +8 -0
- package/dist/src/primitives/createTag.jsx +20 -0
- package/dist/src/primitives/createTag.jsx.map +1 -0
- package/dist/src/primitives/index.d.ts +14 -4
- package/dist/src/primitives/index.js +13 -3
- package/dist/src/primitives/index.js.map +1 -1
- package/dist/src/primitives/types.d.ts +5 -2
- package/dist/src/primitives/useFocusManager.d.ts +2 -2
- package/dist/src/primitives/useFocusManager.js +2 -2
- package/dist/src/primitives/useFocusManager.js.map +1 -1
- package/dist/src/primitives/useHold.d.ts +27 -0
- package/dist/src/primitives/useHold.js +54 -0
- package/dist/src/primitives/useHold.js.map +1 -0
- package/dist/src/primitives/useMouse.d.ts +18 -2
- package/dist/src/primitives/useMouse.js +171 -47
- package/dist/src/primitives/useMouse.js.map +1 -1
- package/dist/src/primitives/utils/chainFunctions.d.ts +30 -4
- package/dist/src/primitives/utils/chainFunctions.js +14 -3
- package/dist/src/primitives/utils/chainFunctions.js.map +1 -1
- package/dist/src/primitives/utils/createBlurredImage.d.ts +56 -0
- package/dist/src/primitives/utils/createBlurredImage.js +223 -0
- package/dist/src/primitives/utils/createBlurredImage.js.map +1 -0
- package/dist/src/primitives/utils/createSpriteMap.d.ts +2 -2
- package/dist/src/primitives/utils/createSpriteMap.js +1 -1
- package/dist/src/primitives/utils/createSpriteMap.js.map +1 -1
- package/dist/src/primitives/utils/handleNavigation.d.ts +79 -5
- package/dist/src/primitives/utils/handleNavigation.js +242 -69
- package/dist/src/primitives/utils/handleNavigation.js.map +1 -1
- package/dist/src/primitives/utils/withScrolling.d.ts +14 -2
- package/dist/src/primitives/utils/withScrolling.js +66 -7
- package/dist/src/primitives/utils/withScrolling.js.map +1 -1
- package/dist/src/render.d.ts +8 -7
- package/dist/src/render.js +5 -1
- package/dist/src/render.js.map +1 -1
- package/dist/src/solidOpts.d.ts +1 -7
- package/dist/src/solidOpts.js +32 -16
- package/dist/src/solidOpts.js.map +1 -1
- package/dist/src/types.d.ts +1 -13
- package/dist/src/universal.d.ts +25 -0
- package/dist/src/universal.js +232 -0
- package/dist/src/universal.js.map +1 -0
- package/dist/src/utils.d.ts +3 -1
- package/dist/src/utils.js +9 -1
- package/dist/src/utils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jsx-runtime.d.ts +2 -4
- package/package.json +17 -15
- package/src/activeElement.ts +1 -1
- package/src/core/animation.ts +183 -0
- package/src/core/config.ts +77 -0
- package/src/core/domRenderer.ts +1308 -0
- package/src/core/elementNode.ts +1198 -0
- package/src/core/flex.ts +284 -0
- package/src/core/focusKeyTypes.ts +87 -0
- package/src/core/focusManager.ts +359 -0
- package/src/core/index.ts +13 -0
- package/src/core/intrinsicTypes.ts +199 -0
- package/src/core/lightningInit.ts +147 -0
- package/src/core/nodeTypes.ts +6 -0
- package/src/core/shaders.ts +567 -0
- package/src/core/states.ts +91 -0
- package/src/core/timings.ts +261 -0
- package/src/core/utils.ts +222 -0
- package/src/devtools/index.ts +1 -1
- package/src/index.ts +3 -3
- package/src/primitives/Column.tsx +10 -12
- package/src/primitives/FPSCounter.tsx +15 -1
- package/src/primitives/Grid.tsx +57 -33
- package/src/primitives/Image.tsx +36 -0
- package/src/primitives/KeepAlive.tsx +124 -0
- package/src/primitives/Lazy.tsx +66 -37
- package/src/primitives/Marquee.tsx +149 -0
- package/src/primitives/Preserve.tsx +18 -0
- package/src/primitives/Row.tsx +13 -14
- package/src/primitives/Suspense.tsx +39 -0
- package/src/primitives/Virtual.tsx +478 -0
- package/src/primitives/VirtualGrid.tsx +220 -0
- package/src/primitives/Visible.tsx +1 -2
- package/src/primitives/announcer/announcer.ts +16 -10
- package/src/primitives/announcer/index.ts +12 -2
- package/src/primitives/announcer/speech.ts +188 -27
- package/src/primitives/createFocusStack.tsx +18 -7
- package/src/primitives/createTag.tsx +31 -0
- package/src/primitives/index.ts +18 -4
- package/src/primitives/types.ts +12 -2
- package/src/primitives/useFocusManager.ts +3 -3
- package/src/primitives/useHold.ts +69 -0
- package/src/primitives/useMouse.ts +306 -67
- package/src/primitives/utils/chainFunctions.ts +40 -9
- package/src/primitives/utils/createBlurredImage.ts +366 -0
- package/src/primitives/utils/createSpriteMap.ts +6 -4
- package/src/primitives/utils/handleNavigation.ts +300 -84
- package/src/primitives/utils/withScrolling.ts +91 -18
- package/src/render.ts +10 -8
- package/src/solidOpts.ts +31 -24
- package/src/types.ts +1 -15
- package/src/utils.ts +11 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as s from 'solid-js'
|
|
2
|
+
import * as lng from '@lightningtv/solid'
|
|
3
|
+
|
|
4
|
+
interface Destroyable {
|
|
5
|
+
(props: lng.NodeProps): s.JSX.Element;
|
|
6
|
+
destroy: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createTag(children: s.JSX.Element): Destroyable {
|
|
10
|
+
const [texture, setTexture] = s.createSignal<lng.Texture | null | undefined>(null);
|
|
11
|
+
const Tag = <view
|
|
12
|
+
display='flex'
|
|
13
|
+
onLayout={(n) => {
|
|
14
|
+
if (n.preFlexwidth && n.width !== n.preFlexwidth) {
|
|
15
|
+
n.rtt = true;
|
|
16
|
+
setTimeout(() => setTexture(n.texture), 1);
|
|
17
|
+
}
|
|
18
|
+
}}
|
|
19
|
+
parent={lng.rootNode} children={children}
|
|
20
|
+
textureOptions={{
|
|
21
|
+
preventCleanup: true
|
|
22
|
+
}} /> as any as lng.ElementNode
|
|
23
|
+
Tag.render(false);
|
|
24
|
+
|
|
25
|
+
const TagComponent = (props: lng.NodeProps) => {
|
|
26
|
+
return <view color={0xffffffff} autosize {...props} texture={texture()} />;
|
|
27
|
+
};
|
|
28
|
+
TagComponent.destroy = () => Tag.destroy();
|
|
29
|
+
|
|
30
|
+
return TagComponent;
|
|
31
|
+
}
|
package/src/primitives/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from './createInfiniteItems.js';
|
|
|
4
4
|
export * from './useMouse.js';
|
|
5
5
|
export * from './portal.jsx';
|
|
6
6
|
export * from './Lazy.jsx';
|
|
7
|
+
export * from './Image.jsx';
|
|
7
8
|
export * from './Visible.jsx';
|
|
8
9
|
export * from './router.js';
|
|
9
10
|
export * from './Column.jsx';
|
|
@@ -11,11 +12,24 @@ export * from './Row.jsx';
|
|
|
11
12
|
export * from './Grid.jsx';
|
|
12
13
|
export * from './FPSCounter.jsx';
|
|
13
14
|
export * from './FadeInOut.jsx';
|
|
15
|
+
export * from './Preserve.jsx';
|
|
16
|
+
export * from './Suspense.jsx';
|
|
17
|
+
export * from './Marquee.jsx';
|
|
14
18
|
export * from './createFocusStack.jsx';
|
|
15
|
-
export
|
|
16
|
-
export
|
|
17
|
-
export
|
|
19
|
+
export * from './useHold.js';
|
|
20
|
+
export * from './KeepAlive.jsx';
|
|
21
|
+
export * from './VirtualGrid.jsx';
|
|
22
|
+
export * from './Virtual.jsx';
|
|
23
|
+
export * from './utils/withScrolling.js';
|
|
24
|
+
export * from './createTag.jsx';
|
|
25
|
+
export {
|
|
26
|
+
type AnyFunction,
|
|
27
|
+
chainFunctions,
|
|
28
|
+
chainRefs,
|
|
29
|
+
} from './utils/chainFunctions.js';
|
|
30
|
+
export * from './utils/handleNavigation.js';
|
|
18
31
|
export { createSpriteMap, type SpriteDef } from './utils/createSpriteMap.js';
|
|
32
|
+
export { createBlurredImage } from './utils/createBlurredImage.js';
|
|
19
33
|
|
|
20
34
|
export type * from './types.js';
|
|
21
|
-
export type { KeyHandler } from '
|
|
35
|
+
export type { KeyHandler } from '../core/focusManager.js';
|
package/src/primitives/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { ElementNode, NodeProps, NodeStyles } from '
|
|
2
|
-
import type { KeyHandler } from '
|
|
1
|
+
import type { ElementNode, NodeProps, NodeStyles } from '../index.js';
|
|
2
|
+
import type { KeyHandler } from '../core/focusManager.js';
|
|
3
|
+
|
|
3
4
|
export type OnSelectedChanged = (
|
|
4
5
|
this: NavigableElement,
|
|
5
6
|
selectedIndex: number,
|
|
@@ -7,6 +8,7 @@ export type OnSelectedChanged = (
|
|
|
7
8
|
active: ElementNode,
|
|
8
9
|
lastSelectedIndex?: number,
|
|
9
10
|
) => void;
|
|
11
|
+
|
|
10
12
|
export interface NavigableProps extends NodeProps {
|
|
11
13
|
/** function to be called when the selected of the component changes */
|
|
12
14
|
onSelectedChanged?: OnSelectedChanged;
|
|
@@ -40,11 +42,19 @@ export interface NavigableProps extends NodeProps {
|
|
|
40
42
|
* Wrap the row so active goes back to the beginning of the row
|
|
41
43
|
*/
|
|
42
44
|
wrap?: boolean;
|
|
45
|
+
|
|
46
|
+
/** function to be called when scrolled */
|
|
47
|
+
onScrolled?: (
|
|
48
|
+
elm: NavigableElement,
|
|
49
|
+
offset: number,
|
|
50
|
+
isInitial: boolean,
|
|
51
|
+
) => void;
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
// @ts-expect-error animationSettings is not identical - weird
|
|
46
55
|
export interface NavigableElement extends ElementNode, NavigableProps {
|
|
47
56
|
selected: number;
|
|
57
|
+
scrollToIndex: (this: NavigableElement, index: number) => void;
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
export interface NavigableStyleProperties {
|
|
@@ -6,13 +6,13 @@ import {
|
|
|
6
6
|
getOwner,
|
|
7
7
|
runWithOwner,
|
|
8
8
|
} from 'solid-js';
|
|
9
|
-
import { Config } from '
|
|
10
|
-
import type { ElementNode } from '
|
|
9
|
+
import { Config } from '../core/index.js';
|
|
10
|
+
import type { ElementNode } from '../core/index.js';
|
|
11
11
|
import {
|
|
12
12
|
useFocusManager as useFocusManagerCore,
|
|
13
13
|
type KeyMap,
|
|
14
14
|
type KeyHoldOptions,
|
|
15
|
-
} from '
|
|
15
|
+
} from '../core/focusManager.js';
|
|
16
16
|
import { activeElement, setActiveElement } from '../activeElement.js';
|
|
17
17
|
|
|
18
18
|
const [focusPath, setFocusPath] = createSignal<ElementNode[]>([]);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createMemo } from 'solid-js';
|
|
2
|
+
|
|
3
|
+
export type UseHoldProps = {
|
|
4
|
+
onHold: () => void;
|
|
5
|
+
onEnter: () => void;
|
|
6
|
+
onRelease?: () => void;
|
|
7
|
+
holdThreshold?: number;
|
|
8
|
+
performOnEnterImmediately?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @example
|
|
13
|
+
* const [holdRight, releaseRight] = useHold({
|
|
14
|
+
* onHold: handleHoldRight,
|
|
15
|
+
* onEnter: handleOnRight,
|
|
16
|
+
* onRelease: handleReleaseHold,
|
|
17
|
+
* holdThreshold: 200,
|
|
18
|
+
* performOnEnterImmediately: true
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* <View
|
|
22
|
+
* onRight={holdRight}
|
|
23
|
+
* onRightRelease={releaseRight}
|
|
24
|
+
* />
|
|
25
|
+
*
|
|
26
|
+
* @param {UseHoldProps} props - The properties for configuring the hold behavior.
|
|
27
|
+
* @returns {[() => boolean, () => boolean]} A tuple containing `startHold` and `releaseHold` functions.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
export function useHold(props: UseHoldProps) {
|
|
31
|
+
const holdThreshold = createMemo(() => props.holdThreshold ?? 500);
|
|
32
|
+
const performOnEnterImmediately = createMemo(
|
|
33
|
+
() => props.performOnEnterImmediately ?? false,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
let holdTimeout = -1;
|
|
37
|
+
let wasHeld = false;
|
|
38
|
+
|
|
39
|
+
const startHold = () => {
|
|
40
|
+
if (holdTimeout === -1) {
|
|
41
|
+
if (performOnEnterImmediately()) {
|
|
42
|
+
props.onEnter();
|
|
43
|
+
}
|
|
44
|
+
holdTimeout = setTimeout(() => {
|
|
45
|
+
wasHeld = true;
|
|
46
|
+
props.onHold();
|
|
47
|
+
}, holdThreshold()) as unknown as number;
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const releaseHold = () => {
|
|
53
|
+
if (holdTimeout !== -1) {
|
|
54
|
+
clearTimeout(holdTimeout);
|
|
55
|
+
holdTimeout = -1;
|
|
56
|
+
if (!wasHeld) {
|
|
57
|
+
if (!performOnEnterImmediately()) props.onEnter();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
props.onRelease?.();
|
|
61
|
+
wasHeld = false;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return [startHold, releaseHold];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default useHold;
|
|
@@ -1,16 +1,51 @@
|
|
|
1
|
-
import type { ElementText,
|
|
1
|
+
import type { ElementText, TextNode } from '../core/index.js';
|
|
2
2
|
import {
|
|
3
|
+
Config,
|
|
3
4
|
ElementNode,
|
|
4
5
|
activeElement,
|
|
5
6
|
isElementNode,
|
|
7
|
+
isFunc,
|
|
6
8
|
isTextNode,
|
|
7
9
|
rootNode,
|
|
8
|
-
|
|
9
|
-
} from '@lightningtv/solid';
|
|
10
|
+
} from '../index.js';
|
|
10
11
|
import { makeEventListener } from '@solid-primitives/event-listener';
|
|
11
12
|
import { useMousePosition } from '@solid-primitives/mouse';
|
|
12
13
|
import { createScheduled, throttle } from '@solid-primitives/scheduled';
|
|
13
|
-
import { createEffect } from 'solid-js';
|
|
14
|
+
import { createEffect, getOwner, runWithOwner } from 'solid-js';
|
|
15
|
+
|
|
16
|
+
type CustomState = `$${string}`;
|
|
17
|
+
|
|
18
|
+
type RenderableNode = ElementNode | ElementText | TextNode;
|
|
19
|
+
|
|
20
|
+
interface MouseStateOptions {
|
|
21
|
+
hoverState: CustomState;
|
|
22
|
+
pressedState: CustomState;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type UseMouseOptions =
|
|
26
|
+
| { customStates: MouseStateOptions }
|
|
27
|
+
| { customStates: undefined };
|
|
28
|
+
|
|
29
|
+
export function addCustomStateToElement(
|
|
30
|
+
element: RenderableNode,
|
|
31
|
+
state: CustomState,
|
|
32
|
+
): void {
|
|
33
|
+
element.states?.add(state);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function removeCustomStateFromElement(
|
|
37
|
+
element: RenderableNode,
|
|
38
|
+
state: CustomState,
|
|
39
|
+
): void {
|
|
40
|
+
element?.states?.remove(state);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function hasCustomState(
|
|
44
|
+
element: RenderableNode,
|
|
45
|
+
state: CustomState,
|
|
46
|
+
): boolean {
|
|
47
|
+
return element.states?.has(state);
|
|
48
|
+
}
|
|
14
49
|
|
|
15
50
|
function createKeyboardEvent(
|
|
16
51
|
key: string,
|
|
@@ -29,6 +64,7 @@ function createKeyboardEvent(
|
|
|
29
64
|
});
|
|
30
65
|
}
|
|
31
66
|
|
|
67
|
+
let scrollTimeout: ReturnType<typeof setTimeout>;
|
|
32
68
|
const handleScroll = throttle((e: WheelEvent): void => {
|
|
33
69
|
const deltaY = e.deltaY;
|
|
34
70
|
if (deltaY < 0) {
|
|
@@ -36,30 +72,174 @@ const handleScroll = throttle((e: WheelEvent): void => {
|
|
|
36
72
|
} else if (deltaY > 0) {
|
|
37
73
|
document.body.dispatchEvent(createKeyboardEvent('ArrowDown', 40));
|
|
38
74
|
}
|
|
75
|
+
|
|
76
|
+
// clear the last timeout if the user is still scrolling
|
|
77
|
+
clearTimeout(scrollTimeout);
|
|
78
|
+
// after 250ms of no scroll events, we send a keyup event to stop the scrolling
|
|
79
|
+
scrollTimeout = setTimeout(() => {
|
|
80
|
+
document.body.dispatchEvent(createKeyboardEvent('ArrowUp', 38, 'keyup'));
|
|
81
|
+
document.body.dispatchEvent(createKeyboardEvent('ArrowDown', 40, 'keyup'));
|
|
82
|
+
}, 250);
|
|
39
83
|
}, 250);
|
|
40
84
|
|
|
41
|
-
|
|
85
|
+
function findElementWithCustomState<TApp extends ElementNode>(
|
|
86
|
+
myApp: TApp,
|
|
87
|
+
x: number,
|
|
88
|
+
y: number,
|
|
89
|
+
customState: CustomState,
|
|
90
|
+
): ElementNode | undefined {
|
|
91
|
+
const result = getChildrenByPosition(myApp, x, y).filter((el) =>
|
|
92
|
+
hasCustomState(el, customState),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (result.length === 0) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let element: ElementNode | undefined = result[result.length - 1];
|
|
100
|
+
|
|
101
|
+
while (element) {
|
|
102
|
+
const elmParent = element.parent;
|
|
103
|
+
if (elmParent?.forwardStates && hasCustomState(elmParent, customState)) {
|
|
104
|
+
element = elmParent;
|
|
105
|
+
} else {
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return element;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function findElementByActiveElement(e: MouseEvent): ElementNode | null {
|
|
42
114
|
const active = activeElement();
|
|
43
115
|
const precision = Config.rendererOptions?.deviceLogicalPixelRatio || 1;
|
|
116
|
+
|
|
44
117
|
if (
|
|
45
118
|
active instanceof ElementNode &&
|
|
46
119
|
testCollision(
|
|
47
120
|
e.clientX,
|
|
48
121
|
e.clientY,
|
|
49
|
-
(active.lng.absX as number) || 0 * precision,
|
|
50
|
-
(active.lng.absY as number) || 0 * precision,
|
|
51
|
-
active.width || 0 * precision,
|
|
52
|
-
active.height || 0 * precision,
|
|
122
|
+
((active.lng.absX as number) || 0) * precision,
|
|
123
|
+
((active.lng.absY as number) || 0) * precision,
|
|
124
|
+
(active.width || 0) * precision,
|
|
125
|
+
(active.height || 0) * precision,
|
|
53
126
|
)
|
|
54
127
|
) {
|
|
128
|
+
return active;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let parent = active?.parent;
|
|
132
|
+
while (parent) {
|
|
133
|
+
if (
|
|
134
|
+
isFunc(parent.onMouseClick) &&
|
|
135
|
+
active &&
|
|
136
|
+
testCollision(
|
|
137
|
+
e.clientX,
|
|
138
|
+
e.clientY,
|
|
139
|
+
((parent.lng.absX as number) || 0) * precision,
|
|
140
|
+
((parent.lng.absY as number) || 0) * precision,
|
|
141
|
+
(parent.width || 0) * precision,
|
|
142
|
+
(parent.height || 0) * precision,
|
|
143
|
+
)
|
|
144
|
+
) {
|
|
145
|
+
return parent;
|
|
146
|
+
}
|
|
147
|
+
parent = parent.parent;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function applyPressedState(
|
|
154
|
+
element: ElementNode,
|
|
155
|
+
pressedState: CustomState,
|
|
156
|
+
): void {
|
|
157
|
+
addCustomStateToElement(element, pressedState);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function handleElementClick(
|
|
161
|
+
clickedElement: ElementNode,
|
|
162
|
+
e: MouseEvent,
|
|
163
|
+
customStates?: MouseStateOptions,
|
|
164
|
+
pressedElementRef?: { current: ElementNode | null },
|
|
165
|
+
): void {
|
|
166
|
+
if (customStates?.pressedState && pressedElementRef?.current) {
|
|
167
|
+
removeCustomStateFromElement(
|
|
168
|
+
pressedElementRef.current,
|
|
169
|
+
customStates.pressedState,
|
|
170
|
+
);
|
|
171
|
+
pressedElementRef.current = null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (isFunc(clickedElement.onMouseClick)) {
|
|
175
|
+
clickedElement.onMouseClick(e, clickedElement);
|
|
176
|
+
return;
|
|
177
|
+
} else if (isFunc(clickedElement.onEnter)) {
|
|
178
|
+
clickedElement.onEnter();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
clickedElement.setFocus();
|
|
183
|
+
setTimeout(() => {
|
|
55
184
|
document.dispatchEvent(createKeyboardEvent('Enter', 13));
|
|
56
185
|
setTimeout(
|
|
57
186
|
() =>
|
|
58
187
|
document.body.dispatchEvent(createKeyboardEvent('Enter', 13, 'keyup')),
|
|
59
188
|
1,
|
|
60
189
|
);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
190
|
+
}, 1);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function createHandleClick<TApp extends ElementNode>(
|
|
194
|
+
myApp: TApp,
|
|
195
|
+
customStates?: MouseStateOptions,
|
|
196
|
+
pressedElementRef?: { current: ElementNode | null },
|
|
197
|
+
) {
|
|
198
|
+
return (e: MouseEvent): void => {
|
|
199
|
+
const clickedElement = customStates
|
|
200
|
+
? findElementWithCustomState(
|
|
201
|
+
myApp,
|
|
202
|
+
e.clientX,
|
|
203
|
+
e.clientY,
|
|
204
|
+
customStates.hoverState,
|
|
205
|
+
)
|
|
206
|
+
: findElementByActiveElement(e);
|
|
207
|
+
|
|
208
|
+
if (!clickedElement) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
handleElementClick(clickedElement, e, customStates, pressedElementRef);
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function createHandleMouseDown<TApp extends ElementNode>(
|
|
217
|
+
myApp: TApp,
|
|
218
|
+
customStates?: MouseStateOptions,
|
|
219
|
+
pressedElementRef?: { current: ElementNode | null },
|
|
220
|
+
) {
|
|
221
|
+
return (e: MouseEvent): void => {
|
|
222
|
+
if (!customStates) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const pressedElement = findElementWithCustomState(
|
|
227
|
+
myApp,
|
|
228
|
+
e.clientX,
|
|
229
|
+
e.clientY,
|
|
230
|
+
customStates.hoverState,
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
if (!pressedElement) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
applyPressedState(pressedElement, customStates.pressedState);
|
|
238
|
+
if (pressedElementRef) {
|
|
239
|
+
pressedElementRef.current = pressedElement;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
63
243
|
|
|
64
244
|
function testCollision(
|
|
65
245
|
px: number,
|
|
@@ -72,106 +252,165 @@ function testCollision(
|
|
|
72
252
|
return px >= cx && px <= cx + cw && py >= cy && py <= cy + ch;
|
|
73
253
|
}
|
|
74
254
|
|
|
75
|
-
function
|
|
76
|
-
node: ElementNode,
|
|
255
|
+
function isNodeAtPosition(
|
|
256
|
+
node: ElementNode | ElementText | TextNode,
|
|
77
257
|
x: number,
|
|
78
258
|
y: number,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
259
|
+
precision: number,
|
|
260
|
+
): node is ElementNode {
|
|
261
|
+
if (!isElementNode(node)) {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return (
|
|
266
|
+
node.alpha !== 0 &&
|
|
267
|
+
!node.skipFocus &&
|
|
268
|
+
testCollision(
|
|
269
|
+
x,
|
|
270
|
+
y,
|
|
271
|
+
((node.lng.absX as number) || 0) * precision,
|
|
272
|
+
((node.lng.absY as number) || 0) * precision,
|
|
273
|
+
(node.width || 0) * precision,
|
|
274
|
+
(node.height || 0) * precision,
|
|
275
|
+
)
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function findHighestZIndexNode(nodes: ElementNode[]): ElementNode | undefined {
|
|
280
|
+
if (nodes.length === 0) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (nodes.length === 1) {
|
|
285
|
+
return nodes[0];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
let maxZIndex = -1;
|
|
289
|
+
let highestNode: ElementNode | undefined = undefined;
|
|
290
|
+
|
|
291
|
+
for (const node of nodes) {
|
|
292
|
+
const zIndex = node.zIndex ?? -1;
|
|
293
|
+
if (zIndex >= maxZIndex) {
|
|
294
|
+
maxZIndex = zIndex;
|
|
295
|
+
highestNode = node;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return highestNode;
|
|
300
|
+
}
|
|
82
301
|
|
|
302
|
+
function getChildrenByPosition<TElement extends ElementNode = ElementNode>(
|
|
303
|
+
node: TElement,
|
|
304
|
+
x: number,
|
|
305
|
+
y: number,
|
|
306
|
+
): TElement[] {
|
|
307
|
+
const result: TElement[] = [];
|
|
308
|
+
const precision = Config.rendererOptions?.deviceLogicalPixelRatio || 1;
|
|
83
309
|
// Queue for BFS
|
|
310
|
+
|
|
84
311
|
let queue: (ElementNode | ElementText | TextNode)[] = [node];
|
|
85
312
|
|
|
86
313
|
while (queue.length > 0) {
|
|
87
314
|
// Process nodes at the current level
|
|
88
|
-
const currentLevelNodes
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
isElementNode(currentNode) &&
|
|
93
|
-
currentNode.alpha !== 0 &&
|
|
94
|
-
!currentNode.skipFocus &&
|
|
95
|
-
testCollision(
|
|
96
|
-
x,
|
|
97
|
-
y,
|
|
98
|
-
(currentNode.lng.absX as number) || 0 * precision,
|
|
99
|
-
(currentNode.lng.absY as number) || 0 * precision,
|
|
100
|
-
(currentNode.width || 0) * precision,
|
|
101
|
-
(currentNode.height || 0) * precision,
|
|
102
|
-
)
|
|
103
|
-
) {
|
|
104
|
-
currentLevelNodes.push(currentNode);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
315
|
+
const currentLevelNodes = queue.filter((currentNode) =>
|
|
316
|
+
isNodeAtPosition(currentNode, x, y, precision),
|
|
317
|
+
);
|
|
107
318
|
|
|
108
|
-
|
|
109
|
-
if (size === 0) {
|
|
319
|
+
if (currentLevelNodes.length === 0) {
|
|
110
320
|
break;
|
|
111
321
|
}
|
|
112
322
|
|
|
113
|
-
|
|
114
|
-
if (size === 1) {
|
|
115
|
-
highestZIndexNode = currentLevelNodes[0];
|
|
116
|
-
} else {
|
|
117
|
-
let maxZIndex = -1;
|
|
118
|
-
|
|
119
|
-
for (const node of currentLevelNodes) {
|
|
120
|
-
const zIndex = node.zIndex ?? -1;
|
|
121
|
-
if (zIndex > maxZIndex) {
|
|
122
|
-
maxZIndex = zIndex;
|
|
123
|
-
highestZIndexNode = node;
|
|
124
|
-
} else if (zIndex === maxZIndex) {
|
|
125
|
-
highestZIndexNode = node;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
323
|
+
const highestZIndexNode = findHighestZIndexNode(currentLevelNodes);
|
|
129
324
|
|
|
130
|
-
if (highestZIndexNode
|
|
131
|
-
|
|
132
|
-
queue = highestZIndexNode.children;
|
|
133
|
-
} else {
|
|
134
|
-
queue = [];
|
|
325
|
+
if (!highestZIndexNode || isTextNode(highestZIndexNode)) {
|
|
326
|
+
break;
|
|
135
327
|
}
|
|
328
|
+
|
|
329
|
+
result.push(highestZIndexNode as TElement);
|
|
330
|
+
queue = highestZIndexNode.children;
|
|
136
331
|
}
|
|
137
332
|
|
|
138
333
|
return result;
|
|
139
334
|
}
|
|
140
335
|
|
|
141
|
-
export function useMouse(
|
|
142
|
-
myApp:
|
|
336
|
+
export function useMouse<TApp extends ElementNode = ElementNode>(
|
|
337
|
+
myApp: TApp = rootNode as TApp,
|
|
143
338
|
throttleBy: number = 100,
|
|
339
|
+
options?: UseMouseOptions,
|
|
144
340
|
): void {
|
|
145
341
|
const pos = useMousePosition();
|
|
146
342
|
const scheduled = createScheduled((fn) => throttle(fn, throttleBy));
|
|
343
|
+
let previousElement: ElementNode | null = null;
|
|
344
|
+
const pressedElementRef: { current: ElementNode | null } = { current: null };
|
|
345
|
+
const customStates = options?.customStates;
|
|
346
|
+
const hoverState = customStates?.hoverState;
|
|
347
|
+
const handleClick = createHandleClick(myApp, customStates, pressedElementRef);
|
|
348
|
+
const handleMouseDown = createHandleMouseDown(
|
|
349
|
+
myApp,
|
|
350
|
+
customStates,
|
|
351
|
+
pressedElementRef,
|
|
352
|
+
);
|
|
353
|
+
const owner = getOwner();
|
|
354
|
+
const handleClickContext = (e: MouseEvent) => {
|
|
355
|
+
runWithOwner(owner, () => handleClick(e));
|
|
356
|
+
};
|
|
357
|
+
const handleMouseDownContext = (e: MouseEvent) => {
|
|
358
|
+
runWithOwner(owner, () => handleMouseDown(e));
|
|
359
|
+
};
|
|
360
|
+
|
|
147
361
|
makeEventListener(window, 'wheel', handleScroll);
|
|
148
|
-
makeEventListener(window, 'click',
|
|
362
|
+
makeEventListener(window, 'click', handleClickContext);
|
|
363
|
+
makeEventListener(window, 'mousedown', handleMouseDownContext);
|
|
149
364
|
createEffect(() => {
|
|
150
365
|
if (scheduled()) {
|
|
151
366
|
const result = getChildrenByPosition(myApp, pos.x, pos.y).filter(
|
|
152
|
-
(el) =>
|
|
367
|
+
(el) =>
|
|
368
|
+
!!(
|
|
369
|
+
el.onEnter ||
|
|
370
|
+
el.onMouseClick ||
|
|
371
|
+
el.onFocus ||
|
|
372
|
+
el[Config.focusStateKey] ||
|
|
373
|
+
(hoverState ? el[hoverState] : false)
|
|
374
|
+
),
|
|
153
375
|
);
|
|
154
376
|
|
|
155
377
|
if (result.length) {
|
|
156
|
-
let activeElm = result[result.length - 1];
|
|
378
|
+
let activeElm: ElementNode | undefined = result[result.length - 1];
|
|
157
379
|
|
|
158
380
|
while (activeElm) {
|
|
159
381
|
const elmParent = activeElm.parent;
|
|
160
382
|
if (elmParent?.forwardStates) {
|
|
161
|
-
activeElm =
|
|
383
|
+
activeElm = elmParent;
|
|
162
384
|
} else {
|
|
163
385
|
break;
|
|
164
386
|
}
|
|
165
387
|
}
|
|
166
388
|
|
|
389
|
+
if (!activeElm) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
167
393
|
// Update Row & Column Selected property
|
|
168
|
-
const activeElmParent = activeElm
|
|
169
|
-
if (
|
|
394
|
+
const activeElmParent = activeElm.parent;
|
|
395
|
+
if (activeElmParent?.selected !== undefined) {
|
|
170
396
|
activeElmParent.selected =
|
|
171
397
|
activeElmParent.children.indexOf(activeElm);
|
|
172
398
|
}
|
|
173
399
|
|
|
174
|
-
activeElm
|
|
400
|
+
if (previousElement && previousElement !== activeElm && hoverState) {
|
|
401
|
+
removeCustomStateFromElement(previousElement, hoverState);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (hoverState) {
|
|
405
|
+
addCustomStateToElement(activeElm, hoverState);
|
|
406
|
+
} else {
|
|
407
|
+
activeElm.setFocus();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
previousElement = activeElm;
|
|
411
|
+
} else if (previousElement && hoverState) {
|
|
412
|
+
removeCustomStateFromElement(previousElement, hoverState);
|
|
413
|
+
previousElement = null;
|
|
175
414
|
}
|
|
176
415
|
}
|
|
177
416
|
});
|