clapton 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) 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 +14 -10
  5. data/lib/clapton/javascripts/dist/client.js +31 -19
  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/initialize-actions.ts +6 -3
  55. data/lib/clapton/javascripts/src/channel/clapton-channel.js +6 -3
  56. data/lib/clapton/javascripts/src/client.ts +15 -15
  57. data/lib/clapton/javascripts/src/components-for-test.ts +29 -0
  58. data/lib/clapton/javascripts/src/components.ts +4 -1
  59. data/lib/clapton/javascripts/src/dom/update-component.ts +3 -2
  60. data/lib/clapton/javascripts/src/inputs/initialize-inputs.ts +2 -2
  61. data/lib/clapton/test_helper/base.rb +1 -1
  62. data/lib/clapton/version.rb +1 -1
  63. metadata +49 -3
  64. 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: 7def55e21b26dedfd8843c9ace0bf4db854abf18205de4dea7c3a203d06130f6
4
+ data.tar.gz: 743f8549cd8a901f2f1c76a4807f05ccace77d58e9665ad35c02626df555a08a
5
5
  SHA512:
6
- metadata.gz: ac72a228524042ed8748391c92076d3f05707b8f0763485ea7316f4b27e6c2a11c29643e4a6f43fc351bfffeb757dfd633afdcc710b81487f6a8754567a7d308
7
- data.tar.gz: c5f4c34dbfc32e4ad8dd0bff57f160568cc98875f9b918c90275aa8abe39aa54dc9a1dddb8ed58eb80c380b45ec3476c78a0afb9de0630e4f65d3d0bfb5a6f66
6
+ metadata.gz: b05ba6c72f46212b628cee66c18e01644e285e6380862669eb7de55174338b4b9109ebf69505d398227c85f0a19a6e55580e6cf760951fad573bc13d78c73845
7
+ data.tar.gz: 2359db5b15a7ef59f43dc61d2bcb96609858cc0a19c996e9b18288046fe739a3d68bf5868f221c97c349af8781ff8483c979199e3c1c27914f617e8912555aba
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
@@ -32,20 +32,24 @@ module Clapton
32
32
  end
33
33
 
34
34
  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"
35
+ FileUtils.mkdir_p(Rails.root.join("public", "clapton")) unless Rails.root.join("public", "clapton").exist?
36
+ File.write(Rails.root.join("public", "clapton", "components.js"), File.read(File.join(__dir__, "javascripts", "dist", "components.js")))
37
+ File.write(Rails.root.join("public", "clapton", "client.js"), File.read(File.join(__dir__, "javascripts", "dist", "client.js")))
41
38
  Dir.glob(Rails.root.join("app", "components", "**", "*.rb")).each do |file|
42
- js += Ruby2JS.convert(File.read(file), preset: true)
39
+ code = File.read(file)
40
+ js = ""
41
+ js += "import { Clapton } from 'components';"
42
+ js += "\n"
43
+ code.scan(/(\w+)Component\.new/).each do |match|
44
+ js += "import { #{match[0]}Component } from '#{match[0]}Component';"
45
+ js += "\n"
46
+ end
47
+ js += Ruby2JS.convert(code, preset: true)
43
48
  js += "\n"
44
- js += "window.#{File.basename(file, ".rb").camelize} = #{File.basename(file, ".rb").camelize};"
49
+ js += "export { #{File.basename(file, ".rb").camelize} };"
45
50
  js += "\n"
51
+ File.write(Rails.root.join("public", "clapton", "#{File.basename(file, ".rb").camelize}.js"), js)
46
52
  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)
49
53
  end
50
54
  end
51
55
  end
@@ -1285,11 +1285,12 @@ 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
1290
  component.setAttribute("data-state", JSON.stringify(state));
1291
1291
  const componentName = component.getAttribute("data-component");
1292
- const ComponentClass = window[componentName];
1292
+ const module = await import(`${componentName}`);
1293
+ const ComponentClass = module[componentName];
1293
1294
  const instance = new ComponentClass(state, component.dataset.id);
