Skip to content

Commit

Permalink
[updated,added] Version 2.0.9 🙌 - Implementation for the For hoc c…
Browse files Browse the repository at this point in the history
…hanged and new hoc -> Index.
  • Loading branch information
michTheBrandofficial committed Dec 18, 2023
1 parent 97e1956 commit 0765fa8
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 97 deletions.
15 changes: 11 additions & 4 deletions dom/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Signal } from "../primitives/classes";
import { callEffect } from "../primitives/index";
import { callEffect, removeEffect } from "../primitives/index";

export function checkDataType(value: any) {
return (
Expand Down Expand Up @@ -44,9 +44,16 @@ export function fillInChildren(
} else if (typeof child === "object") {
if (child instanceof Signal) {
const text = addText(element);
callEffect(() => {
text.textContent = getSignalValue(child);
}, [child]);
// @ts-expect-error
function textEff() {
text.textContent = getSignalValue(child as any);
}
text.addEventListener('remove:node', function removeRxn(e) {
// remove the effect;
removeEffect(textEff, child as any)
e.currentTarget?.removeEventListener?.('remove:node', removeRxn)
})
callEffect(textEff, [child]);
} else {
element?.append?.(child as unknown as string);
}
Expand Down
36 changes: 29 additions & 7 deletions dom/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { callEffect } from "../primitives/index";
import { callEffect, removeEffect } from "../primitives/index";
import { Signal, Store } from "../primitives/classes";
import {
raise,
Expand Down Expand Up @@ -107,12 +107,19 @@ function setAttribute(
`The ${attrName} prop cannot be null or undefined. Skipping attribute parsing.`
);
if (attrValue instanceof Signal) {
callEffect(() => {
// @ts-expect-error
function propEff() {
type === "propertyAttribute"
? // @ts-ignore
(element[attrName] = getSignalValue(attrValue))
: element.setAttribute(attrName, getSignalValue(attrValue));
}, [attrValue]);
: element.setAttribute(attrName, getSignalValue(attrValue as Signal));
}
element.addEventListener('remove:node', function removeRxn(e) {
// remove the effect;
removeEffect(propEff, attrValue as any)
e.currentTarget?.removeEventListener?.('remove:node', removeRxn)
})
callEffect(propEff, [attrValue]);
} else if (checkDataType(attrValue)) {
type === "propertyAttribute"
? // @ts-ignore
Expand All @@ -138,9 +145,16 @@ function setStyle(element: NixixElementType, styleValue: StyleValueType) {
}

if (value instanceof Signal) {
callEffect(() => {
// @ts-expect-error
function styleEff() {
element["style"][styleKey] = getSignalValue(value as Signal);
}, [value]);
}
element.addEventListener('remove:node', function removeRxn(e) {
// remove the effect;
removeEffect(styleEff, value as any)
e.currentTarget?.removeEventListener?.('remove:node', removeRxn)
})
callEffect(styleEff, [value]);
} else {
element["style"][styleKey] = value as string;
}
Expand Down Expand Up @@ -238,6 +252,14 @@ function render(
doBGWork(() => (nixixStore["root"] = root));
}

function removeNode(node: Element | Text) {
const isConnected = node.isConnected;
if (isConnected) node?.remove?.();
node?.dispatchEvent?.(new Event('remove:node'))
node?.childNodes?.forEach?.(child => removeNode(child as any))
return isConnected
}

async function doBGWork(fn: CallableFunction) {
await Promise.resolve();
fn();
Expand All @@ -248,4 +270,4 @@ function turnOnJsx() {
}

export default Nixix;
export { render, setAttribute, turnOnJsx };
export { render, setAttribute, turnOnJsx, removeNode };
5 changes: 5 additions & 0 deletions dom/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export function render(element: NixixNode | (() => NixixNode), root: HTMLElement
commentForLF
}?: RenderConfig): void;

/**
* This function should be used to remove nodes, it also removes reactions and signals from the nodes, thereby helping in garbage collection of dom nodes.
*/
export function removeNode(node: Element | Text): boolean;

type RouteType = {
element?: any;
path?: `/${string}`
Expand Down
27 changes: 3 additions & 24 deletions hoc/For.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,11 @@ export function For(props: ForProps) {
children = arrayOfJSX(each, callback);
liveFragment.replace(createFragment(children));
});
const removedNodes: any[] = [];

callReaction(() => {
const eachLen = each.length;
if (eachLen === 0) {
removeNodes(eachLen, liveFragment, removedNodes);
return liveFragment.replace(createFragment(fallback));
return liveFragment.replace((removeNodes(eachLen, liveFragment),createFragment(fallback)));
} else {
// @ts-expect-error
if (fallback?.[0]?.isConnected || (fallback as Element)?.isConnected) {
Expand All @@ -40,31 +38,12 @@ export function For(props: ForProps) {
let childnodesLength = liveFragment.childNodes.length;
if (childnodesLength === eachLen) return;
if (childnodesLength > eachLen) {
removeNodes(eachLen, liveFragment, removedNodes);
removeNodes(eachLen, liveFragment);
} else if (childnodesLength < eachLen) {
const targetLength =
removedNodes.length + liveFragment.childNodes.length;
if (targetLength === eachLen) {
Boolean(removedNodes.length) &&
liveFragment.appendChild(createFragment(removedNodes));
removedNodes.length = 0;
} else if (targetLength < eachLen) {
Boolean(removedNodes.length) &&
liveFragment.appendChild(createFragment(removedNodes));
childnodesLength = liveFragment.childNodes.length; // 4
if (childnodesLength === eachLen) return;
// nodes -> 3, eachLen -> 6 --> create new nodes and append
const indexArray = numArray(childnodesLength, eachLen);
children = getIncrementalNodes(indexArray, each, callback);
liveFragment.append(createFragment(children));
} else if (targetLength > eachLen) {
// [<div class="text-blue-200" >, <div>, <div>, <div>]
// [<div class="text-blue-200" >, <div>, ]
const restoredNodes = removedNodes.splice(
0,
eachLen - childnodesLength
);
liveFragment.appendChild(createFragment(restoredNodes));
}
}
}
}, [each]);
Expand Down
74 changes: 74 additions & 0 deletions hoc/IndexHOC.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createFragment } from "../dom/helpers";
import { LiveFragment } from "../live-fragment";
import { callEffect, callReaction, Store } from "../primitives";
import {
arrayOfJSX,
createBoundary,
removeNodes,
numArray,
getIncrementalNodes,
compFallback,
} from "./helpers";

export function Index(props: ForProps) {
let { fallback, children, each } = props;
let [callback] = children!;
fallback = fallback || (compFallback() as any);
// create the children on the resolve immediate, because arr.map may access arr.length and return a signal which is not wanted.

const commentBoundary = createBoundary("", "index");
let liveFragment: LiveFragment = new LiveFragment(
commentBoundary.firstChild!,
commentBoundary.lastChild!
);
callEffect(() => {
children = arrayOfJSX(each, callback);
liveFragment.replace(createFragment(children));
});
const removedNodes: any[] = [];

callReaction(() => {
const eachLen = each.length;
if (eachLen === 0) {
removeNodes(eachLen, liveFragment, removedNodes);
return liveFragment.replace(createFragment(fallback));
} else {
// @ts-expect-error
if (fallback?.[0]?.isConnected || (fallback as Element)?.isConnected) {
liveFragment.empty();
}
let childnodesLength = liveFragment.childNodes.length;
if (childnodesLength === eachLen) return;
if (childnodesLength > eachLen) {
removeNodes(eachLen, liveFragment, removedNodes);
} else if (childnodesLength < eachLen) {
const targetLength =
removedNodes.length + liveFragment.childNodes.length;
if (targetLength === eachLen) {
Boolean(removedNodes.length) &&
liveFragment.appendChild(createFragment(removedNodes));
removedNodes.length = 0;
} else if (targetLength < eachLen) {
Boolean(removedNodes.length) &&
liveFragment.appendChild(createFragment(removedNodes));
childnodesLength = liveFragment.childNodes.length; // 4
if (childnodesLength === eachLen) return;
const indexArray = numArray(childnodesLength, eachLen);
children = getIncrementalNodes(indexArray, each, callback);
liveFragment.append(createFragment(children));
} else if (targetLength > eachLen) {
// [<div class="text-blue-200" >, <div>, <div>, <div>]
// [<div class="text-blue-200" >, <div>, ]
const restoredNodes = removedNodes.splice(
0,
eachLen - childnodesLength
);
liveFragment.appendChild(createFragment(restoredNodes));
}
}
}
}, [each]);

return commentBoundary;
}

15 changes: 9 additions & 6 deletions hoc/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { nixixStore } from '../dom';
import { nixixStore, removeNode } from '../dom/index';
import { createFragment, createText } from '../dom/helpers';
import { Store } from '../primitives/classes';
import { LiveFragment } from '../live-fragment/types';
Expand All @@ -11,7 +11,9 @@ export function indexes(arr: Array<any>) {
return [arr[0], arr[arr.length - 1]];
}

export function boundary(commentName?: 'suspense' | 'for' | 'show') {
type CommentName = 'suspense' | 'for' | 'show' | 'index';

export function boundary(commentName?: CommentName) {
const { commentForLF } = nixixStore;
return commentForLF ? comment(`nixix-${commentName}`) : createText('');
}
Expand All @@ -23,7 +25,7 @@ export function compFallback() {

export function createBoundary(
values: any,
commentName: 'suspense' | 'for' | 'show'
commentName: CommentName
): DocumentFragment {
return createFragment([
boundary(commentName),
Expand Down Expand Up @@ -81,12 +83,13 @@ export function getShow(bool: boolean, children: any, fallback: any) {
export function removeNodes(
eachLen: number,
liveFragment: LiveFragment,
removedNodes: any[]
removedNodes?: any[]
) {
const cachedNodes = liveFragment?.childNodes?.slice(eachLen) as any[];
removedNodes.unshift(...cachedNodes);
removedNodes?.unshift?.(...cachedNodes);
cachedNodes.forEach((node) => {
liveFragment.removeChild(node);
liveFragment.removeChild(node)
if (!removedNodes) removeNode(node)
});
}

Expand Down
3 changes: 2 additions & 1 deletion hoc/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Img } from "./Img";
import { Suspense } from "./Suspense";
import { For } from "./For";
import { Index } from './IndexHOC'
import { Show } from "./Show";
import { lazy, asyncComponent } from "./Async";

export { For, Img, Show, Suspense, asyncComponent, lazy };
export { Img, For, Index, Show, Suspense, asyncComponent, lazy };
8 changes: 8 additions & 0 deletions hoc/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,16 @@ interface ForProps<T extends any[]> extends ComponentFallback {
children: (item: T[number], i: number) => someView;
}

/**
* Use this function if you want to destroy nodes when the new state array is less.
*/
export declare function For<T extends any[]>(props?: ForProps<T>): someView;

/**
* Use this function if you want to cache nodes when the new state array is less.
*/
export declare function Index<T extends any[]>(props?: ForProps<T>): someView;

interface ShowProps<T> extends ComponentFallback {
when: () => boolean;
switch: T;
Expand Down
Loading

0 comments on commit 0765fa8

Please sign in to comment.