clapton 0.0.22 → 0.0.24

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: ba556f54f55566ad5502aff210ab368c040d332a62313411377314b99a507ab0
4
- data.tar.gz: f8a71c1947ea6eb6544f637f53641ce87fc904543efadac7e30c244b6d1e8c85
3
+ metadata.gz: f15ceb3c247b4a620ea3508dc66511a2d7beb4f2e1bc32a24dd8e69bd0d70332
4
+ data.tar.gz: 329b1b8107968efdcce936a4f71deb904e5a609d846ca245d8c90cc7040ba144
5
5
  SHA512:
6
- metadata.gz: a8b32c159a66aa00df226c792f99e3d8a2ca96706a6189f3f442fec764eefe9f4a90e66f7f4c050a6a3c84212f46469e5f232c219b10999ebe543affb659a3f2
7
- data.tar.gz: eb01a40f84c3e8437be75b05d716f9702c0ed15666041015d06f74449f317f144558bd4c798ac94710008e63bf4c7a062556c14dcfbb9b3ee693217e905c86e0
6
+ metadata.gz: 24846c65443543d58f3e8f6f6018b5ace3af64166be9b9e2bde2bd0aa5056c7bd2f148ba43b7171a7e43756d0eb485bebaad9fca5ad57a913b038bead70baf8e
7
+ data.tar.gz: 60aaeaae14b6277cd5323f5cf1e36201e94f2887e4c1655838ce37a13a58282de3b05c729e2a8212b2e1705f7e4e538f0b2f2b78e57b872f089d9956acc58b57
data/README.md CHANGED
@@ -177,6 +177,30 @@ class TaskListComponent < Clapton::Component
177
177
  end
178
178
  ```
179
179
 
180
+ ### Effect
181
+
182
+ The `effect` method is a method that is triggered when the state is changed.
183
+
184
+ ```ruby
185
+ # app/components/task_list_component.rb
186
+ class TaskListComponent < Clapton::Component
187
+ effect [:tasks] do |state|
188
+ puts state[:tasks]
189
+ end
190
+ end
191
+ ```
192
+
193
+ If dependencies are not specified, the effect will be triggered on the first render.
194
+
195
+ ```ruby
196
+ # app/components/video_player_component.rb
197
+ class VideoPlayerComponent < Clapton::Component
198
+ effect [] do
199
+ puts "First render"
200
+ end
201
+ end
202
+ ```
203
+
180
204
  ### Preset Components Classes
181
205
 
182
206
  ```ruby
@@ -349,6 +373,15 @@ module ApplicationCable
349
373
  end
