clapton 0.0.14 → 0.0.16

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: 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
- })