glimmer-dsl-web 0.0.1 → 0.0.3

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: 8f25d5b94aa413809d21b46a16bbbe525b3166ff6dc62789942410465795858c
4
- data.tar.gz: 611add41ae0b8db1e9e964be7dbcecc6b046dafac023e109ab9c6e4f51943c24
3
+ metadata.gz: 10f466c47fb515cb9cd28f7eb49920c1cb52ba68126ccd85a94c321f807b43a6
4
+ data.tar.gz: ac50a3b1d5f3d96b4b6770d5a725f900840aca77da0ccef7c7773a31fa43c4df
5
5
  SHA512:
6
- metadata.gz: 8cde325767663eaf70334758888a1c65708b77b41a626f3ad1d990135ebbf8aa424457cf79026d4893a62968b7737661ec5f89cede81f98632a7f69be53e1e59
7
- data.tar.gz: f9d3a94e629ca6eae4663df1e1eb89f5b4f013c225aa1948e7c8acc8420061f9740559dc8a591388720f214646d0a62ab9e5f77265ed34faeb0437c1ea18fb6c
6
+ metadata.gz: e6fc2635c32e91c06843bb230b26e600eefda14c9e2b4b9ca22f4dcb26ebaf5e341dee90074fcedd356a506b2c9f169a5ff51104b3afba37bad6b80b9c570cad
7
+ data.tar.gz: cccb3243bd7351b40564aacabf1c866271b5d6a53ddd0ebda4441b8b90274370469bfa3ba3f38b68d16827850df94fa57dfe7408e2e5091097a4120c4577cc6d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.0.3
4
+
5
+ - Set Glimmer specific element attributes (e.g. `parent`) as data attributes on generated HTML elements
6
+ - Support setting text content by passing as first argument to an element as an alternative to block return value
7
+ - Proxy method/attribute invocations on an element to its HTML element (e.g. `input_element.check_validity` proxies to `checkValidity` JS function)
8
+ - Support JS listeners like `onclick` by nesting an `on_someeventname` block under an element (e.g. `on_click { ... }`)
9
+ - New Hello, Button! Sample: `require 'glimmer-dsl-web/samples/hello/hello_button'`
10
+
11
+ ## 0.0.2
12
+
13
+ - Rename element `:root` option to `:parent` option
14
+ - Set `body` as parent by default if `:parent` option is not specified for a root element
15
+ - Set `class` instead of `id` on generated HTML elements to identify them (e.g. `element-2`).
16
+ - New Hello, World! Sample: `require 'glimmer-dsl-web/samples/hello/hello_world'`
17
+
3
18
  ## 0.0.1
4
19
 
5
20
  - Render top-level HTML element + attributes under a root parent element by specifying `root: 'css_selector'` option (e.g. `div(root: 'css_selector')`)
