svelte-tv 1.0.4 → 1.0.6
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/dist/__package_types_tmp__/core/elementNode.d.ts +1 -0
- package/dist/__package_types_tmp__/core/svelteNode.d.ts +1 -1
- package/dist/__package_types_tmp__/core/svelteRenderer.d.ts +43 -0
- package/dist/__package_types_tmp__/primitives/Icon.svelte.d.ts +9 -0
- package/dist/__package_types_tmp__/primitives/index.d.ts +1 -0
- package/dist/__package_types_tmp__/primitives/router/index.d.ts +1 -1
- package/dist/core/elementNode.d.ts +1 -0
- package/dist/core/elementNode.js +1 -0
- package/dist/core/focusManager.js +18 -18
- package/dist/core/lightningInit.js +1 -1
- package/dist/core/svelteNode.d.ts +1 -1
- package/dist/core/svelteNode.js +55 -3
- package/dist/core/svelteRenderer.d.ts +43 -0
- package/dist/core/svelteRenderer.js +178 -0
- package/dist/core/utils.js +16 -16
- package/dist/primitives/Grid.svelte +1 -1
- package/dist/primitives/Icon.svelte +108 -0
- package/dist/primitives/Icon.svelte.d.ts +9 -0
- package/dist/primitives/Image.svelte +6 -1
- package/dist/primitives/Marquee.svelte +6 -1
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.js +1 -0
- package/dist/primitives/router/index.d.ts +1 -1
- package/dist/primitives/router/index.js +1 -1
- package/dist/primitives/router/match.js +1 -4
- package/dist/svelte-internal.d.ts +3 -0
- package/dist/utils.js +17 -5
- package/package.json +2 -1
|
@@ -41,6 +41,7 @@ export interface ElementNode extends RendererNode, FocusNode {
|
|
|
41
41
|
_transitionAnimations?: Record<string, IAnimationController | undefined>;
|
|
42
42
|
_transitionAnimationVersions?: Record<string, number | undefined>;
|
|
43
43
|
_lastAnyKeyPressTime?: number;
|
|
44
|
+
_svelteEffect?: unknown;
|
|
44
45
|
_type: 'element' | 'textNode';
|
|
45
46
|
_undoStyles?: string[];
|
|
46
47
|
_display?: 'flex' | 'block';
|
|
@@ -15,7 +15,7 @@ export interface LightningComponent {
|
|
|
15
15
|
export declare function getParentNode(): ElementNode | undefined;
|
|
16
16
|
export declare function setParentNode(node: ElementNode): void;
|
|
17
17
|
export declare function createNode(name: 'view' | 'text', props: Record<string, any>): ElementNode;
|
|
18
|
-
export declare function mountNode(node: ElementNode, parent: ElementNode | undefined): void;
|
|
18
|
+
export declare function mountNode(node: ElementNode, parent: ElementNode | undefined, effect?: unknown): void;
|
|
19
19
|
export declare function unmountNode(node: ElementNode): void;
|
|
20
20
|
export declare function applyNodeProps(node: ElementNode, props: Record<string, any>): void;
|
|
21
21
|
export declare function setTextContent(node: ElementNode, text: string | undefined): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ElementNode } from './elementNode.js';
|
|
2
|
+
import { TextNode } from './nodeTypes.js';
|
|
3
|
+
type FragmentNode = {
|
|
4
|
+
_type: 'fragment';
|
|
5
|
+
parent?: ElementNode | FragmentNode;
|
|
6
|
+
children: RendererNode[];
|
|
7
|
+
};
|
|
8
|
+
type CommentNode = {
|
|
9
|
+
_type: 'comment';
|
|
10
|
+
parent?: ElementNode | FragmentNode;
|
|
11
|
+
data: string;
|
|
12
|
+
};
|
|
13
|
+
type RendererNode = ElementNode | TextNode | FragmentNode | CommentNode;
|
|
14
|
+
type RendererParent = ElementNode | FragmentNode;
|
|
15
|
+
declare function createFragment(): FragmentNode;
|
|
16
|
+
declare const renderer: {
|
|
17
|
+
createFragment: typeof createFragment;
|
|
18
|
+
createElement(name: string): ElementNode;
|
|
19
|
+
createTextNode(data: string): TextNode;
|
|
20
|
+
createComment(data: string): CommentNode;
|
|
21
|
+
nodeType(node: RendererNode): "element" | "text" | "fragment" | "comment";
|
|
22
|
+
getNodeValue(node: TextNode | CommentNode): string;
|
|
23
|
+
getAttribute(element: ElementNode, name: string): string | null;
|
|
24
|
+
setAttribute(element: ElementNode, key: string, value: unknown): void;
|
|
25
|
+
removeAttribute(element: ElementNode, name: string): void;
|
|
26
|
+
hasAttribute(element: ElementNode, name: string): boolean;
|
|
27
|
+
setText(node: ElementNode | TextNode | CommentNode, text: string): void;
|
|
28
|
+
getFirstChild(node: RendererParent): RendererNode | null;
|
|
29
|
+
getLastChild(node: RendererParent): RendererNode | null;
|
|
30
|
+
getNextSibling(node: ElementNode | TextNode | CommentNode): RendererNode | null;
|
|
31
|
+
insert(parent: RendererParent, node: RendererNode, anchor: RendererNode | null): void;
|
|
32
|
+
remove(node: ElementNode | TextNode | CommentNode): void;
|
|
33
|
+
getParent(node: ElementNode | TextNode | CommentNode): RendererParent | null;
|
|
34
|
+
addEventListener(target: ElementNode, type: string, handler: any): void;
|
|
35
|
+
removeEventListener(target: ElementNode, type: string, handler: any): void;
|
|
36
|
+
foreign: {
|
|
37
|
+
insertIntoForeign(_parent: Node, node: RendererNode, _anchor: ChildNode | null): void;
|
|
38
|
+
insertForeign(_parent: RendererParent, _node: Node, _anchor: RendererNode | null): void;
|
|
39
|
+
removeForeign(node: ChildNode): void;
|
|
40
|
+
removeFromForeign(node: RendererNode): void;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export default renderer;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IconifyIconOnLoad, IconifyIconProps } from '@iconify/svelte/dist/functions';
|
|
2
|
+
import { type NodeProps } from '../index.js';
|
|
3
|
+
type Props = Omit<NodeProps, 'color' | 'height' | 'width'> & IconifyIconProps & {
|
|
4
|
+
onLoad?: IconifyIconOnLoad;
|
|
5
|
+
onload?: IconifyIconOnLoad;
|
|
6
|
+
};
|
|
7
|
+
declare const Icon: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type Icon = ReturnType<typeof Icon>;
|
|
9
|
+
export default Icon;
|
|
@@ -5,6 +5,7 @@ export { default as Drawer } from './Drawer.svelte';
|
|
|
5
5
|
export { default as FadeInOut } from './FadeInOut.svelte';
|
|
6
6
|
export { default as FPSCounter } from './FPSCounter.svelte';
|
|
7
7
|
export { default as Grid } from './Grid.svelte';
|
|
8
|
+
export { default as Icon } from './Icon.svelte';
|
|
8
9
|
export { default as IconButton } from './IconButton.svelte';
|
|
9
10
|
export { default as Image } from './Image.svelte';
|
|
10
11
|
export { default as Marquee } from './Marquee.svelte';
|
|
@@ -2,7 +2,7 @@ export { default as HashRouter } from './HashRouter.svelte';
|
|
|
2
2
|
export { default as KeepAliveRoute } from './KeepAliveRoute.svelte';
|
|
3
3
|
export { default as Navigate } from './Navigate.svelte';
|
|
4
4
|
export { default as Route } from './Route.svelte';
|
|
5
|
-
export { location, navigate, params, route, routeData
|
|
5
|
+
export { location, navigate, params, route, routeData } from './context.js';
|
|
6
6
|
export { lazy } from './lazy.js';
|
|
7
7
|
export { createLocation, matchRoutes, normalizePath } from './match.js';
|
|
8
8
|
export type { LazyRouteComponent, MatchedRoute, NavigateFn, NavigateOptions, RouteComponent, RouteComponentProps, RouteDefinition, RouteLocation, RouteParams, RoutePreload, RoutePreloadArgs, RouteRenderable, } from './types.js';
|
|
@@ -41,6 +41,7 @@ export interface ElementNode extends RendererNode, FocusNode {
|
|
|
41
41
|
_transitionAnimations?: Record<string, IAnimationController | undefined>;
|
|
42
42
|
_transitionAnimationVersions?: Record<string, number | undefined>;
|
|
43
43
|
_lastAnyKeyPressTime?: number;
|
|
44
|
+
_svelteEffect?: unknown;
|
|
44
45
|
_type: 'element' | 'textNode';
|
|
45
46
|
_undoStyles?: string[];
|
|
46
47
|
_display?: 'flex' | 'block';
|
package/dist/core/elementNode.js
CHANGED
|
@@ -406,6 +406,7 @@ export class ElementNode {
|
|
|
406
406
|
this._transitionAnimations = undefined;
|
|
407
407
|
this._transitionAnimationVersions = undefined;
|
|
408
408
|
this._lastAnyKeyPressTime = undefined;
|
|
409
|
+
this._svelteEffect = undefined;
|
|
409
410
|
this._undoStyles = undefined;
|
|
410
411
|
this._display = undefined;
|
|
411
412
|
this._onLayout = undefined;
|
|
@@ -39,24 +39,24 @@ const addFocusDebug = (prevFocusPath, newFocusPath) => {
|
|
|
39
39
|
if (needFocusDebugStyles) {
|
|
40
40
|
const style = document.createElement('style');
|
|
41
41
|
style.type = 'text/css';
|
|
42
|
-
style.innerHTML = `
|
|
43
|
-
[data-focus="3"] {
|
|
44
|
-
border: 2px solid rgba(255, 33, 33, 0.2);
|
|
45
|
-
border-radius: 5px;
|
|
46
|
-
transition: border-color 0.3s ease;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
[data-focus="2"] {
|
|
50
|
-
border: 2px solid rgba(255, 33, 33, 0.4);
|
|
51
|
-
border-radius: 5px;
|
|
52
|
-
transition: border-color 0.3s ease;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
[data-focus="1"] {
|
|
56
|
-
border: 4px solid rgba(255, 33, 33, 0.9);
|
|
57
|
-
border-radius: 5px;
|
|
58
|
-
transition: border-color 0.5s ease;
|
|
59
|
-
}
|
|
42
|
+
style.innerHTML = `
|
|
43
|
+
[data-focus="3"] {
|
|
44
|
+
border: 2px solid rgba(255, 33, 33, 0.2);
|
|
45
|
+
border-radius: 5px;
|
|
46
|
+
transition: border-color 0.3s ease;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[data-focus="2"] {
|
|
50
|
+
border: 2px solid rgba(255, 33, 33, 0.4);
|
|
51
|
+
border-radius: 5px;
|
|
52
|
+
transition: border-color 0.3s ease;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[data-focus="1"] {
|
|
56
|
+
border: 4px solid rgba(255, 33, 33, 0.9);
|
|
57
|
+
border-radius: 5px;
|
|
58
|
+
transition: border-color 0.5s ease;
|
|
59
|
+
}
|
|
60
60
|
`;
|
|
61
61
|
document.head.appendChild(style);
|
|
62
62
|
needFocusDebugStyles = false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as lng from '@lightningjs/renderer';
|
|
2
|
-
import { CanvasRenderer, CanvasTextRenderer } from '@lightningjs/renderer/canvas';
|
|
2
|
+
import { CanvasRenderer, CanvasTextRenderer, } from '@lightningjs/renderer/canvas';
|
|
3
3
|
import { registerDefaultShaders } from './shaders.js';
|
|
4
4
|
export let renderer;
|
|
5
5
|
export const getRenderer = () => renderer;
|
|
@@ -15,7 +15,7 @@ export interface LightningComponent {
|
|
|
15
15
|
export declare function getParentNode(): ElementNode | undefined;
|
|
16
16
|
export declare function setParentNode(node: ElementNode): void;
|
|
17
17
|
export declare function createNode(name: 'view' | 'text', props: Record<string, any>): ElementNode;
|
|
18
|
-
export declare function mountNode(node: ElementNode, parent: ElementNode | undefined): void;
|
|
18
|
+
export declare function mountNode(node: ElementNode, parent: ElementNode | undefined, effect?: unknown): void;
|
|
19
19
|
export declare function unmountNode(node: ElementNode): void;
|
|
20
20
|
export declare function applyNodeProps(node: ElementNode, props: Record<string, any>): void;
|
|
21
21
|
export declare function setTextContent(node: ElementNode, text: string | undefined): void;
|
package/dist/core/svelteNode.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
|
-
import {
|
|
2
|
+
import { active_effect } from 'svelte/internal/client';
|
|
3
|
+
import { ElementNode, enqueueDelete } from './elementNode.js';
|
|
3
4
|
import { TextNode } from './nodeTypes.js';
|
|
4
5
|
import { isElementText } from './utils.js';
|
|
5
6
|
const nodeContext = Symbol('svelte-tv-node');
|
|
@@ -14,10 +15,11 @@ export function createNode(name, props) {
|
|
|
14
15
|
applyNodeProps(node, props);
|
|
15
16
|
return node;
|
|
16
17
|
}
|
|
17
|
-
export function mountNode(node, parent) {
|
|
18
|
+
export function mountNode(node, parent, effect = active_effect) {
|
|
18
19
|
if (!parent)
|
|
19
20
|
return;
|
|
20
|
-
|
|
21
|
+
node._svelteEffect = effect;
|
|
22
|
+
parent.insertChild(node, findNextSibling(node, parent));
|
|
21
23
|
if (parent.rendered) {
|
|
22
24
|
node.render(true);
|
|
23
25
|
}
|
|
@@ -54,6 +56,56 @@ export function applyNodeProps(node, props) {
|
|
|
54
56
|
}
|
|
55
57
|
node.rerender();
|
|
56
58
|
}
|
|
59
|
+
function findNextSibling(node, parent) {
|
|
60
|
+
const effect = node._svelteEffect;
|
|
61
|
+
if (!isSvelteEffect(effect))
|
|
62
|
+
return undefined;
|
|
63
|
+
for (const child of parent.children) {
|
|
64
|
+
if (isSvelteEffect(child._svelteEffect) &&
|
|
65
|
+
isEffectBefore(effect, child._svelteEffect)) {
|
|
66
|
+
return child;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function isEffectBefore(a, b) {
|
|
71
|
+
if (a === b)
|
|
72
|
+
return false;
|
|
73
|
+
const aPath = effectPath(a);
|
|
74
|
+
const bPath = effectPath(b);
|
|
75
|
+
let index = 0;
|
|
76
|
+
while (aPath[index] && aPath[index] === bPath[index]) {
|
|
77
|
+
index++;
|
|
78
|
+
}
|
|
79
|
+
const aChild = aPath[index];
|
|
80
|
+
const bChild = bPath[index];
|
|
81
|
+
if (!aChild || !bChild)
|
|
82
|
+
return false;
|
|
83
|
+
let sibling = bChild.prev;
|
|
84
|
+
while (sibling) {
|
|
85
|
+
if (sibling === aChild)
|
|
86
|
+
return true;
|
|
87
|
+
sibling = sibling.prev;
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
function effectPath(effect) {
|
|
92
|
+
const path = [];
|
|
93
|
+
let current = effect;
|
|
94
|
+
while (current) {
|
|
95
|
+
path.unshift(current);
|
|
96
|
+
current = current.parent;
|
|
97
|
+
}
|
|
98
|
+
return path;
|
|
99
|
+
}
|
|
100
|
+
function isSvelteEffect(value) {
|
|
101
|
+
if (!value || typeof value !== 'object')
|
|
102
|
+
return false;
|
|
103
|
+
const effect = value;
|
|
104
|
+
return ('parent' in effect &&
|
|
105
|
+
(effect.parent === null || typeof effect.parent === 'object') &&
|
|
106
|
+
'prev' in effect &&
|
|
107
|
+
(effect.prev === null || typeof effect.prev === 'object'));
|
|
108
|
+
}
|
|
57
109
|
export function setTextContent(node, text) {
|
|
58
110
|
if (!isElementText(node))
|
|
59
111
|
return;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ElementNode } from './elementNode.js';
|
|
2
|
+
import { TextNode } from './nodeTypes.js';
|
|
3
|
+
type FragmentNode = {
|
|
4
|
+
_type: 'fragment';
|
|
5
|
+
parent?: ElementNode | FragmentNode;
|
|
6
|
+
children: RendererNode[];
|
|
7
|
+
};
|
|
8
|
+
type CommentNode = {
|
|
9
|
+
_type: 'comment';
|
|
10
|
+
parent?: ElementNode | FragmentNode;
|
|
11
|
+
data: string;
|
|
12
|
+
};
|
|
13
|
+
type RendererNode = ElementNode | TextNode | FragmentNode | CommentNode;
|
|
14
|
+
type RendererParent = ElementNode | FragmentNode;
|
|
15
|
+
declare function createFragment(): FragmentNode;
|
|
16
|
+
declare const renderer: {
|
|
17
|
+
createFragment: typeof createFragment;
|
|
18
|
+
createElement(name: string): ElementNode;
|
|
19
|
+
createTextNode(data: string): TextNode;
|
|
20
|
+
createComment(data: string): CommentNode;
|
|
21
|
+
nodeType(node: RendererNode): "element" | "text" | "fragment" | "comment";
|
|
22
|
+
getNodeValue(node: TextNode | CommentNode): string;
|
|
23
|
+
getAttribute(element: ElementNode, name: string): string | null;
|
|
24
|
+
setAttribute(element: ElementNode, key: string, value: unknown): void;
|
|
25
|
+
removeAttribute(element: ElementNode, name: string): void;
|
|
26
|
+
hasAttribute(element: ElementNode, name: string): boolean;
|
|
27
|
+
setText(node: ElementNode | TextNode | CommentNode, text: string): void;
|
|
28
|
+
getFirstChild(node: RendererParent): RendererNode | null;
|
|
29
|
+
getLastChild(node: RendererParent): RendererNode | null;
|
|
30
|
+
getNextSibling(node: ElementNode | TextNode | CommentNode): RendererNode | null;
|
|
31
|
+
insert(parent: RendererParent, node: RendererNode, anchor: RendererNode | null): void;
|
|
32
|
+
remove(node: ElementNode | TextNode | CommentNode): void;
|
|
33
|
+
getParent(node: ElementNode | TextNode | CommentNode): RendererParent | null;
|
|
34
|
+
addEventListener(target: ElementNode, type: string, handler: any): void;
|
|
35
|
+
removeEventListener(target: ElementNode, type: string, handler: any): void;
|
|
36
|
+
foreign: {
|
|
37
|
+
insertIntoForeign(_parent: Node, node: RendererNode, _anchor: ChildNode | null): void;
|
|
38
|
+
insertForeign(_parent: RendererParent, _node: Node, _anchor: RendererNode | null): void;
|
|
39
|
+
removeForeign(node: ChildNode): void;
|
|
40
|
+
removeFromForeign(node: RendererNode): void;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export default renderer;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { createRenderer } from 'svelte/renderer';
|
|
2
|
+
import { ElementNode, enqueueDelete } from './elementNode.js';
|
|
3
|
+
import { TextNode } from './nodeTypes.js';
|
|
4
|
+
import { rootNode } from './root.js';
|
|
5
|
+
import { applyNodeProps, setTextContent } from './svelteNode.js';
|
|
6
|
+
import { isElementText } from './utils.js';
|
|
7
|
+
function createFragment() {
|
|
8
|
+
return {
|
|
9
|
+
_type: 'fragment',
|
|
10
|
+
children: [],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function getChildren(node) {
|
|
14
|
+
return node.children;
|
|
15
|
+
}
|
|
16
|
+
function getRendererParent(node) {
|
|
17
|
+
const parent = node.parent;
|
|
18
|
+
return parent;
|
|
19
|
+
}
|
|
20
|
+
function detach(node) {
|
|
21
|
+
const parent = getRendererParent(node);
|
|
22
|
+
if (!parent)
|
|
23
|
+
return;
|
|
24
|
+
const children = getChildren(parent);
|
|
25
|
+
const index = children.indexOf(node);
|
|
26
|
+
if (index !== -1) {
|
|
27
|
+
children.splice(index, 1);
|
|
28
|
+
}
|
|
29
|
+
node.parent = undefined;
|
|
30
|
+
}
|
|
31
|
+
function removeRendererNode(node) {
|
|
32
|
+
if (node._type === 'fragment') {
|
|
33
|
+
for (const child of [...node.children]) {
|
|
34
|
+
removeRendererNode(child);
|
|
35
|
+
}
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
renderer.remove(node);
|
|
39
|
+
}
|
|
40
|
+
function renderInserted(node, parent) {
|
|
41
|
+
if (!(node instanceof ElementNode) || !(parent instanceof ElementNode))
|
|
42
|
+
return;
|
|
43
|
+
if (parent.rendered) {
|
|
44
|
+
node.render(true);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function insertChild(parent, node, anchor) {
|
|
48
|
+
detach(node);
|
|
49
|
+
node.parent = parent;
|
|
50
|
+
const children = getChildren(parent);
|
|
51
|
+
const index = anchor ? children.indexOf(anchor) : -1;
|
|
52
|
+
children.splice(index === -1 ? children.length : index, 0, node);
|
|
53
|
+
renderInserted(node, parent);
|
|
54
|
+
}
|
|
55
|
+
function setRef(element, ref) {
|
|
56
|
+
const previous = element._svelteRef;
|
|
57
|
+
if (previous === ref)
|
|
58
|
+
return;
|
|
59
|
+
previous?.(undefined);
|
|
60
|
+
element._svelteRef = ref;
|
|
61
|
+
ref?.(element);
|
|
62
|
+
}
|
|
63
|
+
const renderer = createRenderer({
|
|
64
|
+
createFragment,
|
|
65
|
+
createElement(name) {
|
|
66
|
+
return new ElementNode(name === 'text' ? 'text' : 'view');
|
|
67
|
+
},
|
|
68
|
+
createTextNode(data) {
|
|
69
|
+
return new TextNode(data);
|
|
70
|
+
},
|
|
71
|
+
createComment(data) {
|
|
72
|
+
return {
|
|
73
|
+
_type: 'comment',
|
|
74
|
+
data,
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
nodeType(node) {
|
|
78
|
+
if (node instanceof ElementNode)
|
|
79
|
+
return 'element';
|
|
80
|
+
if (node instanceof TextNode)
|
|
81
|
+
return 'text';
|
|
82
|
+
return node._type;
|
|
83
|
+
},
|
|
84
|
+
getNodeValue(node) {
|
|
85
|
+
return node instanceof TextNode ? node.text : node.data;
|
|
86
|
+
},
|
|
87
|
+
getAttribute(element, name) {
|
|
88
|
+
const value = element[name];
|
|
89
|
+
return value == null ? null : String(value);
|
|
90
|
+
},
|
|
91
|
+
setAttribute(element, key, value) {
|
|
92
|
+
if (key === 'ref') {
|
|
93
|
+
setRef(element, value);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
applyNodeProps(element, { [key]: value });
|
|
97
|
+
},
|
|
98
|
+
removeAttribute(element, name) {
|
|
99
|
+
if (name === 'ref') {
|
|
100
|
+
setRef(element, undefined);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
applyNodeProps(element, { [name]: undefined });
|
|
104
|
+
},
|
|
105
|
+
hasAttribute(element, name) {
|
|
106
|
+
return element[name] !== undefined;
|
|
107
|
+
},
|
|
108
|
+
setText(node, text) {
|
|
109
|
+
if (node instanceof TextNode) {
|
|
110
|
+
node.text = text;
|
|
111
|
+
}
|
|
112
|
+
else if (node instanceof ElementNode && isElementText(node)) {
|
|
113
|
+
setTextContent(node, text);
|
|
114
|
+
}
|
|
115
|
+
else if (!(node instanceof ElementNode)) {
|
|
116
|
+
node.data = text;
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
getFirstChild(node) {
|
|
120
|
+
return getChildren(node)[0] ?? null;
|
|
121
|
+
},
|
|
122
|
+
getLastChild(node) {
|
|
123
|
+
return getChildren(node).at(-1) ?? null;
|
|
124
|
+
},
|
|
125
|
+
getNextSibling(node) {
|
|
126
|
+
const parent = getRendererParent(node);
|
|
127
|
+
if (!parent)
|
|
128
|
+
return null;
|
|
129
|
+
const children = getChildren(parent);
|
|
130
|
+
const index = children.indexOf(node);
|
|
131
|
+
return index === -1 ? null : (children[index + 1] ?? null);
|
|
132
|
+
},
|
|
133
|
+
insert(parent, node, anchor) {
|
|
134
|
+
if (node._type === 'fragment') {
|
|
135
|
+
for (const child of [...node.children]) {
|
|
136
|
+
insertChild(parent, child, anchor);
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
insertChild(parent, node, anchor);
|
|
141
|
+
},
|
|
142
|
+
remove(node) {
|
|
143
|
+
detach(node);
|
|
144
|
+
if (node instanceof ElementNode) {
|
|
145
|
+
setRef(node, undefined);
|
|
146
|
+
enqueueDelete(node, -1);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
getParent(node) {
|
|
150
|
+
return getRendererParent(node) ?? null;
|
|
151
|
+
},
|
|
152
|
+
addEventListener(target, type, handler) {
|
|
153
|
+
target.onEvent = {
|
|
154
|
+
...target.onEvent,
|
|
155
|
+
[type]: handler,
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
removeEventListener(target, type, handler) {
|
|
159
|
+
const onEvent = target.onEvent;
|
|
160
|
+
if (onEvent && onEvent[type] === handler) {
|
|
161
|
+
delete onEvent[type];
|
|
162
|
+
target.onEvent = onEvent;
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
foreign: {
|
|
166
|
+
insertIntoForeign(_parent, node, _anchor) {
|
|
167
|
+
renderer.insert(rootNode, node, null);
|
|
168
|
+
},
|
|
169
|
+
insertForeign(_parent, _node, _anchor) { },
|
|
170
|
+
removeForeign(node) {
|
|
171
|
+
node.remove();
|
|
172
|
+
},
|
|
173
|
+
removeFromForeign(node) {
|
|
174
|
+
removeRendererNode(node);
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
export default renderer;
|
package/dist/core/utils.js
CHANGED
|
@@ -80,17 +80,17 @@ export function logRenderTree(node) {
|
|
|
80
80
|
parent = parent.parent;
|
|
81
81
|
}
|
|
82
82
|
tree.reverse();
|
|
83
|
-
let output = `
|
|
84
|
-
function convertEffectsToShader(styleEffects) {
|
|
85
|
-
const effects = [];
|
|
86
|
-
let index = 0;
|
|
87
|
-
|
|
88
|
-
for (const [type, props] of Object.entries(styleEffects)) {
|
|
89
|
-
effects.push({ type, props });
|
|
90
|
-
index++;
|
|
91
|
-
}
|
|
92
|
-
return createShader('DynamicShader', { effects });
|
|
93
|
-
}
|
|
83
|
+
let output = `
|
|
84
|
+
function convertEffectsToShader(styleEffects) {
|
|
85
|
+
const effects = [];
|
|
86
|
+
let index = 0;
|
|
87
|
+
|
|
88
|
+
for (const [type, props] of Object.entries(styleEffects)) {
|
|
89
|
+
effects.push({ type, props });
|
|
90
|
+
index++;
|
|
91
|
+
}
|
|
92
|
+
return createShader('DynamicShader', { effects });
|
|
93
|
+
}
|
|
94
94
|
`;
|
|
95
95
|
tree.forEach((node, i) => {
|
|
96
96
|
if (!node._rendererProps) {
|
|
@@ -103,11 +103,11 @@ function convertEffectsToShader(styleEffects) {
|
|
|
103
103
|
? `props${i}.shader = convertEffectsToShader(${JSON.stringify(node._effects, null, 2)});`
|
|
104
104
|
: '';
|
|
105
105
|
const parent = i === 0 ? 'rootNode' : `node${i - 1}`;
|
|
106
|
-
output += `
|
|
107
|
-
const props${i} = ${props};
|
|
108
|
-
props${i}.parent = ${parent};
|
|
109
|
-
${effects}
|
|
110
|
-
const node${i} = renderer.createNode(props${i});
|
|
106
|
+
output += `
|
|
107
|
+
const props${i} = ${props};
|
|
108
|
+
props${i}.parent = ${parent};
|
|
109
|
+
${effects}
|
|
110
|
+
const node${i} = renderer.createNode(props${i});
|
|
111
111
|
`;
|
|
112
112
|
});
|
|
113
113
|
return output;
|
|
@@ -98,7 +98,7 @@ const nodeProps = $derived.by(() => {
|
|
|
98
98
|
onFocus={chainFunctions(props.onFocus, onFocus)}
|
|
99
99
|
strictBounds={false}
|
|
100
100
|
y={props.scroll === 'none'
|
|
101
|
-
? props.y ?? 0
|
|
101
|
+
? (props.y ?? 0)
|
|
102
102
|
: -Math.floor(focusedIndex / columns()) * totalHeight() + (props.y || 0)}
|
|
103
103
|
>
|
|
104
104
|
{#each props.items as item, index}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<script lang="ts">import { addIcon, generateIcon, getIcon, loadIcon } from "@iconify/svelte/dist/functions";
|
|
2
|
+
import { renderer } from "../index.js";
|
|
3
|
+
import View from "../View.svelte";
|
|
4
|
+
let props = $props();
|
|
5
|
+
let texture = $state();
|
|
6
|
+
let viewWidth = $state();
|
|
7
|
+
let viewHeight = $state();
|
|
8
|
+
let loadId = 0;
|
|
9
|
+
const storagePrefix = "svelte-tv:iconify:v1:";
|
|
10
|
+
function numberSize(size) {
|
|
11
|
+
if (typeof size === "number") return size;
|
|
12
|
+
if (typeof size !== "string") return undefined;
|
|
13
|
+
const match = size.match(/^(\d+(?:\.\d+)?)(px)?$/);
|
|
14
|
+
return match ? Number(match[1]) : undefined;
|
|
15
|
+
}
|
|
16
|
+
function base64Encode(value) {
|
|
17
|
+
const encoded = encodeURIComponent(value);
|
|
18
|
+
let binary = "";
|
|
19
|
+
for (let i = 0; i < encoded.length; i++) {
|
|
20
|
+
if (encoded[i] === "%") {
|
|
21
|
+
binary += String.fromCharCode(parseInt(encoded.slice(i + 1, i + 3), 16));
|
|
22
|
+
i += 2;
|
|
23
|
+
} else {
|
|
24
|
+
binary += encoded[i];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return btoa(binary);
|
|
28
|
+
}
|
|
29
|
+
function createSvg(icon) {
|
|
30
|
+
const data = generateIcon(icon, {
|
|
31
|
+
...props,
|
|
32
|
+
mode: "svg"
|
|
33
|
+
});
|
|
34
|
+
if (!data?.svg) return undefined;
|
|
35
|
+
const width = numberSize(data.attributes.width) ?? icon.width ?? 16;
|
|
36
|
+
const height = numberSize(data.attributes.height) ?? icon.height ?? 16;
|
|
37
|
+
const attributes = Object.entries(data.attributes).map(([key, value]) => `${key}="${String(value).replace(/"/g, """)}"`).join(" ");
|
|
38
|
+
viewWidth = typeof props.w === "number" ? props.w : width;
|
|
39
|
+
viewHeight = typeof props.h === "number" ? props.h : height;
|
|
40
|
+
return `<svg ${attributes}>${data.body}</svg>`;
|
|
41
|
+
}
|
|
42
|
+
function createTexture(icon) {
|
|
43
|
+
const svg = createSvg(icon);
|
|
44
|
+
if (!svg) return undefined;
|
|
45
|
+
return renderer.createTexture("ImageTexture", {
|
|
46
|
+
src: `data:image/svg+xml;base64,${base64Encode(svg)}`,
|
|
47
|
+
type: "svg",
|
|
48
|
+
w: viewWidth,
|
|
49
|
+
h: viewHeight
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function readStoredIcon(name) {
|
|
53
|
+
if (typeof localStorage === "undefined") return undefined;
|
|
54
|
+
try {
|
|
55
|
+
const raw = localStorage.getItem(storagePrefix + name);
|
|
56
|
+
if (!raw) return undefined;
|
|
57
|
+
const icon = JSON.parse(raw);
|
|
58
|
+
if (icon && typeof icon.body === "string") {
|
|
59
|
+
addIcon(name, icon);
|
|
60
|
+
return getIcon(name) ?? icon;
|
|
61
|
+
}
|
|
62
|
+
} catch {}
|
|
63
|
+
}
|
|
64
|
+
function storeIcon(name, icon) {
|
|
65
|
+
if (typeof localStorage === "undefined") return;
|
|
66
|
+
try {
|
|
67
|
+
localStorage.setItem(storagePrefix + name, JSON.stringify(icon));
|
|
68
|
+
} catch {}
|
|
69
|
+
}
|
|
70
|
+
$effect(() => {
|
|
71
|
+
const currentLoadId = ++loadId;
|
|
72
|
+
texture = undefined;
|
|
73
|
+
if (typeof props.icon !== "string") {
|
|
74
|
+
texture = createTexture(props.icon);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const cachedIcon = getIcon(props.icon);
|
|
78
|
+
const storedIcon = cachedIcon === undefined ? readStoredIcon(props.icon) : cachedIcon;
|
|
79
|
+
if (storedIcon) {
|
|
80
|
+
texture = createTexture(storedIcon);
|
|
81
|
+
props.onLoad?.(props.icon);
|
|
82
|
+
props.onload?.(props.icon);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (storedIcon === null) return;
|
|
86
|
+
loadIcon(props.icon).then((icon) => {
|
|
87
|
+
if (currentLoadId !== loadId) return;
|
|
88
|
+
storeIcon(props.icon, icon);
|
|
89
|
+
texture = createTexture(icon);
|
|
90
|
+
props.onLoad?.(props.icon);
|
|
91
|
+
props.onload?.(props.icon);
|
|
92
|
+
}).catch(() => {});
|
|
93
|
+
});
|
|
94
|
+
const nodeProps = $derived.by(() => {
|
|
95
|
+
const { icon, mode, color, flip, hFlip, vFlip, rotate, inline, width, height, id, onLoad, onload, ...rest } = props;
|
|
96
|
+
return rest;
|
|
97
|
+
});
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
{#if texture}
|
|
101
|
+
<View
|
|
102
|
+
{...nodeProps}
|
|
103
|
+
{texture}
|
|
104
|
+
w={viewWidth}
|
|
105
|
+
h={viewHeight}
|
|
106
|
+
color={0xffffffff}
|
|
107
|
+
/>
|
|
108
|
+
{/if}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IconifyIconOnLoad, IconifyIconProps } from '@iconify/svelte/dist/functions';
|
|
2
|
+
import { type NodeProps } from '../index.js';
|
|
3
|
+
type Props = Omit<NodeProps, 'color' | 'height' | 'width'> & IconifyIconProps & {
|
|
4
|
+
onLoad?: IconifyIconOnLoad;
|
|
5
|
+
onload?: IconifyIconOnLoad;
|
|
6
|
+
};
|
|
7
|
+
declare const Icon: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type Icon = ReturnType<typeof Icon>;
|
|
9
|
+
export default Icon;
|
|
@@ -34,6 +34,11 @@ const nodeProps = $derived.by(() => {
|
|
|
34
34
|
});
|
|
35
35
|
</script>
|
|
36
36
|
|
|
37
|
-
<View
|
|
37
|
+
<View
|
|
38
|
+
{...nodeProps}
|
|
39
|
+
src={currentSrc}
|
|
40
|
+
color={props.color || 0xffffffff}
|
|
41
|
+
{texture}
|
|
42
|
+
>
|
|
38
43
|
{@render props.children?.()}
|
|
39
44
|
</View>
|
|
@@ -57,6 +57,11 @@ const textNodeProps = $derived.by(() => {
|
|
|
57
57
|
<View {...nodeProps} clipping={props.marquee}>
|
|
58
58
|
<Text {...textNodeProps} bind:this={textNode} text={props.text} />
|
|
59
59
|
{#if props.marquee}
|
|
60
|
-
<Text
|
|
60
|
+
<Text
|
|
61
|
+
{...textNodeProps}
|
|
62
|
+
bind:this={repeatNode}
|
|
63
|
+
x={repeatX}
|
|
64
|
+
text={props.text}
|
|
65
|
+
/>
|
|
61
66
|
{/if}
|
|
62
67
|
</View>
|
|
@@ -5,6 +5,7 @@ export { default as Drawer } from './Drawer.svelte';
|
|
|
5
5
|
export { default as FadeInOut } from './FadeInOut.svelte';
|
|
6
6
|
export { default as FPSCounter } from './FPSCounter.svelte';
|
|
7
7
|
export { default as Grid } from './Grid.svelte';
|
|
8
|
+
export { default as Icon } from './Icon.svelte';
|
|
8
9
|
export { default as IconButton } from './IconButton.svelte';
|
|
9
10
|
export { default as Image } from './Image.svelte';
|
|
10
11
|
export { default as Marquee } from './Marquee.svelte';
|
package/dist/primitives/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { default as Drawer } from './Drawer.svelte';
|
|
|
5
5
|
export { default as FadeInOut } from './FadeInOut.svelte';
|
|
6
6
|
export { default as FPSCounter } from './FPSCounter.svelte';
|
|
7
7
|
export { default as Grid } from './Grid.svelte';
|
|
8
|
+
export { default as Icon } from './Icon.svelte';
|
|
8
9
|
export { default as IconButton } from './IconButton.svelte';
|
|
9
10
|
export { default as Image } from './Image.svelte';
|
|
10
11
|
export { default as Marquee } from './Marquee.svelte';
|
|
@@ -2,7 +2,7 @@ export { default as HashRouter } from './HashRouter.svelte';
|
|
|
2
2
|
export { default as KeepAliveRoute } from './KeepAliveRoute.svelte';
|
|
3
3
|
export { default as Navigate } from './Navigate.svelte';
|
|
4
4
|
export { default as Route } from './Route.svelte';
|
|
5
|
-
export { location, navigate, params, route, routeData
|
|
5
|
+
export { location, navigate, params, route, routeData } from './context.js';
|
|
6
6
|
export { lazy } from './lazy.js';
|
|
7
7
|
export { createLocation, matchRoutes, normalizePath } from './match.js';
|
|
8
8
|
export type { LazyRouteComponent, MatchedRoute, NavigateFn, NavigateOptions, RouteComponent, RouteComponentProps, RouteDefinition, RouteLocation, RouteParams, RoutePreload, RoutePreloadArgs, RouteRenderable, } from './types.js';
|
|
@@ -2,6 +2,6 @@ export { default as HashRouter } from './HashRouter.svelte';
|
|
|
2
2
|
export { default as KeepAliveRoute } from './KeepAliveRoute.svelte';
|
|
3
3
|
export { default as Navigate } from './Navigate.svelte';
|
|
4
4
|
export { default as Route } from './Route.svelte';
|
|
5
|
-
export { location, navigate, params, route, routeData
|
|
5
|
+
export { location, navigate, params, route, routeData } from './context.js';
|
|
6
6
|
export { lazy } from './lazy.js';
|
|
7
7
|
export { createLocation, matchRoutes, normalizePath } from './match.js';
|
|
@@ -43,10 +43,7 @@ function matchChildren(routes, segments, index, params, basePath, chain) {
|
|
|
43
43
|
if (!result)
|
|
44
44
|
continue;
|
|
45
45
|
const fullPath = joinPaths(basePath, route.path);
|
|
46
|
-
const nextChain = [
|
|
47
|
-
...chain,
|
|
48
|
-
{ route, params: result.params, fullPath },
|
|
49
|
-
];
|
|
46
|
+
const nextChain = [...chain, { route, params: result.params, fullPath }];
|
|
50
47
|
const nextIndex = result.index;
|
|
51
48
|
const score = result.score;
|
|
52
49
|
if (route.children.length > 0) {
|
package/dist/utils.js
CHANGED
|
@@ -5,7 +5,7 @@ const WEBGL_CONTEXT_IDS = [
|
|
|
5
5
|
'experimental-webgl2',
|
|
6
6
|
'experimental-webgl',
|
|
7
7
|
];
|
|
8
|
-
|
|
8
|
+
const supportedWebglVersions = new Map();
|
|
9
9
|
export function hexColor(color = '') {
|
|
10
10
|
if (isInteger(color)) {
|
|
11
11
|
return color;
|
|
@@ -44,13 +44,19 @@ export function mod(n, m) {
|
|
|
44
44
|
return ((n % m) + m) % m;
|
|
45
45
|
}
|
|
46
46
|
export function getWebglSupportedVersions(webglContextIds = WEBGL_CONTEXT_IDS) {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const cacheKey = webglContextIds.join('|');
|
|
48
|
+
const cached = supportedWebglVersions.get(cacheKey);
|
|
49
|
+
if (cached !== undefined) {
|
|
50
|
+
return cached;
|
|
49
51
|
}
|
|
50
52
|
const cv = document.createElement('canvas');
|
|
53
|
+
let probeContext = null;
|
|
51
54
|
const supports = webglContextIds.filter((id) => {
|
|
52
55
|
try {
|
|
53
56
|
const context = cv.getContext(id);
|
|
57
|
+
if (context !== null && probeContext === null) {
|
|
58
|
+
probeContext = context;
|
|
59
|
+
}
|
|
54
60
|
return !!(context &&
|
|
55
61
|
(context instanceof WebGLRenderingContext ||
|
|
56
62
|
context instanceof WebGL2RenderingContext ||
|
|
@@ -61,9 +67,15 @@ export function getWebglSupportedVersions(webglContextIds = WEBGL_CONTEXT_IDS) {
|
|
|
61
67
|
return false;
|
|
62
68
|
}
|
|
63
69
|
});
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
const probe = probeContext;
|
|
71
|
+
if (probe !== null &&
|
|
72
|
+
'getExtension' in probe &&
|
|
73
|
+
probe.isContextLost() === false) {
|
|
74
|
+
probe
|
|
75
|
+
.getExtension('WEBGL_lose_context')
|
|
76
|
+
?.loseContext();
|
|
66
77
|
}
|
|
78
|
+
supportedWebglVersions.set(cacheKey, supports);
|
|
67
79
|
return supports;
|
|
68
80
|
}
|
|
69
81
|
export const supportsWebGL = (webGLSupportedVersion) => ['webgl', 'experimental-webgl', 'webgl2'].some((ver) => webGLSupportedVersion.includes(ver));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-tv",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Svelte speed for every screen 🚀✨",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"svelte": "^5.56.4"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
+
"@iconify/svelte": "^5.2.2",
|
|
69
70
|
"@lightningjs/msdf-generator": "^1.3.0",
|
|
70
71
|
"@lightningjs/renderer": "^3.0.6",
|
|
71
72
|
"esm-env": "^1.2.2"
|