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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a69f6b50e12d2656c4e56ea26907b860f49f74c386b33899cb56f987ab63161
4
- data.tar.gz: c4aa0019acf0fb2573d8bede7112f371c446df08e82d3e88f67889c7a28d3e03
3
+ metadata.gz: e238016cc8650d4f218c6a542329d5fcbbe844dd68124f2464063df016f25c1f
4
+ data.tar.gz: 36ae66989cf010520398499d54f007dbebb7deb6f91d37a578677eb8555bb53c
5
5
  SHA512:
6
- metadata.gz: 4f714e419f33f1ee5a3a1c9e0a3eadc682d219bb3ba89347dcbee75bb2b70d7b8c123a9abc7d3f6053e7daf874d2a361a4acc7b36b4eab4c857782eb14095106
7
- data.tar.gz: 59ae4e4669c37a77bfd1c4287af8ecea6704009f7145a35568451c0634efb81de0c5a509b11472ccf159835f063b032cf6d0be2d6711a42bf1b1eaa421f4b981
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.
@@ -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
- const targetComponent = target.closest(`[data-component="${stateName.replace("State", "Component")}"]`);
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$1();
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
- const targetComponent = target.closest(`[data-component="${stateName.replace("State", "Component")}"]`) as HTMLElement;
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
  }
@@ -0,0 +1,8 @@
1
+ import { describe, it, expect } from "vitest"
2
+ import { Embed } from "./embed"
3
+
4
+ describe("Embed", () => {
5
+ it("returns empty string if no params", () => {
6
+ expect(new Embed("<blockquote></blockquote>").render).toBe("<blockquote></blockquote>")
7
+ })
8
+ })
@@ -0,0 +1,11 @@
1
+ export class Embed {
2
+ html: string;
3
+
4
+ constructor(html: string) {
5
+ this.html = html;
6
+ }
7
+
8
+ get render(): string {
9
+ return this.html;
10
+ }
11
+ }
@@ -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 };
@@ -1,4 +1,4 @@
1
- import { updateComponent } from "dom/update-component";
1
+ import { updateComponent } from "../dom/update-component";
2
2
 
3
3
  export const initializeInputs = () => {
4
4
  const inputElements = document.querySelectorAll("[data-attribute]");
@@ -1,3 +1,4 @@
1
1
  module Clapton
2
- VERSION = '0.0.12'
2
+ VERSION = '0.0.13'
3
3
  end
4
+
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.12
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-13 00:00:00.000000000 Z
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