clapton 0.0.12 → 0.0.14
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 +33 -0
- data/app/helpers/clapton/clapton_helper.rb +16 -1
- data/lib/clapton/engine.rb +16 -10
- data/lib/clapton/javascripts/dist/client.js +65 -39
- data/lib/clapton/javascripts/dist/components-for-test.js +439 -0
- data/lib/clapton/javascripts/dist/components.js +357 -369
- 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 -1
- data/lib/clapton/javascripts/src/actions/initialize-actions.ts +11 -0
- data/lib/clapton/javascripts/src/channel/clapton-channel.js +8 -4
- data/lib/clapton/javascripts/src/client.ts +15 -18
- data/lib/clapton/javascripts/src/components/box.ts +5 -0
- data/lib/clapton/javascripts/src/components/embed.spec.ts +8 -0
- data/lib/clapton/javascripts/src/components/embed.ts +11 -0
- data/lib/clapton/javascripts/src/components-for-test.ts +29 -0
- data/lib/clapton/javascripts/src/components.ts +5 -1
- data/lib/clapton/javascripts/src/dom/update-component.ts +3 -2
- data/lib/clapton/javascripts/src/inputs/initialize-inputs.ts +3 -3
- data/lib/clapton/test_helper/base.rb +1 -1
- data/lib/clapton/version.rb +2 -1
- metadata +51 -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: 7def55e21b26dedfd8843c9ace0bf4db854abf18205de4dea7c3a203d06130f6
|
|
4
|
+
data.tar.gz: 743f8549cd8a901f2f1c76a4807f05ccace77d58e9665ad35c02626df555a08a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
|
@@ -159,6 +160,23 @@ After running the generator, you will see the following files:
|
|
|
159
160
|
- `app/components/task_list_component.rb`
|
|
160
161
|
- `app/states/task_list_state.rb`
|
|
161
162
|
|
|
163
|
+
### Special Event
|
|
164
|
+
|
|
165
|
+
#### render
|
|
166
|
+
|
|
167
|
+
The `render` event is a special event that is triggered when the component is rendered.
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
# app/components/task_list_component.rb
|
|
171
|
+
class TaskListComponent < Clapton::Component
|
|
172
|
+
def render
|
|
173
|
+
# ...
|
|
174
|
+
@root.add_action(:render, :TaskListState, :add_empty_task, debounce: 500)
|
|
175
|
+
@root.render
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
```
|
|
179
|
+
|
|
162
180
|
### Preset Components
|
|
163
181
|
|
|
164
182
|
```ruby
|
|
@@ -183,6 +201,8 @@ datetime_field = Clapton::DateTimeField.new(:ExampleState, :example_attribute, {
|
|
|
183
201
|
element = Clapton::Element.new("div", { id: "example-element" })
|
|
184
202
|
element.add(Clapton::Text.new("Hello"))
|
|
185
203
|
|
|
204
|
+
embed = Clapton::Embed.new("<blockquote>This is a test</blockquote>")
|
|
205
|
+
|
|
186
206
|
emphasis = Clapton::Emphasis.new
|
|
187
207
|
emphasis.add(Clapton::Text.new("Hello"))
|
|
188
208
|
|
|
@@ -310,6 +330,19 @@ class TaskListComponentTest < ActiveSupport::TestCase
|
|
|
310
330
|
end
|
|
311
331
|
```
|
|
312
332
|
|
|
333
|
+
## Deployment
|
|
334
|
+
|
|
335
|
+
Run `bundle exec rake clapton:compile` to compile the components.
|
|
336
|
+
|
|
337
|
+
`app/components` is codes that are compiled to JavaScript.
|
|
338
|
+
So, you need to ignore the directory from autoloading.
|
|
339
|
+
|
|
340
|
+
```ruby
|
|
341
|
+
# config/application.rb
|
|
342
|
+
|
|
343
|
+
Rails.autoloaders.main.ignore(Rails.root.join("app/components"))
|
|
344
|
+
```
|
|
345
|
+
|
|
313
346
|
## Development
|
|
314
347
|
|
|
315
348
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/dev` to start the development server.
|
|
@@ -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,8 @@ module Clapton
|
|
|
21
21
|
FileUtils.mkdir_p(components_path) unless components_path.exist?
|
|
22
22
|
FileUtils.touch(components_path.join(".keep"))
|
|
23
23
|
|
|
24
|
+
compile_components
|
|
25
|
+
|
|
24
26
|
listener = Listen.to(Rails.root.join("app", "components")) do |modified, added, removed|
|
|
25
27
|
compile_components
|
|
26
28
|
end
|
|
@@ -30,20 +32,24 @@ module Clapton
|
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def compile_components
|
|
33
|
-
|
|
34
|
-
js
|
|
35
|
-
js
|
|
36
|
-
js += "\n"
|
|
37
|
-
js += "window.components = [];"
|
|
38
|
-
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")))
|
|
39
38
|
Dir.glob(Rails.root.join("app", "components", "**", "*.rb")).each do |file|
|
|
40
|
-
|
|
39
|
+
code = File.read(file)
|
|
40
|
+
js = ""
|
|
41
|
+
js += "import { Clapton } from 'components';"
|
|
41
42
|
js += "\n"
|
|
42
|
-
|
|
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"
|
|
49
|
+
js += "export { #{File.basename(file, ".rb").camelize} };"
|
|
50
|
+
js += "\n"
|
|
51
|
+
File.write(Rails.root.join("public", "clapton", "#{File.basename(file, ".rb").camelize}.js"), js)
|
|
44
52
|
end
|
|
45
|
-
FileUtils.mkdir_p(Rails.root.join("public", "clapton")) unless Rails.root.join("public", "clapton").exist?
|
|
46
|
-
File.write(Rails.root.join("public", "clapton", "index.js"), js)
|
|
47
53
|
end
|
|
48
54
|
end
|
|
49
55
|
end
|
|
@@ -1285,30 +1285,66 @@ function getConfig(name) {
|
|
|
1285
1285
|
}
|
|
1286
1286
|
}
|
|
1287
1287
|
|
|
1288
|
+
const updateComponent = async (component, state, property, target) => {
|
|
1289
|
+
state[property] = target.value;
|
|
1290
|
+
component.setAttribute("data-state", JSON.stringify(state));
|
|
1291
|
+
const componentName = component.getAttribute("data-component");
|
|
1292
|
+
const module = await import(`${componentName}`);
|
|
1293
|
+
const ComponentClass = module[componentName];
|
|
1294
|
+
const instance = new ComponentClass(state, component.dataset.id);
|
|
1295
|
+
morphdom(component, instance.render);
|
|
1296
|
+
};
|
|
1297
|
+
|
|
1298
|
+
const initializeInputs = () => {
|
|
1299
|
+
const inputElements = document.querySelectorAll("[data-attribute]");
|
|
1300
|
+
inputElements.forEach((element) => {
|
|
1301
|
+
const attribute = element.getAttribute("data-attribute");
|
|
1302
|
+
const component = element.closest(`[data-component]`);
|
|
1303
|
+
const state = JSON.parse(component.getAttribute("data-state") || "{}");
|
|
1304
|
+
if (!attribute || !component)
|
|
1305
|
+
return;
|
|
1306
|
+
if (element.tagName === "INPUT") {
|
|
1307
|
+
element.addEventListener("input", async (event) => {
|
|
1308
|
+
await updateComponent(component, state, attribute, event.target);
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
};
|
|
1313
|
+
|
|
1288
1314
|
const consumer = createConsumer();
|
|
1289
1315
|
|
|
1290
1316
|
const claptonChannel = consumer.subscriptions.create("Clapton::ClaptonChannel", {
|
|
1291
|
-
connected() {
|
|
1317
|
+
connected() {
|
|
1318
|
+
window.actionCableConnected = true;
|
|
1319
|
+
},
|
|
1292
1320
|
|
|
1293
1321
|
disconnected() {},
|
|
1294
1322
|
|
|
1295
|
-
received(response) {
|
|
1323
|
+
async received(response) {
|
|
1296
1324
|
const { data, errors } = response;
|
|
1297
1325
|
const component = document.querySelector(`[data-id="${data.component.id}"]`);
|
|
1298
|
-
const
|
|
1326
|
+
const module = await import(`${data.component.name}`);
|
|
1327
|
+
const instance = new module[data.component.name](data.state, data.component.id, errors);
|
|
1299
1328
|
morphdom(component, instance.render, {
|
|
1300
1329
|
onBeforeElUpdated: (_fromEl, toEl) => {
|
|
1301
1330
|
toEl.setAttribute("data-set-event-handler", "true");
|
|
1302
1331
|
return true;
|
|
1303
1332
|
}
|
|
1304
1333
|
});
|
|
1334
|
+
|
|
1305
1335
|
initializeInputs();
|
|
1306
1336
|
initializeActions();
|
|
1307
1337
|
}
|
|
1308
1338
|
});
|
|
1309
1339
|
|
|
1310
1340
|
const handleAction = async (target, stateName, fn) => {
|
|
1311
|
-
|
|
1341
|
+
let targetComponent = target;
|
|
1342
|
+
if (target.dataset.component === stateName.replace("State", "Component")) {
|
|
1343
|
+
targetComponent = target;
|
|
1344
|
+
}
|
|
1345
|
+
else {
|
|
1346
|
+
targetComponent = target.closest(`[data-component="${stateName.replace("State", "Component")}"]`);
|
|
1347
|
+
}
|
|
1312
1348
|
if (!targetComponent)
|
|
1313
1349
|
return;
|
|
1314
1350
|
const component = target.closest(`[data-component]`);
|
|
@@ -1356,6 +1392,16 @@ const initializeActionsForElement = (element) => {
|
|
|
1356
1392
|
const { eventType, componentName, stateName, fnName, bounceTime } = splitActionAttribute(action);
|
|
1357
1393
|
if (!eventType || !componentName || !fnName)
|
|
1358
1394
|
return;
|
|
1395
|
+
if (eventType === "render") {
|
|
1396
|
+
const interval = setInterval(() => {
|
|
1397
|
+
if (window.actionCableConnected === true) {
|
|
1398
|
+
handleAction(element, stateName, fnName);
|
|
1399
|
+
clearInterval(interval);
|
|
1400
|
+
}
|
|
1401
|
+
}, 10);
|
|
1402
|
+
element.setAttribute("data-render-event-handler", "true");
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1359
1405
|
if (bounceTime > 0) {
|
|
1360
1406
|
element.addEventListener(eventType, debounce((event) => handleAction(event.target, stateName, fnName), bounceTime));
|
|
1361
1407
|
}
|
|
@@ -1366,50 +1412,30 @@ const initializeActionsForElement = (element) => {
|
|
|
1366
1412
|
});
|
|
1367
1413
|
};
|
|
1368
1414
|
|
|
1369
|
-
const
|
|
1370
|
-
state[property] = target.value;
|
|
1371
|
-
component.setAttribute("data-state", JSON.stringify(state));
|
|
1372
|
-
const componentName = component.getAttribute("data-component");
|
|
1373
|
-
const ComponentClass = window[componentName];
|
|
1374
|
-
const instance = new ComponentClass(state, component.dataset.id);
|
|
1375
|
-
morphdom(component, instance.render);
|
|
1376
|
-
};
|
|
1377
|
-
|
|
1378
|
-
const initializeInputs$1 = () => {
|
|
1379
|
-
const inputElements = document.querySelectorAll("[data-attribute]");
|
|
1380
|
-
inputElements.forEach((element) => {
|
|
1381
|
-
const attribute = element.getAttribute("data-attribute");
|
|
1382
|
-
const component = element.closest(`[data-component]`);
|
|
1383
|
-
const state = JSON.parse(component.getAttribute("data-state") || "{}");
|
|
1384
|
-
if (!attribute || !component)
|
|
1385
|
-
return;
|
|
1386
|
-
if (element.tagName === "INPUT") {
|
|
1387
|
-
element.addEventListener("input", (event) => {
|
|
1388
|
-
updateComponent(component, state, attribute, event.target);
|
|
1389
|
-
});
|
|
1390
|
-
}
|
|
1391
|
-
});
|
|
1392
|
-
};
|
|
1393
|
-
|
|
1394
|
-
const initializeComponents = () => {
|
|
1415
|
+
const initializeComponents = async () => {
|
|
1395
1416
|
const components = document.querySelector("#clapton")?.getAttribute("data-clapton") || "[]";
|
|
1396
|
-
JSON.parse(components)
|
|
1397
|
-
|
|
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) {
|
|
1398
1423
|
const component = JSON.parse(element.getAttribute("data-clapton") || "{}");
|
|
1399
|
-
createAndAppendComponent(component, element);
|
|
1400
|
-
}
|
|
1424
|
+
await createAndAppendComponent(component, element);
|
|
1425
|
+
}
|
|
1401
1426
|
};
|
|
1402
|
-
const createAndAppendComponent = (component, element) => {
|
|
1427
|
+
const createAndAppendComponent = async (component, element) => {
|
|
1403
1428
|
const componentDom = document.createElement('div');
|
|
1404
|
-
const
|
|
1429
|
+
const module = await import(`${component.component}`);
|
|
1430
|
+
const instance = new module[component.component](component.state);
|
|
1405
1431
|
componentDom.innerHTML = instance.render;
|
|
1406
1432
|
const firstChild = componentDom.firstChild;
|
|
1407
1433
|
if (firstChild) {
|
|
1408
1434
|
element.appendChild(firstChild);
|
|
1409
1435
|
}
|
|
1410
1436
|
};
|
|
1411
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
1412
|
-
initializeComponents();
|
|
1437
|
+
document.addEventListener("DOMContentLoaded", async () => {
|
|
1438
|
+
await initializeComponents();
|
|
1413
1439
|
initializeActions();
|
|
1414
|
-
initializeInputs
|
|
1440
|
+
initializeInputs();
|
|
1415
1441
|
});
|