clapton 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/app/helpers/clapton/clapton_helper.rb +16 -1
- data/lib/clapton/engine.rb +20 -10
- data/lib/clapton/javascripts/dist/client.js +38 -27
- data/lib/clapton/javascripts/dist/components-for-test.js +439 -0
- data/lib/clapton/javascripts/dist/components.js +356 -382
- data/lib/clapton/javascripts/node_modules/diff-dom/LICENSE.txt +165 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/README.md +224 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/browser/diffDOM.js +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/browser/diffDOM.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/TraceLogger.d.ts +28 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/apply.d.ts +4 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/fromVirtual.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/index.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/undo.d.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/helpers.d.ts +11 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/index.d.ts +10 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/types.d.ts +104 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/apply.d.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/diff.d.ts +22 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/fromDOM.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/fromString.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/helpers.d.ts +40 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/index.d.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/index.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.d.ts +136 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.js +1996 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.min.js +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.min.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/module.js +1991 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/module.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/index.html +62 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/package.json +54 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/rollup.config.mjs +67 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/TraceLogger.ts +143 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/apply.ts +227 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/fromVirtual.ts +83 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/index.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/undo.ts +90 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/helpers.ts +40 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/index.ts +121 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/types.ts +154 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/apply.ts +349 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/diff.ts +855 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/fromDOM.ts +74 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/fromString.ts +239 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/helpers.ts +461 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/index.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/index.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/tsconfig.json +103 -0
- data/lib/clapton/javascripts/rollup.config.mjs +17 -2
- data/lib/clapton/javascripts/src/actions/handle-action.ts +6 -6
- data/lib/clapton/javascripts/src/actions/initialize-actions.ts +6 -3
- data/lib/clapton/javascripts/src/channel/clapton-channel.js +6 -3
- data/lib/clapton/javascripts/src/client.ts +15 -15
- data/lib/clapton/javascripts/src/components-for-test.ts +29 -0
- data/lib/clapton/javascripts/src/components.ts +4 -1
- data/lib/clapton/javascripts/src/dom/update-component.ts +4 -4
- data/lib/clapton/javascripts/src/inputs/initialize-inputs.ts +2 -2
- data/lib/clapton/test_helper/base.rb +1 -1
- data/lib/clapton/version.rb +1 -2
- metadata +49 -3
- data/lib/clapton/javascripts/src/dom/update-component.spec.ts +0 -32
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 31438028bfe58d5ce7e560543850167002e53f148784072f20834081c0421b34
|
|
4
|
+
data.tar.gz: 2509467d610325dbaf0c1a20a953e50ecd568b8722d1b2fd7a1bc1acbb1bd6aa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c68a61320829b1fcd198c627af8ff48de08479f1a725ebb51c14dfcbfc74277c4c0fec8735c4becf2994ebb1abcb36222a102dc9403d1d581406534a0fc11ca6
|
|
7
|
+
data.tar.gz: 5500b02be72eb9b161c1882ac913fd48ff6a0114e1bb3aa46b38572eb5885e00a4b2b7a10bc4968b3535331ba67916df292eb65dceff6a203e960d0c46eb47bd
|
data/README.md
CHANGED
|
@@ -12,6 +12,7 @@ Clapton is a Ruby on Rails gem for building web apps with pure Ruby only (no Jav
|
|
|
12
12
|
- Action Cable (WebSocket)
|
|
13
13
|
- [Ruby2JS](https://www.ruby2js.com/) (for compiling Ruby to JavaScript)
|
|
14
14
|
- [Morphdom](https://github.com/patrick-steele-idem/morphdom)
|
|
15
|
+
- importmap
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
|
@@ -2,7 +2,22 @@ module Clapton
|
|
|
2
2
|
module ClaptonHelper
|
|
3
3
|
|
|
4
4
|
def clapton_javascript_tag
|
|
5
|
-
|
|
5
|
+
all_components = Dir.glob(Rails.root.join("app", "components", "**", "*.rb"))
|
|
6
|
+
tags = <<~HTML
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{
|
|
9
|
+
"imports": {
|
|
10
|
+
"client": "/clapton/client.js",
|
|
11
|
+
"components": "/clapton/components.js",
|
|
12
|
+
#{ all_components.map do
|
|
13
|
+
|component| "\"#{File.basename(component, ".rb").camelize}\": \"/clapton/#{File.basename(component, ".rb").camelize}.js\""
|
|
14
|
+
end.join(",\n") }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
<script type="module" src="/clapton/client.js"></script>
|
|
19
|
+
HTML
|
|
20
|
+
tags.html_safe
|
|
6
21
|
end
|
|
7
22
|
|
|
8
23
|
def clapton_tag
|
data/lib/clapton/engine.rb
CHANGED
|
@@ -21,6 +21,10 @@ module Clapton
|
|
|
21
21
|
FileUtils.mkdir_p(components_path) unless components_path.exist?
|
|
22
22
|
FileUtils.touch(components_path.join(".keep"))
|
|
23
23
|
|
|
24
|
+
FileUtils.mkdir_p(Rails.root.join("public", "clapton")) unless Rails.root.join("public", "clapton").exist?
|
|
25
|
+
File.write(Rails.root.join("public", "clapton", "components.js"), File.read(File.join(__dir__, "javascripts", "dist", "components.js")))
|
|
26
|
+
File.write(Rails.root.join("public", "clapton", "client.js"), File.read(File.join(__dir__, "javascripts", "dist", "client.js")))
|
|
27
|
+
|
|
24
28
|
compile_components
|
|
25
29
|
|
|
26
30
|
listener = Listen.to(Rails.root.join("app", "components")) do |modified, added, removed|
|
|
@@ -32,20 +36,26 @@ module Clapton
|
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
def compile_components
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
js += "\n"
|
|
39
|
-
js += "window.components = [];"
|
|
40
|
-
js += "\n"
|
|
39
|
+
puts "Clapton: Compiling components"
|
|
40
|
+
|
|
41
|
+
start_time = Time.now
|
|
41
42
|
Dir.glob(Rails.root.join("app", "components", "**", "*.rb")).each do |file|
|
|
42
|
-
|
|
43
|
+
code = File.read(file)
|
|
44
|
+
js = ""
|
|
45
|
+
js += "import { Clapton } from 'components';"
|
|
46
|
+
js += "\n"
|
|
47
|
+
code.scan(/(\w+)Component\.new/).each do |match|
|
|
48
|
+
js += "import { #{match[0]}Component } from '#{match[0]}Component';"
|
|
49
|
+
js += "\n"
|
|
50
|
+
end
|
|
51
|
+
js += Ruby2JS.convert(code, preset: true)
|
|
43
52
|
js += "\n"
|
|
44
|
-
js += "
|
|
53
|
+
js += "export { #{File.basename(file, ".rb").camelize} };"
|
|
45
54
|
js += "\n"
|
|
55
|
+
File.write(Rails.root.join("public", "clapton", "#{File.basename(file, ".rb").camelize}.js"), js)
|
|
46
56
|
end
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
end_time = Time.now
|
|
58
|
+
puts "Clapton: Component compilation took #{end_time - start_time} seconds"
|
|
49
59
|
end
|
|
50
60
|
end
|
|
51
61
|
end
|
|
@@ -1285,11 +1285,11 @@ function getConfig(name) {
|
|
|
1285
1285
|
}
|
|
1286
1286
|
}
|
|
1287
1287
|
|
|
1288
|
-
const updateComponent = (component, state, property, target) => {
|
|
1288
|
+
const updateComponent = async (component, state, property, target) => {
|
|
1289
1289
|
state[property] = target.value;
|
|
1290
|
-
component.
|
|
1291
|
-
const
|
|
1292
|
-
const ComponentClass =
|
|
1290
|
+
const componentName = component.dataset.component;
|
|
1291
|
+
const module = await import(`${componentName}`);
|
|
1292
|
+
const ComponentClass = module[componentName];
|
|
1293
1293
|
const instance = new ComponentClass(state, component.dataset.id);
|
|
1294
1294
|
morphdom(component, instance.render);
|
|
1295
1295
|
};
|
|
@@ -1303,8 +1303,8 @@ const initializeInputs = () => {
|
|
|
1303
1303
|
if (!attribute || !component)
|
|
1304
1304
|
return;
|
|
1305
1305
|
if (element.tagName === "INPUT") {
|
|
1306
|
-
element.addEventListener("input", (event) => {
|
|
1307
|
-
updateComponent(component, state, attribute, event.target);
|
|
1306
|
+
element.addEventListener("input", async (event) => {
|
|
1307
|
+
await updateComponent(component, state, attribute, event.target);
|
|
1308
1308
|
});
|
|
1309
1309
|
}
|
|
1310
1310
|
});
|
|
@@ -1313,14 +1313,17 @@ const initializeInputs = () => {
|
|
|
1313
1313
|
const consumer = createConsumer();
|
|
1314
1314
|
|
|
1315
1315
|
const claptonChannel = consumer.subscriptions.create("Clapton::ClaptonChannel", {
|
|
1316
|
-
connected() {
|
|
1316
|
+
connected() {
|
|
1317
|
+
window.actionCableConnected = true;
|
|
1318
|
+
},
|
|
1317
1319
|
|
|
1318
1320
|
disconnected() {},
|
|
1319
1321
|
|
|
1320
|
-
received(response) {
|
|
1322
|
+
async received(response) {
|
|
1321
1323
|
const { data, errors } = response;
|
|
1322
1324
|
const component = document.querySelector(`[data-id="${data.component.id}"]`);
|
|
1323
|
-
const
|
|
1325
|
+
const module = await import(`${data.component.name}`);
|
|
1326
|
+
const instance = new module[data.component.name](data.state, data.component.id, errors);
|
|
1324
1327
|
morphdom(component, instance.render, {
|
|
1325
1328
|
onBeforeElUpdated: (_fromEl, toEl) => {
|
|
1326
1329
|
toEl.setAttribute("data-set-event-handler", "true");
|
|
@@ -1344,26 +1347,26 @@ const handleAction = async (target, stateName, fn) => {
|
|
|
1344
1347
|
if (!targetComponent)
|
|
1345
1348
|
return;
|
|
1346
1349
|
const component = target.closest(`[data-component]`);
|
|
1347
|
-
const attribute = target.
|
|
1350
|
+
const attribute = target.dataset.attribute;
|
|
1348
1351
|
if (attribute) {
|
|
1349
|
-
const state = JSON.parse(component.
|
|
1352
|
+
const state = JSON.parse(component.dataset.state || "{}");
|
|
1350
1353
|
if (target.tagName === "INPUT") {
|
|
1351
1354
|
state[attribute] = target.value;
|
|
1352
|
-
component.
|
|
1355
|
+
component.dataset.state = JSON.stringify(state);
|
|
1353
1356
|
}
|
|
1354
1357
|
}
|
|
1355
1358
|
claptonChannel.perform("action", {
|
|
1356
1359
|
data: {
|
|
1357
1360
|
component: {
|
|
1358
1361
|
name: stateName.replace("State", "Component"),
|
|
1359
|
-
id: targetComponent.
|
|
1362
|
+
id: targetComponent.dataset.id,
|
|
1360
1363
|
},
|
|
1361
1364
|
state: {
|
|
1362
1365
|
name: stateName,
|
|
1363
1366
|
action: fn,
|
|
1364
|
-
attributes: JSON.parse(targetComponent.
|
|
1367
|
+
attributes: JSON.parse(targetComponent.dataset.state || "{}"),
|
|
1365
1368
|
},
|
|
1366
|
-
params: JSON.parse(component.
|
|
1369
|
+
params: JSON.parse(component.dataset.state || "{}")
|
|
1367
1370
|
}
|
|
1368
1371
|
});
|
|
1369
1372
|
};
|
|
@@ -1389,9 +1392,12 @@ const initializeActionsForElement = (element) => {
|
|
|
1389
1392
|
if (!eventType || !componentName || !fnName)
|
|
1390
1393
|
return;
|
|
1391
1394
|
if (eventType === "render") {
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
+
const interval = setInterval(() => {
|
|
1396
|
+
if (window.actionCableConnected === true) {
|
|
1397
|
+
handleAction(element, stateName, fnName);
|
|
1398
|
+
clearInterval(interval);
|
|
1399
|
+
}
|
|
1400
|
+
}, 10);
|
|
1395
1401
|
element.setAttribute("data-render-event-handler", "true");
|
|
1396
1402
|
return;
|
|
1397
1403
|
}
|
|
@@ -1405,25 +1411,30 @@ const initializeActionsForElement = (element) => {
|
|
|
1405
1411
|
});
|
|
1406
1412
|
};
|
|
1407
1413
|
|
|
1408
|
-
const initializeComponents = () => {
|
|
1414
|
+
const initializeComponents = async () => {
|
|
1409
1415
|
const components = document.querySelector("#clapton")?.getAttribute("data-clapton") || "[]";
|
|
1410
|
-
JSON.parse(components)
|
|
1411
|
-
|
|
1416
|
+
const componentArray = JSON.parse(components);
|
|
1417
|
+
for (const component of componentArray) {
|
|
1418
|
+
await createAndAppendComponent(component, document.querySelector("#clapton"));
|
|
1419
|
+
}
|
|
1420
|
+
const elements = document.querySelectorAll(".clapton-component");
|
|
1421
|
+
for (const element of elements) {
|
|
1412
1422
|
const component = JSON.parse(element.getAttribute("data-clapton") || "{}");
|
|
1413
|
-
createAndAppendComponent(component, element);
|
|
1414
|
-
}
|
|
1423
|
+
await createAndAppendComponent(component, element);
|
|
1424
|
+
}
|
|
1415
1425
|
};
|
|
1416
|
-
const createAndAppendComponent = (component, element) => {
|
|
1426
|
+
const createAndAppendComponent = async (component, element) => {
|
|
1417
1427
|
const componentDom = document.createElement('div');
|
|
1418
|
-
const
|
|
1428
|
+
const module = await import(`${component.component}`);
|
|
1429
|
+
const instance = new module[component.component](component.state);
|
|
1419
1430
|
componentDom.innerHTML = instance.render;
|
|
1420
1431
|
const firstChild = componentDom.firstChild;
|
|
1421
1432
|
if (firstChild) {
|
|
1422
1433
|
element.appendChild(firstChild);
|
|
1423
1434
|
}
|
|
1424
1435
|
};
|
|
1425
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
1426
|
-
initializeComponents();
|
|
1436
|
+
document.addEventListener("DOMContentLoaded", async () => {
|
|
1437
|
+
await initializeComponents();
|
|
1427
1438
|
initializeActions();
|
|
1428
1439
|
initializeInputs();
|
|
1429
1440
|
});
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
var Clapton = (function (exports) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const htmlAttributes = (params) => {
|
|
5
|
+
const customDataAttributes = params.data || {};
|
|
6
|
+
const others = Object.keys(params).filter(key => key !== "data");
|
|
7
|
+
const flattenDataAttributes = (data, prefix = "data") => {
|
|
8
|
+
return Object.keys(data).reduce((acc, key) => {
|
|
9
|
+
const value = data[key];
|
|
10
|
+
if (typeof value === "object" && value !== null) {
|
|
11
|
+
acc.push(...flattenDataAttributes(value, `${prefix}-${key}`));
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
acc.push(`${prefix}-${key}='${escapeHtml(value)}'`);
|
|
15
|
+
}
|
|
16
|
+
return acc;
|
|
17
|
+
}, []);
|
|
18
|
+
};
|
|
19
|
+
return [
|
|
20
|
+
others.map(key => {
|
|
21
|
+
if (key === "disabled") {
|
|
22
|
+
if (params[key] === false) {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
return `${key}`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return `${key}='${escapeHtml(params[key])}'`;
|
|
30
|
+
}).join(" "),
|
|
31
|
+
flattenDataAttributes(customDataAttributes).join(" ")
|
|
32
|
+
].filter(Boolean).join(" ");
|
|
33
|
+
};
|
|
34
|
+
const escapeHtml = (unsafe) => {
|
|
35
|
+
if (typeof unsafe !== "string") {
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
return unsafe
|
|
39
|
+
.replace(/&/g, "&")
|
|
40
|
+
.replace(/</g, "<")
|
|
41
|
+
.replace(/>/g, ">")
|
|
42
|
+
.replace(/"/g, """)
|
|
43
|
+
.replace(/'/g, "'");
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
class BlockQuote {
|
|
47
|
+
constructor(attributes = {}) {
|
|
48
|
+
this.children = [];
|
|
49
|
+
this.attributes = attributes;
|
|
50
|
+
}
|
|
51
|
+
get render() {
|
|
52
|
+
return `<blockquote ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</blockquote>`;
|
|
53
|
+
}
|
|
54
|
+
add(child) {
|
|
55
|
+
this.children.push(child);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class Box {
|
|
61
|
+
constructor(attributes = {}) {
|
|
62
|
+
this.children = [];
|
|
63
|
+
this.attributes = attributes;
|
|
64
|
+
}
|
|
65
|
+
add(child) {
|
|
66
|
+
this.children.push(child);
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
get render() {
|
|
70
|
+
return `<div ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</div>`;
|
|
71
|
+
}
|
|
72
|
+
add_action(eventType, stateName, fnName, options = {}) {
|
|
73
|
+
this.attributes["data-action"] = `${this.attributes["data-action"] || ""} ${eventType}->${stateName}#${fnName}@${options.debounce || 0}`;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
class Button {
|
|
79
|
+
constructor(attributes = {}) {
|
|
80
|
+
this.attributes = attributes;
|
|
81
|
+
this.children = [];
|
|
82
|
+
}
|
|
83
|
+
add(child) {
|
|
84
|
+
this.children.push(child);
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
get render() {
|
|
88
|
+
return `<button ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</button>`;
|
|
89
|
+
}
|
|
90
|
+
add_action(event, klass, fn, options = {}) {
|
|
91
|
+
this.attributes["data-action"] = `${this.attributes["data-action"] || ""} ${event}->${klass}#${fn}@${options.debounce || 0}`;
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
class Checkbox {
|
|
97
|
+
constructor(state, attribute, attributes = {}) {
|
|
98
|
+
this.state = state;
|
|
99
|
+
this.attributes = attributes;
|
|
100
|
+
this.attribute = attribute;
|
|
101
|
+
this.attributes["data-attribute"] = attribute;
|
|
102
|
+
}
|
|
103
|
+
get render() {
|
|
104
|
+
return `<input type='checkbox' ${htmlAttributes(this.attributes)} value='${this.state[this.attribute] || ""}'/>`;
|
|
105
|
+
}
|
|
106
|
+
add_action(event, klass, fn, options = {}) {
|
|
107
|
+
this.attributes["data-action"] = `${this.attributes["data-action"] || ""} ${event}->${klass}#${fn}@${options.debounce || 0}`;
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
class Code {
|
|
113
|
+
constructor(attributes = {}) {
|
|
114
|
+
this.children = [];
|
|
115
|
+
this.attributes = attributes;
|
|
116
|
+
}
|
|
117
|
+
get render() {
|
|
118
|
+
return `<code ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</code>`;
|
|
119
|
+
}
|
|
120
|
+
add(child) {
|
|
121
|
+
this.children.push(child);
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
class DateTimeField {
|
|
127
|
+
constructor(state, attribute, attributes = {}) {
|
|
128
|
+
this.attributes = {};
|
|
129
|
+
this.state = state;
|
|
130
|
+
this.attribute = attribute;
|
|
131
|
+
this.attributes = attributes;
|
|
132
|
+
this.attributes["data-attribute"] = attribute;
|
|
133
|
+
}
|
|
134
|
+
get render() {
|
|
135
|
+
const value = this.state[this.attribute] ? this.datetime_local_value(this.state[this.attribute]) : "";
|
|
136
|
+
return `<input type='datetime-local' ${htmlAttributes(this.attributes)} value='${value}'/>`;
|
|
137
|
+
}
|
|
138
|
+
add_action(event, klass, fn, options = {}) {
|
|
139
|
+
this.attributes["data-action"] = `${this.attributes["data-action"] || ""} ${event}->${klass}#${fn}@${options.debounce || 0}`;
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
datetime_local_value(value) {
|
|
143
|
+
if (!value) {
|
|
144
|
+
return "";
|
|
145
|
+
}
|
|
146
|
+
const date = new Date(value);
|
|
147
|
+
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
|
148
|
+
let month = date.getMonth() + 1;
|
|
149
|
+
let day = date.getDate();
|
|
150
|
+
let hours = date.getHours();
|
|
151
|
+
let minutes = date.getMinutes();
|
|
152
|
+
if (month < 10) {
|
|
153
|
+
month = `0${month}`;
|
|
154
|
+
}
|
|
155
|
+
if (day < 10) {
|
|
156
|
+
day = `0${day}`;
|
|
157
|
+
}
|
|
158
|
+
if (hours < 10) {
|
|
159
|
+
hours = `0${hours}`;
|
|
160
|
+
}
|
|
161
|
+
if (minutes < 10) {
|
|
162
|
+
minutes = `0${minutes}`;
|
|
163
|
+
}
|
|
164
|
+
return `${date.getFullYear()}-${month}-${day}T${hours}:${minutes}`;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
class Element {
|
|
169
|
+
constructor(type, attributes = {}) {
|
|
170
|
+
this.children = [];
|
|
171
|
+
this.type = type;
|
|
172
|
+
this.attributes = attributes;
|
|
173
|
+
}
|
|
174
|
+
get render() {
|
|
175
|
+
return `<${this.type} ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</${this.type}>`;
|
|
176
|
+
}
|
|
177
|
+
add(child) {
|
|
178
|
+
this.children.push(child);
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
class Embed {
|
|
184
|
+
constructor(html) {
|
|
185
|
+
this.html = html;
|
|
186
|
+
}
|
|
187
|
+
get render() {
|
|
188
|
+
return this.html;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
class Emphasis {
|
|
193
|
+
constructor(attributes = {}) {
|
|
194
|
+
this.children = [];
|
|
195
|
+
this.attributes = attributes;
|
|
196
|
+
}
|
|
197
|
+
get render() {
|
|
198
|
+
return `<em ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</em>`;
|
|
199
|
+
}
|
|
200
|
+
add(child) {
|
|
201
|
+
this.children.push(child);
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
class Form {
|
|
207
|
+
constructor(attributes = {}) {
|
|
208
|
+
this.children = [];
|
|
209
|
+
this.attributes = attributes;
|
|
210
|
+
}
|
|
211
|
+
get render() {
|
|
212
|
+
return `<form ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</form>`;
|
|
213
|
+
}
|
|
214
|
+
add(child) {
|
|
215
|
+
this.children.push(child);
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
class Heading {
|
|
221
|
+
constructor(level, attributes = {}) {
|
|
222
|
+
this.children = [];
|
|
223
|
+
this.level = level;
|
|
224
|
+
this.attributes = attributes;
|
|
225
|
+
}
|
|
226
|
+
get render() {
|
|
227
|
+
return `<h${this.level} ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</h${this.level}>`;
|
|
228
|
+
}
|
|
229
|
+
add(child) {
|
|
230
|
+
this.children.push(child);
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
class Image {
|
|
236
|
+
constructor(src, alt, attributes = {}) {
|
|
237
|
+
this.children = [];
|
|
238
|
+
this.attributes = attributes;
|
|
239
|
+
this.src = src;
|
|
240
|
+
this.alt = alt;
|
|
241
|
+
}
|
|
242
|
+
get render() {
|
|
243
|
+
return `<img src='${this.src}' alt='${this.alt}' ${htmlAttributes(this.attributes)}/>`;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
class Link {
|
|
248
|
+
constructor(href, attributes = {}) {
|
|
249
|
+
this.children = [];
|
|
250
|
+
this.attributes = attributes;
|
|
251
|
+
this.href = href;
|
|
252
|
+
}
|
|
253
|
+
get render() {
|
|
254
|
+
return `<a href='${this.href}' ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</a>`;
|
|
255
|
+
}
|
|
256
|
+
add(child) {
|
|
257
|
+
this.children.push(child);
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
class ListItem {
|
|
263
|
+
constructor(attributes = {}) {
|
|
264
|
+
this.children = [];
|
|
265
|
+
this.attributes = attributes;
|
|
266
|
+
}
|
|
267
|
+
add(child) {
|
|
268
|
+
this.children.push(child);
|
|
269
|
+
return this;
|
|
270
|
+
}
|
|
271
|
+
get render() {
|
|
272
|
+
return `<li ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</li>`;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
class List {
|
|
277
|
+
constructor(attributes = {}) {
|
|
278
|
+
this.children = [];
|
|
279
|
+
this.attributes = attributes;
|
|
280
|
+
}
|
|
281
|
+
add(child) {
|
|
282
|
+
this.children.push(child);
|
|
283
|
+
return this;
|
|
284
|
+
}
|
|
285
|
+
get render() {
|
|
286
|
+
return `<ul ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</ul>`;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
class OrderedList {
|
|
291
|
+
constructor(attributes = {}) {
|
|
292
|
+
this.children = [];
|
|
293
|
+
this.attributes = attributes;
|
|
294
|
+
}
|
|
295
|
+
add(child) {
|
|
296
|
+
this.children.push(child);
|
|
297
|
+
return this;
|
|
298
|
+
}
|
|
299
|
+
get render() {
|
|
300
|
+
return `<ol ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</ol>`;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
class Paragraph {
|
|
305
|
+
constructor(attributes = {}) {
|
|
306
|
+
this.children = [];
|
|
307
|
+
this.attributes = attributes;
|
|
308
|
+
}
|
|
309
|
+
get render() {
|
|
310
|
+
return `<p ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</p>`;
|
|
311
|
+
}
|
|
312
|
+
add(child) {
|
|
313
|
+
this.children.push(child);
|
|
314
|
+
return this;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
class Quote {
|
|
319
|
+
constructor(attributes = {}) {
|
|
320
|
+
this.children = [];
|
|
321
|
+
this.attributes = attributes;
|
|
322
|
+
}
|
|
323
|
+
get render() {
|
|
324
|
+
return `<q ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</q>`;
|
|
325
|
+
}
|
|
326
|
+
add(child) {
|
|
327
|
+
this.children.push(child);
|
|
328
|
+
return this;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
class RadioButton {
|
|
333
|
+
constructor(state, attribute, attributes = {}) {
|
|
334
|
+
this.state = state;
|
|
335
|
+
this.attributes = attributes;
|
|
336
|
+
this.attribute = attribute;
|
|
337
|
+
this.attributes["data-attribute"] = attribute;
|
|
338
|
+
}
|
|
339
|
+
get render() {
|
|
340
|
+
return `<input type='radio' ${htmlAttributes(this.attributes)} value='${this.state[this.attribute] || ""}'/>`;
|
|
341
|
+
}
|
|
342
|
+
add_action(event, klass, fn, options = {}) {
|
|
343
|
+
this.attributes["data-action"] = `${this.attributes["data-action"] || ""} ${event}->${klass}#${fn}@${options.debounce || 0}`;
|
|
344
|
+
return this;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
class Select {
|
|
349
|
+
constructor(options = [], state, attribute, attributes = {}) {
|
|
350
|
+
this.children = [];
|
|
351
|
+
this.options = options;
|
|
352
|
+
this.state = state;
|
|
353
|
+
this.attribute = attribute;
|
|
354
|
+
this.attributes = attributes;
|
|
355
|
+
}
|
|
356
|
+
get render() {
|
|
357
|
+
return `<select ${htmlAttributes(this.attributes)}>${this.options.map(option => `<option value='${option.value}'${option.value === this.state[this.attribute] ? " selected" : ""}>${option.text}</option>`).join("")}${this.children.map(child => child.render).join("")}</select>`;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
class Span {
|
|
362
|
+
constructor(attributes = {}) {
|
|
363
|
+
this.children = [];
|
|
364
|
+
this.attributes = attributes;
|
|
365
|
+
}
|
|
366
|
+
get render() {
|
|
367
|
+
return `<span ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</span>`;
|
|
368
|
+
}
|
|
369
|
+
add(child) {
|
|
370
|
+
this.children.push(child);
|
|
371
|
+
return this;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
class Component {
|
|
376
|
+
constructor(state = {}, id = Math.random().toString(36).substring(2, 10), errors = []) {
|
|
377
|
+
this._state = state;
|
|
378
|
+
this.id = id;
|
|
379
|
+
this._errors = errors;
|
|
380
|
+
this._root = new Box({ data: { component: this.constructor.name, state: JSON.stringify(this._state), id: this.id, errors: this._errors } });
|
|
381
|
+
}
|
|
382
|
+
get render() {
|
|
383
|
+
return this._root.render;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
class TextField {
|
|
388
|
+
constructor(state, attribute, attributes = {}) {
|
|
389
|
+
this.state = state;
|
|
390
|
+
this.attributes = attributes;
|
|
391
|
+
this.attribute = attribute;
|
|
392
|
+
this.attributes["data-attribute"] = attribute;
|
|
393
|
+
}
|
|
394
|
+
get render() {
|
|
395
|
+
return `<input type='text' ${htmlAttributes(this.attributes)} value='${this.state[this.attribute] || ""}'/>`;
|
|
396
|
+
}
|
|
397
|
+
add_action(event, klass, fn, options = {}) {
|
|
398
|
+
this.attributes["data-action"] = `${this.attributes["data-action"] || ""} ${event}->${klass}#${fn}@${options.debounce || 0}`;
|
|
399
|
+
return this;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
class Text {
|
|
404
|
+
constructor(value) {
|
|
405
|
+
this.value = value;
|
|
406
|
+
}
|
|
407
|
+
get render() {
|
|
408
|
+
return this.value;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
exports.BlockQuote = BlockQuote;
|
|
413
|
+
exports.Box = Box;
|
|
414
|
+
exports.Button = Button;
|
|
415
|
+
exports.Checkbox = Checkbox;
|
|
416
|
+
exports.Code = Code;
|
|
417
|
+
exports.Component = Component;
|
|
418
|
+
exports.DateTimeField = DateTimeField;
|
|
419
|
+
exports.Element = Element;
|
|
420
|
+
exports.Embed = Embed;
|
|
421
|
+
exports.Emphasis = Emphasis;
|
|
422
|
+
exports.Form = Form;
|
|
423
|
+
exports.Heading = Heading;
|
|
424
|
+
exports.Image = Image;
|
|
425
|
+
exports.Link = Link;
|
|
426
|
+
exports.List = List;
|
|
427
|
+
exports.ListItem = ListItem;
|
|
428
|
+
exports.OrderedList = OrderedList;
|
|
429
|
+
exports.Paragraph = Paragraph;
|
|
430
|
+
exports.Quote = Quote;
|
|
431
|
+
exports.RadioButton = RadioButton;
|
|
432
|
+
exports.Select = Select;
|
|
433
|
+
exports.Span = Span;
|
|
434
|
+
exports.Text = Text;
|
|
435
|
+
exports.TextField = TextField;
|
|
436
|
+
|
|
437
|
+
return exports;
|
|
438
|
+
|
|
439
|
+
})({});
|