350
374
  ```
351
375
 
376
+ ### Using with importmap-rails
377
+
378
+ Use `clapton_javascript_tag` instead of `javascript_importmap_tags`.
379
+
380
+ ```diff
381
+ - <%= javascript_importmap_tags %>
382
+ + <%= clapton_javascript_tag %>
383
+ ```
384
+
352
385
  ### Events
353
386
 
354
387
  #### clapton:render
@@ -1,11 +1,10 @@
1
1
  module Clapton
2
2
  module ClaptonHelper
3
3
 
4
- def clapton_javascript_tag
4
+ def clapton_javascript_tag(entry_point = "application", importmap: nil)
5
5
  all_components = Dir.glob(Rails.root.join("app", "components", "**", "*.rb"))
6
- tags = <<~HTML
7
- <script type="importmap">
8
- {
6
+ clapton_json = JSON.parse <<~JSON
7
+ {
9
8
  "imports": {
10
9
  "client": "/clapton/client.js",
11
10
  "components": "/clapton/components.js",
@@ -15,10 +14,25 @@ module Clapton
15
14
  end.join(",\n") }
16
15
  }
17
16
  }
18
- </script>
19
- <script type="module" src="/clapton/client.js"></script>
20
- HTML
21
- tags.html_safe
17
+ JSON
18
+ if defined?(javascript_importmap_tags)
19
+ importmap ||= Rails.application.importmap
20
+ json = { imports: JSON.parse(importmap.to_json(resolver: self))["imports"].merge(clapton_json["imports"]) }
21
+ safe_join [
22
+ javascript_inline_importmap_tag(json.to_json),
23
+ javascript_importmap_module_preload_tags(importmap, entry_point:),
24
+ javascript_import_module_tag(entry_point),
25
+ tag.script(type: "module", src: "/clapton/client.js"),
26
+ ], "\n"
27
+ else
28
+ html = <<~HTML
29
+ <script type="importmap">
30
+ #{clapton_json.to_json}
31
+ </script>
32
+ <script type="module" src="/clapton/client.js"></script>
33
+ HTML
34
+ html.html_safe
35
+ end
22
36
  end
23
37
 
24
38
  def clapton_tag
@@ -405,7 +405,28 @@ var c = (function () {
405
405
  }
406
406
  return root.renderWrapper;
407
407
  }
408
+ static effect(dependencies, callback) {
409
+ this._effects.push({ dependencies, callback });
410
+ }
411
+ get effects() {
412
+ return this.constructor._effects;
413
+ }
414
+ runEffects() {
415
+ this.effects.forEach((effect) => {
416
+ if (effect.dependencies.some((dependency) => this._state[dependency] !== undefined)) {
417
+ effect.callback(this._state);
418
+ }
419
+ });
420
+ }
421
+ runEffectOnFirstRender() {
422
+ this.effects.forEach((effect) => {
423
+ if (effect.dependencies.length === 0) {
424
+ effect.callback(this._state);
425
+ }
426
+ });
427
+ }
408
428
  }
429
+ Component._effects = [];
409
430
 
410
431
  class TextField {
411
432
  constructor(state, attribute, attributes = {}) {
@@ -402,7 +402,28 @@ class Component {
402
402
  }
403
403
  return root.renderWrapper;
404
404
  }
405
+ static effect(dependencies, callback) {
406
+ this._effects.push({ dependencies, callback });
407
+ }
408
+ get effects() {
409
+ return this.constructor._effects;
410
+ }
411
+ runEffects() {
412
+ this.effects.forEach((effect) => {
413
+ if (effect.dependencies.some((dependency) => this._state[dependency] !== undefined)) {
414
+ effect.callback(this._state);
415
+ }
416
+ });
417
+ }
418
+ runEffectOnFirstRender() {
419
+ this.effects.forEach((effect) => {
420
+ if (effect.dependencies.length === 0) {
421
+ effect.callback(this._state);
422
+ }
423
+ });
424
+ }
405
425
  }
426
+ Component._effects = [];
406
427
 
407
428
  class TextField {
408
429
  constructor(state, attribute, attributes = {}) {
@@ -1292,6 +1292,7 @@ const updateComponent = async (component, state, property, target) => {
1292
1292
  const ComponentClass = module[componentName];
1293
1293
  const instance = new ComponentClass(state, component.dataset.id);
1294
1294
  morphdom(component, instance.renderWrapper);
1295
+ instance.runEffects();
1295
1296
  };
1296
1297
 
1297
1298
  const initializeInputs = () => {
@@ -1333,6 +1334,7 @@ const claptonChannel = consumer.subscriptions.create("Clapton::ClaptonChannel",
1333
1334
 
1334
1335
  initializeInputs();
1335
1336
  initializeActions();
1337
+ instance.runEffects();
1336
1338
  }
1337
1339
  });
1338
1340
 
@@ -1440,6 +1442,7 @@ const createAndAppendComponent = async (component, element) => {
1440
1442
  element.outerHTML = firstChild.outerHTML;
1441
1443
  }
1442
1444
  }
1445
+ instance.runEffects();
1443
1446
  };
1444
1447
  document.addEventListener("DOMContentLoaded", async () => {
1445
1448
  await initializeComponents();
@@ -1448,6 +1451,13 @@ document.addEventListener("DOMContentLoaded", async () => {
1448
1451
  const event = new Event('clapton:render');
1449
1452
  document.dispatchEvent(event);
1450
1453
  });
1454
+ document.addEventListener("turbo:render", async () => {
1455
+ await initializeComponents();
1456
+ initializeActions();
1457
+ initializeInputs();
1458
+ const event = new Event('clapton:render');
1459
+ document.dispatchEvent(event);
1460
+ });
1451
1461
  window.addEventListener('beforeunload', () => {
1452
1462
  sessionStorage.setItem('scrollPosition', window.scrollY.toString());
1453
1463
  });
@@ -405,7 +405,28 @@ var Clapton = (function (exports) {
405
405
  }
406
406
  return root.renderWrapper;
407
407
  }
408
+ static effect(dependencies, callback) {
409
+ this._effects.push({ dependencies, callback });
410
+ }
411
+ get effects() {
412
+ return this.constructor._effects;
413
+ }
414
+ runEffects() {
415
+ this.effects.forEach((effect) => {
416
+ if (effect.dependencies.some((dependency) => this._state[dependency] !== undefined)) {
417
+ effect.callback(this._state);
418
+ }
419
+ });
420
+ }
421
+ runEffectOnFirstRender() {
422
+ this.effects.forEach((effect) => {
423
+ if (effect.dependencies.length === 0) {
424
+ effect.callback(this._state);
425
+ }
426
+ });
427
+ }
408
428
  }
429
+ Component._effects = [];
409
430
 
410
431
  class TextField {
411
432
  constructor(state, attribute, attributes = {}) {
@@ -402,7 +402,28 @@ class Component {
402
402
  }
403
403
  return root.renderWrapper;
404
404
  }
405
+ static effect(dependencies, callback) {
406
+ this._effects.push({ dependencies, callback });
407
+ }
408
+ get effects() {
409
+ return this.constructor._effects;
410
+ }
411
+ runEffects() {
412
+ this.effects.forEach((effect) => {
413
+ if (effect.dependencies.some((dependency) => this._state[dependency] !== undefined)) {
414
+ effect.callback(this._state);
415
+ }
416
+ });
417
+ }
418
+ runEffectOnFirstRender() {
419
+ this.effects.forEach((effect) => {
420
+ if (effect.dependencies.length === 0) {
421
+ effect.callback(this._state);
422
+ }
423
+ });
424
+ }
405
425
  }
426
+ Component._effects = [];
406
427
 
407
428
  class TextField {
408
429
  constructor(state, attribute, attributes = {}) {
@@ -0,0 +1,160 @@
1
+ import { Clapton } from "components"
2
+
3
+ const bq = (...props: any[]) => {
4
+ return new Clapton.BlockQuote(...props)
5
+ }
6
+
7
+ const box = (...props: any[]) => {
8
+ return new Clapton.Box(...props)
9
+ }
10
+
11
+ const b = (...props: any[]) => {
12
+ return new Clapton.Bold(...props)
13
+ }
14
+
15
+ const button = (...props: any[]) => {
16
+ return new Clapton.Button(...props)
17
+ }
18
+
19
+ const check = (...props: any[]) => {
20
+ return new Clapton.Checkbox(props[0], props[1], props[2])
21
+ }
22
+
23
+ const code = (...props: any[]) => {
24
+ return new Clapton.Code(...props)
25
+ }
26
+
27
+ const datetime = (...props: any[]) => {
28
+ return new Clapton.DateTimeField(props[0], props[1], props[2])
29
+ }
30
+
31
+ const el = (...props: any[]) => {
32
+ return new Clapton.Element(props[0], props[1])
33
+ }
34
+
35
+ const embed = (...props: any[]) => {
36
+ return new Clapton.Embed(props[0])
37
+ }
38
+
39
+ const em = (...props: any[]) => {
40
+ return new Clapton.Emphasis(...props)
41
+ }
42
+
43
+ const form = (...props: any[]) => {
44
+ return new Clapton.Form(...props)
45
+ }
46
+
47
+ const h = (...props: any[]) => {
48
+ return new Clapton.Heading(props[0], props[1])
49
+ }
50
+
51
+ const img = (...props: any[]) => {
52
+ return new Clapton.Image(props[0], props[1], props[2])
53
+ }
54
+
55
+ const a = (...props: any[]) => {
56
+ return new Clapton.Link(props[0], props[1])
57
+ }
58
+
59
+ const li = (...props: any[]) => {
60
+ return new Clapton.ListItem(...props)
61
+ }
62
+
63
+ const ul = (...props: any[]) => {
64
+ return new Clapton.List(...props)
65
+ }
66
+
67
+ const ol = (...props: any[]) => {
68
+ return new Clapton.OrderedList(...props)
69
+ }
70
+
71
+ const p = (...props: any[]) => {
72
+ return new Clapton.Paragraph(...props)
73
+ }
74
+
75
+ const q = (...props: any[]) => {
76
+ return new Clapton.Quote(...props)
77
+ }
78
+
79
+ const radio = (...props: any[]) => {
80
+ return new Clapton.RadioButton(props[0], props[1], props[2])
81
+ }
82
+
83
+ const select = (...props: any[]) => {
84
+ return new Clapton.Select(props[0], props[1], props[2])
85
+ }
86
+
87
+ const span = (...props: any[]) => {
88
+ return new Clapton.Span(...props)
89
+ }
90
+
91
+ const textarea = (...props: any[]) => {
92
+ return new Clapton.TextArea(props[0], props[1], props[2])
93
+ }
94
+
95
+ const input = (...props: any[]) => {
96
+ return new Clapton.TextField(props[0], props[1], props[2])
97
+ }
98
+
99
+ const text = (...props: any[]) => {
100
+ return new Clapton.Text(props[0])
101
+ }
102
+
103
+ const c = (name: string, ...props: any[]) => {
104
+ switch (name) {
105
+ case "bq":
106
+ return bq(...props)
107
+ case "box":
108
+ return box(...props)
109
+ case "b":
110
+ return b(...props)
111
+ case "button":
112
+ return button(...props)
113
+ case "check":
114
+ return check(...props)
115
+ case "code":
116
+ return code(...props)
117
+ case "datetime":
118
+ return datetime(...props)
119
+ case "el":
120
+ return el(...props)
121
+ case "embed":
122
+ return embed(...props)
123
+ case "em":
124
+ return em(...props)
125
+ case "form":
126
+ return form(...props)
127
+ case "h":
128
+ return h(...props)
129
+ case "img":
130
+ return img(...props)
131
+ case "a":
132
+ return a(...props)
133
+ case "li":
134
+ return li(...props)
135
+ case "ul":
136
+ return ul(...props)
137
+ case "ol":
138
+ return ol(...props)
139
+ case "p":
140
+ return p(...props)
141
+ case "q":
142
+ return q(...props)
143
+ case "radio":
144
+ return radio(...props)
145
+ case "select":
146
+ return select(...props)
147
+ case "span":
148
+ return span(...props)
149
+ case "textarea":
150
+ return textarea(...props)
151
+ case "input":
152
+ return input(...props)
153
+ case "text":
154
+ return text(...props)
155
+ default:
156
+ return new Clapton.Component(...props)
157
+ }
158
+ }
159
+
160
+ export { c }
@@ -1,160 +1,3 @@
1
- import { Clapton } from "components"
2
-
3
- const bq = (...props: any[]) => {
4
- return new Clapton.BlockQuote(...props)
5
- }
6
-
7
- const box = (...props: any[]) => {
8
- return new Clapton.Box(...props)
9
- }
10
-
11
- const b = (...props: any[]) => {
12
- return new Clapton.Bold(...props)
13
- }
14
-
15
- const button = (...props: any[]) => {
16
- return new Clapton.Button(...props)
17
- }
18
-
19
- const check = (...props: any[]) => {
20
- return new Clapton.Checkbox(props[0], props[1], props[2])
21
- }
22
-
23
- const code = (...props: any[]) => {
24
- return new Clapton.Code(...props)
25
- }
26
-
27
- const datetime = (...props: any[]) => {
28
- return new Clapton.DateTimeField(props[0], props[1], props[2])
29
- }
30
-
31
- const el = (...props: any[]) => {
32
- return new Clapton.Element(props[0], props[1])
33
- }
34
-
35
- const embed = (...props: any[]) => {
36
- return new Clapton.Embed(props[0])
37
- }
38
-
39
- const em = (...props: any[]) => {
40
- return new Clapton.Emphasis(...props)
41
- }
42
-
43
- const form = (...props: any[]) => {
44
- return new Clapton.Form(...props)
45
- }
46
-
47
- const h = (...props: any[]) => {
48
- return new Clapton.Heading(props[0], props[1])
49
- }
50
-
51
- const img = (...props: any[]) => {
52
- return new Clapton.Image(props[0], props[1], props[2])
53
- }
54
-
55
- const a = (...props: any[]) => {
56
- return new Clapton.Link(props[0], props[1])
57
- }
58
-
59
- const li = (...props: any[]) => {
60
- return new Clapton.ListItem(...props)
61
- }
62
-
63
- const ul = (...props: any[]) => {
64
- return new Clapton.List(...props)
65
- }
66
-
67
- const ol = (...props: any[]) => {
68
- return new Clapton.OrderedList(...props)
69
- }
70
-
71
- const p = (...props: any[]) => {
72
- return new Clapton.Paragraph(...props)
73
- }
74
-
75
- const q = (...props: any[]) => {
76
- return new Clapton.Quote(...props)
77
- }
78
-
79
- const radio = (...props: any[]) => {
80
- return new Clapton.RadioButton(props[0], props[1], props[2])
81
- }
82
-
83
- const select = (...props: any[]) => {
84
- return new Clapton.Select(props[0], props[1], props[2])
85
- }
86
-
87
- const span = (...props: any[]) => {
88
- return new Clapton.Span(...props)
89
- }
90
-
91
- const textarea = (...props: any[]) => {
92
- return new Clapton.TextArea(props[0], props[1], props[2])
93
- }
94
-
95
- const input = (...props: any[]) => {
96
- return new Clapton.TextField(props[0], props[1], props[2])
97
- }
98
-
99
- const text = (...props: any[]) => {
100
- return new Clapton.Text(props[0])
101
- }
102
-
103
- const c = (name: string, ...props: any[]) => {
104
- switch (name) {
105
- case "bq":
106
- return bq(...props)
107
- case "box":
108
- return box(...props)
109
- case "b":
110
- return b(...props)
111
- case "button":
112
- return button(...props)
113
- case "check":
114
- return check(...props)
115
- case "code":
116
- return code(...props)
117
- case "datetime":
118
- return datetime(...props)
119
- case "el":
120
- return el(...props)
121
- case "embed":
122
- return embed(...props)
123
- case "em":
124
- return em(...props)
125
- case "form":
126
- return form(...props)
127
- case "h":
128
- return h(...props)
129
- case "img":
130
- return img(...props)
131
- case "a":
132
- return a(...props)
133
- case "li":
134
- return li(...props)
135
- case "ul":
136
- return ul(...props)
137
- case "ol":
138
- return ol(...props)
139
- case "p":
140
- return p(...props)
141
- case "q":
142
- return q(...props)
143
- case "radio":
144
- return radio(...props)
145
- case "select":
146
- return select(...props)
147
- case "span":
148
- return span(...props)
149
- case "textarea":
150
- return textarea(...props)
151
- case "input":
152
- return input(...props)
153
- case "text":
154
- return text(...props)
155
- default:
156
- return new Clapton.Component(...props)
157
- }
158
- }
1
+ import { c } from "./c-base"
159
2
 
160
3
  export default c
@@ -1,160 +1,3 @@
1
- import { Clapton } from "components"
2
-
3
- const bq = (...props: any[]) => {
4
- return new Clapton.BlockQuote(...props)
5
- }
6
-
7
- const box = (...props: any[]) => {
8
- return new Clapton.Box(...props)
9
- }
10
-
11
- const b = (...props: any[]) => {
12
- return new Clapton.Bold(...props)
13
- }
14
-
15
- const button = (...props: any[]) => {
16
- return new Clapton.Button(...props)
17
- }
18
-
19
- const check = (...props: any[]) => {
20
- return new Clapton.Checkbox(props[0], props[1], props[2])
21
- }
22
-
23
- const code = (...props: any[]) => {
24
- return new Clapton.Code(...props)
25
- }
26
-
27
- const datetime = (...props: any[]) => {
28
- return new Clapton.DateTimeField(props[0], props[1], props[2])
29
- }
30
-
31
- const el = (...props: any[]) => {
32
- return new Clapton.Element(props[0], props[1])
33
- }
34
-
35
- const embed = (...props: any[]) => {
36
- return new Clapton.Embed(props[0])
37
- }
38
-
39
- const em = (...props: any[]) => {
40
- return new Clapton.Emphasis(...props)
41
- }
42
-
43
- const form = (...props: any[]) => {
44
- return new Clapton.Form(...props)
45
- }
46
-
47
- const h = (...props: any[]) => {
48
- return new Clapton.Heading(props[0], props[1])
49
- }
50
-
51
- const img = (...props: any[]) => {
52
- return new Clapton.Image(props[0], props[1], props[2])
53
- }
54
-
55
- const a = (...props: any[]) => {
56
- return new Clapton.Link(props[0], props[1])
57
- }
58
-
59
- const li = (...props: any[]) => {
60
- return new Clapton.ListItem(...props)
61
- }
62
-
63
- const ul = (...props: any[]) => {
64
- return new Clapton.List(...props)
65
- }
66
-
67
- const ol = (...props: any[]) => {
68
- return new Clapton.OrderedList(...props)
69
- }
70
-
71
- const p = (...props: any[]) => {
72
- return new Clapton.Paragraph(...props)
73
- }
74
-
75
- const q = (...props: any[]) => {
76
- return new Clapton.Quote(...props)
77
- }
78
-
79
- const radio = (...props: any[]) => {
80
- return new Clapton.RadioButton(props[0], props[1], props[2])
81
- }
82
-
83
- const select = (...props: any[]) => {
84
- return new Clapton.Select(props[0], props[1], props[2])
85
- }
86
-
87
- const span = (...props: any[]) => {
88
- return new Clapton.Span(...props)
89
- }
90
-
91
- const textarea = (...props: any[]) => {
92
- return new Clapton.TextArea(props[0], props[1], props[2])
93
- }
94
-
95
- const input = (...props: any[]) => {
96
- return new Clapton.TextField(props[0], props[1], props[2])
97
- }
98
-
99
- const text = (...props: any[]) => {
100
- return new Clapton.Text(props[0])
101
- }
102
-
103
- const c = (name: string, ...props: any[]) => {
104
- switch (name) {
105
- case "bq":
106
- return bq(...props)
107
- case "box":
108
- return box(...props)
109
- case "b":
110
- return b(...props)
111
- case "button":
112
- return button(...props)
113
- case "check":
114
- return check(...props)
115
- case "code":
116
- return code(...props)
117
- case "datetime":
118
- return datetime(...props)
119
- case "el":
120
- return el(...props)
121
- case "embed":
122
- return embed(...props)
123
- case "em":
124
- return em(...props)
125
- case "form":
126
- return form(...props)
127
- case "h":
128
- return h(...props)
129
- case "img":
130
- return img(...props)
131
- case "a":
132
- return a(...props)
133
- case "li":
134
- return li(...props)
135
- case "ul":
136
- return ul(...props)
137
- case "ol":
138
- return ol(...props)
139
- case "p":
140
- return p(...props)
141
- case "q":
142
- return q(...props)
143
- case "radio":
144
- return radio(...props)
145
- case "select":
146
- return select(...props)
147
- case "span":
148
- return span(...props)
149
- case "textarea":
150
- return textarea(...props)
151
- case "input":
152
- return input(...props)
153
- case "text":
154
- return text(...props)
155
- default:
156
- return new Clapton.Component(...props)
157
- }
158
- }
1
+ import { c } from "./c-base"
159
2
 
160
3
  export { c }
@@ -25,5 +25,6 @@ export const claptonChannel = consumer.subscriptions.create("Clapton::ClaptonCha
25
25
 
26
26
  initializeInputs();
27
27
  initializeActions();
28
+ instance.runEffects();
28
29
  }
29
30
  })
@@ -36,6 +36,7 @@ const createAndAppendComponent = async (component: ComponentDefinition, element:
36
36
  element.outerHTML = firstChild.outerHTML;
37
37
  }
38
38
  }
39
+ instance.runEffects();
39
40
  };
40
41
 
41
42
  document.addEventListener("DOMContentLoaded", async () => {
@@ -46,6 +47,14 @@ document.addEventListener("DOMContentLoaded", async () => {
46
47
  document.dispatchEvent(event);
47
48
  });
48
49
 
50
+ document.addEventListener("turbo:render", async () => {
51
+ await initializeComponents();
52
+ initializeActions();
53
+ initializeInputs();
54
+ const event = new Event('clapton:render');
55
+ document.dispatchEvent(event);
56
+ });
57
+
49
58
  window.addEventListener('beforeunload', () => {
50
59
  sessionStorage.setItem('scrollPosition', window.scrollY.toString());
51
60
  });
@@ -5,6 +5,8 @@ export class Component {
5
5
  _state: any;
6
6
  _errors: any[];
7
7
 
8
+ static _effects: any[] = [];
9
+
8
10
  constructor(state: any = {}, id: string = Math.random().toString(36).substring(2, 10), errors: any[] = []) {
9
11
  this._state = state;
10
12
  this.id = id;
@@ -24,4 +26,28 @@ export class Component {
24
26
  }
25
27
  return root.renderWrapper;
26
28
  }
29
+
30
+ static effect(dependencies: any[], callback: () => void) {
31
+ this._effects.push({ dependencies, callback });
32
+ }
33
+
34
+ get effects(): any[] {
35
+ return (this.constructor as typeof Component)._effects;
36
+ }
37
+
38
+ runEffects() {
39
+ this.effects.forEach((effect) => {
40
+ if (effect.dependencies.some((dependency: any) => this._state[dependency] !== undefined)) {
41
+ effect.callback(this._state);
42
+ }
43
+ });
44
+ }
45
+
46
+ runEffectOnFirstRender() {
47
+ this.effects.forEach((effect) => {
48
+ if (effect.dependencies.length === 0) {
49
+ effect.callback(this._state);
50
+ }
51
+ });
52
+ }
27
53
  }
@@ -7,4 +7,5 @@ export const updateComponent = async (component: HTMLElement, state: any, proper
7
7
  const ComponentClass = module[componentName] as any;
8
8
  const instance = new ComponentClass(state, component.dataset.id);
9
9
  morphdom(component, instance.renderWrapper);
10
+ instance.runEffects();
10
11
  };
@@ -1,3 +1,3 @@
1
1
  module Clapton
2
- VERSION = '0.0.22'
2
+ VERSION = '0.0.24'
3
3
  end
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.22
4
+ version: 0.0.24
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-16 00:00:00.000000000 Z
11
+ date: 2024-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -6706,6 +6706,7 @@ files:
6706
6706
  - lib/clapton/javascripts/src/actions/handle-action.spec.ts
6707
6707
  - lib/clapton/javascripts/src/actions/handle-action.ts
6708
6708
  - lib/clapton/javascripts/src/actions/initialize-actions.ts
6709
+ - lib/clapton/javascripts/src/c-base.ts
6709
6710
  - lib/clapton/javascripts/src/c-for-test.ts
6710
6711
  - lib/clapton/javascripts/src/c.ts
6711
6712
  - lib/clapton/javascripts/src/channel/clapton-channel.js