clapton 0.0.14 → 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7def55e21b26dedfd8843c9ace0bf4db854abf18205de4dea7c3a203d06130f6
4
- data.tar.gz: 743f8549cd8a901f2f1c76a4807f05ccace77d58e9665ad35c02626df555a08a
3
+ metadata.gz: 9f420efd6abefefc796433bad77369a93a3686057519240abf5a9e118758ade2
4
+ data.tar.gz: f34d535b066e305f893bd291e464d2dfd231df8fbf837b04668fcbaa0a0b4b84
5
5
  SHA512:
6
- metadata.gz: b05ba6c72f46212b628cee66c18e01644e285e6380862669eb7de55174338b4b9109ebf69505d398227c85f0a19a6e55580e6cf760951fad573bc13d78c73845
7
- data.tar.gz: 2359db5b15a7ef59f43dc61d2bcb96609858cc0a19c996e9b18288046fe739a3d68bf5868f221c97c349af8781ff8483c979199e3c1c27914f617e8912555aba
6
+ metadata.gz: '08125a3863fc85cae262381041e72fdacdfc1fe8dd4f677cd4f5f55e658b713b3c8fa6238289c2b0fc1b5e4cc1bbb4e9ead48179eee8af6cde67b26fe5293e30'
7
+ data.tar.gz: 6e9a70c6be7c02f4642d44ce4f766ea8a72460962e8a0437f71bafbdaad11df2f0cfbe6fd4c10953a63ab0d324c5720286a17edc5dc66a59ca4637c5ef239258
data/README.md CHANGED
@@ -37,10 +37,10 @@ class TaskListComponent < Clapton::Component
37
37
  @state.tasks.each do |task|
38
38
  @root.add(TaskItemComponent.new(id: task[:id], title: task[:title], due: task[:due], done: task[:done]))
39
39
  end
40
- add_button = Clapton::Button.new
41
- add_button.add(Clapton::Text.new("Add Task"))
42
- add_button.add_action(:click, :TaskListState, :add_task)
43
- @root.add(add_button)
40
+ btn = c.button
41
+ btn.add(c.text("Add Task"))
42
+ btn.add_action(:click, :TaskListState, :add_task)
43
+ @root.add(btn)
44
44
  @root.render
45
45
  end
46
46
  end
@@ -51,17 +51,17 @@ end
51
51
  # app/components/task_item_component.rb
52
52
  class TaskItemComponent < Clapton::Component
53
53
  def render
54
- button = Clapton::Button.new
55
- button.add(Clapton::Text.new(@state.done ? "✅" : "🟩"))
56
- button.add_action(:click, :TaskListState, :toggle_done)
54
+ btn = c.button
55
+ btn.add(c.text(@state.done ? "✅" : "🟩"))
56
+ btn.add_action(:click, :TaskListState, :toggle_done)
57
57
 
58
- text_field = Clapton::TextField.new(@state, :title)
59
- text_field.add_action(:input, :TaskListState, :update_title)
58
+ tf = c.input(@state, :title)
59
+ tf.add_action(:input, :TaskListState, :update_title)
60
60
 
61
- datetime_field = Clapton::DateTimeField.new(@state, :due)
62
- datetime_field.add_action(:input, :TaskListState, :update_due)
61
+ dt = c.datetime(@state, :due)
62
+ dt.add_action(:input, :TaskListState, :update_due)
63
63
 
64
- @root.add(button).add(text_field).add(datetime_field)
64
+ @root.add(btn).add(tf).add(dt)
65
65
  @root.render
66
66
  end
67
67
  end
@@ -177,13 +177,13 @@ class TaskListComponent < Clapton::Component
177
177
  end
178
178
  ```
179
179
 
180
- ### Preset Components
180
+ ### Preset Components Classes
181
181
 
182
182
  ```ruby
183
183
  block_quote = Clapton::BlockQuote.new
184
184
  block_quote.add(Clapton::Text.new("Hello"))
185
185
 
186
- box = Clapton::Box.new
186
+ box = **Clapton**::Box.new
187
187
  box.add(Clapton::Text.new("Hello"))
188
188
 
189
189
  button = Clapton::Button.new
