mirror of https://github.com/n2geoff/um
first pass
This commit is contained in:
parent
76e2d94b7f
commit
e79e241236
|
@ -0,0 +1,5 @@
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
.scratch/
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Tagged
|
||||||
|
|
||||||
|
> Minimal Javascript UI Library
|
||||||
|
|
||||||
|
This is an experimental composable ui library that takes ideas from Elm Architecture, but without the doctrine - this is Javascript!
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- No Virtual Dom
|
||||||
|
- No Build System
|
||||||
|
- No Over Engineering
|
||||||
|
- ~100 lines of code
|
||||||
|
- Totally inefficient rendering (at scale)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
the `app` builder takes an `opts` object that expects it to include:
|
||||||
|
|
||||||
|
- `data` as initial data `{object}`
|
||||||
|
- `methods` as `{object}` with functions definitions
|
||||||
|
- `view` as `{function}` that returns valid dom
|
||||||
|
|
||||||
|
and a querySelector compatible `selector` to mount the ui.
|
||||||
|
|
||||||
|
`app` returns:
|
||||||
|
|
||||||
|
- a object that returns a `state` object that you can use to update in other components
|
||||||
|
- `methods` you can call outside the app component
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="module">
|
||||||
|
import {app, h} from "./tagged.js";
|
||||||
|
|
||||||
|
const myapp = app({
|
||||||
|
data: {name: "[Your Name Here]", job: "Developer"},
|
||||||
|
view(state, methods) {
|
||||||
|
return h("main", [
|
||||||
|
h("strong", `Greeting from ${state.name}`),
|
||||||
|
h("div", `Your local ${state.job}`),
|
||||||
|
h("div", {id: "test"}, [
|
||||||
|
h("h1", "Hello Tagged"),
|
||||||
|
h("p", 21),
|
||||||
|
h("hr")
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}, "#main");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main id="app"></main>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inspired By
|
||||||
|
|
||||||
|
- hyperapp
|
||||||
|
- mithril
|
||||||
|
- Elm Architecture
|
||||||
|
|
||||||
|
## Todo
|
||||||
|
|
||||||
|
- Methods
|
||||||
|
- Update API: state, actions
|
||||||
|
|
||||||
|
> WORK-IN-PROGRESS
|
|
@ -0,0 +1,52 @@
|
||||||
|
export default function app(opts, selector = "body") {
|
||||||
|
// initial setup
|
||||||
|
let data = {};
|
||||||
|
let view = () => null;
|
||||||
|
let methods = {};
|
||||||
|
|
||||||
|
// query helper
|
||||||
|
const $ = document.querySelector.bind(document);
|
||||||
|
|
||||||
|
// state helper
|
||||||
|
const state = (state) => {
|
||||||
|
if(typeof state === "object") {
|
||||||
|
data = {...data, ...state};
|
||||||
|
}
|
||||||
|
|
||||||
|
// update ui
|
||||||
|
render();
|
||||||
|
|
||||||
|
// return current state
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const render = () => {
|
||||||
|
$(selector).replaceChildren(view(data, methods));
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup view
|
||||||
|
if (opts.view && typeof opts.view === "function") {
|
||||||
|
view = opts?.view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup data
|
||||||
|
if (opts.data && typeof opts.data === "object") {
|
||||||
|
// wrap data in state object
|
||||||
|
data = state(opts.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup methods
|
||||||
|
if (opts.methods && typeof opts.methods === "object") {
|
||||||
|
methods = opts.methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mount view
|
||||||
|
if (opts.view && selector) {
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
methods,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import tag from "./tag.js";
|
||||||
|
import app from "./app.js";
|
||||||
|
|
||||||
|
export {app};
|
||||||
|
export const h = tag;
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* Creates new DOM element(s) from tag name(s) and attributes
|
||||||
|
*
|
||||||
|
* @param {string} tag - tag to create
|
||||||
|
* @param {...any} args - attributes and/or child tag elements
|
||||||
|
*
|
||||||
|
* @returns {HTMLElement} The created DOM element(s)
|
||||||
|
*/
|
||||||
|
export default function tag(tag, ...args) {
|
||||||
|
const el = document.createElement(tag);
|
||||||
|
|
||||||
|
args.forEach((arg) => {
|
||||||
|
if (typeof arg === "string" || typeof arg === "number") {
|
||||||
|
el.appendChild(document.createTextNode(arg));
|
||||||
|
} else if (Array.isArray(arg)) {
|
||||||
|
el.append(...arg);
|
||||||
|
} else {
|
||||||
|
Object.assign(el, arg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Tagged UI Creation Lib</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
Tagged Loading...
|
||||||
|
</div>
|
||||||
|
<script type="module">
|
||||||
|
import {app, h} from "../src/index.js";
|
||||||
|
|
||||||
|
const myapp = app({
|
||||||
|
data: {name: "[Your Name Here]", job: "Developer"},
|
||||||
|
view(state, methods) {
|
||||||
|
return h("main", [
|
||||||
|
h("strong", `Greeting from ${state.name}`),
|
||||||
|
h("div", `Your local ${state.job}`),
|
||||||
|
h("div", {id: "test"}, [
|
||||||
|
h("h1", "Hello Tagged"),
|
||||||
|
h("p", 21),
|
||||||
|
h("hr")
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}, "#app");
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue