diff --git a/src/components/README.md b/src/components/README.md index b6ef9c2..9fcb7d1 100644 --- a/src/components/README.md +++ b/src/components/README.md @@ -32,3 +32,4 @@ - `@lit-labs/signals` is also marked as experimental - Considering making a custom `lit-html` integration with `nanostores`. - `@nanostores/lit` assumes use of `LitElement` / web components + - Thankfully `lit` provides an awesome [async directives tutorial](https://lit.dev/tutorials/async-directive/) diff --git a/src/components/board.ts b/src/components/board.ts index 120319a..f27a182 100644 --- a/src/components/board.ts +++ b/src/components/board.ts @@ -1,5 +1,5 @@ -import { signal } from '@lit-labs/signals' import { html } from 'lit-html' +import { atom } from 'nanostores' import { createCard } from '#core/card.ts' import type { GameCard } from '#lobo/card.ts' @@ -8,7 +8,7 @@ import { Card } from './card' import { Hand } from './hand' export function Board() { - const nextCard = signal(createCard(1)) + const nextCard = atom(createCard(1)) return html`
, selected: boolean | null = null) { - const value = computed(() => toValue(card).value) +export function Card(card: MaybeAtom, selected: boolean | null = null) { + const value = computed([toAtom(card)], (c) => c.value) function click(this: HTMLButtonElement) { this.dispatchEvent(new CustomEvent('card:click', { bubbles: true, detail: toValue(card) })) diff --git a/src/core/lit-nanostores/watch.ts b/src/core/lit-nanostores/watch.ts new file mode 100644 index 0000000..d8a7373 --- /dev/null +++ b/src/core/lit-nanostores/watch.ts @@ -0,0 +1,48 @@ +import { + AsyncDirective, + directive, + type DirectiveParameters, + type Part, +} from 'lit-html/async-directive.js' +import type { Atom } from 'nanostores' + +class NanoStoreWatch extends AsyncDirective { + private sub: (() => void) | undefined + private atom!: Atom + + render(atom: Atom) { + return atom.get() + } + + update(_part: Part, [atom]: DirectiveParameters) { + this.atom = atom + if (this.isConnected) { + this.startSub() + } + return this.render(atom) + } + + private startSub() { + if (this.sub) { + return + } + this.sub = this.atom.subscribe(() => { + this.setValue(this.render(this.atom)) + }) + } + + private stopSub() { + this.sub?.() + this.sub = undefined + } + + protected reconnected(): void { + this.startSub() + } + + protected disconnected(): void { + this.stopSub() + } +} + +export const watch = directive(NanoStoreWatch) diff --git a/src/types/atom.ts b/src/types/atom.ts new file mode 100644 index 0000000..5f69d25 --- /dev/null +++ b/src/types/atom.ts @@ -0,0 +1,17 @@ +import { atom, type Atom } from 'nanostores' + +export type MaybeAtom = T | Atom +export function isAtom(value: MaybeAtom): value is Atom { + return ( + value instanceof Object && + value !== null && + typeof value.lc === 'number' && + typeof value.get === 'function' + ) +} +export function toValue(value: MaybeAtom): T { + return isAtom(value) ? value.get() : value +} +export function toAtom(value: MaybeAtom): Atom { + return isAtom(value) ? value : atom(value) +}