@@ -253,6 +253,72 @@ text_field = Clapton::TextField.new(:ExampleState, :example_attribute, { id: "ex
253
253
  text = Clapton::Text.new("Hello")`
254
254
  ```
255
255
 
256
+ ### Preset Component Methods
257
+
258
+ ```javascript
259
+ c.bq(...props)
260
+ c.box(...props)
261
+ c.b(...props)
262
+ c.button(...props)
263
+ c.check(...props)
264
+ c.code(...props)
265
+ c.datetime(...props)
266
+ c.el(...props)
267
+ c.embed(...props)
268
+ c.em(...props)
269
+ c.form(...props)
270
+ c.h(...props)
271
+ c.img(...props)
272
+ c.a(...props)
273
+ c.li(...props)
274
+ c.ul(...props)
275
+ c.ol(...props)
276
+ c.p(...props)
277
+ c.q(...props)
278
+ c.radio(...props)
279
+ c.select(...props)
280
+ c.span(...props)
281
+ c.textarea(...props)
282
+ c.input(...props)
283
+ c.text(...props)
284
+ ```
285
+
286
+ ### Streaming
287
+
288
+ Clapton supports streaming.
289
+
290
+ ```ruby
291
+ # app/states/chat_state.rb
292
+ class ChatState < Clapton::State
293
+ attribute :messages
294
+
295
+ def send(params)
296
+ self.messages << { role: "user", content: params[:content] }
297
+ yield continue: true # Continue the streaming
298
+
299
+ client = OpenAI::Client.new(
300
+ access_token: ENV.fetch("OPENAI_ACCESS_TOKEN"),
301
+ log_errors: true
302
+ )
303
+ self.messages << { role: "assistant", content: "" }
304
+ client.chat(
305
+ parameters: {
306
+ model: "gpt-4o-mini",
307
+ messages: messages,
308
+ stream: proc do |chunk, _bytesize|
309
+ if chunk.dig("choices", 0, "finish_reason") == "stop"
310
+ yield continue: false # Stop the streaming
311
+ end
312
+
313
+ self.messages.last[:content] << chunk.dig("choices", 0, "delta", "content")
314
+ yield continue: true
315
+ end
316
+ }
317
+ )
318
+ end
319
+ end
320
+ ```
321
+
256
322
  ### Optional
257
323
 
258
324
  #### Action Cable
@@ -21,6 +21,10 @@ module Clapton
21
21
  FileUtils.mkdir_p(components_path) unless components_path.exist?
22
22
  FileUtils.touch(components_path.join(".keep"))
23
23
 
24
+ FileUtils.mkdir_p(Rails.root.join("public", "clapton")) unless Rails.root.join("public", "clapton").exist?
25
+ File.write(Rails.root.join("public", "clapton", "components.js"), File.read(File.join(__dir__, "javascripts", "dist", "components.js")))
26
+ File.write(Rails.root.join("public", "clapton", "client.js"), File.read(File.join(__dir__, "javascripts", "dist", "client.js")))
27
+
24
28
  compile_components
25
29
 
26
30
  listener = Listen.to(Rails.root.join("app", "components")) do |modified, added, removed|
@@ -32,9 +36,9 @@ module Clapton
32
36
  end
33
37
 
34
38
  def compile_components
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
+ puts "Clapton: Compiling components"
40
+
41
+ start_time = Time.now
38
42
  Dir.glob(Rails.root.join("app", "components", "**", "*.rb")).each do |file|
39
43
  code = File.read(file)
40
44
  js = ""
@@ -44,12 +48,16 @@ module Clapton
44
48
  js += "import { #{match[0]}Component } from '#{match[0]}Component';"
45
49
  js += "\n"
46
50
  end
51
+ code = code.gsub(/([^a-zA-Z0-9])c\.(\w+?)\(/, '\1@c.\2(')
52
+ code = code.gsub(/([^a-zA-Z0-9])c\.(\w+?)\./, '\1@c.\2().')
47
53
  js += Ruby2JS.convert(code, preset: true)
48
54
  js += "\n"
49
55
  js += "export { #{File.basename(file, ".rb").camelize} };"
50
56
  js += "\n"
51
57
  File.write(Rails.root.join("public", "clapton", "#{File.basename(file, ".rb").camelize}.js"), js)
52
58
  end
59
+ end_time = Time.now
60
+ puts "Clapton: Component compilation took #{end_time - start_time} seconds"
53
61
  end
54
62
  end
55
63
  end
@@ -1287,8 +1287,7 @@ function getConfig(name) {
1287
1287
 
1288
1288
  const updateComponent = async (component, state, property, target) => {
1289
1289
  state[property] = target.value;
1290
- component.setAttribute("data-state", JSON.stringify(state));
1291
- const componentName = component.getAttribute("data-component");
1290
+ const componentName = component.dataset.component;
1292
1291
  const module = await import(`${componentName}`);
1293
1292
  const ComponentClass = module[componentName];
1294
1293
  const instance = new ComponentClass(state, component.dataset.id);
@@ -1348,26 +1347,26 @@ const handleAction = async (target, stateName, fn) => {
1348
1347
  if (!targetComponent)
1349
1348
  return;
1350
1349
  const component = target.closest(`[data-component]`);
1351
- const attribute = target.getAttribute("data-attribute");
1350
+ const attribute = target.dataset.attribute;
1352
1351
  if (attribute) {
1353
- const state = JSON.parse(component.getAttribute("data-state") || "{}");
1352
+ const state = JSON.parse(component.dataset.state || "{}");
1354
1353
  if (target.tagName === "INPUT") {
1355
1354
  state[attribute] = target.value;
1356
- component.setAttribute("data-state", JSON.stringify(state));
1355
+ component.dataset.state = JSON.stringify(state);
1357
1356
  }
1358
1357
  }
1359
1358
  claptonChannel.perform("action", {
1360
1359
  data: {
1361
1360
  component: {
1362
1361
  name: stateName.replace("State", "Component"),
1363
- id: targetComponent.getAttribute("data-id"),
1362
+ id: targetComponent.dataset.id,
1364
1363
  },
1365
1364
  state: {
1366
1365
  name: stateName,
1367
1366
  action: fn,
1368
- attributes: JSON.parse(targetComponent.getAttribute("data-state") || "{}"),
1367
+ attributes: JSON.parse(targetComponent.dataset.state || "{}"),
1369
1368
  },
1370
- params: JSON.parse(component.getAttribute("data-state") || "{}")
1369
+ params: JSON.parse(component.dataset.state || "{}")
1371
1370
  }
1372
1371
  });
1373
1372
  };
@@ -75,6 +75,20 @@ var Clapton = (function (exports) {
75
75
  }
76
76
  }
77
77
 
78
+ class Bold {
79
+ constructor(attributes = {}) {
80
+ this.children = [];
81
+ this.attributes = attributes;
82
+ }
83
+ get render() {
84
+ return `<strong ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</strong>`;
85
+ }
86
+ add(child) {
87
+ this.children.push(child);
88
+ return this;
89
+ }
90
+ }
91
+
78
92
  class Button {
79
93
  constructor(attributes = {}) {
80
94
  this.attributes = attributes;
@@ -372,18 +386,6 @@ var Clapton = (function (exports) {
372
386
  }
373
387
  }
374
388
 
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
389
  class TextField {
388
390
  constructor(state, attribute, attributes = {}) {
389
391
  this.state = state;
@@ -409,7 +411,119 @@ var Clapton = (function (exports) {
409
411
  }
410
412
  }
411
413
 
414
+ class TextArea {
415
+ constructor(state, attribute, attributes = {}) {
416
+ this.state = state;
417
+ this.attributes = attributes;
418
+ this.attribute = attribute;
419
+ this.attributes["data-attribute"] = attribute;
420
+ }
421
+ get render() {
422
+ return `<textarea ${htmlAttributes(this.attributes)}>${this.state[this.attribute] || ""}</textarea>`;
423
+ }
424
+ add_action(event, klass, fn, options = {}) {
425
+ this.attributes["data-action"] = `${this.attributes["data-action"] || ""} ${event}->${klass}#${fn}@${options.debounce || 0}`;
426
+ return this;
427
+ }
428
+ }
429
+
430
+ const Clapton = {
431
+ Box, Component, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span, Embed, Bold, TextArea
432
+ };
433
+
434
+ class Presets {
435
+ bq(...props) {
436
+ return new Clapton.BlockQuote(...props);
437
+ }
438
+ box(...props) {
439
+ return new Clapton.Box(...props);
440
+ }
441
+ b(...props) {
442
+ return new Clapton.Bold(...props);
443
+ }
444
+ button(...props) {
445
+ return new Clapton.Button(...props);
446
+ }
447
+ check(...props) {
448
+ return new Clapton.Checkbox(props[0], props[1], props[2]);
449
+ }
450
+ code(...props) {
451
+ return new Clapton.Code(...props);
452
+ }
453
+ datetime(...props) {
454
+ return new Clapton.DateTimeField(props[0], props[1], props[2]);
455
+ }
456
+ el(...props) {
457
+ return new Clapton.Element(props[0], props[1]);
458
+ }
459
+ embed(...props) {
460
+ return new Clapton.Embed(props[0]);
461
+ }
462
+ em(...props) {
463
+ return new Clapton.Emphasis(...props);
464
+ }
465
+ form(...props) {
466
+ return new Clapton.Form(...props);
467
+ }
468
+ h(...props) {
469
+ return new Clapton.Heading(props[0], props[1]);
470
+ }
471
+ img(...props) {
472
+ return new Clapton.Image(props[0], props[1]);
473
+ }
474
+ a(...props) {
475
+ return new Clapton.Link(props[0], props[1]);
476
+ }
477
+ li(...props) {
478
+ return new Clapton.ListItem(...props);
479
+ }
480
+ ul(...props) {
481
+ return new Clapton.List(...props);
482
+ }
483
+ ol(...props) {
484
+ return new Clapton.OrderedList(...props);
485
+ }
486
+ p(...props) {
487
+ return new Clapton.Paragraph(...props);
488
+ }
489
+ q(...props) {
490
+ return new Clapton.Quote(...props);
491
+ }
492
+ radio(...props) {
493
+ return new Clapton.RadioButton(props[0], props[1], props[2]);
494
+ }
495
+ select(...props) {
496
+ return new Clapton.Select(props[0], props[1], props[2]);
497
+ }
498
+ span(...props) {
499
+ return new Clapton.Span(...props);
500
+ }
501
+ textarea(...props) {
502
+ return new Clapton.TextArea(props[0], props[1], props[2]);
503
+ }
504
+ input(...props) {
505
+ return new Clapton.TextField(props[0], props[1], props[2]);
506
+ }
507
+ text(...props) {
508
+ return new Clapton.Text(props[0]);
509
+ }
510
+ }
511
+
512
+ class Component {
513
+ constructor(state = {}, id = Math.random().toString(36).substring(2, 10), errors = []) {
514
+ this._state = state;
515
+ this.id = id;
516
+ this._errors = errors;
517
+ this._root = new Box({ data: { component: this.constructor.name, state: JSON.stringify(this._state), id: this.id, errors: this._errors } });
518
+ this._c = new Presets();
519
+ }
520
+ get render() {
521
+ return this._root.render;
522
+ }
523
+ }
524
+
412
525
  exports.BlockQuote = BlockQuote;
526
+ exports.Bold = Bold;
413
527
  exports.Box = Box;
414
528
  exports.Button = Button;
415
529
  exports.Checkbox = Checkbox;
@@ -432,6 +546,7 @@ var Clapton = (function (exports) {
432
546
  exports.Select = Select;
433
547
  exports.Span = Span;
434
548
  exports.Text = Text;
549
+ exports.TextArea = TextArea;
435
550
  exports.TextField = TextField;
436
551
 
437
552
  return exports;
@@ -90,6 +90,20 @@ class Button {
90
90
  }
91
91
  }
92
92
 
93
+ class Bold {
94
+ constructor(attributes = {}) {
95
+ this.children = [];
96
+ this.attributes = attributes;
97
+ }
98
+ get render() {
99
+ return `<strong ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</strong>`;
100
+ }
101
+ add(child) {
102
+ this.children.push(child);
103
+ return this;
104
+ }
105
+ }
106
+
93
107
  class Checkbox {
94
108
  constructor(state, attribute, attributes = {}) {
95
109
  this.state = state;
@@ -369,12 +383,91 @@ class Span {
369
383
  }
370
384
  }
371
385
 
386
+ class Presets {
387
+ bq(...props) {
388
+ return new Clapton.BlockQuote(...props);
389
+ }
390
+ box(...props) {
391
+ return new Clapton.Box(...props);
392
+ }
393
+ b(...props) {
394
+ return new Clapton.Bold(...props);
395
+ }
396
+ button(...props) {
397
+ return new Clapton.Button(...props);
398
+ }
399
+ check(...props) {
400
+ return new Clapton.Checkbox(props[0], props[1], props[2]);
401
+ }
402
+ code(...props) {
403
+ return new Clapton.Code(...props);
404
+ }
405
+ datetime(...props) {
406
+ return new Clapton.DateTimeField(props[0], props[1], props[2]);
407
+ }
408
+ el(...props) {
409
+ return new Clapton.Element(props[0], props[1]);
410
+ }
411
+ embed(...props) {
412
+ return new Clapton.Embed(props[0]);
413
+ }
414
+ em(...props) {
415
+ return new Clapton.Emphasis(...props);
416
+ }
417
+ form(...props) {
418
+ return new Clapton.Form(...props);
419
+ }
420
+ h(...props) {
421
+ return new Clapton.Heading(props[0], props[1]);
422
+ }
423
+ img(...props) {
424
+ return new Clapton.Image(props[0], props[1]);
425
+ }
426
+ a(...props) {
427
+ return new Clapton.Link(props[0], props[1]);
428
+ }
429
+ li(...props) {
430
+ return new Clapton.ListItem(...props);
431
+ }
432
+ ul(...props) {
433
+ return new Clapton.List(...props);
434
+ }
435
+ ol(...props) {
436
+ return new Clapton.OrderedList(...props);
437
+ }
438
+ p(...props) {
439
+ return new Clapton.Paragraph(...props);
440
+ }
441
+ q(...props) {
442
+ return new Clapton.Quote(...props);
443
+ }
444
+ radio(...props) {
445
+ return new Clapton.RadioButton(props[0], props[1], props[2]);
446
+ }
447
+ select(...props) {
448
+ return new Clapton.Select(props[0], props[1], props[2]);
449
+ }
450
+ span(...props) {
451
+ return new Clapton.Span(...props);
452
+ }
453
+ textarea(...props) {
454
+ return new Clapton.TextArea(props[0], props[1], props[2]);
455
+ }
456
+ input(...props) {
457
+ return new Clapton.TextField(props[0], props[1], props[2]);
458
+ }
459
+ text(...props) {
460
+ return new Clapton.Text(props[0]);
461
+ }
462
+ }
463
+
372
464
  class Component {
373
465
  constructor(state = {}, id = Math.random().toString(36).substring(2, 10), errors = []) {
374
466
  this._state = state;
375
467
  this.id = id;
376
468
  this._errors = errors;
377
469
  this._root = new Box({ data: { component: this.constructor.name, state: JSON.stringify(this._state), id: this.id, errors: this._errors } });
470
+ this._c = new Presets();
378
471
  }
379
472
  get render() {
380
473
  return this._root.render;
@@ -406,8 +499,24 @@ class Text {
406
499
  }
407
500
  }
408
501
 
502
+ class TextArea {
503
+ constructor(state, attribute, attributes = {}) {
504
+ this.state = state;
505
+ this.attributes = attributes;
506
+ this.attribute = attribute;
507
+ this.attributes["data-attribute"] = attribute;
508
+ }
509
+ get render() {
510
+ return `<textarea ${htmlAttributes(this.attributes)}>${this.state[this.attribute] || ""}</textarea>`;
511
+ }
512
+ add_action(event, klass, fn, options = {}) {
513
+ this.attributes["data-action"] = `${this.attributes["data-action"] || ""} ${event}->${klass}#${fn}@${options.debounce || 0}`;
514
+ return this;
515
+ }
516
+ }
517
+
409
518
  const Clapton = {
410
- Box, Component, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span, Embed
519
+ Box, Component, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span, Embed, Bold, TextArea
411
520
  };
412
521
 
413
522
  export { Clapton };
@@ -9,12 +9,12 @@ export const handleAction = async (target: HTMLElement, stateName: string, fn: s
9
9
  }
10
10
  if (!targetComponent) return;
11
11
  const component = target.closest(`[data-component]`) as HTMLElement;
12
- const attribute = target.getAttribute("data-attribute");
12
+ const attribute = target.dataset.attribute;
13
13
  if (attribute) {
14
- const state = JSON.parse(component.getAttribute("data-state") || "{}");
14
+ const state = JSON.parse(component.dataset.state || "{}");
15
15
  if (target.tagName === "INPUT") {
16
16
  state[attribute] = (target as HTMLInputElement).value;
17
- component.setAttribute("data-state", JSON.stringify(state));
17
+ component.dataset.state = JSON.stringify(state);
18
18
  }
19
19
  };
20
20
  claptonChannel.perform(
@@ -23,14 +23,14 @@ export const handleAction = async (target: HTMLElement, stateName: string, fn: s
23
23
  data: {
24
24
  component: {
25
25
  name: stateName.replace("State", "Component"),
26
- id: targetComponent.getAttribute("data-id"),
26
+ id: targetComponent.dataset.id,
27
27
  },
28
28
  state: {
29
29
  name: stateName,
30
30
  action: fn,
31
- attributes: JSON.parse(targetComponent.getAttribute("data-state") || "{}"),
31
+ attributes: JSON.parse(targetComponent.dataset.state || "{}"),
32
32
  },
33
- params: JSON.parse(component.getAttribute("data-state") || "{}")
33
+ params: JSON.parse(component.dataset.state || "{}")
34
34
  }
35
35
  }
36
36
  );
@@ -0,0 +1,21 @@
1
+ import { describe, it, expect } from "vitest"
2
+ import { Text } from "./text"
3
+ import { Bold } from "./bold"
4
+
5
+ describe("Bold", () => {
6
+ it("returns empty string if no params", () => {
7
+ expect(new Bold().render).toBe("<strong ></strong>")
8
+ })
9
+
10
+ it("returns attributes and data attributes", () => {
11
+ expect(new Bold({ id: "1", "data-foo": "bar" }).render).toBe(`<strong id='1' data-foo='bar'></strong>`)
12
+ })
13
+
14
+
15
+ it("adds children", () => {
16
+ const text = new Text("Hello")
17
+ const bold = new Bold()
18
+ bold.add(text)
19
+ expect(bold.render).toBe(`<strong >Hello</strong>`)
20
+ })
21
+ })
@@ -0,0 +1,20 @@
1
+ import { htmlAttributes } from "../html/html-attributes";
2
+
3
+ export class Bold {
4
+ attributes: Record<string, any>;
5
+ children: any[];
6
+
7
+ constructor(attributes: Record<string, any> = {}) {
8
+ this.children = [];
9
+ this.attributes = attributes;
10
+ }
11
+
12
+ get render(): string {
13
+ return `<strong ${htmlAttributes(this.attributes)}>${this.children.map(child => child.render).join("")}</strong>`;
14
+ }
15
+
16
+ add(child: any): Bold {
17
+ this.children.push(child);
18
+ return this;
19
+ }
20
+ }
@@ -1,16 +1,19 @@
1
1
  import { Box } from "./box";
2
+ import { Presets } from "./presets";
2
3
 
3
4
  export class Component {
4
5
  id: string;
5
6
  _state: any;
6
7
  _errors: any[];
7
8
  _root: Box;
9
+ _c: Presets;
8
10
 
9
11
  constructor(state: any = {}, id: string = Math.random().toString(36).substring(2, 10), errors: any[] = []) {
10
12
  this._state = state;
11
13
  this.id = id;
12
14
  this._errors = errors;
13
15
  this._root = new Box({ data: { component: this.constructor.name, state: JSON.stringify(this._state), id: this.id, errors: this._errors } });
16
+ this._c = new Presets()
14
17
  }
15
18
 
16
19
  get render(): string {
@@ -0,0 +1,105 @@
1
+ import { Clapton } from "components";
2
+
3
+ export class Presets {
4
+ bq(...props: any[]) {
5
+ return new Clapton.BlockQuote(...props)
6
+ }
7
+
8
+ box(...props: any[]) {
9
+ return new Clapton.Box(...props)
10
+ }
11
+
12
+ b(...props: any[]) {
13
+ return new Clapton.Bold(...props)
14
+ }
15
+
16
+ button(...props: any[]) {
17
+ return new Clapton.Button(...props)
18
+ }
19
+
20
+ check(...props: any[]) {
21
+ return new Clapton.Checkbox(props[0], props[1], props[2])
22
+ }
23
+
24
+ code(...props: any[]) {
25
+ return new Clapton.Code(...props)
26
+ }
27
+
28
+ datetime(...props: any[]) {
29
+ return new Clapton.DateTimeField(props[0], props[1], props[2])
30
+ }
31
+
32
+ el(...props: any[]) {
33
+ return new Clapton.Element(props[0], props[1])
34
+ }
35
+
36
+ embed(...props: any[]) {
37
+ return new Clapton.Embed(props[0])
38
+ }
39
+
40
+ em(...props: any[]) {
41
+ return new Clapton.Emphasis(...props)
42
+ }
43
+
44
+ form(...props: any[]) {
45
+ return new Clapton.Form(...props)
46
+ }
47
+
48
+ h(...props: any[]) {
49
+ return new Clapton.Heading(props[0], props[1])
50
+ }
51
+
52
+ img(...props: any[]) {
53
+ return new Clapton.Image(props[0], props[1])
54
+ }
55
+
56
+ a(...props: any[]) {
57
+ return new Clapton.Link(props[0], props[1])
58
+ }
59
+
60
+ li(...props: any[]) {
61
+ return new Clapton.ListItem(...props)
62
+ }
63
+
64
+ ul(...props: any[]) {
65
+ return new Clapton.List(...props)
66
+ }
67
+
68
+ ol(...props: any[]) {
69
+ return new Clapton.OrderedList(...props)
70
+ }
71
+
72
+
73
+ p(...props: any[]) {
74
+ return new Clapton.Paragraph(...props)
75
+ }
76
+
77
+ q(...props: any[]) {
78
+ return new Clapton.Quote(...props)
79
+ }
80
+
81
+ radio(...props: any[]) {
82
+ return new Clapton.RadioButton(props[0], props[1], props[2])
83
+ }
84
+
85
+ select(...props: any[]) {
86
+ return new Clapton.Select(props[0], props[1], props[2])
87
+ }
88
+
89
+
90
+ span(...props: any[]) {
91
+ return new Clapton.Span(...props)
92
+ }
93
+
94
+ textarea(...props: any[]) {
95
+ return new Clapton.TextArea(props[0], props[1], props[2])
96
+ }
97
+
98
+ input(...props: any[]) {
99
+ return new Clapton.TextField(props[0], props[1], props[2])
100
+ }
101
+
102
+ text(...props: any[]) {
103
+ return new Clapton.Text(props[0])
104
+ }
105
+ }
@@ -1,5 +1,6 @@
1
1
  import { BlockQuote } from "./components/block-quote"
2
2
  import { Box } from "./components/box"
3
+ import { Bold } from "./components/bold"
3
4
  import { Button } from "./components/button"
4
5
  import { Checkbox } from "./components/checkbox"
5
6
  import { Code } from "./components/code"
@@ -22,8 +23,8 @@ import { Span } from "./components/span"
22
23
  import { Component } from "./components/component"
23
24
  import { TextField } from "./components/text-field"
24
25
  import { Text } from "./components/text"
25
-
26
+ import { TextArea } from "./components/text-area"
26
27
 
27
28
  export {
28
- Box, Component, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span, Embed
29
+ Component, Box, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span, Embed, Bold, TextArea
29
30
  };
@@ -1,6 +1,7 @@
1
1
  import { BlockQuote } from "./components/block-quote"
2
2
  import { Box } from "./components/box"
3
3
  import { Button } from "./components/button"
4
+ import { Bold } from "./components/bold"
4
5
  import { Checkbox } from "./components/checkbox"
5
6
  import { Code } from "./components/code"
6
7
  import { DateTimeField } from "./components/datetime-field"
@@ -22,8 +23,8 @@ import { Span } from "./components/span"
22
23
  import { Component } from "./components/component"
23
24
  import { TextField } from "./components/text-field"
24
25
  import { Text } from "./components/text"
25
-
26
+ import { TextArea } from "./components/text-area"
26
27
 
27
28
  export const Clapton = {
28
- Box, Component, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span, Embed
29
+ Box, Component, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span, Embed, Bold, TextArea
29
30
  };
@@ -2,8 +2,7 @@ import morphdom from "morphdom";
2
2
 
3
3
  export const updateComponent = async (component: HTMLElement, state: any, property: string, target: HTMLInputElement) => {
4
4
  state[property] = target.value;
5
- component.setAttribute("data-state", JSON.stringify(state));
6
- const componentName = component.getAttribute("data-component") as string;
5
+ const componentName = component.dataset.component as string;
7
6
  const module = await import(`${componentName}`);
8
7
  const ComponentClass = module[componentName] as any;
9
8
  const instance = new ComponentClass(state, component.dataset.id);
@@ -3,14 +3,22 @@ module Clapton
3
3
  module Base
4
4
  require "execjs"
5
5
 
6
- def render_component(component, **kwargs)
6
+ def render_component(component, params)
7
7
  js = File.read(File.join(__dir__, "..", "javascripts", "dist", "components-for-test.js"))
8
8
  Dir.glob(Rails.root.join("app", "components", "**", "*.rb")).each do |file|
9
- js += Ruby2JS.convert(File.read(file), preset: true)
9
+ code = File.read(file)
10
+ code = code.gsub(/([^a-zA-Z0-9])c\.(\w+?)\(/, '\1@c.\2(')
11
+ code = code.gsub(/([^a-zA-Z0-9])c\.(\w+?)\./, '\1@c.\2().')
12
+ js += Ruby2JS.convert(code, preset: true)
10
13
  js += "\n"
11
14
  end
15
+ js = js.sub("const Clapton = {
16
+ Box, Component, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span, Embed, Bold, TextArea
17
+ };", "const Clapton = {
18
+ Box, Text, TextField, Button, DateTimeField, BlockQuote, Checkbox, Code, Element, Emphasis, Form, Heading, Image, Link, List, ListItem, OrderedList, Paragraph, Quote, RadioButton, Select, Span, Embed, Bold, TextArea
19
+ };")
12
20
  context = ExecJS.compile(js)
13
- html = context.eval("new #{component.name.camelize}(#{kwargs.to_json}).render")
21
+ html = context.eval("new #{component.name.camelize}(#{params.to_json}).render")
14
22
  @page = Capybara.string(html)
15
23
  end
16
24
 
@@ -1,4 +1,3 @@
1
1
  module Clapton
2
- VERSION = '0.0.14'
2
+ VERSION = '0.0.16'
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.14
4
+ version: 0.0.16
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-15 00:00:00.000000000 Z
11
+ date: 2024-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -6708,6 +6708,8 @@ files:
6708
6708
  - lib/clapton/javascripts/src/components.ts
6709
6709
  - lib/clapton/javascripts/src/components/block-quote.spec.ts
6710
6710
  - lib/clapton/javascripts/src/components/block-quote.ts
6711
+ - lib/clapton/javascripts/src/components/bold.spec.ts
6712
+ - lib/clapton/javascripts/src/components/bold.ts
6711
6713
  - lib/clapton/javascripts/src/components/box.spec.ts
6712
6714
  - lib/clapton/javascripts/src/components/box.ts
6713
6715
  - lib/clapton/javascripts/src/components/button.spec.ts
@@ -6716,7 +6718,6 @@ files:
6716
6718
  - lib/clapton/javascripts/src/components/checkbox.ts
6717
6719
  - lib/clapton/javascripts/src/components/code.spec.ts
6718
6720
  - lib/clapton/javascripts/src/components/code.ts
6719
- - lib/clapton/javascripts/src/components/component.spec.ts
6720
6721
  - lib/clapton/javascripts/src/components/component.ts
6721
6722
  - lib/clapton/javascripts/src/components/datetime-field.spec.ts
6722
6723
  - lib/clapton/javascripts/src/components/datetime-field.ts
@@ -6742,6 +6743,7 @@ files:
6742
6743
  - lib/clapton/javascripts/src/components/ordered-list.ts
6743
6744
  - lib/clapton/javascripts/src/components/paragraph.spec.ts
6744
6745
  - lib/clapton/javascripts/src/components/paragraph.ts
6746
+ - lib/clapton/javascripts/src/components/presets.ts
6745
6747
  - lib/clapton/javascripts/src/components/quote.spec.ts
6746
6748
  - lib/clapton/javascripts/src/components/quote.ts
6747
6749
  - lib/clapton/javascripts/src/components/radio-button.spec.ts
@@ -1,16 +0,0 @@
1
- import { describe, it, expect } from "vitest"
2
- import { Component } from "./component"
3
- import { Text } from "./text"
4
-
5
- class TestComponent extends Component {
6
- get render() {
7
- this._root.add(new Text("Hello, world!"))
8
- return this._root.render
9
- }
10
- }
11
-
12
- describe("Component", () => {
13
- it("returns empty string if no params", () => {
14
- expect(new TestComponent().render).toMatch(/<div data-component='TestComponent' data-state='{}' data-id='.{8}'>Hello, world!<\/div>/)
15
- })
16
- })