glimmer-dsl-web 0.0.4 → 0.0.5
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +244 -203
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +5 -3
- data/lib/glimmer/dsl/web/dsl.rb +1 -0
- data/lib/glimmer/dsl/web/element_expression.rb +1 -1
- data/lib/glimmer/dsl/web/p_expression.rb +31 -0
- data/lib/glimmer/web/element_proxy.rb +7 -15
- data/lib/glimmer/web/event_proxy.rb +59 -0
- data/lib/glimmer/web/listener_proxy.rb +36 -14
- data/lib/glimmer-dsl-web/samples/hello/hello_button.rb +4 -69
- data/lib/glimmer-dsl-web/samples/hello/hello_form.rb +102 -0
- data/lib/glimmer-dsl-web.rb +1 -6
- metadata +4 -2
- data/lib/glimmer/data_binding/observable_element.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9250414d9d8804f3af1ddf5cc083ce9e06ff87e3a57e1686c1e9bb7cb1ee847
|
4
|
+
data.tar.gz: f9312dfb0e0a87a047b4b25216031587f965b70d8059a7de6d670105b2ce41ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05be5bd7202f612582819cf970eb8185e530c2dff83e90caae1e3d5b09ad18d1aad44cd622e5b1b6d17f5afd83eb61762af2a941ab7bc77ea39b409e81ce4e6c
|
7
|
+
data.tar.gz: 6a979d023e5faf0b78656ca332bf6593ea33ed224ab8f1f06490767466dd62861e6a560fec63be25352500e0e2fde92cfa9d31e47089a6f5a629f7546816c283
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.0.5
|
4
|
+
|
5
|
+
- Support `p` element as it was overriden by Ruby's `p` method.
|
6
|
+
- Update listener syntax to be the original HTML event name (e.g. `onclick`) without being underscored to keep the transition to the Glimmer GUI DSL simple (e.g. `onclick` not `on_click`)
|
7
|
+
- Wrap listener `event` argument with `Glimmer::Web::Event` object, which proxies calls to JS event when needed
|
8
|
+
- Rename Hello Button! Sample to Hello, Form! Sample: `require 'glimmer-dsl-web/samples/hello/hello_form'`
|
9
|
+
- Update Hello, Form! Sample to display browser native validation errors and have automatic focus support on the name field
|
10
|
+
- Event listeners do not call `prevent_default` by default anymore, leaving it to the consumer of the library to decide
|
11
|
+
- New Hello, Button! Sample (replacing older one): `require 'glimmer-dsl-web/samples/hello/hello_button'`
|
12
|
+
|
3
13
|
## 0.0.4
|
4
14
|
|
5
15
|
- Support nesting attributes/properties under element (e.g. `input { value 'something'; dir 'rtl' }`)
|
data/README.md
CHANGED
@@ -1,15 +1,11 @@
|
|
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
|
+
# [<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.5 (Early Alpha)
|
2
2
|
## Ruby in the Browser Web GUI Frontend Library
|
3
3
|
[](http://badge.fury.io/rb/glimmer-dsl-web)
|
4
4
|
[](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
5
5
|
|
6
|
-
|
6
|
+
[Glimmer](https://github.com/AndyObtiva/glimmer) DSL for Web enables building Web GUI frontends using [Ruby in the Browser](https://www.youtube.com/watch?v=4AdcfbI6A4c), as per [Matz's recommendation in his RubyConf 2022 keynote speech to replace JavaScript with Ruby](https://youtu.be/knutsgHTrfQ?t=789). It aims at providing the simplest frontend library in existence. You can finally live in pure Rubyland on the Web in both the frontend and backend with [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web)!
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
### You can finally live in pure Rubyland on the web!
|
11
|
-
|
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).
|
8
|
+
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. [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) mainly differs from Glimmer DSL for Opal by adopting a DSL that follows web-like HTML syntax in Ruby to facilitate leveraging existing HTML/CSS/JS skills instead of adopting a desktop GUI DSL that is webified.
|
13
9
|
|
14
10
|
**Sample**
|
15
11
|
|
@@ -81,7 +77,43 @@ That produces the following under `<body></body>`:
|
|
81
77
|
|
82
78
|

|
83
79
|
|
84
|
-
**Hello, Button
|
80
|
+
**Hello, Button!**
|
81
|
+
|
82
|
+
Event listeners can be setup on any element using the same event names used in HTML (e.g. `onclick`) while passing in a standard Ruby block to handle behavior. `$$` gives access to `window` to invoke functions like `alert`.
|
83
|
+
|
84
|
+
Glimmer GUI code:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
require 'glimmer-dsl-web'
|
88
|
+
|
89
|
+
include Glimmer
|
90
|
+
|
91
|
+
Document.ready? do
|
92
|
+
div {
|
93
|
+
button('Greet') {
|
94
|
+
onclick do
|
95
|
+
$$.alert('Hello, Button!')
|
96
|
+
end
|
97
|
+
}
|
98
|
+
}.render
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
That produces the following under `<body></body>`:
|
103
|
+
|
104
|
+
```html
|
105
|
+
<div data-parent="body" class="element element-1">
|
106
|
+
<button class="element element-2">Greet</button>
|
107
|
+
</div>
|
108
|
+
```
|
109
|
+
|
110
|
+
Screenshot:
|
111
|
+
|
112
|
+

|
113
|
+
|
114
|
+
**Hello, Form!**
|
115
|
+
|
116
|
+
[Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) gives access to all Web Browser built-in features like HTML form validations, input focus, events, and element functions from a very terse and productive Ruby GUI DSL.
|
85
117
|
|
86
118
|
Glimmer GUI code:
|
87
119
|
|
@@ -93,44 +125,50 @@ include Glimmer
|
|
93
125
|
Document.ready? do
|
94
126
|
div {
|
95
127
|
h1('Contact Form')
|
128
|
+
|
96
129
|
form {
|
97
|
-
div
|
130
|
+
div {
|
98
131
|
label('Name: ', for: 'name-field')
|
99
|
-
@name_input = input(
|
132
|
+
@name_input = input(type: 'text', id: 'name-field', required: true, autofocus: true)
|
100
133
|
}
|
101
|
-
|
134
|
+
|
135
|
+
div {
|
102
136
|
label('Email: ', for: 'email-field')
|
103
|
-
@email_input = input(
|
137
|
+
@email_input = input(type: 'email', id: 'email-field', required: true)
|
104
138
|
}
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
139
|
+
|
140
|
+
div {
|
141
|
+
input(type: 'submit', value: 'Add Contact') {
|
142
|
+
onclick do |event|
|
143
|
+
if ([@name_input, @email_input].all? {|input| input.check_validity })
|
144
|
+
# re-open table content and add row
|
145
|
+
@table.content {
|
146
|
+
tr {
|
147
|
+
td { @name_input.value }
|
148
|
+
td { @email_input.value }
|
149
|
+
}
|
112
150
|
}
|
113
|
-
|
114
|
-
|
115
|
-
|
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"))
|
151
|
+
@email_input.value = @name_input.value = ''
|
152
|
+
@name_input.focus
|
153
|
+
end
|
120
154
|
end
|
121
|
-
|
155
|
+
}
|
122
156
|
}
|
123
157
|
}
|
158
|
+
|
124
159
|
h1('Contacts Table')
|
160
|
+
|
125
161
|
@table = table {
|
126
162
|
tr {
|
127
163
|
th('Name')
|
128
164
|
th('Email')
|
129
165
|
}
|
166
|
+
|
130
167
|
tr {
|
131
168
|
td('John Doe')
|
132
169
|
td('johndoe@example.com')
|
133
170
|
}
|
171
|
+
|
134
172
|
tr {
|
135
173
|
td('Jane Doe')
|
136
174
|
td('janedoe@example.com')
|
@@ -140,15 +178,11 @@ Document.ready? do
|
|
140
178
|
# CSS Styles
|
141
179
|
style {
|
142
180
|
<<~CSS
|
143
|
-
|
144
|
-
margin:
|
145
|
-
}
|
146
|
-
.field {
|
147
|
-
margin-left: 5px;
|
181
|
+
input {
|
182
|
+
margin: 5px;
|
148
183
|
}
|
149
|
-
|
150
|
-
|
151
|
-
margin: 10px 5px;
|
184
|
+
input[type=submit] {
|
185
|
+
margin: 5px 0;
|
152
186
|
}
|
153
187
|
table {
|
154
188
|
border:1px solid grey;
|
@@ -171,83 +205,66 @@ That produces the following under `<body></body>`:
|
|
171
205
|
```html
|
172
206
|
<div data-parent="body" class="element element-1">
|
173
207
|
<h1 class="element element-2">Contact Form</h1>
|
208
|
+
|
174
209
|
<form class="element element-3">
|
175
|
-
<div class="
|
210
|
+
<div class="element element-4">
|
176
211
|
<label for="name-field" class="element element-5">Name: </label>
|
177
|
-
<input id="name-field"
|
212
|
+
<input type="text" id="name-field" required="true" autofocus="true" class="element element-6">
|
178
213
|
</div>
|
179
|
-
|
214
|
+
|
215
|
+
<div class="element element-7">
|
180
216
|
<label for="email-field" class="element element-8">Email: </label>
|
181
|
-
<input id="email-field" class="
|
217
|
+
<input type="email" id="email-field" required="true" class="element element-9">
|
218
|
+
</div>
|
219
|
+
|
220
|
+
<div class="element element-10">
|
221
|
+
<input type="submit" value="Add Contact" class="element element-11">
|
182
222
|
</div>
|
183
|
-
<button class="submit-button element element-10">Add Contact</button>
|
184
223
|
</form>
|
185
|
-
|
186
|
-
<
|
187
|
-
|
188
|
-
|
189
|
-
|
224
|
+
|
225
|
+
<h1 class="element element-12">Contacts Table</h1>
|
226
|
+
|
227
|
+
<table class="element element-13">
|
228
|
+
<tr class="element element-14">
|
229
|
+
<th class="element element-15">Name</th>
|
230
|
+
<th class="element element-16">Email</th>
|
190
231
|
</tr>
|
191
|
-
|
192
|
-
|
193
|
-
<td class="element element-18">
|
232
|
+
|
233
|
+
<tr class="element element-17">
|
234
|
+
<td class="element element-18">John Doe</td>
|
235
|
+
<td class="element element-19">johndoe@example.com</td>
|
194
236
|
</tr>
|
195
|
-
|
196
|
-
|
197
|
-
<td class="element element-21">
|
237
|
+
|
238
|
+
<tr class="element element-20">
|
239
|
+
<td class="element element-21">Jane Doe</td>
|
240
|
+
<td class="element element-22">janedoe@example.com</td>
|
198
241
|
</tr>
|
199
242
|
</table>
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
background: #ccc;
|
219
|
-
}
|
243
|
+
|
244
|
+
<style class="element element-23">
|
245
|
+
input {
|
246
|
+
margin: 5px;
|
247
|
+
}
|
248
|
+
input[type=submit] {
|
249
|
+
margin: 5px 0;
|
250
|
+
}
|
251
|
+
table {
|
252
|
+
border:1px solid grey;
|
253
|
+
border-spacing: 0;
|
254
|
+
}
|
255
|
+
table tr td, table tr th {
|
256
|
+
padding: 5px;
|
257
|
+
}
|
258
|
+
table tr:nth-child(even) {
|
259
|
+
background: #ccc;
|
260
|
+
}
|
220
261
|
</style>
|
221
262
|
</div>
|
222
263
|
```
|
223
264
|
|
224
|
-
|
225
|
-
|
226
|
-
---
|
227
|
-
|
228
|
-
***Hello, Button!***
|
229
|
-
|
230
|
-

|
231
|
-
|
232
|
-
---
|
233
|
-
|
234
|
-
***Hello, Button! Submitted Invalid Data***
|
235
|
-
|
236
|
-

|
237
|
-
|
238
|
-
---
|
239
|
-
|
240
|
-
***Hello, Button! Filled Valid Name and Email***
|
241
|
-
|
242
|
-

|
243
|
-
|
244
|
-
---
|
245
|
-
|
246
|
-
***Hello, Button! Added Contact***
|
247
|
-
|
248
|
-

|
265
|
+
Screenshot:
|
249
266
|
|
250
|
-
|
267
|
+

|
251
268
|
|
252
269
|
**Button Counter Sample**
|
253
270
|
|
@@ -279,7 +296,7 @@ class HelloButton
|
|
279
296
|
|
280
297
|
markup {
|
281
298
|
# This will hook into element #app-container and then build HTML inside it using Ruby DSL code
|
282
|
-
div(
|
299
|
+
div(parent: parent_selector) {
|
283
300
|
text 'Button Counter'
|
284
301
|
|
285
302
|
button {
|
@@ -288,7 +305,7 @@ class HelloButton
|
|
288
305
|
# copied to button innerText (content) to display to the user
|
289
306
|
inner_text <= [@counter, :count, on_read: ->(value) { "Click To Increment: #{value} " }]
|
290
307
|
|
291
|
-
|
308
|
+
onclick {
|
292
309
|
@counter.increment!
|
293
310
|
}
|
294
311
|
}
|
@@ -349,7 +366,7 @@ Learn more about the differences between various [Glimmer](https://github.com/An
|
|
349
366
|
- [Samples](#samples)
|
350
367
|
- [Hello Samples](#hello-samples)
|
351
368
|
- [Hello, World!](#hello-world)
|
352
|
-
- [Hello,
|
369
|
+
- [Hello, Form!](#hello-form)
|
353
370
|
- [Button Counter](#button-counter)
|
354
371
|
- [Glimmer Process](#glimmer-process)
|
355
372
|
- [Help](#help)
|
@@ -363,11 +380,12 @@ Learn more about the differences between various [Glimmer](https://github.com/An
|
|
363
380
|
|
364
381
|
## Prerequisites
|
365
382
|
|
383
|
+
[Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) will begin by supporting [Opal Ruby](https://opalrb.com/) on [Rails](https://rubyonrails.org/). [Opal](https://opalrb.com/) is a lightweight Ruby to JavaScript transpiler that results in small downloadables compared to WASM. In the future, the project 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.
|
384
|
+
|
366
385
|
- Ruby 3.0 (newer Ruby versions are not supported at this time)
|
367
386
|
- Rails 6-7: [https://github.com/rails/rails](https://github.com/rails/rails)
|
368
|
-
- Opal 1.4.1 for Rails 6-7
|
369
|
-
- Opal-Rails 2.0.2 for Rails 6-7
|
370
|
-
- jQuery 3 (included): [https://code.jquery.com/](https://code.jquery.com/) (jQuery 3.6.0 is included in the [glimmer-dsl-web](https://rubygems.org/gems/glimmer-dsl-web) gem)
|
387
|
+
- Opal 1.4.1 for Rails 6-7: [https://github.com/opal/opal](https://github.com/opal/opal)
|
388
|
+
- Opal-Rails 2.0.2 for Rails 6-7: [https://github.com/opal/opal-rails](https://github.com/opal/opal-rails)
|
371
389
|
|
372
390
|
## Setup
|
373
391
|
|
@@ -398,7 +416,7 @@ gem 'opal', '1.4.1'
|
|
398
416
|
gem 'opal-rails', '2.0.2'
|
399
417
|
gem 'opal-async', '~> 1.4.0'
|
400
418
|
gem 'opal-jquery', '~> 0.4.6'
|
401
|
-
gem 'glimmer-dsl-web', '~> 0.0.
|
419
|
+
gem 'glimmer-dsl-web', '~> 0.0.5'
|
402
420
|
gem 'glimmer-dsl-xml', '~> 1.3.1', require: false
|
403
421
|
gem 'glimmer-dsl-css', '~> 1.2.1', require: false
|
404
422
|
```
|
@@ -543,7 +561,7 @@ gem 'opal', '1.4.1'
|
|
543
561
|
gem 'opal-rails', '2.0.2'
|
544
562
|
gem 'opal-async', '~> 1.4.0'
|
545
563
|
gem 'opal-jquery', '~> 0.4.6'
|
546
|
-
gem 'glimmer-dsl-web', '~> 0.0.
|
564
|
+
gem 'glimmer-dsl-web', '~> 0.0.5'
|
547
565
|
gem 'glimmer-dsl-xml', '~> 1.3.1', require: false
|
548
566
|
gem 'glimmer-dsl-css', '~> 1.2.1', require: false
|
549
567
|
```
|
@@ -669,19 +687,19 @@ Otherwise, if you still cannot setup successfully (even with the help of the sam
|
|
669
687
|
|
670
688
|
Glimmer DSL for Web offers a GUI DSL for building HTML Web User Interfaces declaratively in Ruby.
|
671
689
|
|
672
|
-
1- Keywords (HTML Elements)
|
690
|
+
1- **Keywords (HTML Elements)**
|
673
691
|
|
674
692
|
You can declare any HTML element by simply using the lowercase underscored version of its name (Ruby convention for method names) like `div`, `span`, `form`, `input`, `button`, `table`, `tr`, `th`, and `td`.
|
675
693
|
|
676
694
|
Under the hood, HTML element DSL keywords are invoked as Ruby methods.
|
677
695
|
|
678
|
-
2- Arguments (HTML Attributes + Text Content)
|
696
|
+
2- **Arguments (HTML Attributes + Text Content)**
|
679
697
|
|
680
698
|
You can set any HTML element attributes by passing as keyword arguments to element methods like `div(id: 'container', class: 'stack')` or `input(type: 'email', required: true)`
|
681
699
|
|
682
700
|
Also, if the element has a little bit of text content that can fit in one line, it can be passed as the 1st argument like `label('Name: ', for: 'name_field')`, `button('Calculate', class: 'round-button')`, or `span('Mr')`
|
683
701
|
|
684
|
-
3- Content Block (Properties + Listeners + Nested Elements + Text Content)
|
702
|
+
3- **Content Block (Properties + Listeners + Nested Elements + Text Content)**
|
685
703
|
|
686
704
|
Element methods can accept a Ruby content block. It intentionally has a `{...}` style even as a multi-line block to indicate that the code is declarative GUI structure code.
|
687
705
|
|
@@ -693,11 +711,11 @@ input(type: 'text') {
|
|
693
711
|
}
|
694
712
|
```
|
695
713
|
|
696
|
-
You can nest HTML event listeners under an element by using
|
714
|
+
You can nest HTML event listeners under an element by using the HTML event listener name (e.g. `onclick`, `onchange`, `onblur`):
|
697
715
|
|
698
716
|
```ruby
|
699
717
|
button('Add') {
|
700
|
-
|
718
|
+
onclick do
|
701
719
|
@model.add_selected_element
|
702
720
|
end
|
703
721
|
}
|
@@ -718,7 +736,7 @@ form {
|
|
718
736
|
input(id: 'email-field', class: 'field', type: 'email', required: true)
|
719
737
|
}
|
720
738
|
button('Add Contact', class: 'submit-button') {
|
721
|
-
|
739
|
+
onclick do
|
722
740
|
...
|
723
741
|
end
|
724
742
|
}
|
@@ -728,17 +746,23 @@ form {
|
|
728
746
|
You can nest text content underneath an element's Ruby block, like:
|
729
747
|
|
730
748
|
```ruby
|
731
|
-
|
749
|
+
p(class: 'summary') {
|
732
750
|
'This text content is going into the body of the span element'
|
733
751
|
}
|
734
752
|
```
|
735
753
|
|
754
|
+
4- **Operations (Properties + Functions)**
|
755
|
+
|
756
|
+
You can get/set any element property or invoke any element function by simply calling the lowercase underscored version of their name in Ruby like `input.check_validity`, `input.value`, and `input.id`.
|
757
|
+
|
736
758
|
## Supported Glimmer DSL Keywords
|
737
759
|
|
738
760
|
[All HTML elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
|
739
761
|
|
740
762
|
[All HTML attributes](https://www.w3schools.com/html/html_attributes.asp).
|
741
763
|
|
764
|
+
[All HTML events](https://www.w3schools.com/tags/ref_eventattributes.asp).
|
765
|
+
|
742
766
|
## Samples
|
743
767
|
|
744
768
|
This external sample app contains all the samples mentioned below configured inside a Rails [Opal](https://opalrb.com/) app with all the prerequisites ready to go for convenience:
|
@@ -806,47 +830,85 @@ require 'glimmer-dsl-web'
|
|
806
830
|
|
807
831
|
include Glimmer
|
808
832
|
|
833
|
+
Document.ready? do
|
834
|
+
div {
|
835
|
+
button('Greet') {
|
836
|
+
onclick do
|
837
|
+
$$.alert('Hello, Button!')
|
838
|
+
end
|
839
|
+
}
|
840
|
+
}.render
|
841
|
+
end
|
842
|
+
```
|
843
|
+
|
844
|
+
That produces the following under `<body></body>`:
|
845
|
+
|
846
|
+
```html
|
847
|
+
<div data-parent="body" class="element element-1">
|
848
|
+
<button class="element element-2">Greet</button>
|
849
|
+
</div>
|
850
|
+
```
|
851
|
+
|
852
|
+
Screenshot:
|
853
|
+
|
854
|
+

|
855
|
+
|
856
|
+
#### Hello, Form!
|
857
|
+
|
858
|
+
Glimmer GUI code:
|
859
|
+
|
860
|
+
```ruby
|
861
|
+
require 'glimmer-dsl-web'
|
862
|
+
|
863
|
+
include Glimmer
|
864
|
+
|
809
865
|
Document.ready? do
|
810
866
|
div {
|
811
867
|
h1('Contact Form')
|
868
|
+
|
812
869
|
form {
|
813
|
-
div
|
870
|
+
div {
|
814
871
|
label('Name: ', for: 'name-field')
|
815
|
-
@name_input = input(
|
872
|
+
@name_input = input(type: 'text', id: 'name-field', required: true, autofocus: true)
|
816
873
|
}
|
817
|
-
|
874
|
+
|
875
|
+
div {
|
818
876
|
label('Email: ', for: 'email-field')
|
819
|
-
@email_input = input(
|
877
|
+
@email_input = input(type: 'email', id: 'email-field', required: true)
|
820
878
|
}
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
879
|
+
|
880
|
+
div {
|
881
|
+
input(type: 'submit', value: 'Add Contact') {
|
882
|
+
onclick do |event|
|
883
|
+
if ([@name_input, @email_input].all? {|input| input.check_validity })
|
884
|
+
# re-open table content and add row
|
885
|
+
@table.content {
|
886
|
+
tr {
|
887
|
+
td { @name_input.value }
|
888
|
+
td { @email_input.value }
|
889
|
+
}
|
828
890
|
}
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
error_messages = []
|
833
|
-
error_messages << "Name is not valid! Make sure it is filled." if !@name_input.check_validity
|
834
|
-
error_messages << "Email is not valid! Make sure it is filled and has a valid format." if !@email_input.check_validity
|
835
|
-
$$.alert(error_messages.join("\n"))
|
891
|
+
@email_input.value = @name_input.value = ''
|
892
|
+
@name_input.focus
|
893
|
+
end
|
836
894
|
end
|
837
|
-
|
895
|
+
}
|
838
896
|
}
|
839
897
|
}
|
898
|
+
|
840
899
|
h1('Contacts Table')
|
900
|
+
|
841
901
|
@table = table {
|
842
902
|
tr {
|
843
903
|
th('Name')
|
844
904
|
th('Email')
|
845
905
|
}
|
906
|
+
|
846
907
|
tr {
|
847
908
|
td('John Doe')
|
848
909
|
td('johndoe@example.com')
|
849
910
|
}
|
911
|
+
|
850
912
|
tr {
|
851
913
|
td('Jane Doe')
|
852
914
|
td('janedoe@example.com')
|
@@ -856,15 +918,11 @@ Document.ready? do
|
|
856
918
|
# CSS Styles
|
857
919
|
style {
|
858
920
|
<<~CSS
|
859
|
-
|
860
|
-
margin:
|
861
|
-
}
|
862
|
-
.field {
|
863
|
-
margin-left: 5px;
|
921
|
+
input {
|
922
|
+
margin: 5px;
|
864
923
|
}
|
865
|
-
|
866
|
-
|
867
|
-
margin: 10px 5px;
|
924
|
+
input[type=submit] {
|
925
|
+
margin: 5px 0;
|
868
926
|
}
|
869
927
|
table {
|
870
928
|
border:1px solid grey;
|
@@ -887,83 +945,66 @@ That produces the following under `<body></body>`:
|
|
887
945
|
```html
|
888
946
|
<div data-parent="body" class="element element-1">
|
889
947
|
<h1 class="element element-2">Contact Form</h1>
|
948
|
+
|
890
949
|
<form class="element element-3">
|
891
|
-
<div class="
|
950
|
+
<div class="element element-4">
|
892
951
|
<label for="name-field" class="element element-5">Name: </label>
|
893
|
-
<input id="name-field"
|
952
|
+
<input type="text" id="name-field" required="true" autofocus="true" class="element element-6">
|
894
953
|
</div>
|
895
|
-
|
954
|
+
|
955
|
+
<div class="element element-7">
|
896
956
|
<label for="email-field" class="element element-8">Email: </label>
|
897
|
-
<input id="email-field" class="
|
957
|
+
<input type="email" id="email-field" required="true" class="element element-9">
|
958
|
+
</div>
|
959
|
+
|
960
|
+
<div class="element element-10">
|
961
|
+
<input type="submit" value="Add Contact" class="element element-11">
|
898
962
|
</div>
|
899
|
-
<button class="submit-button element element-10">Add Contact</button>
|
900
963
|
</form>
|
901
|
-
|
902
|
-
<
|
903
|
-
|
904
|
-
|
905
|
-
|
964
|
+
|
965
|
+
<h1 class="element element-12">Contacts Table</h1>
|
966
|
+
|
967
|
+
<table class="element element-13">
|
968
|
+
<tr class="element element-14">
|
969
|
+
<th class="element element-15">Name</th>
|
970
|
+
<th class="element element-16">Email</th>
|
906
971
|
</tr>
|
907
|
-
|
908
|
-
|
909
|
-
<td class="element element-18">
|
972
|
+
|
973
|
+
<tr class="element element-17">
|
974
|
+
<td class="element element-18">John Doe</td>
|
975
|
+
<td class="element element-19">johndoe@example.com</td>
|
910
976
|
</tr>
|
911
|
-
|
912
|
-
|
913
|
-
<td class="element element-21">
|
977
|
+
|
978
|
+
<tr class="element element-20">
|
979
|
+
<td class="element element-21">Jane Doe</td>
|
980
|
+
<td class="element element-22">janedoe@example.com</td>
|
914
981
|
</tr>
|
915
982
|
</table>
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
background: #ccc;
|
935
|
-
}
|
983
|
+
|
984
|
+
<style class="element element-23">
|
985
|
+
input {
|
986
|
+
margin: 5px;
|
987
|
+
}
|
988
|
+
input[type=submit] {
|
989
|
+
margin: 5px 0;
|
990
|
+
}
|
991
|
+
table {
|
992
|
+
border:1px solid grey;
|
993
|
+
border-spacing: 0;
|
994
|
+
}
|
995
|
+
table tr td, table tr th {
|
996
|
+
padding: 5px;
|
997
|
+
}
|
998
|
+
table tr:nth-child(even) {
|
999
|
+
background: #ccc;
|
1000
|
+
}
|
936
1001
|
</style>
|
937
1002
|
</div>
|
938
1003
|
```
|
939
1004
|
|
940
|
-
|
941
|
-
|
942
|
-
---
|
943
|
-
|
944
|
-
***Hello, Button!***
|
945
|
-
|
946
|
-

|
947
|
-
|
948
|
-
---
|
949
|
-
|
950
|
-
***Hello, Button! Submitted Invalid Data***
|
951
|
-
|
952
|
-

|
953
|
-
|
954
|
-
---
|
955
|
-
|
956
|
-
***Hello, Button! Filled Valid Name and Email***
|
957
|
-
|
958
|
-

|
959
|
-
|
960
|
-
---
|
961
|
-
|
962
|
-
***Hello, Button! Added Contact***
|
963
|
-
|
964
|
-

|
1005
|
+
Screenshot:
|
965
1006
|
|
966
|
-
|
1007
|
+

|
967
1008
|
|
968
1009
|
#### Button Counter
|
969
1010
|
|
@@ -995,7 +1036,7 @@ class HelloButton
|
|
995
1036
|
|
996
1037
|
markup {
|
997
1038
|
# This will hook into element #app-container and then build HTML inside it using Ruby DSL code
|
998
|
-
div(
|
1039
|
+
div(parent: parent_selector) {
|
999
1040
|
text 'Button Counter'
|
1000
1041
|
|
1001
1042
|
button {
|
@@ -1004,7 +1045,7 @@ class HelloButton
|
|
1004
1045
|
# copied to button innerText (content) to display to the user
|
1005
1046
|
inner_text <= [@counter, :count, on_read: ->(value) { "Click To Increment: #{value} " }]
|
1006
1047
|
|
1007
|
-
|
1048
|
+
onclick {
|
1008
1049
|
@counter.increment!
|
1009
1050
|
}
|
1010
1051
|
}
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.5
|
data/glimmer-dsl-web.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
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.
|
5
|
+
# stub: glimmer-dsl-web 0.0.5 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "glimmer-dsl-web".freeze
|
9
|
-
s.version = "0.0.
|
9
|
+
s.version = "0.0.5".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]
|
@@ -31,18 +31,20 @@ Gem::Specification.new do |s|
|
|
31
31
|
"lib/glimmer-dsl-web/ext/date.rb",
|
32
32
|
"lib/glimmer-dsl-web/ext/exception.rb",
|
33
33
|
"lib/glimmer-dsl-web/samples/hello/hello_button.rb",
|
34
|
+
"lib/glimmer-dsl-web/samples/hello/hello_form.rb",
|
34
35
|
"lib/glimmer-dsl-web/samples/hello/hello_world.rb",
|
35
36
|
"lib/glimmer-dsl-web/vendor/jquery.js",
|
36
37
|
"lib/glimmer/config/opal_logger.rb",
|
37
38
|
"lib/glimmer/data_binding/element_binding.rb",
|
38
|
-
"lib/glimmer/data_binding/observable_element.rb",
|
39
39
|
"lib/glimmer/dsl/web/dsl.rb",
|
40
40
|
"lib/glimmer/dsl/web/element_expression.rb",
|
41
41
|
"lib/glimmer/dsl/web/listener_expression.rb",
|
42
|
+
"lib/glimmer/dsl/web/p_expression.rb",
|
42
43
|
"lib/glimmer/dsl/web/property_expression.rb",
|
43
44
|
"lib/glimmer/util/proc_tracker.rb",
|
44
45
|
"lib/glimmer/web.rb",
|
45
46
|
"lib/glimmer/web/element_proxy.rb",
|
47
|
+
"lib/glimmer/web/event_proxy.rb",
|
46
48
|
"lib/glimmer/web/listener_proxy.rb",
|
47
49
|
"lib/glimmer/web/property_owner.rb"
|
48
50
|
]
|
data/lib/glimmer/dsl/web/dsl.rb
CHANGED
@@ -11,7 +11,7 @@ module Glimmer
|
|
11
11
|
def can_interpret?(parent, keyword, *args, &block)
|
12
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
|
-
!keyword.to_s.start_with?('
|
14
|
+
!keyword.to_s.start_with?('on')
|
15
15
|
end
|
16
16
|
|
17
17
|
def interpret(parent, keyword, *args, &block)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'glimmer/dsl/static_expression'
|
2
|
+
require 'glimmer/dsl/parent_expression'
|
3
|
+
|
4
|
+
require 'glimmer/web/element_proxy'
|
5
|
+
|
6
|
+
module Glimmer
|
7
|
+
module DSL
|
8
|
+
module Web
|
9
|
+
class PExpression < StaticExpression
|
10
|
+
include ParentExpression
|
11
|
+
|
12
|
+
def interpret(parent, keyword, *args, &block)
|
13
|
+
Glimmer::Web::ElementProxy.for(keyword, parent, args, block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_content(parent, keyword, *args, &block)
|
17
|
+
if parent.rendered? || parent.skip_content_on_render_blocks?
|
18
|
+
return_value = super(parent, keyword, *args, &block)
|
19
|
+
if return_value.is_a?(String) && parent.dom_element.text.to_s.empty?
|
20
|
+
parent.add_text_content(return_value)
|
21
|
+
end
|
22
|
+
parent.post_add_content
|
23
|
+
return_value
|
24
|
+
else
|
25
|
+
parent.add_content_on_render(&block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -74,7 +74,7 @@ module Glimmer
|
|
74
74
|
|
75
75
|
GLIMMER_ATTRIBUTES = [:parent]
|
76
76
|
|
77
|
-
attr_reader :keyword, :parent, :args, :options, :children, :enabled, :foreground, :background, :
|
77
|
+
attr_reader :keyword, :parent, :args, :options, :children, :enabled, :foreground, :background, :removed?, :rendered
|
78
78
|
alias rendered? rendered
|
79
79
|
|
80
80
|
def initialize(keyword, parent, args, block)
|
@@ -176,16 +176,6 @@ module Glimmer
|
|
176
176
|
dom_element.css('background-color', background.to_css) unless background.nil?
|
177
177
|
end
|
178
178
|
|
179
|
-
def focus=(value)
|
180
|
-
@focus = value
|
181
|
-
dom_element.focus # TODO consider if a delay or async_exec is needed here
|
182
|
-
end
|
183
|
-
|
184
|
-
def set_focus
|
185
|
-
self.focus = true
|
186
|
-
end
|
187
|
-
alias setFocus set_focus
|
188
|
-
|
189
179
|
def parent_selector
|
190
180
|
@parent&.selector
|
191
181
|
end
|
@@ -226,7 +216,7 @@ module Glimmer
|
|
226
216
|
end
|
227
217
|
end
|
228
218
|
end
|
229
|
-
alias
|
219
|
+
alias rerender render
|
230
220
|
|
231
221
|
def attach(the_parent_dom_element)
|
232
222
|
the_parent_dom_element.append(@dom)
|
@@ -743,7 +733,7 @@ module Glimmer
|
|
743
733
|
def can_handle_observation_request?(keyword)
|
744
734
|
# TODO sort this out for Opal
|
745
735
|
keyword = keyword.to_s
|
746
|
-
keyword.start_with?('
|
736
|
+
keyword.start_with?('on')
|
747
737
|
# if keyword.start_with?('on_swt_')
|
748
738
|
# constant_name = keyword.sub(/^on_swt_/, '')
|
749
739
|
# SWTProxy.has_constant?(constant_name)
|
@@ -765,10 +755,10 @@ module Glimmer
|
|
765
755
|
|
766
756
|
def handle_javascript_observation_request(keyword, original_event_listener)
|
767
757
|
listener = ListenerProxy.new(
|
768
|
-
|
758
|
+
element: self,
|
769
759
|
selector: selector,
|
770
760
|
dom_element: dom_element,
|
771
|
-
|
761
|
+
event_attribute: keyword,
|
772
762
|
listener: original_event_listener,
|
773
763
|
original_event_listener: original_event_listener
|
774
764
|
)
|
@@ -827,6 +817,7 @@ module Glimmer
|
|
827
817
|
end
|
828
818
|
|
829
819
|
def respond_to_missing?(method_name, include_private = false)
|
820
|
+
# TODO consider doing more correct checking of availability of properties/methods using native `` ticks
|
830
821
|
property_name = property_name_for(method_name)
|
831
822
|
super(method_name, include_private) ||
|
832
823
|
(dom_element && dom_element.length > 0 && Native.call(dom_element, '0').respond_to?(method_name.to_s.camelcase, include_private)) ||
|
@@ -836,6 +827,7 @@ module Glimmer
|
|
836
827
|
end
|
837
828
|
|
838
829
|
def method_missing(method_name, *args, &block)
|
830
|
+
# TODO consider doing more correct checking of availability of properties/methods using native `` ticks
|
839
831
|
property_name = property_name_for(method_name)
|
840
832
|
if method_name.to_s.start_with?('on_')
|
841
833
|
handle_observation_request(method_name, block)
|
@@ -0,0 +1,59 @@
|
|
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
|
+
module Glimmer
|
23
|
+
module Web
|
24
|
+
class EventProxy
|
25
|
+
attr_reader :js_event, :listener
|
26
|
+
|
27
|
+
def initialize(js_event:, listener:)
|
28
|
+
@js_event = js_event
|
29
|
+
@listener = listener
|
30
|
+
end
|
31
|
+
|
32
|
+
def element = listener.element
|
33
|
+
def event_name = listener.event_name
|
34
|
+
def event_attribute = listener.event_attribute
|
35
|
+
|
36
|
+
def original_event
|
37
|
+
Native(`#{js_event.to_n}.originalEvent`)
|
38
|
+
end
|
39
|
+
|
40
|
+
def respond_to_missing?(method_name, include_private = false)
|
41
|
+
property_name = method_name.to_s.camelcase
|
42
|
+
super(method_name, include_private) ||
|
43
|
+
js_event.respond_to?(method_name, include_private) ||
|
44
|
+
`#{property_name} in #{original_event.to_n}`
|
45
|
+
end
|
46
|
+
|
47
|
+
def method_missing(method_name, *args, &block)
|
48
|
+
property_name = method_name.to_s.camelcase
|
49
|
+
if js_event.respond_to?(method_name, true)
|
50
|
+
js_event.send(method_name, *args, &block)
|
51
|
+
elsif `#{property_name} in #{original_event.to_n}`
|
52
|
+
original_event[property_name]
|
53
|
+
else
|
54
|
+
super(method_name, *args, &block)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,36 +1,58 @@
|
|
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/web/event_proxy'
|
23
|
+
|
1
24
|
module Glimmer
|
2
25
|
module Web
|
3
26
|
class ListenerProxy
|
4
|
-
attr_reader :
|
27
|
+
attr_reader :element, :event_attribute, :event_name, :dom_element, :selector, :listener, :js_listener, :original_event_listener
|
5
28
|
|
6
|
-
def initialize(
|
7
|
-
@
|
8
|
-
@
|
29
|
+
def initialize(element:, event_attribute:, dom_element:, selector:, listener:)
|
30
|
+
@element = element
|
31
|
+
@event_attribute = event_attribute
|
32
|
+
@event_name = event_attribute.sub(/^on/, '')
|
9
33
|
@dom_element = dom_element
|
10
34
|
@selector = selector
|
11
35
|
@listener = listener
|
12
|
-
@js_listener = lambda do |
|
13
|
-
event.prevent
|
14
|
-
event.prevent_default
|
15
|
-
event.stop_propagation
|
16
|
-
event.stop_immediate_propagation
|
36
|
+
@js_listener = lambda do |js_event|
|
17
37
|
# TODO wrap event with a Ruby Event object before passing to listener
|
18
|
-
|
19
|
-
|
38
|
+
event = EventProxy.new(js_event: js_event, listener: self)
|
39
|
+
result = listener.call(event)
|
40
|
+
result = true if result.nil?
|
41
|
+
result
|
20
42
|
end
|
21
43
|
@original_event_listener = original_event_listener
|
22
44
|
end
|
23
45
|
|
24
46
|
def register
|
25
|
-
@dom_element.on(@
|
47
|
+
@dom_element.on(@event_name, &@js_listener)
|
26
48
|
end
|
27
49
|
alias observe register
|
28
50
|
alias reregister register
|
29
51
|
|
30
52
|
def unregister
|
31
53
|
# TODO contribute fix to opal to allow passing observer with & to off with selector not specified as nil
|
32
|
-
@dom_element.off(@
|
33
|
-
@
|
54
|
+
@dom_element.off(@event_name, @js_listener)
|
55
|
+
@element.listeners_for(@event_attribute).delete(self)
|
34
56
|
end
|
35
57
|
alias unobserve unregister
|
36
58
|
alias deregister unregister
|
@@ -25,75 +25,10 @@ include Glimmer
|
|
25
25
|
|
26
26
|
Document.ready? do
|
27
27
|
div {
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
-
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
|
28
|
+
button('Greet') {
|
29
|
+
onclick do
|
30
|
+
$$.alert('Hello, Button!')
|
31
|
+
end
|
97
32
|
}
|
98
33
|
}.render
|
99
34
|
end
|
@@ -0,0 +1,102 @@
|
|
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
|
+
|
30
|
+
form {
|
31
|
+
div {
|
32
|
+
label('Name: ', for: 'name-field')
|
33
|
+
@name_input = input(type: 'text', id: 'name-field', required: true, autofocus: true)
|
34
|
+
}
|
35
|
+
|
36
|
+
div {
|
37
|
+
label('Email: ', for: 'email-field')
|
38
|
+
@email_input = input(type: 'email', id: 'email-field', required: true)
|
39
|
+
}
|
40
|
+
|
41
|
+
div {
|
42
|
+
input(type: 'submit', value: 'Add Contact') {
|
43
|
+
onclick do |event|
|
44
|
+
if ([@name_input, @email_input].all? {|input| input.check_validity })
|
45
|
+
event.prevent_default
|
46
|
+
# re-open table content and add row
|
47
|
+
@table.content {
|
48
|
+
tr {
|
49
|
+
td { @name_input.value }
|
50
|
+
td { @email_input.value }
|
51
|
+
}
|
52
|
+
}
|
53
|
+
@email_input.value = @name_input.value = ''
|
54
|
+
@name_input.focus
|
55
|
+
end
|
56
|
+
end
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
h1('Contacts Table')
|
62
|
+
|
63
|
+
@table = table {
|
64
|
+
tr {
|
65
|
+
th('Name')
|
66
|
+
th('Email')
|
67
|
+
}
|
68
|
+
|
69
|
+
tr {
|
70
|
+
td('John Doe')
|
71
|
+
td('johndoe@example.com')
|
72
|
+
}
|
73
|
+
|
74
|
+
tr {
|
75
|
+
td('Jane Doe')
|
76
|
+
td('janedoe@example.com')
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
# CSS Styles
|
81
|
+
style {
|
82
|
+
<<~CSS
|
83
|
+
input {
|
84
|
+
margin: 5px;
|
85
|
+
}
|
86
|
+
input[type=submit] {
|
87
|
+
margin: 5px 0;
|
88
|
+
}
|
89
|
+
table {
|
90
|
+
border:1px solid grey;
|
91
|
+
border-spacing: 0;
|
92
|
+
}
|
93
|
+
table tr td, table tr th {
|
94
|
+
padding: 5px;
|
95
|
+
}
|
96
|
+
table tr:nth-child(even) {
|
97
|
+
background: #ccc;
|
98
|
+
}
|
99
|
+
CSS
|
100
|
+
}
|
101
|
+
}.render
|
102
|
+
end
|
data/lib/glimmer-dsl-web.rb
CHANGED
@@ -76,12 +76,7 @@ if RUBY_ENGINE == 'opal'
|
|
76
76
|
require 'glimmer-dsl-xml'
|
77
77
|
require 'glimmer-dsl-css'
|
78
78
|
|
79
|
-
|
80
|
-
Element.alias_native :replace_with, :replaceWith
|
81
|
-
Element.alias_native :select
|
82
|
-
# Element.alias_native :dialog
|
83
|
-
|
84
|
-
Glimmer::Config.loop_max_count = 150 # TODO disable
|
79
|
+
Glimmer::Config.loop_max_count = 150 # TODO consider disabling if preferred
|
85
80
|
|
86
81
|
original_logger_level = Glimmer::Config.logger.level
|
87
82
|
Glimmer::Config.logger = Glimmer::Config::OpalLogger.new(STDOUT)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer-dsl-web
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
@@ -259,18 +259,20 @@ files:
|
|
259
259
|
- lib/glimmer-dsl-web/ext/date.rb
|
260
260
|
- lib/glimmer-dsl-web/ext/exception.rb
|
261
261
|
- lib/glimmer-dsl-web/samples/hello/hello_button.rb
|
262
|
+
- lib/glimmer-dsl-web/samples/hello/hello_form.rb
|
262
263
|
- lib/glimmer-dsl-web/samples/hello/hello_world.rb
|
263
264
|
- lib/glimmer-dsl-web/vendor/jquery.js
|
264
265
|
- lib/glimmer/config/opal_logger.rb
|
265
266
|
- lib/glimmer/data_binding/element_binding.rb
|
266
|
-
- lib/glimmer/data_binding/observable_element.rb
|
267
267
|
- lib/glimmer/dsl/web/dsl.rb
|
268
268
|
- lib/glimmer/dsl/web/element_expression.rb
|
269
269
|
- lib/glimmer/dsl/web/listener_expression.rb
|
270
|
+
- lib/glimmer/dsl/web/p_expression.rb
|
270
271
|
- lib/glimmer/dsl/web/property_expression.rb
|
271
272
|
- lib/glimmer/util/proc_tracker.rb
|
272
273
|
- lib/glimmer/web.rb
|
273
274
|
- lib/glimmer/web/element_proxy.rb
|
275
|
+
- lib/glimmer/web/event_proxy.rb
|
274
276
|
- lib/glimmer/web/listener_proxy.rb
|
275
277
|
- lib/glimmer/web/property_owner.rb
|
276
278
|
homepage: http://github.com/AndyObtiva/glimmer-dsl-web
|
@@ -1,14 +0,0 @@
|
|
1
|
-
module Glimmer
|
2
|
-
module DataBinding
|
3
|
-
module ObservableElement
|
4
|
-
def method_missing(method, *args, &block)
|
5
|
-
method_name = method.to_s
|
6
|
-
if method_name.start_with?('on_')
|
7
|
-
handle_observation_request(method_name, block)
|
8
|
-
else
|
9
|
-
super
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|