Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions examples/kanban/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import './styles.css'

import { html } from 'dhtml'
import { onUnmount } from 'dhtml/client'
import { Database, type ID } from './db'
import { Bus } from './util/bus'
import { Router } from './util/router'
Expand All @@ -23,11 +22,10 @@ export class App {
bus = new Bus<BusEvent>('app')
db = new Database()

constructor() {
onUnmount(this, async () => {
this.bus.close()
await this.db.close()
})
async stop() {
this.router.stop()
this.bus.close()
await this.db.close()
}

render() {
Expand Down
4 changes: 2 additions & 2 deletions examples/kanban/src/util/query.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Renderable } from 'dhtml'
import { invalidate, onMount } from 'dhtml/client'
import { invalidate } from 'dhtml/client'
import type { Bus } from './bus'
import { suspend } from './suspense'

Expand Down Expand Up @@ -39,6 +39,6 @@ export function createSubscribedQuery<T, Event extends string>(
fn: QueryFn<T>,
): Query<T> {
const query = createQuery(renderable, fn)
onMount(renderable, () => bus.subscribe(event, query.revalidate))
bus.subscribe(event, query.revalidate)
return query
}
12 changes: 5 additions & 7 deletions examples/kanban/src/util/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
} from '@tombl/router/browser'
import type { Params } from '@tombl/router/matcher'
import { html, type Displayable } from 'dhtml'
import { onMount } from 'dhtml/client'
import { state } from './decorators'

type PageClass<Path extends string, Context> = new (ctx: Context, params: Params<Path>) => Displayable
Expand Down Expand Up @@ -51,12 +50,11 @@ export class Router<Context, Routes extends { [Path in keyof Routes & string]: P
},
})

onMount(this, () => {
this.#router.start()
return () => {
this.#router.stop()
}
})
this.#router.start()
}

stop() {
this.#router.stop()
}

navigate(pathname: string) {
Expand Down
2 changes: 1 addition & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { invalidate, keyed, onMount, onUnmount } from './client/controller.ts'
export { invalidate, keyed } from './client/controller.ts'
export { attr_directive as attr, type Directive } from './client/parts.ts'
export { createRoot, type Root } from './client/root.ts'
39 changes: 5 additions & 34 deletions src/client/controller.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,13 @@
import type { Displayable, Renderable } from '../index.ts'
import { assert, is_renderable } from '../shared.ts'
import { type Cleanup } from './util.ts'
import { assert } from '../shared.ts'

export type Key = string | number | bigint | boolean | symbol | object | null

export interface Controller {
_mount_callbacks: (() => Cleanup)[]
_unmount_callbacks: Cleanup[]
_invalidate: Map<object, () => void>
}

export const controllers: WeakMap<Renderable, Controller> = new WeakMap()
export const controllers: WeakMap<Renderable, Map<object, () => void>> = new WeakMap()

export function get_controller(renderable: Renderable): Controller {
export function get_controller(renderable: Renderable): Map<object, () => void> {
let controller = controllers.get(renderable)
if (!controller)
controllers.set(
renderable,
(controller = {
_mount_callbacks: [],
_unmount_callbacks: [],
_invalidate: new Map(),
}),
)
if (!controller) controllers.set(renderable, (controller = new Map<object, () => void>()))
return controller
}

Expand All @@ -31,21 +16,7 @@ const keys: WeakMap<Displayable & object, Key> = new WeakMap()
export function invalidate(renderable: Renderable): void {
const controller = controllers.get(renderable)
assert(controller, 'the renderable has not been rendered')
controller._invalidate.forEach(invalidate => invalidate())
}

export function onMount(renderable: Renderable, callback: () => Cleanup): void {
assert(is_renderable(renderable), 'expected a renderable')
const controller = get_controller(renderable)
if (controller._invalidate.size) {
controller._unmount_callbacks.push(callback())
} else {
controller._mount_callbacks.push(callback)
}
}

export function onUnmount(renderable: Renderable, callback: () => void): void {
onMount(renderable, () => callback)
controller.forEach(invalidate => invalidate())
}

export function keyed<T extends Displayable & object>(displayable: T, key: Key): T {
Expand Down
30 changes: 3 additions & 27 deletions src/client/parts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,13 @@ export function create_child_part_inner(get_span: () => Span): Part {

function switch_renderable(next: Renderable | null) {
if (current_renderable && current_renderable !== next) {
const controller = controllers.get(current_renderable)
if (controller) {
controller._invalidate.delete(switch_renderable)

// If this was the last instance, call unmount callbacks
if (!controller._invalidate.size) {
controller._unmount_callbacks.forEach(callback => callback?.())
controller._unmount_callbacks.length = 0
}
}
controllers.get(current_renderable)?.delete(switch_renderable)
}
current_renderable = next
}

function disconnect_root() {
if (template_parts !== undefined) {
for (const [, part] of template_parts) part(null)
old_template = undefined
template_parts = undefined
}
Expand All @@ -85,12 +75,7 @@ export function create_child_part_inner(get_span: () => Span): Part {
switch_renderable(value)

const renderable = value
const controller = get_controller(renderable)
// If this is the first mounted instance, call mount callbacks
if (!controller._invalidate.size) {
controller._unmount_callbacks = controller._mount_callbacks.map(callback => callback())
}
controller._invalidate.set(switch_renderable, () => {
get_controller(renderable).set(switch_renderable, () => {
assert(renderable === current_renderable)
needs_revalidate = true
update(renderable)
Expand Down Expand Up @@ -177,10 +162,7 @@ export function create_child_part_inner(get_span: () => Span): Part {
}

return
} else if (entries) {
for (const entry of entries) entry._part(null)
entries = undefined
}
} else entries = undefined

if (is_html(value)) {
const { _dynamics: dynamics, _statics: statics } = value
Expand All @@ -192,12 +174,6 @@ export function create_child_part_inner(get_span: () => Span): Part {
)

if (old_template !== template) {
if (template_parts !== undefined) {
// scan through all the parts of the previous tree, and clear any renderables.
for (const [_idx, part] of template_parts) part(null)
template_parts = undefined
}

old_template = template

const doc = old_template._content.cloneNode(true) as DocumentFragment
Expand Down
Loading