Skip to content

Commit

Permalink
add ability to SkipRender and update repeat to self contain
Browse files Browse the repository at this point in the history
  • Loading branch information
ECorreia45 committed Aug 11, 2024
1 parent eaeda7b commit 6d9425b
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 83 deletions.
4 changes: 3 additions & 1 deletion src/ReactiveNode.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HtmlTemplate } from './html'
import { effect } from './state'
import { syncNodes } from './utils/sync-nodes'
import { EffectUnSubscriber } from './types'
import { EffectUnSubscriber, SkipRender } from './types'
import { renderContent } from './utils/render-content'
import { DoubleLinkedList } from './DoubleLinkedList'

Expand Down Expand Up @@ -46,6 +46,8 @@ export class ReactiveNode {
this.#unsubEffect = effect(() => {
const res = action(this.#anchor)

if (res instanceof SkipRender) return

if (init) {
this.#result = syncNodes(
this.#result,
Expand Down
100 changes: 53 additions & 47 deletions src/helpers/repeat.helper.spec.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,88 @@
import '../../test.common'
import { repeat } from './repeat.helper'
import { html, HtmlTemplate } from '../html'
import { html } from '../html'

describe('repeat', () => {
const anchor = document.createTextNode('');

beforeEach(() => {
document.body.appendChild(anchor)
})

it('should handle number', () => {
expect(repeat(3, (n: number) => n)()).toEqual([1, 2, 3])

repeat(3, (n: number) => n)(anchor);

expect(document.body.innerHTML).toBe('123')
})

it('should handle updates', () => {
let count = 3
const r = repeat<number>(
() => count,
(n: number) => html`sample-${n}`
)

const res = r() as HtmlTemplate[]


r(anchor)

expect(document.body.innerHTML).toBe('sample-1sample-2sample-3')

count = 4

const res2 = r() as HtmlTemplate[]

expect(res).toHaveLength(3)
expect(res2).toHaveLength(4)

expect(res[0]).toEqual(res2[0])
expect(res[1]).toEqual(res2[1])
expect(res[2]).toEqual(res2[2])

r(anchor)

expect(document.body.innerHTML).toBe('sample-1sample-2sample-3sample-4')
})

it('should handle empty', () => {
expect(repeat([], (n) => n, () => 'no items')()).toEqual('no items')
expect(repeat(0, (n) => n, () => 'no items')()).toEqual('no items')
expect(repeat(0, (n) => n)()).toEqual([])
repeat([], (n) => n)(anchor)
repeat([], (n) => n, () => 'no items')(anchor)

expect(document.body.innerHTML).toBe('no items')
})

it('should handle array with unique values', () => {
it('should handle array with unique primitives', () => {
const list = Array.from({ length: 3 }, (_, i) => i + 1)

expect(repeat(list, (n: number) => n + 1)()).toEqual([2, 3, 4])


repeat(list, (n: number) => n + 1)(anchor)

expect(document.body.innerHTML).toBe('234')
})

it('should handle array with unique non-primitives', () => {
const list = Array.from({ length: 3 }, (_, i) => i + 1)

const r = repeat(
() => list,
(n: number) => html`sample-${n}`
)

const res = r() as HtmlTemplate[]


r(anchor)

expect(document.body.innerHTML).toBe('sample-1sample-2sample-3')

list.push(4)

const res2 = r() as HtmlTemplate[]

expect(res).toHaveLength(3)
expect(res2).toHaveLength(4)

expect(res[0]).toEqual(res2[0])
expect(res[1]).toEqual(res2[1])
expect(res[2]).toEqual(res2[2])

r(anchor)

expect(document.body.innerHTML).toBe('sample-1sample-2sample-3sample-4')
})

it('should handle array with repeated values', () => {
const list = Array.from({ length: 3 }, () => '-')

expect(repeat(list, (n) => n)()).toEqual(['-', '-', '-'])
const list = Array.from({ length: 3 }, () => 1)

const r = repeat(
() => list,
(n) => html`sample-${n}`
)

const res = r() as HtmlTemplate[]

list.push('--')

const res2 = r() as HtmlTemplate[]
r(anchor)

expect(document.body.innerHTML).toBe('sample-1')

expect(res).toHaveLength(3)
expect(res2).toHaveLength(4)
list.push(2)

expect(res[0]).toEqual(res2[0])
expect(res[1]).toEqual(res2[1])
expect(res[2]).toEqual(res2[2])
r(anchor)

expect(document.body.innerHTML).toBe('sample-1sample-2')
})
})
53 changes: 37 additions & 16 deletions src/helpers/repeat.helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { val } from '../utils/val'
import { DoubleLinkedList } from '../DoubleLinkedList'
import { HtmlTemplate } from '../html'
import { SkipRender } from '../types'
import { syncNodes } from '../utils/sync-nodes'
import { getNodeOrTemplate } from '../utils/get-node-or-template'

type DataGetter<T> = () => number | Array<T>

Expand Down Expand Up @@ -27,38 +32,54 @@ export const repeat = <T>(
cb: (data: T, index: number) => unknown,
whenEmpty?: () => unknown
) => {
const cache: Map<T, unknown> = new Map()
const cache: Map<T, Node | HtmlTemplate> = new Map()
let currentRenderedNodes = new DoubleLinkedList<Node | HtmlTemplate>()
let prevList: T[] = []

const each = (d: T, i: number) => {
if (!cache.has(d)) {
cache.set(d, cb(d, i))
cache.set(d, getNodeOrTemplate(cb(d, i)))
}

return cache.get(d)
return cache.get(d) as Node | HtmlTemplate
}

return () => {
const list = getList(data)
return (anchor: Node) => {
const list = getList(data) as T[]

if (list.length === 0) {
const res = whenEmpty?.() ?? []

currentRenderedNodes = syncNodes(
currentRenderedNodes,
Array.isArray(res) ? res : [res],
anchor
)

prevList = []
cache.clear()
return whenEmpty?.() ?? []
}
} else {
const prevListSet = new Set(prevList)
const renderedList: Array<Node | HtmlTemplate> = []

const prevListSet = new Set(prevList)
const renderedList = []
for (let i = 0; i < list.length; i++) {
const item = list[i]

for (let i = 0; i < list.length; i++) {
prevListSet.delete(list[i])
renderedList.push(each(list[i], i))
}
prevListSet.delete(item)
renderedList.push(each(item, i))
}

prevListSet.forEach((d) => cache.delete(d))

prevListSet.forEach((d) => cache.delete(d))
prevList = list

prevList = list
currentRenderedNodes = syncNodes(
currentRenderedNodes,
renderedList,
anchor
)
}

return renderedList
return new SkipRender()
}
}
3 changes: 1 addition & 2 deletions src/html.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,7 @@ describe('html', () => {

items.render(document.body)

expect(document.body.innerHTML).toBe('item 1item 3item 5item 3')
expect(document.body.innerHTML).toBe('item 1item 5item 3')
})

it('with array containing repeated values as html instance', () => {
Expand Down Expand Up @@ -1128,7 +1128,6 @@ describe('html', () => {
'<li>one: <span>1</span><span>2</span></li>'
)


setData(prev => {
prev[0] = {
...prev[0],
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ export interface ElementOptions<A> {
htmlContent?: string
ns?: 'http://www.w3.org/1999/xhtml' | 'http://www.w3.org/2000/svg' | ''
}

export class SkipRender {}
29 changes: 12 additions & 17 deletions src/utils/insert-node-after.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
export function insertNodeAfter(newNode: Node, referenceNode: Node) {
requestAnimationFrame(() => {
if (
referenceNode.nextSibling &&
referenceNode.nextSibling !== newNode
) {
referenceNode.parentNode?.insertBefore(
newNode,
referenceNode.nextSibling
)
} else if (
referenceNode.parentNode?.childNodes[
referenceNode.parentNode?.childNodes.length - 1
] !== newNode
) {
referenceNode.parentNode?.appendChild(newNode)
}
})
if (referenceNode.nextSibling && referenceNode.nextSibling !== newNode) {
referenceNode.parentNode?.insertBefore(
newNode,
referenceNode.nextSibling
)
} else if (
referenceNode.parentNode?.childNodes[
referenceNode.parentNode?.childNodes.length - 1
] !== newNode
) {
referenceNode.parentNode?.appendChild(newNode)
}
}

0 comments on commit 6d9425b

Please sign in to comment.