clapton 0.0.12 → 0.0.13
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 +32 -0
- data/lib/clapton/engine.rb +2 -0
- data/lib/clapton/javascripts/dist/client.js +41 -27
- data/lib/clapton/javascripts/dist/components.js +14 -0
- data/lib/clapton/javascripts/src/actions/handle-action.ts +6 -1
- data/lib/clapton/javascripts/src/actions/initialize-actions.ts +8 -0
- data/lib/clapton/javascripts/src/channel/clapton-channel.js +2 -1
- data/lib/clapton/javascripts/src/client.ts +0 -3
- 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.ts +2 -1
- data/lib/clapton/javascripts/src/inputs/initialize-inputs.ts +1 -1
- data/lib/clapton/version.rb +2 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e238016cc8650d4f218c6a542329d5fcbbe844dd68124f2464063df016f25c1f
|
4
|
+
data.tar.gz: 36ae66989cf010520398499d54f007dbebb7deb6f91d37a578677eb8555bb53c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac72a228524042ed8748391c92076d3f05707b8f0763485ea7316f4b27e6c2a11c29643e4a6f43fc351bfffeb757dfd633afdcc710b81487f6a8754567a7d308
|
7
|
+
data.tar.gz: c5f4c34dbfc32e4ad8dd0bff57f160568cc98875f9b918c90275aa8abe39aa54dc9a1dddb8ed58eb80c380b45ec3476c78a0afb9de0630e4f65d3d0bfb5a6f66
|
data/README.md
CHANGED
@@ -159,6 +159,23 @@ After running the generator, you will see the following files:
|
|
159
159
|
- `app/components/task_list_component.rb`
|
160
160
|
- `app/states/task_list_state.rb`
|
161
161
|
|
162
|
+
### Special Event
|
163
|
+
|
164
|
+
#### render
|
165
|
+
|
166
|
+
The `render` event is a special event that is triggered when the component is rendered.
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
# app/components/task_list_component.rb
|
170
|
+
class TaskListComponent < Clapton::Component
|
171
|
+
def render
|
172
|
+
# ...
|
173
|
+
@root.add_action(:render, :TaskListState, :add_empty_task, debounce: 500)
|
174
|
+
@root.render
|
175
|
+
end
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
162
179
|
### Preset Components
|
163
180
|
|
164
181
|
```ruby
|
@@ -183,6 +200,8 @@ datetime_field = Clapton::DateTimeField.new(:ExampleState, :example_attribute, {
|
|
183
200
|
element = Clapton::Element.new("div", { id: "example-element" })
|
184
201
|
element.add(Clapton::Text.new("Hello"))
|
185
202
|
|
203
|
+
embed = Clapton::Embed.new("<blockquote>This is a test</blockquote>")
|
204
|
+
|
186
205
|
emphasis = Clapton::Emphasis.new
|
187
206
|
emphasis.add(Clapton::Text.new("Hello"))
|
188
207
|
|
@@ -310,6 +329,19 @@ class TaskListComponentTest < ActiveSupport::TestCase
|
|
310
329
|
end
|
311
330
|
```
|
312
331
|
|
332
|
+
## Deployment
|
333
|
+
|
334
|
+
Run `bundle exec rake clapton:compile` to compile the components.
|
335
|
+
|
336
|
+
`app/components` is codes that are compiled to JavaScript.
|
337
|
+
So, you need to ignore the directory from autoloading.
|
338
|
+
|
339
|
+
```ruby
|
340
|
+
# config/application.rb
|
341
|
+
|
342
|
+
Rails.autoloaders.main.ignore(Rails.root.join("app/components"))
|
343
|
+
```
|
344
|
+
|
313
345
|
## Development
|
314
346
|
|
315
347
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/dev` to start the development server.
|
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
|
@@ -1285,6 +1285,31 @@ function getConfig(name) {
|
|
1285
1285
|
}
|
1286
1286
|
}
|
1287
1287
|
|
1288
|
+
const updateComponent = (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 ComponentClass = window[componentName];
|
1293
|
+
const instance = new ComponentClass(state, component.dataset.id);
|
1294
|
+
morphdom(component, instance.render);
|
1295
|
+
};
|
1296
|
+
|
1297
|
+
const initializeInputs = () => {
|
1298
|
+
const inputElements = document.querySelectorAll("[data-attribute]");
|
1299
|
+
inputElements.forEach((element) => {
|
1300
|
+
const attribute = element.getAttribute("data-attribute");
|
1301
|
+
const component = element.closest(`[data-component]`);
|
1302
|
+
const state = JSON.parse(component.getAttribute("data-state") || "{}");
|
1303
|
+
if (!attribute || !component)
|
1304
|
+
return;
|
1305
|
+
if (element.tagName === "INPUT") {
|
1306
|
+
element.addEventListener("input", (event) => {
|
1307
|
+
updateComponent(component, state, attribute, event.target);
|
1308
|
+
});
|
1309
|
+
}
|
1310
|
+
});
|
1311
|
+
};
|
1312
|
+
|
1288
1313
|
const consumer = createConsumer();
|
1289
1314
|
|
1290
1315
|
const claptonChannel = consumer.subscriptions.create("Clapton::ClaptonChannel", {
|
@@ -1302,13 +1327,20 @@ const claptonChannel = consumer.subscriptions.create("Clapton::ClaptonChannel",
|
|
1302
1327
|
return true;
|
1303
1328
|
}
|
1304
1329
|
});
|
1330
|
+
|
1305
1331
|
initializeInputs();
|
1306
1332
|
initializeActions();
|
1307
1333
|
}
|
1308
1334
|
});
|
1309
1335
|
|
1310
1336
|
const handleAction = async (target, stateName, fn) => {
|
1311
|
-
|
1337
|
+
let targetComponent = target;
|
1338
|
+
if (target.dataset.component === stateName.replace("State", "Component")) {
|
1339
|
+
targetComponent = target;
|
1340
|
+
}
|
1341
|
+
else {
|
1342
|
+
targetComponent = target.closest(`[data-component="${stateName.replace("State", "Component")}"]`);
|
1343
|
+
}
|
1312
1344
|
if (!targetComponent)
|
1313
1345
|
return;
|
1314
1346
|
const component = target.closest(`[data-component]`);
|
@@ -1356,6 +1388,13 @@ const initializeActionsForElement = (element) => {
|
|
1356
1388
|
const { eventType, componentName, stateName, fnName, bounceTime } = splitActionAttribute(action);
|
1357
1389
|
if (!eventType || !componentName || !fnName)
|
1358
1390
|
return;
|
1391
|
+
if (eventType === "render") {
|
1392
|
+
setTimeout(() => {
|
1393
|
+
handleAction(element, stateName, fnName);
|
1394
|
+
}, 100);
|
1395
|
+
element.setAttribute("data-render-event-handler", "true");
|
1396
|
+
return;
|
1397
|
+
}
|
1359
1398
|
if (bounceTime > 0) {
|
1360
1399
|
element.addEventListener(eventType, debounce((event) => handleAction(event.target, stateName, fnName), bounceTime));
|
1361
1400
|
}
|
@@ -1366,31 +1405,6 @@ const initializeActionsForElement = (element) => {
|
|
1366
1405
|
});
|
1367
1406
|
};
|
1368
1407
|
|
1369
|
-
const updateComponent = (component, state, property, target) => {
|
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
1408
|
const initializeComponents = () => {
|
1395
1409
|
const components = document.querySelector("#clapton")?.getAttribute("data-clapton") || "[]";
|
1396
1410
|
JSON.parse(components).forEach((component) => createAndAppendComponent(component, document.querySelector("#clapton")));
|
@@ -1411,5 +1425,5 @@ const createAndAppendComponent = (component, element) => {
|
|
1411
1425
|
document.addEventListener("DOMContentLoaded", () => {
|
1412
1426
|
initializeComponents();
|
1413
1427
|
initializeActions();
|
1414
|
-
initializeInputs
|
1428
|
+
initializeInputs();
|
1415
1429
|
});
|
@@ -69,6 +69,10 @@ var Clapton = (function (exports) {
|
|
69
69
|
get render() {
|
70
70
|
return `<div ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</div>`;
|
71
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
|
+
}
|
72
76
|
}
|
73
77
|
|
74
78
|
class Button {
|
@@ -176,6 +180,15 @@ var Clapton = (function (exports) {
|
|
176
180
|
}
|
177
181
|
}
|
178
182
|
|
183
|
+
class Embed {
|
184
|
+
constructor(html) {
|
185
|
+
this.html = html;
|
186
|
+
}
|
187
|
+
get render() {
|
188
|
+
return this.html;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
179
192
|
class Emphasis {
|
180
193
|
constructor(attributes = {}) {
|
181
194
|
this.children = [];
|
@@ -404,6 +417,7 @@ var Clapton = (function (exports) {
|
|
404
417
|
exports.Component = Component;
|
405
418
|
exports.DateTimeField = DateTimeField;
|
406
419
|
exports.Element = Element;
|
420
|
+
exports.Embed = Embed;
|
407
421
|
exports.Emphasis = Emphasis;
|
408
422
|
exports.Form = Form;
|
409
423
|
exports.Heading = Heading;
|
@@ -1,7 +1,12 @@
|
|
1
1
|
import { claptonChannel } from "../channel/clapton-channel";
|
2
2
|
|
3
3
|
export const handleAction = async (target: HTMLElement, stateName: string, fn: string) => {
|
4
|
-
|
4
|
+
let targetComponent = target;
|
5
|
+
if (target.dataset.component === stateName.replace("State", "Component")) {
|
6
|
+
targetComponent = target
|
7
|
+
} else {
|
8
|
+
targetComponent = target.closest(`[data-component="${stateName.replace("State", "Component")}"]`) as HTMLElement;
|
9
|
+
}
|
5
10
|
if (!targetComponent) return;
|
6
11
|
const component = target.closest(`[data-component]`) as HTMLElement;
|
7
12
|
const attribute = target.getAttribute("data-attribute");
|
@@ -14,6 +14,14 @@ const initializeActionsForElement = (element: HTMLElement) => {
|
|
14
14
|
const { eventType, componentName, stateName, fnName, bounceTime } = splitActionAttribute(action);
|
15
15
|
if (!eventType || !componentName || !fnName) return;
|
16
16
|
|
17
|
+
if (eventType === "render") {
|
18
|
+
setTimeout(() => {
|
19
|
+
handleAction(element, stateName, fnName)
|
20
|
+
}, 100);
|
21
|
+
element.setAttribute("data-render-event-handler", "true");
|
22
|
+
return;
|
23
|
+
}
|
24
|
+
|
17
25
|
if (bounceTime > 0) {
|
18
26
|
element.addEventListener(eventType, debounce((event) =>
|
19
27
|
handleAction(event.target as HTMLElement, stateName, fnName), bounceTime)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import morphdom from "morphdom"
|
2
2
|
import { createConsumer } from "@rails/actioncable"
|
3
3
|
import { initializeActions } from "../actions/initialize-actions.ts"
|
4
|
-
|
4
|
+
import { initializeInputs } from "../inputs/initialize-inputs.ts"
|
5
5
|
const consumer = createConsumer()
|
6
6
|
|
7
7
|
export const claptonChannel = consumer.subscriptions.create("Clapton::ClaptonChannel", {
|
@@ -19,6 +19,7 @@ export const claptonChannel = consumer.subscriptions.create("Clapton::ClaptonCha
|
|
19
19
|
return true;
|
20
20
|
}
|
21
21
|
});
|
22
|
+
|
22
23
|
initializeInputs();
|
23
24
|
initializeActions();
|
24
25
|
}
|
@@ -1,6 +1,3 @@
|
|
1
|
-
import { splitActionAttribute } from "./html/split-action-attribute"
|
2
|
-
import { updateComponent } from "./dom/update-component"
|
3
|
-
import { handleAction } from "./actions/handle-action"
|
4
1
|
import { initializeActions } from "actions/initialize-actions";
|
5
2
|
import { initializeInputs } from "inputs/initialize-inputs";
|
6
3
|
|
@@ -17,4 +17,9 @@ export class Box {
|
|
17
17
|
get render(): string {
|
18
18
|
return `<div ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</div>`;
|
19
19
|
}
|
20
|
+
|
21
|
+
add_action(eventType: string, stateName: string, fnName: string, options: Record<string, any> = {}): this {
|
22
|
+
this.attributes["data-action"] = `${this.attributes["data-action"] || ""} ${eventType}->${stateName}#${fnName}@${options.debounce || 0}`;
|
23
|
+
return this;
|
24
|
+
}
|
20
25
|
}
|
@@ -5,6 +5,7 @@ import { Checkbox } from "./components/checkbox"
|
|
5
5
|
import { Code } from "./components/code"
|
6
6
|
import { DateTimeField } from "./components/datetime-field"
|
7
7
|
import { Element } from "./components/element"
|
8
|
+
import { Embed } from "./components/embed"
|
8
9
|
import { Emphasis } from "./components/emphasis"
|
9
10
|
import { Form } from "./components/form"
|
10
11
|
import { Heading } from "./components/heading"
|
@@ -22,4 +23,4 @@ import { Component } from "./components/component"
|
|
22
23
|
import { TextField } from "./components/text-field"
|
23
24
|
import { Text } from "./components/text"
|
24
25
|
|
25
|
-
export { Box, Component, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span };
|
26
|
+
export { Box, Component, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span, Embed };
|
data/lib/clapton/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clapton
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Moeki Kawakami
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -6675,6 +6675,8 @@ files:
|
|
6675
6675
|
- lib/clapton/javascripts/src/components/datetime-field.ts
|
6676
6676
|
- lib/clapton/javascripts/src/components/element.spec.ts
|
6677
6677
|
- lib/clapton/javascripts/src/components/element.ts
|
6678
|
+
- lib/clapton/javascripts/src/components/embed.spec.ts
|
6679
|
+
- lib/clapton/javascripts/src/components/embed.ts
|
6678
6680
|
- lib/clapton/javascripts/src/components/emphasis.spec.ts
|
6679
6681
|
- lib/clapton/javascripts/src/components/emphasis.ts
|
6680
6682
|
- lib/clapton/javascripts/src/components/form.spec.ts
|