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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/app/helpers/clapton/clapton_helper.rb +16 -1
  4. data/lib/clapton/engine.rb +20 -10
  5. data/lib/clapton/javascripts/dist/client.js +38 -27
  6. data/lib/clapton/javascripts/dist/components-for-test.js +439 -0
  7. data/lib/clapton/javascripts/dist/components.js +356 -382
  8. data/lib/clapton/javascripts/node_modules/diff-dom/LICENSE.txt +165 -0
  9. data/lib/clapton/javascripts/node_modules/diff-dom/README.md +224 -0
  10. data/lib/clapton/javascripts/node_modules/diff-dom/browser/diffDOM.js +2 -0
  11. data/lib/clapton/javascripts/node_modules/diff-dom/browser/diffDOM.js.map +1 -0
  12. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/TraceLogger.d.ts +28 -0
  13. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/apply.d.ts +4 -0
  14. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/fromVirtual.d.ts +2 -0
  15. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/index.d.ts +2 -0
  16. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/undo.d.ts +3 -0
  17. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/helpers.d.ts +11 -0
  18. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/index.d.ts +10 -0
  19. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/types.d.ts +104 -0
  20. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/apply.d.ts +3 -0
  21. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/diff.d.ts +22 -0
  22. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/fromDOM.d.ts +2 -0
  23. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/fromString.d.ts +2 -0
  24. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/helpers.d.ts +40 -0
  25. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/index.d.ts +3 -0
  26. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/index.d.ts +2 -0
  27. data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.d.ts +136 -0
  28. data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.js +1996 -0
  29. data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.js.map +1 -0
  30. data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.min.js +2 -0
  31. data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.min.js.map +1 -0
  32. data/lib/clapton/javascripts/node_modules/diff-dom/dist/module.js +1991 -0
  33. data/lib/clapton/javascripts/node_modules/diff-dom/dist/module.js.map +1 -0
  34. data/lib/clapton/javascripts/node_modules/diff-dom/index.html +62 -0
  35. data/lib/clapton/javascripts/node_modules/diff-dom/package.json +54 -0
  36. data/lib/clapton/javascripts/node_modules/diff-dom/rollup.config.mjs +67 -0
  37. data/lib/clapton/javascripts/node_modules/diff-dom/src/TraceLogger.ts +143 -0
  38. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/apply.ts +227 -0
  39. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/fromVirtual.ts +83 -0
  40. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/index.ts +2 -0
  41. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/undo.ts +90 -0
  42. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/helpers.ts +40 -0
  43. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/index.ts +121 -0
  44. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/types.ts +154 -0
  45. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/apply.ts +349 -0
  46. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/diff.ts +855 -0
  47. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/fromDOM.ts +74 -0
  48. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/fromString.ts +239 -0
  49. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/helpers.ts +461 -0
  50. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/index.ts +3 -0
  51. data/lib/clapton/javascripts/node_modules/diff-dom/src/index.ts +2 -0
  52. data/lib/clapton/javascripts/node_modules/diff-dom/tsconfig.json +103 -0
  53. data/lib/clapton/javascripts/rollup.config.mjs +17 -2
  54. data/lib/clapton/javascripts/src/actions/handle-action.ts +6 -6
  55. data/lib/clapton/javascripts/src/actions/initialize-actions.ts +6 -3
  56. data/lib/clapton/javascripts/src/channel/clapton-channel.js +6 -3
  57. data/lib/clapton/javascripts/src/client.ts +15 -15
  58. data/lib/clapton/javascripts/src/components-for-test.ts +29 -0
  59. data/lib/clapton/javascripts/src/components.ts +4 -1
  60. data/lib/clapton/javascripts/src/dom/update-component.ts +4 -4
  61. data/lib/clapton/javascripts/src/inputs/initialize-inputs.ts +2 -2
  62. data/lib/clapton/test_helper/base.rb +1 -1
  63. data/lib/clapton/version.rb +1 -2
  64. metadata +49 -3
  65. 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: e238016cc8650d4f218c6a542329d5fcbbe844dd68124f2464063df016f25c1f
4
- data.tar.gz: 36ae66989cf010520398499d54f007dbebb7deb6f91d37a578677eb8555bb53c
3
+ metadata.gz: 31438028bfe58d5ce7e560543850167002e53f148784072f20834081c0421b34
4
+ data.tar.gz: 2509467d610325dbaf0c1a20a953e50ecd568b8722d1b2fd7a1bc1acbb1bd6aa
5
5
  SHA512:
6
- metadata.gz: ac72a228524042ed8748391c92076d3f05707b8f0763485ea7316f4b27e6c2a11c29643e4a6f43fc351bfffeb757dfd633afdcc710b81487f6a8754567a7d308
7
- data.tar.gz: c5f4c34dbfc32e4ad8dd0bff57f160568cc98875f9b918c90275aa8abe39aa54dc9a1dddb8ed58eb80c380b45ec3476c78a0afb9de0630e4f65d3d0bfb5a6f66
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
- tag.script(src: "/clapton/index.js", type: "text/javascript")
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
@@ -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
- js = File.read(File.join(__dir__, "javascripts", "dist", "components.js"))
36
- js += "\n"
37
- js += File.read(File.join(__dir__, "javascripts", "dist", "client.js"))
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
- js += Ruby2JS.convert(File.read(file), preset: true)
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 += "window.#{File.basename(file, ".rb").camelize} = #{File.basename(file, ".rb").camelize};"
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
- FileUtils.mkdir_p(Rails.root.join("public", "clapton")) unless Rails.root.join("public", "clapton").exist?
48
- File.write(Rails.root.join("public", "clapton", "index.js"), js)
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.setAttribute("data-state", JSON.stringify(state));
1291
- const componentName = component.getAttribute("data-component");
1292
- const ComponentClass = window[componentName];
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 instance = new window[data.component.name](data.state, data.component.id, errors);
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.getAttribute("data-attribute");
1350
+ const attribute = target.dataset.attribute;
1348
1351
  if (attribute) {
1349
- const state = JSON.parse(component.getAttribute("data-state") || "{}");
1352
+ const state = JSON.parse(component.dataset.state || "{}");
1350
1353
  if (target.tagName === "INPUT") {
1351
1354
  state[attribute] = target.value;
1352
- component.setAttribute("data-state", JSON.stringify(state));
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.getAttribute("data-id"),
1362
+ id: targetComponent.dataset.id,
1360
1363
  },
1361
1364
  state: {
1362
1365
  name: stateName,
1363
1366
  action: fn,
1364
- attributes: JSON.parse(targetComponent.getAttribute("data-state") || "{}"),
1367
+ attributes: JSON.parse(targetComponent.dataset.state || "{}"),
1365
1368
  },
1366
- params: JSON.parse(component.getAttribute("data-state") || "{}")
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
- setTimeout(() => {
1393
- handleAction(element, stateName, fnName);
1394
- }, 100);
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).forEach((component) => createAndAppendComponent(component, document.querySelector("#clapton")));
1411
- document.querySelectorAll(".clapton-component").forEach((element) => {
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 instance = new window[component.component](component.state);
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, "&amp;")
40
+ .replace(/</g, "&lt;")
41
+ .replace(/>/g, "&gt;")
42
+ .replace(/"/g, "&quot;")
43
+ .replace(/'/g, "&#039;");
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
+ })({});