affirmations_v2.html
· 4.0 KiB · HTML
Surowy
Playground
<!DOCTYPE html>
<html>
<div id="app"></div>
<style>
#app, body {
height: 100%;
}
#edit-affirmations {
display: grid;
grid-template-rows: 80% 20%;
height: 80vh;
}
.affirmation {
width: 100%;
}
.half-half {
display: grid;
grid-template-columns: 50% 50%;
width: 100%;
}
.right-corner {
width: 25%;
justify-self: end;
}
.w-full { width: 100%; }
.grid { display: grid; }
#edit-affirmations textarea {
height: 80%;
}
</style>
<template id="mode-edit-affirmations">
<div id="edit-affirmations">
<textarea class="wide 80-tall" id="affirmation-list"></textarea>
<button id="save-affirmations">Save!</button>
</div>
</template>
<template id="mode-view-affirmations">
<div class="w-full grid">
<button id="go-to-edit-mode" class="right-corner"></button>
</div>
<div id="affirmation-list"></div>
<div class="half-half">
<button id="add-affirmation">another</button>
<button id="clear-affirmations">clear</button>
</div>
</template>
<template id="affirmation"><h3 class="affirmation"></h3></template>
<script src="hex.js"></script>
<script>
hex.arise(function() {
let affirmation_idx = 0;
let app = hex.id("app");
const get_affirmations = () => hex.recall("affirmations") || ["You need to add affirmations!"];
const next_affirmation = () => {
const affirmations = get_affirmations().filter(x => !(/^\s+$/.test(x)));
const ret = affirmations[affirmation_idx];
affirmation_idx = (affirmation_idx + 1) % affirmations.length;
return ret;
}
var affirmHeader = hex.spell("#affirmation", { value: ".affirmation" });
var modeRecite = hex.spell("#mode-view-affirmations", {
toEdit: ["#go-to-edit-mode", {t: "edit", on: {
click() {
hex.become(modeEdit, app, {affirmations: get_affirmations()});
}
} }],
affirmations: ["#affirmation-list", (el, affirmation) => affirmHeader(el, { value: affirmation })],
another: ["#add-affirmation",
{ t: "another",
init() {
this._affirmations = [];
this._next = () => {
this._affirmations.push(next_affirmation());
this.affirmations(this._affirmations)
}
this._next();
},
on: { click() { this._next(); } }
}
],
clear: ["#clear-affirmations", {t: "clear", on: { click() { this._affirmations = []; this.affirmations([]); } } }]
});
var modeEdit = hex.spell("#mode-edit-affirmations", {
affirmations: ["#affirmation-list", {
init(arg) {
this.affirmations = arg.affirmations.join("\n");
this._affirmations = arg.affirmations || get_affirmations();
},
on: { input(e) { this._affirmations = e.target.value.split("\n"); } }
}],
save: ["#save-affirmations",
{ t:"save", on: { click() {
console.log(this);
hex.scrawl("affirmations", this._affirmations);
hex.become(modeRecite, app, {affirmations: get_affirmations()});
}}}
],
});
hex.become(modeRecite, app, {affirmations: get_affirmations()});
});
</script>
</html>
1 | <!DOCTYPE html> |
2 | <html> |
3 | |
4 | <div id="app"></div> |
5 | <style> |
6 | |
7 | #app, body { |
8 | height: 100%; |
9 | } |
10 | #edit-affirmations { |
11 | display: grid; |
12 | grid-template-rows: 80% 20%; |
13 | height: 80vh; |
14 | } |
15 | |
16 | .affirmation { |
17 | width: 100%; |
18 | } |
19 | .half-half { |
20 | display: grid; |
21 | grid-template-columns: 50% 50%; |
22 | width: 100%; |
23 | } |
24 | .right-corner { |
25 | width: 25%; |
26 | justify-self: end; |
27 | } |
28 | .w-full { width: 100%; } |
29 | .grid { display: grid; } |
30 | |
31 | #edit-affirmations textarea { |
32 | height: 80%; |
33 | } |
34 | |
35 | </style> |
36 | |
37 | <template id="mode-edit-affirmations"> |
38 | <div id="edit-affirmations"> |
39 | <textarea class="wide 80-tall" id="affirmation-list"></textarea> |
40 | <button id="save-affirmations">Save!</button> |
41 | </div> |
42 | </template> |
43 | <template id="mode-view-affirmations"> |
44 | <div class="w-full grid"> |
45 | <button id="go-to-edit-mode" class="right-corner"></button> |
46 | </div> |
47 | <div id="affirmation-list"></div> |
48 | <div class="half-half"> |
49 | <button id="add-affirmation">another</button> |
50 | <button id="clear-affirmations">clear</button> |
51 | </div> |
52 | </template> |
53 | <template id="affirmation"><h3 class="affirmation"></h3></template> |
54 | |
55 | <script src="hex.js"></script> |
56 | <script> |
57 | hex.arise(function() { |
58 | let affirmation_idx = 0; |
59 | |
60 | let app = hex.id("app"); |
61 | const get_affirmations = () => hex.recall("affirmations") || ["You need to add affirmations!"]; |
62 | const next_affirmation = () => { |
63 | const affirmations = get_affirmations().filter(x => !(/^\s+$/.test(x))); |
64 | const ret = affirmations[affirmation_idx]; |
65 | affirmation_idx = (affirmation_idx + 1) % affirmations.length; |
66 | return ret; |
67 | } |
68 | |
69 | var affirmHeader = hex.spell("#affirmation", { value: ".affirmation" }); |
70 | |
71 | var modeRecite = hex.spell("#mode-view-affirmations", { |
72 | toEdit: ["#go-to-edit-mode", {t: "edit", on: { |
73 | click() { |
74 | hex.become(modeEdit, app, {affirmations: get_affirmations()}); |
75 | } |
76 | } }], |
77 | affirmations: ["#affirmation-list", (el, affirmation) => affirmHeader(el, { value: affirmation })], |
78 | another: ["#add-affirmation", |
79 | { t: "another", |
80 | init() { |
81 | this._affirmations = []; |
82 | this._next = () => { |
83 | this._affirmations.push(next_affirmation()); |
84 | this.affirmations(this._affirmations) |
85 | } |
86 | this._next(); |
87 | }, |
88 | on: { click() { this._next(); } } |
89 | } |
90 | ], |
91 | clear: ["#clear-affirmations", {t: "clear", on: { click() { this._affirmations = []; this.affirmations([]); } } }] |
92 | }); |
93 | |
94 | var modeEdit = hex.spell("#mode-edit-affirmations", { |
95 | affirmations: ["#affirmation-list", { |
96 | init(arg) { |
97 | this.affirmations = arg.affirmations.join("\n"); |
98 | this._affirmations = arg.affirmations || get_affirmations(); |
99 | }, |
100 | on: { input(e) { this._affirmations = e.target.value.split("\n"); } } |
101 | }], |
102 | |
103 | save: ["#save-affirmations", |
104 | { t:"save", on: { click() { |
105 | console.log(this); |
106 | hex.scrawl("affirmations", this._affirmations); |
107 | hex.become(modeRecite, app, {affirmations: get_affirmations()}); |
108 | }}} |
109 | ], |
110 | }); |
111 | |
112 | hex.become(modeRecite, app, {affirmations: get_affirmations()}); |
113 | }); |
114 | </script> |
115 | </html> |
116 |
hex.js
· 5.1 KiB · JavaScript
Surowy
Playground
const hex = (function() {
const q = (el, sel) => !sel ? document.querySelectorAll(el) : el.querySelectorAll(sel);
const id = (sel) => document.getElementById(sel);
const q1 = (el, sel) => !sel ? document.querySelector(el) : el.querySelector(sel);
const isObj = (obj) => { var type = typeof obj; return type === 'object' && !!obj; };
const isSimple = (v) => {var type = typeof v; return type=== "number" || type === "string" || type === "boolean"; }
const arise = (fn) => document.addEventListener('DOMContentLoaded', fn, false);
const scrawl = (k, v) => localStorage.setItem(k, JSON.stringify(v));
const recall = (k) => JSON.parse(localStorage.getItem(k) || "null");
function setterFor(node) {
switch (node.tagName) {
case "INPUT":
return (val) => node.value = val;
default:
return (val) => { node.innerText = val; };
}
}
let templs = new Map();
function from(tempSel) {
let toClone;
if (templs.has(tempSel)) {
toClone = templs.get(tempSel);
} else {
let n = q1(tempSel);
if (n) {
templs.set(tempSel, n);
toClone = n;
} else {
throw tempSel + " failed";
}
}
return toClone.content.cloneNode(true);
}
function spell(template, config) {
return function(into, start) {
const basis = from(template)
const state = { vars: {}, dom: {}, setters: {} };
const interface = Object.create(spell);
const recursionGuard = new Set();
for (let [k, v] of Object.entries(config)) {
const makeProp = (el, initialValue) => {
state.setters[k] = setterFor(el);
Object.defineProperty(interface, k, {
get() { return state.vars[k] },
set(v) {
state.vars[k] = v;
state.setters[k]((v || "") + "");
}
});
interface[k] = initialValue;
}
if (typeof v === "string" && v) {
const el = q1(basis, v);
state.dom[k] = el;
makeProp(el, start[k]);
} else if (Array.isArray(v)) {
if (v.length <= 0) {
throw new Error("Empty array not valid!");
}
let el;
if (typeof v[0] === "string") {
el = q1(basis, v[0]);
state.dom[k] = el;
} else {
throw new Error(`Selector for ${k} must be a string!`);
}
if (v.length == 1 && (typeof v[0]) == "string") {
state.vars[k] = start[k];
makeProp(el, start[k]);
} else if (v.length == 2 && (typeof v[0]) === "string" && isSimple(v[1])) {
makeProp(el, start[k] || v[1]);
} else if (v.length == 2 && (typeof v[0]) === "string" && isObj(v[1])) {
makeProp(el, start[k]);
const obj = v[1];
if (obj.on) {
for (const [evt, fn] of Object.entries(obj.on)) {
let inner = fn.bind(interface);
let handler = function() {
if (recursionGuard.has(handler)) { return; }
try {
recursionGuard.add(handler);
inner(...arguments);
} finally {
recursionGuard.delete(handler);
}
};
el.addEventListener(evt, handler);
}
}
if (obj.init && typeof obj.init === "function") {
obj.init.call(interface, start);
}
} else if (v.length === 2 &&
(typeof v[0]) === "string" &&
(typeof v[1]) === "function"
&& Array.isArray(start[k])
) {
state.vars[k] = start[k].map((data) => v[1](el, data));
interface[k] = function(new_data) {
el.innerHTML = "";
state.vars[k] = new_data.map((data) => v[1](el, data));
}
} else {
throw new Error(`Invalid config: ${k}`);
}
}
}
interface._ = state;
into.appendChild(basis);
return interface;
}
}
return {q, q1, id, from, arise, scrawl, recall, spell};
})();
window.hex = hex;
1 | const hex = (function() { |
2 | const q = (el, sel) => !sel ? document.querySelectorAll(el) : el.querySelectorAll(sel); |
3 | const id = (sel) => document.getElementById(sel); |
4 | const q1 = (el, sel) => !sel ? document.querySelector(el) : el.querySelector(sel); |
5 | const isObj = (obj) => { var type = typeof obj; return type === 'object' && !!obj; }; |
6 | const isSimple = (v) => {var type = typeof v; return type=== "number" || type === "string" || type === "boolean"; } |
7 | const arise = (fn) => document.addEventListener('DOMContentLoaded', fn, false); |
8 | const scrawl = (k, v) => localStorage.setItem(k, JSON.stringify(v)); |
9 | const recall = (k) => JSON.parse(localStorage.getItem(k) || "null"); |
10 | |
11 | function setterFor(node) { |
12 | switch (node.tagName) { |
13 | case "INPUT": |
14 | return (val) => node.value = val; |
15 | default: |
16 | return (val) => { node.innerText = val; }; |
17 | } |
18 | } |
19 | |
20 | let templs = new Map(); |
21 | function from(tempSel) { |
22 | let toClone; |
23 | if (templs.has(tempSel)) { |
24 | toClone = templs.get(tempSel); |
25 | } else { |
26 | let n = q1(tempSel); |
27 | if (n) { |
28 | templs.set(tempSel, n); |
29 | toClone = n; |
30 | } else { |
31 | throw tempSel + " failed"; |
32 | } |
33 | } |
34 | return toClone.content.cloneNode(true); |
35 | } |
36 | |
37 | function spell(template, config) { |
38 | |
39 | return function(into, start) { |
40 | const basis = from(template) |
41 | const state = { vars: {}, dom: {}, setters: {} }; |
42 | const interface = Object.create(spell); |
43 | const recursionGuard = new Set(); |
44 | |
45 | for (let [k, v] of Object.entries(config)) { |
46 | const makeProp = (el, initialValue) => { |
47 | state.setters[k] = setterFor(el); |
48 | |
49 | Object.defineProperty(interface, k, { |
50 | get() { return state.vars[k] }, |
51 | set(v) { |
52 | state.vars[k] = v; |
53 | state.setters[k]((v || "") + ""); |
54 | } |
55 | }); |
56 | |
57 | interface[k] = initialValue; |
58 | } |
59 | if (typeof v === "string" && v) { |
60 | const el = q1(basis, v); |
61 | state.dom[k] = el; |
62 | makeProp(el, start[k]); |
63 | } else if (Array.isArray(v)) { |
64 | if (v.length <= 0) { |
65 | throw new Error("Empty array not valid!"); |
66 | } |
67 | let el; |
68 | if (typeof v[0] === "string") { |
69 | el = q1(basis, v[0]); |
70 | state.dom[k] = el; |
71 | } else { |
72 | throw new Error(`Selector for ${k} must be a string!`); |
73 | } |
74 | |
75 | if (v.length == 1 && (typeof v[0]) == "string") { |
76 | state.vars[k] = start[k]; |
77 | makeProp(el, start[k]); |
78 | } else if (v.length == 2 && (typeof v[0]) === "string" && isSimple(v[1])) { |
79 | makeProp(el, start[k] || v[1]); |
80 | } else if (v.length == 2 && (typeof v[0]) === "string" && isObj(v[1])) { |
81 | makeProp(el, start[k]); |
82 | const obj = v[1]; |
83 | if (obj.on) { |
84 | for (const [evt, fn] of Object.entries(obj.on)) { |
85 | let inner = fn.bind(interface); |
86 | let handler = function() { |
87 | if (recursionGuard.has(handler)) { return; } |
88 | try { |
89 | recursionGuard.add(handler); |
90 | inner(...arguments); |
91 | } finally { |
92 | recursionGuard.delete(handler); |
93 | } |
94 | }; |
95 | |
96 | el.addEventListener(evt, handler); |
97 | } |
98 | } |
99 | if (obj.init && typeof obj.init === "function") { |
100 | obj.init.call(interface, start); |
101 | } |
102 | |
103 | } else if (v.length === 2 && |
104 | (typeof v[0]) === "string" && |
105 | (typeof v[1]) === "function" |
106 | && Array.isArray(start[k]) |
107 | ) { |
108 | state.vars[k] = start[k].map((data) => v[1](el, data)); |
109 | interface[k] = function(new_data) { |
110 | el.innerHTML = ""; |
111 | state.vars[k] = new_data.map((data) => v[1](el, data)); |
112 | } |
113 | } else { |
114 | throw new Error(`Invalid config: ${k}`); |
115 | } |
116 | } |
117 | } |
118 | |
119 | interface._ = state; |
120 | into.appendChild(basis); |
121 | return interface; |
122 | } |
123 | } |
124 | |
125 | |
126 | |
127 | return {q, q1, id, from, arise, scrawl, recall, spell}; |
128 | })(); |
129 | |
130 | window.hex = hex; |
131 |