1294
1295
  morphdom(component, instance.render);
1295
1296
  };
@@ -1303,8 +1304,8 @@ const initializeInputs = () => {
1303
1304
  if (!attribute || !component)
1304
1305
  return;
1305
1306
  if (element.tagName === "INPUT") {
1306
- element.addEventListener("input", (event) => {
1307
- updateComponent(component, state, attribute, event.target);
1307
+ element.addEventListener("input", async (event) => {
1308
+ await updateComponent(component, state, attribute, event.target);
1308
1309
  });
1309
1310
  }
1310
1311
  });
@@ -1313,14 +1314,17 @@ const initializeInputs = () => {
1313
1314
  const consumer = createConsumer();
1314
1315
 
1315
1316
  const claptonChannel = consumer.subscriptions.create("Clapton::ClaptonChannel", {
1316
- connected() {},
1317
+ connected() {
1318
+ window.actionCableConnected = true;
1319
+ },
1317
1320
 
1318
1321
  disconnected() {},
1319
1322
 
1320
- received(response) {
1323
+ async received(response) {
1321
1324
  const { data, errors } = response;
1322
1325
  const component = document.querySelector(`[data-id="${data.component.id}"]`);
1323
- const instance = new window[data.component.name](data.state, data.component.id, errors);
1326
+ const module = await import(`${data.component.name}`);
1327
+ const instance = new module[data.component.name](data.state, data.component.id, errors);
1324
1328
  morphdom(component, instance.render, {
1325
1329
  onBeforeElUpdated: (_fromEl, toEl) => {
1326
1330
  toEl.setAttribute("data-set-event-handler", "true");
@@ -1389,9 +1393,12 @@ const initializeActionsForElement = (element) => {
1389
1393
  if (!eventType || !componentName || !fnName)
1390
1394
  return;
1391
1395
  if (eventType === "render") {
1392
- setTimeout(() => {
1393
- handleAction(element, stateName, fnName);
1394
- }, 100);
1396
+ const interval = setInterval(() => {
1397
+ if (window.actionCableConnected === true) {
1398
+ handleAction(element, stateName, fnName);
1399
+ clearInterval(interval);
1400
+ }
1401
+ }, 10);
1395
1402
  element.setAttribute("data-render-event-handler", "true");
1396
1403
  return;
1397
1404
  }
@@ -1405,25 +1412,30 @@ const initializeActionsForElement = (element) => {
1405
1412
  });
1406
1413
  };
1407
1414
 
1408
- const initializeComponents = () => {
1415
+ const initializeComponents = async () => {
1409
1416
  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) => {
1417
+ const componentArray = JSON.parse(components);
1418
+ for (const component of componentArray) {
1419
+ await createAndAppendComponent(component, document.querySelector("#clapton"));
1420
+ }
1421
+ const elements = document.querySelectorAll(".clapton-component");
1422
+ for (const element of elements) {
1412
1423
  const component = JSON.parse(element.getAttribute("data-clapton") || "{}");
1413
- createAndAppendComponent(component, element);
1414
- });
1424
+ await createAndAppendComponent(component, element);
1425
+ }
1415
1426
  };
1416
- const createAndAppendComponent = (component, element) => {
1427
+ const createAndAppendComponent = async (component, element) => {
1417
1428
  const componentDom = document.createElement('div');
1418
- const instance = new window[component.component](component.state);
1429
+ const module = await import(`${component.component}`);
1430
+ const instance = new module[component.component](component.state);
1419
1431
  componentDom.innerHTML = instance.render;
1420
1432
  const firstChild = componentDom.firstChild;
1421
1433
  if (firstChild) {
1422
1434
  element.appendChild(firstChild);
1423
1435
  }
1424
1436
  };
1425
- document.addEventListener("DOMContentLoaded", () => {
1426
- initializeComponents();
1437
+ document.addEventListener("DOMContentLoaded", async () => {
1438
+ await initializeComponents();
1427
1439
  initializeActions();
1428
1440
  initializeInputs();
1429
1441
  });
@@ -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
+ })({});