A frontend UI tool, encourages local event handlers and signals
- Cell encourages local event handlers pattern
- Cell encourages signals pattern for remote effects
- Lightweight (< 1.5 kiB gzipped)
- TypeScript friendly
See the live demos.
TodoMVC implementation is also available here.
npx jsr add @kt3k/cell
Or, in Deno,
deno add @kt3k/cell
The below is an example of cell
component. A cell
component is a function of
the type (ctx: Context) => string | undefined
.
Context
includes many handy helpers for implementing UI behavior easily and
quickly.
The below example uses on
helper, which registers the event handler to the
mounted dom element, which is <button>
element in this case.
import { type Context, register } from "@kt3k/cell"
function MyComponent({ on }: Context) {
on("click", () => {
alert("hello")
})
}
register(MyComponent, "js-hello")
<button class="js-hello">Click</button>
When you click this button, it alerts "hello".
The next component shows how you can copy the input text into other dom element.
query
is helper to query by the selector inside your component.
query(".dest")
is equivalent of el.querySelector(".dest")
.
import { type Context, register } from "@kt3k/cell"
function Mirroring({ on, query }: Context) {
on("input", () => {
query(".dest").textContent = query(".src").value
})
}
register(Mirroring, "js-mirroring")
<div class="js-mirroring">
<input class="src" placeholder="type something" />
<p class="dest"></p>
</div>
If you pass a string (a selector) as the second argument of on
function, the
event handler is only invoked when the event comes from the element which
matches the given selector.
import { type Context, register } from "@kt3k/cell"
function DelegateComponent({ on, query }: Context) {
on("click", ".btn", () => {
query(".result").textContext += " .btn clicked!"
})
}
register(DelegateComponent, "js-delegate")
By calling onOutside(event, handler)
, you can handle the event outside of the
component's DOM.
This is convenient, for example, when you like to close the modal dialog when the user clicking the outside of it.
import { type Context, register } from "@kt3k/cell"
function OutsideClickComponent({ onOutside }: Context) {
onOutside("click", ({ e }) => {
console.log("The outside of my-component has been clicked!")
})
}
register(OutsideClickComponent, "js-outside-click")
The below example shows how you can use cell
directly in the browsers.
<script type="module">
import { register } from "https://kt3k.github.io/cell/dist.min.js"
function Mirroring({ on, query }) {
on("input", () => {
query(".dest").textContent = query(".src").value
})
}
register(Mirroring, "js-mirroring")
</script>
<div class="js-mirroring">
<input class="src" placeholder="Type something" />
<p class="dest"></p>
</div>
cell
recommends handling event locally, but in many cases you would also need
to make effects to remote elements.
If you need to affects the components in remote places (i.e. components not an
ancestor or decendant of the component), we commend using signals
for
communicating with them.
signals
are event emitter with values, whose events are triggered only when
the values are changed.
import { Context, Signal } from "@kt3k/cell"
const sig = new Signal(0)
function Component({ el, subscribe }: Context) {
subscribe(sig, (v) => {
el.textContent = `The value is ${v}`
})
}
sig.update(1)
sig.update(2)
Use @b-fuse/deno-dom
for polyfill document
object. An example of basic test
case of a component looks like the below:
import { DOMParser } from "@b-fuze/deno-dom"
import { assertEquals } from "@std/assert"
import { type Context, mount, register } from "@kt3k/cell"
Deno.test("A test case of Component", () => {
function Component({ el }: Context) {
el.textContent = "a"
}
register(Component, "js-component")
globalThis.document = new DOMParser().parseFromString(
`<body><div class="js-component"></div></body>`,
"text/html",
// deno-lint-ignore no-explicit-any
) as any
mount()
assertEquals(document.body.firstChild?.textContent, "a")
})
- 2024-06-18 Forked from capsule.
MIT