2021-09-13 18:12:45 +00:00
|
|
|
# Skooma
|
|
|
|
|
|
|
|
A functional-friendly helper library for procedural DOM generation and
|
|
|
|
templating.
|
|
|
|
|
2023-09-29 15:16:24 +00:00
|
|
|
## Overview
|
|
|
|
|
|
|
|
```js
|
|
|
|
import {html} from "skooma.js"
|
|
|
|
|
|
|
|
document.body.append(html.div(
|
|
|
|
html.h1("Hello, World!"),
|
|
|
|
html.p("Skooma is cool", {class: "amazing"}),
|
|
|
|
html.button("Show Proof", click: event => { alert("It's true!") })
|
|
|
|
))
|
|
|
|
```
|
2021-09-13 18:12:45 +00:00
|
|
|
|
|
|
|
## Interface / Examples
|
|
|
|
|
2023-09-29 15:16:24 +00:00
|
|
|
### HTML generation
|
|
|
|
|
2021-09-13 18:12:45 +00:00
|
|
|
```js
|
|
|
|
html.div()
|
2023-09-29 15:16:24 +00:00
|
|
|
// Creates a <div></div> element
|
2021-09-13 18:12:45 +00:00
|
|
|
html.div("hello", "world")
|
2023-09-29 15:16:24 +00:00
|
|
|
// <div>helloworld</div>
|
2021-09-13 18:12:45 +00:00
|
|
|
html.div(html.span())
|
2023-09-29 15:16:24 +00:00
|
|
|
// <div><span></span></div>
|
2021-09-13 18:12:45 +00:00
|
|
|
html.div([html.span(), html.span()])
|
2023-09-29 15:16:24 +00:00
|
|
|
// <div> <span></span> <span></span> </div>
|
2021-09-13 18:12:45 +00:00
|
|
|
html.div({class: "foo"})
|
2023-09-29 15:16:24 +00:00
|
|
|
// <div class="foo"></div>
|
2021-09-13 18:12:45 +00:00
|
|
|
html.div({class: ["foo", "bar"]})
|
2023-09-29 15:16:24 +00:00
|
|
|
// <div class="foo bar"></div>
|
2021-09-13 18:12:45 +00:00
|
|
|
html.div({click: 1})
|
2023-09-29 15:16:24 +00:00
|
|
|
// <div click="1"></div>
|
2021-09-13 18:12:45 +00:00
|
|
|
html.div({click: event => console.log(event.target)})
|
|
|
|
// Creates a <div> with an event listener for "click" events
|
|
|
|
html.div(player: {username: "KhajiitSlayer3564"})
|
|
|
|
// Creates a <div> with the attribute "player" set to a JSON-encoded Object
|
2023-09-29 15:16:24 +00:00
|
|
|
html.div("Old content", self => self.innerText = "Hello, World!")
|
|
|
|
// Creates a <div> and passes it to a function for further processing
|
2021-09-13 18:12:45 +00:00
|
|
|
html.div({foo: true})
|
2023-09-29 15:16:24 +00:00
|
|
|
// <div foo></div>
|
2021-09-13 18:12:45 +00:00
|
|
|
html.div({foo: "bar"}, {foo: false})
|
2023-09-29 15:16:24 +00:00
|
|
|
// <div></div>
|
2021-09-13 18:12:45 +00:00
|
|
|
|
|
|
|
// Special keys:
|
|
|
|
|
2023-09-29 15:16:24 +00:00
|
|
|
html.div({dataset: {foo: 1, bar: 2}})
|
|
|
|
// <div data-foo="1" data-bar="2"></div>
|
|
|
|
|
|
|
|
html.div({shadowRoot: html.span("Shadow root content")})
|
|
|
|
// Attaches a shadow root with a span
|
2021-09-13 18:12:45 +00:00
|
|
|
```
|
|
|
|
|
2021-11-25 09:08:59 +00:00
|
|
|
Generators can be called with many arguments. Arrays get iterated recursively as
|
|
|
|
if they were part of a flat argument list.
|
|
|
|
|
2023-09-29 15:16:24 +00:00
|
|
|
### Generating Text Nodes
|
|
|
|
|
2021-11-23 15:27:25 +00:00
|
|
|
```js
|
|
|
|
text("Hello, World")
|
|
|
|
// Wraps document.createTextNode
|
|
|
|
text()
|
2021-11-25 09:08:59 +00:00
|
|
|
// Defaults to empty string instead of erroring
|
|
|
|
text(null)
|
|
|
|
// Non-string arguments still error
|
2021-12-26 16:54:04 +00:00
|
|
|
|
|
|
|
text`Hello, World!`
|
|
|
|
// returns a new document fragment containing the text node "Hello, World!"
|
|
|
|
text`Hello, ${user}!`
|
|
|
|
// returns a document fragment containing 3 nodes:
|
|
|
|
// "Hello, ", the interpolated value of `user` and "!"
|
2023-09-29 15:16:24 +00:00
|
|
|
text`Hello, ${html.b(user)}!`
|
|
|
|
// Text node for Hello, the <b> tag with the user's name, and a text node for !
|
2021-11-23 15:27:25 +00:00
|
|
|
```
|
|
|
|
|
2021-11-25 09:08:59 +00:00
|
|
|
## bind
|
|
|
|
|
|
|
|
This function offers a generic mechanism for binding elements to dynamic state.
|
|
|
|
It takes a register function that satisfies the following criteria:
|
|
|
|
|
|
|
|
- It returns an initial state as an array
|
|
|
|
- It accepts a callback function
|
|
|
|
- On state change, it calls it with the new state as its arguments
|
|
|
|
|
|
|
|
And returns a second function, which takes a transformation (another functuion)
|
|
|
|
from input state to DOM node. This transformation will be used to create an
|
|
|
|
initial element from the initial state, which will be returned.
|
|
|
|
|
|
|
|
On every state change, the transform ation will be called on the new state to
|
|
|
|
generate a new DOM Node, which replace the current one.
|
|
|
|
|
|
|
|
```js
|
|
|
|
bind(register)(html.div)
|
|
|
|
// Returns a div element bound to register
|
|
|
|
// Assuming register is a higher order function
|
|
|
|
// and html.div is a transformation from input state to a <div> node
|
|
|
|
```
|
2021-09-13 20:38:25 +00:00
|
|
|
|
2021-11-25 09:50:44 +00:00
|
|
|
Since references to the bound element can become stale, a `current` property
|
|
|
|
is set on every element that returns the current element. This will keep working
|
|
|
|
even after several state changes.
|
|
|
|
|
2021-10-05 17:28:36 +00:00
|
|
|
## handle
|
|
|
|
|
|
|
|
Since it is common for event handlers to call `preventDefault()`, skooma
|
|
|
|
provides a helper function called `handle` with the following definition:
|
|
|
|
|
|
|
|
```js
|
|
|
|
fn => event => { event.preventDefault(); return fn(event) }
|
|
|
|
```
|
|
|
|
|
2021-09-13 20:38:25 +00:00
|
|
|
## A few more examples:
|
|
|
|
|
|
|
|
Create a Button that deletes itself:
|
|
|
|
|
|
|
|
```js
|
|
|
|
document.body.append(
|
|
|
|
html.button("Delete Me", {click: event => event.target.remove()})
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
Turn a two-dimensional array into an HTML table:
|
|
|
|
```js
|
|
|
|
const table = rows =>
|
|
|
|
html.table(html.tbody(rows.map(
|
|
|
|
row => html.tr(row.map(
|
|
|
|
cell => html.rd(cell, {dataset: {
|
|
|
|
content: cell.toLowerCase(),
|
|
|
|
}})
|
|
|
|
))
|
|
|
|
)))
|
|
|
|
```
|
|
|
|
|
|
|
|
A list that you can add items to
|
|
|
|
```js
|
|
|
|
let list, input = ""
|
|
|
|
document.body.append(html.div([
|
|
|
|
list=html.ul(),
|
|
|
|
html.input({type: 'text', input: e => input = e.target.value}),
|
|
|
|
html.button({click: event => list.append(html.li(input))}, "Add"),
|
|
|
|
]))
|
|
|
|
```
|
|
|
|
|
|
|
|
A list that you can also delete items from
|
|
|
|
```js
|
|
|
|
const listItem = content => html.li(
|
|
|
|
html.span(content), " ", html.a("[remove]", {
|
|
|
|
click: event => event.target.closest("li").remove(),
|
|
|
|
style: { cursor: 'pointer', color: 'red' },
|
|
|
|
})
|
|
|
|
)
|
|
|
|
let list, input = ""
|
|
|
|
document.body.append(html.div([
|
|
|
|
list=html.ul(),
|
|
|
|
html.input({type: 'text', input: e => input = e.target.value}),
|
|
|
|
html.button({click: event => list.append(listItem(input))}, "Add"),
|
|
|
|
]))
|
|
|
|
```
|