data/README.md CHANGED
@@ -1,21 +1,25 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Web 0.0.1
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Web 0.0.3 (Early Alpha)
2
2
  ## Ruby in the Browser Web GUI Library
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-web.svg)](http://badge.fury.io/rb/glimmer-dsl-web)
4
4
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5
5
 
6
6
  This project is inspired-by [Glimmer DSL for Opal](https://github.com/AndyObtiva/glimmer-dsl-opal) and is similar in enabling frontend GUI development with Ruby, but it mainly differs from Glimmer DSL for Opal by adopting a DSL that follows web-like HTML syntax in Ruby (enabling transfer of HTML/CSS/JS skills) instead of adopting a desktop GUI DSL that is webified. Also, it will begin by supporting [Opal Ruby](https://opalrb.com/), but it might grow to support [Ruby WASM](https://github.com/ruby/ruby.wasm) as an alternative to [Opal Ruby](https://opalrb.com/) that could be switched to with a simple configuration change.
7
7
 
8
+ Note that the library is an Early Alpha and its APIs might change frequently until hitting a minor release at least.
9
+
8
10
  ### You can finally live in pure Rubyland on the web!
9
11
 
10
12
  [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for Web is an upcoming **pre-alpha** [gem](https://rubygems.org/gems/glimmer-dsl-web) that enables building web GUI in pure Ruby via [Opal](https://opalrb.com/) on [Rails](https://rubyonrails.org/) (and potentially [Ruby WASM](https://github.com/ruby/ruby.wasm) in the future).
11
13
 
12
- **Hello, World! Sample**
14
+ **Sample**
13
15
 
14
16
  Initial HTML Markup:
15
17
 
16
18
  ```html
19
+ ...
17
20
  <div id="app-container">
18
21
  </div>
22
+ ...
19
23
  ```
20
24
 
21
25
  Glimmer GUI code:
@@ -25,28 +29,228 @@ require 'glimmer-dsl-web'
25
29
 
26
30
  include Glimmer
27
31
 
28
- # This will hook into element #application and then build HTML inside it using Ruby DSL code
29
- div(root: '#app-container') {
30
- label(class: 'greeting') {
31
- 'Hello, World!'
32
- }
33
- }
32
+ Document.ready? do
33
+ # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
34
+ div(parent: '#app-container') {
35
+ label(class: 'greeting') {
36
+ 'Hello, World!'
37
+ }
38
+ }.render
39
+ end
34
40
  ```
35
41
 
36
42
  That produces:
37
43
 
38
44
  ```html
45
+ ...
39
46
  <div id="app-container">
40
- <div root="#app-container" id="element-1" class="element">
41
- <label class="greeting element" id="element-2">
47
+ <div parent="#app-container" class="element element-1">
48
+ <label class="greeting element element-2">
42
49
  Hello, World!
43
50
  </label>
44
51
  </div>
45
52
  </div>
53
+ ...
46
54
  ```
47
55
 
56
+ ![setup is working](/images/glimmer-dsl-web-setup-example-working.png)
57
+
58
+ **Hello, World! Sample**
59
+
60
+ Glimmer GUI code:
61
+
62
+ ```ruby
63
+ require 'glimmer-dsl-web'
64
+
65
+ include Glimmer
66
+
67
+ Document.ready? do
68
+ div {
69
+ 'Hello, World!'
70
+ }.render
71
+ end
72
+ ```
73
+
74
+ That produces the following under `<body></body>`:
75
+
76
+ ```html
77
+ <div parent="body" class="element element-1">
78
+ Hello, World!
79
+ </div>
80
+ ```
81
+
82
+ ![setup is working](/images/glimmer-dsl-web-setup-example-working.png)
83
+
48
84
  **Hello, Button! Sample**
49
85
 
86
+ Glimmer GUI code:
87
+
88
+ ```ruby
89
+ require 'glimmer-dsl-web'
90
+
91
+ include Glimmer
92
+
93
+ Document.ready? do
94
+ div {
95
+ h1('Contact Form')
96
+ form {
97
+ div(class: 'field-row') {
98
+ label('Name: ', for: 'name-field')
99
+ @name_input = input(id: 'name-field', class: 'field', type: 'text', required: true)
100
+ }
101
+ div(class: 'field-row') {
102
+ label('Email: ', for: 'email-field')
103
+ @email_input = input(id: 'email-field', class: 'field', type: 'email', required: true)
104
+ }
105
+ @add_button = button('Add Contact', class: 'submit-button') {
106
+ on_click do
107
+ if ([@name_input, @email_input].all? {|input| input.check_validity })
108
+ @table.content {
109
+ tr {
110
+ td { @name_input.value }
111
+ td { @email_input.value }
112
+ }
113
+ }
114
+ @email_input.value = @name_input.value = ''
115
+ else
116
+ error_messages = []
117
+ error_messages << "Name is not valid! Make sure it is filled." if !@name_input.check_validity
118
+ error_messages << "Email is not valid! Make sure it is filled and has a valid format." if !@email_input.check_validity
119
+ $$.alert(error_messages.join("\n"))
120
+ end
121
+ end
122
+ }
123
+ }
124
+ h1('Contacts Table')
125
+ @table = table {
126
+ tr {
127
+ th('Name')
128
+ th('Email')
129
+ }
130
+ tr {
131
+ td('John Doe')
132
+ td('johndoe@example.com')
133
+ }
134
+ tr {
135
+ td('Jane Doe')
136
+ td('janedoe@example.com')
137
+ }
138
+ }
139
+
140
+ # CSS Styles
141
+ style {
142
+ <<~CSS
143
+ .field-row {
144
+ margin: 10px 5px;
145
+ }
146
+ .field {
147
+ margin-left: 5px;
148
+ }
149
+ .submit-button {
150
+ display: block;
151
+ margin: 10px 5px;
152
+ }
153
+ table {
154
+ border:1px solid grey;
155
+ border-spacing: 0;
156
+ }
157
+ table tr td, table tr th {
158
+ padding: 5px;
159
+ }
160
+ table tr:nth-child(even) {
161
+ background: #ccc;
162
+ }
163
+ CSS
164
+ }
165
+ }.render
166
+ end
167
+ ```
168
+
169
+ That produces the following under `<body></body>`:
170
+
171
+ ```html
172
+ <div data-parent="body" class="element element-1">
173
+ <h1 class="element element-2">Contact Form</h1>
174
+ <form class="element element-3">
175
+ <div class="field-row element element-4">
176
+ <label for="name-field" class="element element-5">Name: </label>
177
+ <input id="name-field" class="field element element-6" type="text" required="true">
178
+ </div>
179
+ <div class="field-row element element-7">
180
+ <label for="email-field" class="element element-8">Email: </label>
181
+ <input id="email-field" class="field element element-9" type="email" required="true">
182
+ </div>
183
+ <button class="submit-button element element-10">Add Contact</button>
184
+ </form>
185
+ <h1 class="element element-11">Contacts Table</h1>
186
+ <table class="element element-12">
187
+ <tr class="element element-13">
188
+ <th class="element element-14">Name</th>
189
+ <th class="element element-15">Email</th>
190
+ </tr>
191
+ <tr class="element element-16">
192
+ <td class="element element-17">John Doe</td>
193
+ <td class="element element-18">johndoe@example.com</td>
194
+ </tr>
195
+ <tr class="element element-19">
196
+ <td class="element element-20">Jane Doe</td>
197
+ <td class="element element-21">janedoe@example.com</td>
198
+ </tr>
199
+ </table>
200
+ <style class="element element-22">.field-row {
201
+ margin: 10px 5px;
202
+ }
203
+ .field {
204
+ margin-left: 5px;
205
+ }
206
+ .submit-button {
207
+ display: block;
208
+ margin: 10px 5px;
209
+ }
210
+ table {
211
+ border:1px solid grey;
212
+ border-spacing: 0;
213
+ }
214
+ table tr td, table tr th {
215
+ padding: 5px;
216
+ }
217
+ table tr:nth-child(even) {
218
+ background: #ccc;
219
+ }
220
+ </style>
221
+ </div>
222
+ ```
223
+
224
+ Screenshots:
225
+
226
+ ---
227
+
228
+ ***Hello, Button!***
229
+
230
+ ![Hello, Button!](/images/glimmer-dsl-web-samples-hello-hello-button.png)
231
+
232
+ ---
233
+
234
+ ***Hello, Button! Submitted Invalid Data***
235
+
236
+ ![Hello, Button! Invalid Data](/images/glimmer-dsl-web-samples-hello-hello-button-invalid-data.png)
237
+
238
+ ---
239
+
240
+ ***Hello, Button! Filled Valid Name and Email***
241
+
242
+ ![Hello, Button! Filled](/images/glimmer-dsl-web-samples-hello-hello-button-filled-name-and-email.png)
243
+
244
+ ---
245
+
246
+ ***Hello, Button! Added Contact***
247
+
248
+ ![Hello, Button! Added Contact](/images/glimmer-dsl-web-samples-hello-hello-button-added-contact.png)
249
+
250
+ ---
251
+
252
+ **Button Counter Sample**
253
+
50
254
  **UPCOMING (NOT RELEASED OR SUPPORTED YET)**
51
255
 
52
256
  Glimmer GUI code demonstrating MVC + Glimmer Web Components (Views) + Data-Binding:
@@ -74,9 +278,9 @@ class HelloButton
74
278
  end
75
279
 
76
280
  markup {
77
- # This will hook into element #application and then build HTML inside it using Ruby DSL code
281
+ # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
78
282
  div(root_css_selector) {
79
- text 'Hello, Button!'
283
+ text 'Button Counter'
80
284
 
81
285
  button {
82
286
  # Unidirectional Data-Binding indicating that on every change to @counter.count, the value
@@ -92,7 +296,7 @@ class HelloButton
92
296
  }
93
297
  end
94
298
 
95
- HelloButton.render('#application')
299
+ HelloButton.render('#app-container')
96
300
  ```
97
301
 
98
302
  That produces:
@@ -145,6 +349,7 @@ Learn more about the differences between various [Glimmer](https://github.com/An
145
349
  - [Hello Samples](#hello-samples)
146
350
  - [Hello, World!](#hello-world)
147
351
  - [Hello, Button!](#hello-button)
352
+ - [Button Counter](#button-counter)
148
353
  - [Glimmer Process](#glimmer-process)
149
354
  - [Help](#help)
150
355
  - [Issues](#issues)
@@ -191,7 +396,7 @@ gem 'opal', '1.4.1'
191
396
  gem 'opal-rails', '2.0.2'
192
397
  gem 'opal-async', '~> 1.4.0'
193
398
  gem 'opal-jquery', '~> 0.4.6'
194
- gem 'glimmer-dsl-web', '~> 0.0.1'
399
+ gem 'glimmer-dsl-web', '~> 0.0.3'
195
400
  gem 'glimmer-dsl-xml', '~> 1.3.1', require: false
196
401
  gem 'glimmer-dsl-css', '~> 1.2.1', require: false
197
402
  ```
@@ -255,8 +460,10 @@ Example to confirm setup is working:
255
460
  Initial HTML Markup:
256
461
 
257
462
  ```html
463
+ ...
258
464
  <div id="app-container">
259
465
  </div>
466
+ ...
260
467
  ```
261
468
 
262
469
  Glimmer GUI code:
@@ -266,24 +473,28 @@ require 'glimmer-dsl-web'
266
473
 
267
474
  include Glimmer
268
475
 
269
- # This will hook into element #application and then build HTML inside it using Ruby DSL code
270
- div(root: '#app-container') {
271
- label(class: 'greeting') {
272
- 'Hello, World!'
273
- }
274
- }
476
+ Document.ready? do
477
+ # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
478
+ div(parent: '#app-container') {
479
+ label(class: 'greeting') {
480
+ 'Hello, World!'
481
+ }
482
+ }.render
483
+ end
275
484
  ```
276
485
 
277
486
  That produces:
278
487
 
279
488
  ```html
489
+ ...
280
490
  <div id="app-container">
281
- <div root="#app-container" id="element-1" class="element">
282
- <label class="greeting element" id="element-2">
491
+ <div parent="#app-container" class="element element-1">
492
+ <label class="greeting element element-2">
283
493
  Hello, World!
284
494
  </label>
285
495
  </div>
286
496
  </div>
497
+ ...
287
498
  ```
288
499
 
289
500
  Start the Rails server:
@@ -330,7 +541,7 @@ gem 'opal', '1.4.1'
330
541
  gem 'opal-rails', '2.0.2'
331
542
  gem 'opal-async', '~> 1.4.0'
332
543
  gem 'opal-jquery', '~> 0.4.6'
333
- gem 'glimmer-dsl-web', '~> 0.0.1'
544
+ gem 'glimmer-dsl-web', '~> 0.0.3'
334
545
  gem 'glimmer-dsl-xml', '~> 1.3.1', require: false
335
546
  gem 'glimmer-dsl-css', '~> 1.2.1', require: false
336
547
  ```
@@ -398,8 +609,10 @@ Example to confirm setup is working:
398
609
  Initial HTML Markup:
399
610
 
400
611
  ```html
612
+ ...
401
613
  <div id="app-container">
402
614
  </div>
615
+ ...
403
616
  ```
404
617
 
405
618
  Glimmer GUI code:
@@ -409,24 +622,28 @@ require 'glimmer-dsl-web'
409
622
 
410
623
  include Glimmer
411
624
 
412
- # This will hook into element #application and then build HTML inside it using Ruby DSL code
413
- div(root: '#app-container') {
414
- label(class: 'greeting') {
415
- 'Hello, World!'
416
- }
417
- }
625
+ Document.ready? do
626
+ # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
627
+ div(parent: '#app-container') {
628
+ label(class: 'greeting') {
629
+ 'Hello, World!'
630
+ }
631
+ }.render
632
+ end
418
633
  ```
419
634
 
420
635
  That produces:
421
636
 
422
637
  ```html
638
+ ...
423
639
  <div id="app-container">
424
- <div root="#app-container" id="element-1" class="element">
425
- <label class="greeting element" id="element-2">
640
+ <div parent="#app-container" class="element element-1">
641
+ <label class="greeting element element-2">
426
642
  Hello, World!
427
643
  </label>
428
644
  </div>
429
645
  </div>
646
+ ...
430
647
  ```
431
648
 
432
649
  Start the Rails server:
@@ -464,13 +681,30 @@ https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app
464
681
 
465
682
  #### Hello, World!
466
683
 
467
- Initial HTML Markup:
684
+ Glimmer GUI code:
685
+
686
+ ```ruby
687
+ require 'glimmer-dsl-web'
688
+
689
+ include Glimmer
690
+
691
+ Document.ready? do
692
+ div {
693
+ 'Hello, World!'
694
+ }.render
695
+ end
696
+ ```
697
+
698
+ That produces the following under `<body></body>`:
468
699
 
469
700
  ```html
470
- <div id="app-container">
701
+ <div parent="body" class="element element-1">
702
+ Hello, World!
471
703
  </div>
472
704
  ```
473
705
 
706
+ #### Hello, Button!
707
+
474
708
  Glimmer GUI code:
475
709
 
476
710
  ```ruby
@@ -478,27 +712,166 @@ require 'glimmer-dsl-web'
478
712
 
479
713
  include Glimmer
480
714
 
481
- # This will hook into element #application and then build HTML inside it using Ruby DSL code
482
- div(root: '#application') {
483
- label(class: 'greeting') {
484
- 'Hello, World!'
485
- }
486
- }
715
+ Document.ready? do
716
+ div {
717
+ h1('Contact Form')
718
+ form {
719
+ div(class: 'field-row') {
720
+ label('Name: ', for: 'name-field')
721
+ @name_input = input(id: 'name-field', class: 'field', type: 'text', required: true)
722
+ }
723
+ div(class: 'field-row') {
724
+ label('Email: ', for: 'email-field')
725
+ @email_input = input(id: 'email-field', class: 'field', type: 'email', required: true)
726
+ }
727
+ @add_button = button('Add Contact', class: 'submit-button') {
728
+ on_click do
729
+ if ([@name_input, @email_input].all? {|input| input.check_validity })
730
+ @table.content {
731
+ tr {
732
+ td { @name_input.value }
733
+ td { @email_input.value }
734
+ }
735
+ }
736
+ @email_input.value = @name_input.value = ''
737
+ else
738
+ error_messages = []
739
+ error_messages << "Name is not valid! Make sure it is filled." if !@name_input.check_validity
740
+ error_messages << "Email is not valid! Make sure it is filled and has a valid format." if !@email_input.check_validity
741
+ $$.alert(error_messages.join("\n"))
742
+ end
743
+ end
744
+ }
745
+ }
746
+ h1('Contacts Table')
747
+ @table = table {
748
+ tr {
749
+ th('Name')
750
+ th('Email')
751
+ }
752
+ tr {
753
+ td('John Doe')
754
+ td('johndoe@example.com')
755
+ }
756
+ tr {
757
+ td('Jane Doe')
758
+ td('janedoe@example.com')
759
+ }
760
+ }
761
+
762
+ # CSS Styles
763
+ style {
764
+ <<~CSS
765
+ .field-row {
766
+ margin: 10px 5px;
767
+ }
768
+ .field {
769
+ margin-left: 5px;
770
+ }
771
+ .submit-button {
772
+ display: block;
773
+ margin: 10px 5px;
774
+ }
775
+ table {
776
+ border:1px solid grey;
777
+ border-spacing: 0;
778
+ }
779
+ table tr td, table tr th {
780
+ padding: 5px;
781
+ }
782
+ table tr:nth-child(even) {
783
+ background: #ccc;
784
+ }
785
+ CSS
786
+ }
787
+ }.render
788
+ end
487
789
  ```
488
790
 
489
- That produces:
791
+ That produces the following under `<body></body>`:
490
792
 
491
793
  ```html
492
- <div id="app-container">
493
- <div root="#app-container" id="element-1" class="element">
494
- <label class="greeting element" id="element-2">
495
- Hello, World!
496
- </label>
497
- </div>
794
+ <div data-parent="body" class="element element-1">
795
+ <h1 class="element element-2">Contact Form</h1>
796
+ <form class="element element-3">
797
+ <div class="field-row element element-4">
798
+ <label for="name-field" class="element element-5">Name: </label>
799
+ <input id="name-field" class="field element element-6" type="text" required="true">
800
+ </div>
801
+ <div class="field-row element element-7">
802
+ <label for="email-field" class="element element-8">Email: </label>
803
+ <input id="email-field" class="field element element-9" type="email" required="true">
804
+ </div>
805
+ <button class="submit-button element element-10">Add Contact</button>
806
+ </form>
807
+ <h1 class="element element-11">Contacts Table</h1>
808
+ <table class="element element-12">
809
+ <tr class="element element-13">
810
+ <th class="element element-14">Name</th>
811
+ <th class="element element-15">Email</th>
812
+ </tr>
813
+ <tr class="element element-16">
814
+ <td class="element element-17">John Doe</td>
815
+ <td class="element element-18">johndoe@example.com</td>
816
+ </tr>
817
+ <tr class="element element-19">
818
+ <td class="element element-20">Jane Doe</td>
819
+ <td class="element element-21">janedoe@example.com</td>
820
+ </tr>
821
+ </table>
822
+ <style class="element element-22">.field-row {
823
+ margin: 10px 5px;
824
+ }
825
+ .field {
826
+ margin-left: 5px;
827
+ }
828
+ .submit-button {
829
+ display: block;
830
+ margin: 10px 5px;
831
+ }
832
+ table {
833
+ border:1px solid grey;
834
+ border-spacing: 0;
835
+ }
836
+ table tr td, table tr th {
837
+ padding: 5px;
838
+ }
839
+ table tr:nth-child(even) {
840
+ background: #ccc;
841
+ }
842
+ </style>
498
843
  </div>
499
844
  ```
500
845
 
501
- #### Hello, Button!
846
+ Screenshots:
847
+
848
+ ---
849
+
850
+ ***Hello, Button!***
851
+
852
+ ![Hello, Button!](/images/glimmer-dsl-web-samples-hello-hello-button.png)
853
+
854
+ ---
855
+
856
+ ***Hello, Button! Submitted Invalid Data***
857
+
858
+ ![Hello, Button! Invalid Data](/images/glimmer-dsl-web-samples-hello-hello-button-invalid-data.png)
859
+
860
+ ---
861
+
862
+ ***Hello, Button! Filled Valid Name and Email***
863
+
864
+ ![Hello, Button! Filled](/images/glimmer-dsl-web-samples-hello-hello-button-filled-name-and-email.png)
865
+
866
+ ---
867
+
868
+ ***Hello, Button! Added Contact***
869
+
870
+ ![Hello, Button! Added Contact](/images/glimmer-dsl-web-samples-hello-hello-button-added-contact.png)
871
+
872
+ ---
873
+
874
+ #### Button Counter
502
875
 
503
876
  **UPCOMING (NOT RELEASED OR SUPPORTED YET)**
504
877
 
@@ -527,9 +900,9 @@ class HelloButton
527
900
  end
528
901
 
529
902
  markup {
530
- # This will hook into element #application and then build HTML inside it using Ruby DSL code
903
+ # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
531
904
  div(root_css_selector) {
532
- text 'Hello, Button!'
905
+ text 'Button Counter'
533
906
 
534
907
  button {
535
908
  # Unidirectional Data-Binding indicating that on every change to @counter.count, the value
@@ -545,7 +918,7 @@ class HelloButton
545
918
  }
546
919
  end
547
920
 
548
- HelloButton.render('#application')
921
+ HelloButton.render('#app-container')
549
922
  ```
550
923
 
551
924
  That produces:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.3
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: glimmer-dsl-web 0.0.1 ruby lib
5
+ # stub: glimmer-dsl-web 0.0.3 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-web".freeze
9
- s.version = "0.0.1".freeze
9
+ s.version = "0.0.3".freeze
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Andy Maleh".freeze]
14
- s.date = "2023-12-27"
14
+ s.date = "2023-12-28"
15
15
  s.description = "Glimmer DSL for Web (Ruby in the Browser Web GUI Library) - Enables frontend GUI development with Ruby by adopting a DSL that follows web-like HTML syntax, enabling the transfer of HTML/CSS/JS skills to Ruby frontend development. This library relies on Opal Ruby.".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.extra_rdoc_files = [
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
30
30
  "lib/glimmer-dsl-web/ext/class.rb",
31
31
  "lib/glimmer-dsl-web/ext/date.rb",
32
32
  "lib/glimmer-dsl-web/ext/exception.rb",
33
+ "lib/glimmer-dsl-web/samples/hello/hello_button.rb",
33
34
  "lib/glimmer-dsl-web/samples/hello/hello_world.rb",
34
35
  "lib/glimmer-dsl-web/vendor/jquery.js",
35
36
  "lib/glimmer/config/opal_logger.rb",
@@ -37,9 +38,11 @@ Gem::Specification.new do |s|
37
38
  "lib/glimmer/data_binding/observable_element.rb",
38
39
  "lib/glimmer/dsl/web/dsl.rb",
39
40
  "lib/glimmer/dsl/web/element_expression.rb",
41
+ "lib/glimmer/dsl/web/listener_expression.rb",
40
42
  "lib/glimmer/util/proc_tracker.rb",
41
43
  "lib/glimmer/web.rb",
42
44
  "lib/glimmer/web/element_proxy.rb",
45
+ "lib/glimmer/web/listener_proxy.rb",
43
46
  "lib/glimmer/web/property_owner.rb"
44
47
  ]
45
48
  s.homepage = "http://github.com/AndyObtiva/glimmer-dsl-web".freeze
@@ -1,13 +1,14 @@
1
1
  require 'glimmer/dsl/engine'
2
2
  # Dir[File.expand_path('../*_expression.rb', __FILE__)].each {|f| require f}
3
3
  require 'glimmer/dsl/web/element_expression'
4
+ require 'glimmer/dsl/web/listener_expression'
4
5
 
5
6
  module Glimmer
6
7
  module DSL
7
8
  module Web
8
9
  # TODO implement all those expressions
9
10
  # %w[
10
- # event_listener
11
+ # listener
11
12
  # data_binding
12
13
  # attribute
13
14
  # shine_data_binding
@@ -16,6 +17,7 @@ module Glimmer
16
17
  Engine.add_dynamic_expressions(
17
18
  Web,
18
19
  %w[
20
+ listener
19
21
  element
20
22
  ]
21
23
  )
@@ -9,10 +9,9 @@ module Glimmer
9
9
  include ParentExpression
10
10
 
11
11
  def can_interpret?(parent, keyword, *args, &block)
12
- # TODO automatically pass root as element if not passed instead of rejecting elements without a paraent nor root
12
+ # TODO automatically pass parent option as element if not passed instead of rejecting elements without a paraent nor root
13
13
  # TODO raise a proper error if root is an element that is not found (maybe do this in model)
14
- options = args.last.is_a?(Hash) ? args.last : {}
15
- (parent or options[:root])
14
+ !keyword.to_s.start_with?('on_')
16
15
  end
17
16
 
18
17
  def interpret(parent, keyword, *args, &block)
@@ -22,7 +21,7 @@ module Glimmer
22
21
  def add_content(parent, keyword, *args, &block)
23
22
  if parent.rendered? || parent.skip_content_on_render_blocks?
24
23
  return_value = super(parent, keyword, *args, &block)
25
- if return_value.is_a?(String)
24
+ if return_value.is_a?(String) && parent.dom_element.text.to_s.empty?
26
25
  parent.add_text_content(return_value)
27
26
  end
28
27
  parent.post_add_content
@@ -0,0 +1,19 @@
1
+ require 'glimmer/dsl/expression'
2
+
3
+ module Glimmer
4
+ module DSL
5
+ module Web
6
+ class ListenerExpression < Expression
7
+ def can_interpret?(parent, keyword, *args, &block)
8
+ parent and
9
+ parent.respond_to?(:can_handle_observation_request?) and
10
+ parent.can_handle_observation_request?(keyword)
11
+ end
12
+
13
+ def interpret(parent, keyword, *args, &block)
14
+ parent.handle_observation_request(keyword, block)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020-2022 Andy Maleh
1
+ # Copyright (c) 2023 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020-2022 Andy Maleh
1
+ # Copyright (c) 2023 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -21,6 +21,7 @@
21
21
 
22
22
  # require 'glimmer/web/event_listener_proxy'
23
23
  require 'glimmer/web/property_owner'
24
+ require 'glimmer/web/listener_proxy'
24
25
 
25
26
  # TODO implement menu (which delays building it till render using add_content_on_render)
26
27
 
@@ -68,10 +69,12 @@ module Glimmer
68
69
 
69
70
  include Glimmer
70
71
  include PropertyOwner
71
-
72
+
72
73
  Event = Struct.new(:widget, keyword_init: true)
73
-
74
- attr_reader :keyword, :parent, :args, :options, :path, :children, :enabled, :foreground, :background, :focus, :removed?, :rendered
74
+
75
+ GLIMMER_ATTRIBUTES = [:parent]
76
+
77
+ attr_reader :keyword, :parent, :args, :options, :children, :enabled, :foreground, :background, :focus, :removed?, :rendered
75
78
  alias rendered? rendered
76
79
 
77
80
  def initialize(keyword, parent, args, block)
@@ -106,7 +109,7 @@ module Glimmer
106
109
 
107
110
  def remove
108
111
  remove_all_listeners
109
- Document.find(path).remove
112
+ dom_element.remove
110
113
  parent&.post_remove_child(self)
111
114
  # children.each(:remove) # TODO enable this safely
112
115
  @removed = true
@@ -128,10 +131,10 @@ module Glimmer
128
131
  end
129
132
  end
130
133
 
131
- def path
132
- "#{parent_path} #{element}##{id}.#{name}"
134
+ # Subclasses can override with their own selector
135
+ def selector
136
+ ".#{element_id}"
133
137
  end
134
- alias widget_path path # pure path without subchildren modifications
135
138
 
136
139
  # Root element representing widget. Must be overridden by subclasses if different from div
137
140
  def element
@@ -190,15 +193,17 @@ module Glimmer
190
193
  end
191
194
  alias setFocus set_focus
192
195
 
193
- def parent_path
194
- @parent&.path
196
+ def parent_selector
197
+ @parent&.selector
195
198
  end
196
199
 
197
200
  def parent_dom_element
198
- if parent_path
199
- Document.find(parent_path)
200
- elsif options[:root]
201
- Document.find(options[:root])
201
+ if parent_selector
202
+ Document.find(parent_selector)
203
+ else
204
+ # TODO consider moving this to initializer
205
+ options[:parent] ||= 'body'
206
+ Document.find(options[:parent])
202
207
  end
203
208
  end
204
209
 
@@ -239,7 +244,7 @@ module Glimmer
239
244
  end
240
245
 
241
246
  def add_text_content(text)
242
- dom_element.append(text)
247
+ dom_element.append(text.to_s)
243
248
  end
244
249
 
245
250
  def content_on_render_blocks
@@ -262,17 +267,12 @@ module Glimmer
262
267
  # TODO consider passing parent element instead and having table item include a table cell widget only for opal
263
268
  @dom = nil
264
269
  @dom = dom # TODO unify how to build dom for most widgets based on element, id, and name (class)
265
- @dom = @parent.get_layout.dom(@dom) if @parent.respond_to?(:layout) && @parent.get_layout
270
+ # @dom = @parent.get_layout.dom(@dom) if @parent.respond_to?(:layout) && @parent.get_layout
266
271
  @dom
267
272
  end
268
273
 
269
274
  def dom
270
- body_id = id
271
- body_class = ([name] + css_classes.to_a).join(' ')
272
- html_options = options.dup
273
- html_options[:id] ||= body_id
274
- html_options[:class] ||= ''
275
- html_options[:class] = "#{html_options[:class]} #{body_class}".strip
275
+ # TODO auto-convert known glimmer attributes like parent to data attributes like data-parent
276
276
  @dom ||= html {
277
277
  send(keyword, html_options) {
278
278
  # TODO consider supporting the idea of dynamic CSS building on close of shell that adds only as much CSS as needed for widgets that were mentioned
@@ -286,10 +286,24 @@ module Glimmer
286
286
  # }
287
287
  # end
288
288
  # end
289
+ args.first if args.first.is_a?(String)
289
290
  }
290
291
  }.to_s
291
292
  end
292
293
 
294
+ def html_options
295
+ body_class = ([name, element_id] + css_classes.to_a).join(' ')
296
+ html_options = options.dup
297
+ GLIMMER_ATTRIBUTES.each do |attribute|
298
+ next unless html_options.include?(attribute)
299
+ data_normalized_attribute = attribute.split('_').join('-')
300
+ html_options["data-#{data_normalized_attribute}"] = html_options.delete(attribute)
301
+ end
302
+ html_options[:class] ||= ''
303
+ html_options[:class] = "#{html_options[:class]} #{body_class}".strip
304
+ html_options
305
+ end
306
+
293
307
  def content(&block)
294
308
  Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Web::ElementExpression.new, keyword, &block)
295
309
  end
@@ -657,20 +671,11 @@ module Glimmer
657
671
  self.class.name.split('::').last.underscore.sub(/_proxy$/, '').gsub('_', '-')
658
672
  end
659
673
 
660
- def id
661
- return options[:id] if options.include?(:id)
662
- @id ||= "#{name}-#{ElementProxy.next_id_number_for(name)}"
663
- end
664
-
665
- # Sets id explicitly. Useful in cases of wanting to maintain a stable id
666
- def id=(value)
667
- # TODO delete this method if it is not needed and isn't accurate in what it does
668
- @id = value
669
- end
670
-
671
- # Subclasses can override with their own selector
672
- def selector
673
- "#{name}##{id}"
674
+ # element ID is used as a css class to identify the element.
675
+ # It is intentionally not set as the actual HTML element ID to let software engineers
676
+ # specify their own IDs if they wanted
677
+ def element_id
678
+ @element_id ||= "element-#{ElementProxy.next_id_number_for(name)}"
674
679
  end
675
680
 
676
681
  def add_css_class(css_class)
@@ -699,7 +704,7 @@ module Glimmer
699
704
 
700
705
  def dom_element
701
706
  # TODO consider making this pick an element in relation to its parent, allowing unhooked dom elements to be built if needed (unhooked to the visible page dom)
702
- Document.find(path)
707
+ Document.find(selector)
703
708
  end
704
709
 
705
710
  # TODO consider adding a default #dom method implementation for the common case, automatically relying on #element and other methods to build the dom html
@@ -718,12 +723,12 @@ module Glimmer
718
723
  element
719
724
  end
720
725
 
721
- def listener_path
722
- path
726
+ def listener_selector
727
+ selector
723
728
  end
724
729
 
725
730
  def listener_dom_element
726
- Document.find(listener_path)
731
+ Document.find(listener_selector)
727
732
  end
728
733
 
729
734
  def observation_requests
@@ -754,58 +759,69 @@ module Glimmer
754
759
  listeners[listener_event.to_s] ||= []
755
760
  end
756
761
 
757
- def can_handle_observation_request?(observation_request)
762
+ def can_handle_observation_request?(keyword)
758
763
  # TODO sort this out for Opal
759
- observation_request = observation_request.to_s
760
- if observation_request.start_with?('on_swt_')
761
- constant_name = observation_request.sub(/^on_swt_/, '')
762
- SWTProxy.has_constant?(constant_name)
763
- elsif observation_request.start_with?('on_')
764
- # event = observation_request.sub(/^on_/, '')
765
- # can_add_listener?(event) || can_handle_drag_observation_request?(observation_request) || can_handle_drop_observation_request?(observation_request)
766
- true # TODO filter by valid listeners only in the future
767
- end
764
+ keyword = keyword.to_s
765
+ keyword.start_with?('on_')
766
+ # if keyword.start_with?('on_swt_')
767
+ # constant_name = keyword.sub(/^on_swt_/, '')
768
+ # SWTProxy.has_constant?(constant_name)
769
+ # elsif keyword.start_with?('on_')
770
+ # # event = keyword.sub(/^on_/, '')
771
+ # # can_add_listener?(event) || can_handle_drag_observation_request?(keyword) || can_handle_drop_observation_request?(keyword)
772
+ # true # TODO filter by valid listeners only in the future
773
+ # end
768
774
  end
769
775
 
770
776
  def handle_observation_request(keyword, original_event_listener)
771
- case keyword
772
- when 'on_widget_removed'
773
- listeners_for(keyword.sub(/^on_/, '')) << original_event_listener.to_proc
774
- else
777
+ # case keyword
778
+ # when 'on_widget_removed'
779
+ # listeners_for(keyword.sub(/^on_/, '')) << original_event_listener.to_proc
780
+ # else
775
781
  handle_javascript_observation_request(keyword, original_event_listener)
776
- end
782
+ # end
777
783
  end
778
784
 
779
785
  def handle_javascript_observation_request(keyword, original_event_listener)
780
- return unless effective_observation_request_to_event_mapping.keys.include?(keyword)
781
- event = nil
782
- delegate = nil
783
- effective_observation_request_to_event_mapping[keyword].to_collection.each do |mapping|
784
- observation_requests[keyword] ||= Set.new
785
- observation_requests[keyword] << original_event_listener
786
- event = mapping[:event]
787
- event_handler = mapping[:event_handler]
788
- event_element_css_selector = mapping[:event_element_css_selector]
789
- potential_event_listener = event_handler&.call(original_event_listener)
790
- event_listener = potential_event_listener || original_event_listener
791
- async_event_listener = proc do |event|
792
- # TODO look into the issue with using async::task.new here. maybe put it in event listener (like not being able to call preventDefault or return false successfully )
793
- # maybe consider pushing inside the widget classes instead where needed only or implement universal doit support correctly to bypass this issue
794
- # Async::Task.new do
795
- @@widget_handling_listener = self
796
- # TODO also make sure to disable all widgets for suspension
797
- event_listener.call(event) unless dialog_ancestor&.event_handling_suspended?
798
- @widget_handling_listener = nil
799
- # end
800
- end
801
- the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element
802
- unless the_listener_dom_element.empty?
803
- the_listener_dom_element.on(event, &async_event_listener)
804
- # TODO ensure uniqueness of insertion (perhaps adding equals/hash method to event listener proxy)
805
-
806
- event_listener_proxies << EventListenerProxy.new(element_proxy: self, selector: selector, dom_element: the_listener_dom_element, event: event, listener: async_event_listener, original_event_listener: original_event_listener)
807
- end
808
- end
786
+ listener = ListenerProxy.new(
787
+ element_proxy: self,
788
+ selector: selector,
789
+ dom_element: dom_element,
790
+ event: keyword.sub(/^on_/, ''),
791
+ listener: original_event_listener,
792
+ original_event_listener: original_event_listener
793
+ )
794
+ listener.register
795
+ listener
796
+ # return unless effective_observation_request_to_event_mapping.keys.include?(keyword)
797
+ # event = nil
798
+ # delegate = nil
799
+ # effective_observation_request_to_event_mapping[keyword].to_collection.each do |mapping|
800
+ # observation_requests[keyword] ||= Set.new
801
+ # observation_requests[keyword] << original_event_listener
802
+ # event = mapping[:event]
803
+ # event_handler = mapping[:event_handler]
804
+ # event_element_css_selector = mapping[:event_element_css_selector]
805
+ # potential_event_listener = event_handler&.call(original_event_listener)
806
+ # event_listener = potential_event_listener || original_event_listener
807
+ # async_event_listener = proc do |event|
808
+ ## TODO look into the issue with using async::task.new here. maybe put it in event listener (like not being able to call preventDefault or return false successfully )
809
+ ## maybe consider pushing inside the widget classes instead where needed only or implement universal doit support correctly to bypass this issue
810
+ ## Async::Task.new do
811
+ # @@widget_handling_listener = self
812
+ ## TODO also make sure to disable all widgets for suspension
813
+ # event_listener.call(event) unless dialog_ancestor&.event_handling_suspended?
814
+ # @widget_handling_listener = nil
815
+ ## end
816
+ # end
817
+ # the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element
818
+ # unless the_listener_dom_element.empty?
819
+ # the_listener_dom_element.on(event, &async_event_listener)
820
+ ## TODO ensure uniqueness of insertion (perhaps adding equals/hash method to event listener proxy)
821
+ #
822
+ # event_listener_proxies << EventListenerProxy.new(element_proxy: self, selector: selector, dom_element: the_listener_dom_element, event: event, listener: async_event_listener, original_event_listener: original_event_listener)
823
+ # end
824
+ # end
809
825
  end
810
826
 
811
827
  def remove_event_listener_proxies
@@ -828,11 +844,27 @@ module Glimmer
828
844
  super(attribute_name, *args) # PropertyOwner
829
845
  end
830
846
 
831
- def method_missing(method, *args, &block)
832
- if method.to_s.start_with?('on_')
833
- handle_observation_request(method, block)
847
+ def respond_to_missing?(method_name, include_private = false)
848
+ super(method_name, include_private) ||
849
+ (dom_element && dom_element.length > 0 && Native.call(dom_element, '0').respond_to?(method_name, include_private)) ||
850
+ dom_element.respond_to?(method_name, include_private) ||
851
+ method_name.to_s.start_with?('on_')
852
+ end
853
+
854
+ def method_missing(method_name, *args, &block)
855
+ if method_name.to_s.start_with?('on_')
856
+ handle_observation_request(method_name, block)
857
+ elsif dom_element.respond_to?(method_name)
858
+ dom_element.send(method_name, *args, &block)
859
+ elsif dom_element && dom_element.length > 0
860
+ begin
861
+ js_args = block.nil? ? args : (args + [block])
862
+ Native.call(dom_element, '0').method_missing(method_name.to_s.camelcase, *js_args)
863
+ rescue Exception
864
+ super(method_name, *args, &block)
865
+ end
834
866
  else
835
- super(method, *args, &block)
867
+ super(method_name, *args, &block)
836
868
  end
837
869
  end
838
870
 
@@ -0,0 +1,37 @@
1
+ module Glimmer
2
+ module Web
3
+ class ListenerProxy
4
+ attr_reader :element_proxy, :event, :dom_element, :selector, :listener, :original_event_listener
5
+
6
+ def initialize(element_proxy:, event:, dom_element:, selector:, listener:)
7
+ @element_proxy = element_proxy
8
+ @event = event
9
+ @dom_element = dom_element
10
+ @selector = selector
11
+ @listener = listener
12
+ @js_listener = lambda do |event|
13
+ event.prevent
14
+ event.prevent_default
15
+ event.stop_propagation
16
+ event.stop_immediate_propagation
17
+ # TODO wrap event with a Ruby Event object before passing to listener
18
+ listener.call(event)
19
+ false
20
+ end
21
+ @original_event_listener = original_event_listener
22
+ end
23
+
24
+ def register
25
+ @dom_element.on(@event, &@js_listener)
26
+ end
27
+ alias observe register
28
+ alias reregister register
29
+
30
+ def unregister
31
+ @dom_element.off(@event, &@js_listener)
32
+ end
33
+ alias unobserve unregister
34
+ alias deregister unregister
35
+ end
36
+ end
37
+ end
data/lib/glimmer/web.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020-2022 Andy Maleh
1
+ # Copyright (c) 2023 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -0,0 +1,99 @@
1
+ # Copyright (c) 2023 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer-dsl-web'
23
+
24
+ include Glimmer
25
+
26
+ Document.ready? do
27
+ div {
28
+ h1('Contact Form')
29
+ form {
30
+ div(class: 'field-row') {
31
+ label('Name: ', for: 'name-field')
32
+ @name_input = input(id: 'name-field', class: 'field', type: 'text', required: true)
33
+ }
34
+ div(class: 'field-row') {
35
+ label('Email: ', for: 'email-field')
36
+ @email_input = input(id: 'email-field', class: 'field', type: 'email', required: true)
37
+ }
38
+ @add_button = button('Add Contact', class: 'submit-button') {
39
+ on_click do
40
+ if ([@name_input, @email_input].all? {|input| input.check_validity })
41
+ @table.content {
42
+ tr {
43
+ td { @name_input.value }
44
+ td { @email_input.value }
45
+ }
46
+ }
47
+ @email_input.value = @name_input.value = ''
48
+ else
49
+ error_messages = []
50
+ error_messages << "Name is not valid! Make sure it is filled." if !@name_input.check_validity
51
+ error_messages << "Email is not valid! Make sure it is filled and has a valid format." if !@email_input.check_validity
52
+ $$.alert(error_messages.join("\n"))
53
+ end
54
+ end
55
+ }
56
+ }
57
+ h1('Contacts Table')
58
+ @table = table {
59
+ tr {
60
+ th('Name')
61
+ th('Email')
62
+ }
63
+ tr {
64
+ td('John Doe')
65
+ td('johndoe@example.com')
66
+ }
67
+ tr {
68
+ td('Jane Doe')
69
+ td('janedoe@example.com')
70
+ }
71
+ }
72
+
73
+ # CSS Styles
74
+ style {
75
+ <<~CSS
76
+ .field-row {
77
+ margin: 10px 5px;
78
+ }
79
+ .field {
80
+ margin-left: 5px;
81
+ }
82
+ .submit-button {
83
+ display: block;
84
+ margin: 10px 5px;
85
+ }
86
+ table {
87
+ border:1px solid grey;
88
+ border-spacing: 0;
89
+ }
90
+ table tr td, table tr th {
91
+ padding: 5px;
92
+ }
93
+ table tr:nth-child(even) {
94
+ background: #ccc;
95
+ }
96
+ CSS
97
+ }
98
+ }.render
99
+ end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020-2022 Andy Maleh
1
+ # Copyright (c) 2023 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
@@ -19,11 +19,12 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
+ require 'glimmer-dsl-web'
23
+
22
24
  include Glimmer
23
25
 
24
- shell {
25
- text 'Glimmer'
26
- label {
27
- text 'Hello, World!'
28
- }
29
- }.open
26
+ Document.ready? do
27
+ div {
28
+ 'Hello, World!'
29
+ }.render
30
+ end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020-2022 Andy Maleh
1
+ # Copyright (c) 2023 Andy Maleh
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-27 00:00:00.000000000 Z
11
+ date: 2023-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -258,6 +258,7 @@ files:
258
258
  - lib/glimmer-dsl-web/ext/class.rb
259
259
  - lib/glimmer-dsl-web/ext/date.rb
260
260
  - lib/glimmer-dsl-web/ext/exception.rb
261
+ - lib/glimmer-dsl-web/samples/hello/hello_button.rb
261
262
  - lib/glimmer-dsl-web/samples/hello/hello_world.rb
262
263
  - lib/glimmer-dsl-web/vendor/jquery.js
263
264
  - lib/glimmer/config/opal_logger.rb
@@ -265,9 +266,11 @@ files:
265
266
  - lib/glimmer/data_binding/observable_element.rb
266
267
  - lib/glimmer/dsl/web/dsl.rb
267
268
  - lib/glimmer/dsl/web/element_expression.rb
269
+ - lib/glimmer/dsl/web/listener_expression.rb
268
270
  - lib/glimmer/util/proc_tracker.rb
269
271
  - lib/glimmer/web.rb
270
272
  - lib/glimmer/web/element_proxy.rb
273
+ - lib/glimmer/web/listener_proxy.rb
271
274
  - lib/glimmer/web/property_owner.rb
272
275
  homepage: http://github.com/AndyObtiva/glimmer-dsl-web
273
276
  licenses: