glimmer-dsl-web 0.0.5 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/LICENSE.txt +1 -1
- data/README.md +416 -61
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +11 -6
- data/lib/glimmer/data_binding/element_binding.rb +4 -4
- data/lib/glimmer/dsl/web/bind_expression.rb +36 -0
- data/lib/glimmer/dsl/web/data_binding_expression.rb +30 -0
- data/lib/glimmer/dsl/web/dsl.rb +6 -0
- data/lib/glimmer/dsl/web/element_expression.rb +2 -20
- data/lib/glimmer/dsl/web/general_element_expression.rb +29 -0
- data/lib/glimmer/dsl/web/p_expression.rb +2 -21
- data/lib/glimmer/dsl/web/select_expression.rb +12 -0
- data/lib/glimmer/dsl/web/shine_data_binding_expression.rb +42 -0
- data/lib/glimmer/util/proc_tracker.rb +1 -1
- data/lib/glimmer/web/element_proxy.rb +134 -589
- data/lib/glimmer/web/event_proxy.rb +1 -1
- data/lib/glimmer/web/listener_proxy.rb +1 -1
- data/lib/glimmer/web.rb +1 -1
- data/lib/glimmer-dsl-web/ext/date.rb +4 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_button.rb +1 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb +181 -0
- data/lib/glimmer-dsl-web/samples/hello/hello_form.rb +1 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb +117 -0
- data/lib/glimmer-dsl-web/samples/hello/hello_world.rb +1 -1
- data/lib/glimmer-dsl-web.rb +1 -2
- metadata +9 -23
- data/lib/glimmer/web/property_owner.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2615511253319f0227b782825764b4b009c0a3d4072b366d7409a3255f653d47
|
4
|
+
data.tar.gz: 0d09f5a38cd26cd6f8edad82aab884b89178ffa79043e5e7dc88fbd19f2e5958
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54ce5174ba09451931424f9c7d30b4abc7aebfd8486ecb6fe0863ece4a4747c908c9445753cc68e535bfb90835d1ad1951fde8f31f9fe2e90f34980754b4ecbf
|
7
|
+
data.tar.gz: d654378ed6fa4c7d05322c09054a44039d83ef2d4dcecc048963b2dc31c723a68c679072f71da2a1984ba94cd4630a382d363928ca57315ca673ae33dfe62d59
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.0.7
|
4
|
+
|
5
|
+
- Support input[type=number] value data-binding as a Ruby Numeric object (Integer or Float)
|
6
|
+
- Support input[type=range] value data-binding as a Ruby Numeric object (Integer or Float)
|
7
|
+
- Support input[type=datetime-local] value data-binding as a Ruby Time object
|
8
|
+
- Support input[type=date] value data-binding as a Ruby Date object
|
9
|
+
- Support input[type=time] value data-binding as a Ruby Time object
|
10
|
+
- Update Hello, Data-Binding! Sample to include a checkbox
|
11
|
+
- New Hello, Input (Date/Time)! Sample: `require 'glimmer-dsl-web/samples/hello/hello_input'`
|
12
|
+
|
13
|
+
## 0.0.6
|
14
|
+
|
15
|
+
- Support attribute unidirectional/bidirectional data-binding
|
16
|
+
- Support `select` element (it was blocked by a built-in Ruby method)
|
17
|
+
- Handle case of `:parent` selector being invalid, defaulting to `body`.
|
18
|
+
- Remove pure-struct gem dependency as the latest Opal fixed the implementation of Struct
|
19
|
+
- New Hello, Data-Binding! Sample: `require 'glimmer-dsl-web/samples/hello/hello_data_binding'`
|
20
|
+
|
3
21
|
## 0.0.5
|
4
22
|
|
5
23
|
- Support `p` element as it was overriden by Ruby's `p` method.
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,55 +1,9 @@
|
|
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.7 (Early Alpha)
|
2
2
|
## Ruby in the Browser Web GUI Frontend 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
|
-
[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
|
-
|
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.
|
9
|
-
|
10
|
-
**Sample**
|
11
|
-
|
12
|
-
Initial HTML Markup:
|
13
|
-
|
14
|
-
```html
|
15
|
-
...
|
16
|
-
<div id="app-container">
|
17
|
-
</div>
|
18
|
-
...
|
19
|
-
```
|
20
|
-
|
21
|
-
Glimmer GUI code:
|
22
|
-
|
23
|
-
```ruby
|
24
|
-
require 'glimmer-dsl-web'
|
25
|
-
|
26
|
-
include Glimmer
|
27
|
-
|
28
|
-
Document.ready? do
|
29
|
-
# This will hook into element #app-container and then build HTML inside it using Ruby DSL code
|
30
|
-
div(parent: '#app-container') {
|
31
|
-
label(class: 'greeting') {
|
32
|
-
'Hello, World!'
|
33
|
-
}
|
34
|
-
}.render
|
35
|
-
end
|
36
|
-
```
|
37
|
-
|
38
|
-
That produces:
|
39
|
-
|
40
|
-
```html
|
41
|
-
...
|
42
|
-
<div id="app-container">
|
43
|
-
<div data-parent="#app-container" class="element element-1">
|
44
|
-
<label class="greeting element element-2">
|
45
|
-
Hello, World!
|
46
|
-
</label>
|
47
|
-
</div>
|
48
|
-
</div>
|
49
|
-
...
|
50
|
-
```
|
51
|
-
|
52
|
-
![setup is working](/images/glimmer-dsl-web-setup-example-working.png)
|
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, most intuitive, and most straight-forward frontend library in existence. The library follows the Ruby way (with [DSLs](https://martinfowler.com/books/dsl.html) and [TIMTOWTDI](https://en.wiktionary.org/wiki/TMTOWTDI#English)) and the Rails way ([Convention over Configuration](https://rubyonrails.org/doctrine)) while supporting both Unidirectional (One-Way) Data-Binding (using `<=`) and Bidirectional (Two-Way) Data-Binding (using `<=>`). 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)!
|
53
7
|
|
54
8
|
**Hello, World! Sample**
|
55
9
|
|
@@ -266,6 +220,123 @@ Screenshot:
|
|
266
220
|
|
267
221
|
![Hello, Form!](/images/glimmer-dsl-web-samples-hello-hello-form.gif)
|
268
222
|
|
223
|
+
**Hello, Data-Binding!**
|
224
|
+
|
225
|
+
[Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) intuitively supports both Unidirectional (One-Way) Data-Binding via the `<=` operator and Bidirectional (Two-Way) Data-Binding via the `<=>` operator, incredibly simplifying how to sync View properties with Model attributes with the simplest code to reason about.
|
226
|
+
|
227
|
+
Glimmer GUI code:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
require 'glimmer-dsl-web'
|
231
|
+
|
232
|
+
Address = Struct.new(:street, :street2, :city, :state, :zip_code, keyword_init: true) do
|
233
|
+
STATES = {...} # contains US States
|
234
|
+
|
235
|
+
def state_code
|
236
|
+
STATES.invert[state]
|
237
|
+
end
|
238
|
+
|
239
|
+
def state_code=(value)
|
240
|
+
self.state = STATES[value]
|
241
|
+
end
|
242
|
+
|
243
|
+
def summary
|
244
|
+
string_attributes = to_h.except(:billing_and_shipping)
|
245
|
+
summary = string_attributes.values.map(&:to_s).reject(&:empty?).join(', ')
|
246
|
+
summary += " (Billing & Shipping)" if billing_and_shipping
|
247
|
+
summary
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
@address = Address.new(
|
252
|
+
street: '123 Main St',
|
253
|
+
street2: 'Apartment 3C, 2nd door to the right',
|
254
|
+
city: 'San Diego',
|
255
|
+
state: 'California',
|
256
|
+
zip_code: '91911',
|
257
|
+
billing_and_shipping: true,
|
258
|
+
)
|
259
|
+
|
260
|
+
include Glimmer
|
261
|
+
|
262
|
+
Document.ready? do
|
263
|
+
div {
|
264
|
+
div(style: 'display: grid; grid-auto-columns: 80px 260px;') { |address_div|
|
265
|
+
label('Street: ', for: 'street-field')
|
266
|
+
input(id: 'street-field') {
|
267
|
+
# Bidirectional Data-Binding with <=> ensures input.value and @address.street
|
268
|
+
# automatically stay in sync when either side changes
|
269
|
+
value <=> [@address, :street]
|
270
|
+
}
|
271
|
+
|
272
|
+
label('Street 2: ', for: 'street2-field')
|
273
|
+
textarea(id: 'street2-field') {
|
274
|
+
value <=> [@address, :street2]
|
275
|
+
}
|
276
|
+
|
277
|
+
label('City: ', for: 'city-field')
|
278
|
+
input(id: 'city-field') {
|
279
|
+
value <=> [@address, :city]
|
280
|
+
}
|
281
|
+
|
282
|
+
label('State: ', for: 'state-field')
|
283
|
+
select(id: 'state-field') {
|
284
|
+
Address::STATES.each do |state_code, state|
|
285
|
+
option(value: state_code) { state }
|
286
|
+
end
|
287
|
+
|
288
|
+
value <=> [@address, :state_code]
|
289
|
+
}
|
290
|
+
|
291
|
+
label('Zip Code: ', for: 'zip-code-field')
|
292
|
+
input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
|
293
|
+
# Bidirectional Data-Binding with <=> ensures input.value and @address.zip_code
|
294
|
+
# automatically stay in sync when either side changes
|
295
|
+
# on_write option specifies :to_s method to invoke on value before writing to model attribute
|
296
|
+
# to ensure the numeric zip code value is stored as a String
|
297
|
+
value <=> [@address, :zip_code,
|
298
|
+
on_write: :to_s,
|
299
|
+
]
|
300
|
+
}
|
301
|
+
|
302
|
+
div(style: 'grid-column: 1 / span 2') {
|
303
|
+
input(id: 'billing-and-shipping-field', type: 'checkbox') {
|
304
|
+
checked <=> [@address, :billing_and_shipping]
|
305
|
+
}
|
306
|
+
label(for: 'billing-and-shipping-field') {
|
307
|
+
'Use this address for both Billing & Shipping'
|
308
|
+
}
|
309
|
+
}
|
310
|
+
|
311
|
+
style {
|
312
|
+
<<~CSS
|
313
|
+
#{address_div.selector} * {
|
314
|
+
margin: 5px;
|
315
|
+
}
|
316
|
+
#{address_div.selector} input, #{address_div.selector} select {
|
317
|
+
grid-column: 2;
|
318
|
+
}
|
319
|
+
CSS
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
div(style: 'margin: 5px') {
|
324
|
+
# Unidirectional Data-Binding is done with <= to ensure @address.summary changes
|
325
|
+
# automatically update div.inner_text
|
326
|
+
# (computed by changes to address attributes, meaning if street changes,
|
327
|
+
# @address.summary is automatically recomputed.)
|
328
|
+
inner_text <= [@address, :summary,
|
329
|
+
computed_by: @address.members + ['state_code'],
|
330
|
+
]
|
331
|
+
}
|
332
|
+
}.render
|
333
|
+
end
|
334
|
+
```
|
335
|
+
|
336
|
+
Screenshot:
|
337
|
+
|
338
|
+
![Hello, Data-Binding!](/images/glimmer-dsl-web-samples-hello-hello-data-binding.gif)
|
339
|
+
|
269
340
|
**Button Counter Sample**
|
270
341
|
|
271
342
|
**UPCOMING (NOT RELEASED OR SUPPORTED YET)**
|
@@ -348,7 +419,7 @@ When clicked 7 times:
|
|
348
419
|
|
349
420
|
|
350
421
|
|
351
|
-
NOTE: Glimmer DSL for Web is
|
422
|
+
NOTE: Glimmer DSL for Web is an Early Alpha project. If you want it developed faster, please [open an issue report](https://github.com/AndyObtiva/glimmer-dsl-web/issues/new). I have completed some GitHub project features much faster before due to [issue reports](https://github.com/AndyObtiva/glimmer-dsl-web/issues) and [pull requests](https://github.com/AndyObtiva/glimmer-dsl-web/pulls). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. It is still an early alpha, so the more feedback and issues you report the better.
|
352
423
|
|
353
424
|
Learn more about the differences between various [Glimmer](https://github.com/AndyObtiva/glimmer) DSLs by looking at:
|
354
425
|
|
@@ -363,10 +434,14 @@ Learn more about the differences between various [Glimmer](https://github.com/An
|
|
363
434
|
- [Setup](#setup)
|
364
435
|
- [Usage](#usage)
|
365
436
|
- [Supported Glimmer DSL Keywords](#supported-glimmer-dsl-keywords)
|
437
|
+
- [Coming from Glimmer DSL for Opal](#coming-from-glimmer-dsl-for-opal)
|
366
438
|
- [Samples](#samples)
|
367
439
|
- [Hello Samples](#hello-samples)
|
368
440
|
- [Hello, World!](#hello-world)
|
441
|
+
- [Hello, Button!](#hello-button)
|
369
442
|
- [Hello, Form!](#hello-form)
|
443
|
+
- [Hello, Data-Binding!](#hello-data-binding)
|
444
|
+
- [Hello, Input (Date/Time)!](#hello-input-datetime)
|
370
445
|
- [Button Counter](#button-counter)
|
371
446
|
- [Glimmer Process](#glimmer-process)
|
372
447
|
- [Help](#help)
|
@@ -389,7 +464,7 @@ Learn more about the differences between various [Glimmer](https://github.com/An
|
|
389
464
|
|
390
465
|
## Setup
|
391
466
|
|
392
|
-
(NOTE: Keep in mind this is
|
467
|
+
(NOTE: Keep in mind this is an Early Alpha. If you run into issues, try to go back to a [previous revision](https://rubygems.org/gems/glimmer-dsl-web/versions). Also, there is a slight chance any issues you encounter are fixed in master or some other branch that you could check out instead)
|
393
468
|
|
394
469
|
The [glimmer-dsl-web](https://rubygems.org/gems/glimmer-dsl-web) gem is a [Rails Engine](https://guides.rubyonrails.org/engines.html) gem that includes assets.
|
395
470
|
|
@@ -416,7 +491,7 @@ gem 'opal', '1.4.1'
|
|
416
491
|
gem 'opal-rails', '2.0.2'
|
417
492
|
gem 'opal-async', '~> 1.4.0'
|
418
493
|
gem 'opal-jquery', '~> 0.4.6'
|
419
|
-
gem 'glimmer-dsl-web', '~> 0.0.
|
494
|
+
gem 'glimmer-dsl-web', '~> 0.0.7'
|
420
495
|
gem 'glimmer-dsl-xml', '~> 1.3.1', require: false
|
421
496
|
gem 'glimmer-dsl-css', '~> 1.2.1', require: false
|
422
497
|
```
|
@@ -561,7 +636,7 @@ gem 'opal', '1.4.1'
|
|
561
636
|
gem 'opal-rails', '2.0.2'
|
562
637
|
gem 'opal-async', '~> 1.4.0'
|
563
638
|
gem 'opal-jquery', '~> 0.4.6'
|
564
|
-
gem 'glimmer-dsl-web', '~> 0.0.
|
639
|
+
gem 'glimmer-dsl-web', '~> 0.0.7'
|
565
640
|
gem 'glimmer-dsl-xml', '~> 1.3.1', require: false
|
566
641
|
gem 'glimmer-dsl-css', '~> 1.2.1', require: false
|
567
642
|
```
|
@@ -701,7 +776,7 @@ Also, if the element has a little bit of text content that can fit in one line,
|
|
701
776
|
|
702
777
|
3- **Content Block (Properties + Listeners + Nested Elements + Text Content)**
|
703
778
|
|
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.
|
779
|
+
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 (intentionally breaking away from Ruby imperative code conventions given this is a declarative GUI DSL, meaning a different language that has its own conventions, embedded within Ruby).
|
705
780
|
|
706
781
|
You can nest HTML element properties under an element like:
|
707
782
|
|
@@ -743,7 +818,7 @@ form {
|
|
743
818
|
}
|
744
819
|
```
|
745
820
|
|
746
|
-
You can nest text content underneath an element's Ruby block, like:
|
821
|
+
You can nest text content underneath an element's Ruby block provided it is the return value of the block (last declared value), like:
|
747
822
|
|
748
823
|
```ruby
|
749
824
|
p(class: 'summary') {
|
@@ -757,11 +832,15 @@ You can get/set any element property or invoke any element function by simply ca
|
|
757
832
|
|
758
833
|
## Supported Glimmer DSL Keywords
|
759
834
|
|
760
|
-
[All HTML elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
|
835
|
+
[All HTML elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element), following the Ruby method name standard of lowercase and underscored names.
|
836
|
+
|
837
|
+
[All HTML attributes](https://www.w3schools.com/html/html_attributes.asp), following the Ruby method name standard of lowercase and underscored names.
|
838
|
+
|
839
|
+
[All HTML events](https://www.w3schools.com/tags/ref_eventattributes.asp), same event attribute names as in HTML.
|
761
840
|
|
762
|
-
|
841
|
+
## Coming from Glimmer DSL for Opal
|
763
842
|
|
764
|
-
[
|
843
|
+
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. As a result, applications written in Glimmer DSL for Opal are not compatible with Glimmer DSL for Web.
|
765
844
|
|
766
845
|
## Samples
|
767
846
|
|
@@ -769,8 +848,6 @@ This external sample app contains all the samples mentioned below configured ins
|
|
769
848
|
|
770
849
|
https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app
|
771
850
|
|
772
|
-
**[NOT RELEASED OR SUPPORTED YET]** https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails-app
|
773
|
-
|
774
851
|
### Hello Samples
|
775
852
|
|
776
853
|
#### Hello, World!
|
@@ -799,7 +876,7 @@ That produces the following under `<body></body>`:
|
|
799
876
|
|
800
877
|
![setup is working](/images/glimmer-dsl-web-setup-example-working.png)
|
801
878
|
|
802
|
-
Alternative syntax when an element
|
879
|
+
Alternative syntax (useful when an element has text content that fits in one line):
|
803
880
|
|
804
881
|
```ruby
|
805
882
|
require 'glimmer-dsl-web'
|
@@ -1006,6 +1083,284 @@ Screenshot:
|
|
1006
1083
|
|
1007
1084
|
![Hello, Form!](/images/glimmer-dsl-web-samples-hello-hello-form.gif)
|
1008
1085
|
|
1086
|
+
#### Hello, Data-Binding!
|
1087
|
+
|
1088
|
+
Glimmer GUI code:
|
1089
|
+
|
1090
|
+
```ruby
|
1091
|
+
require 'glimmer-dsl-web'
|
1092
|
+
|
1093
|
+
Address = Struct.new(:street, :street2, :city, :state, :zip_code, keyword_init: true) do
|
1094
|
+
STATES = {
|
1095
|
+
"AK"=>"Alaska",
|
1096
|
+
"AL"=>"Alabama",
|
1097
|
+
"AR"=>"Arkansas",
|
1098
|
+
"AS"=>"American Samoa",
|
1099
|
+
"AZ"=>"Arizona",
|
1100
|
+
"CA"=>"California",
|
1101
|
+
"CO"=>"Colorado",
|
1102
|
+
"CT"=>"Connecticut",
|
1103
|
+
"DC"=>"District of Columbia",
|
1104
|
+
"DE"=>"Delaware",
|
1105
|
+
"FL"=>"Florida",
|
1106
|
+
"GA"=>"Georgia",
|
1107
|
+
"GU"=>"Guam",
|
1108
|
+
"HI"=>"Hawaii",
|
1109
|
+
"IA"=>"Iowa",
|
1110
|
+
"ID"=>"Idaho",
|
1111
|
+
"IL"=>"Illinois",
|
1112
|
+
"IN"=>"Indiana",
|
1113
|
+
"KS"=>"Kansas",
|
1114
|
+
"KY"=>"Kentucky",
|
1115
|
+
"LA"=>"Louisiana",
|
1116
|
+
"MA"=>"Massachusetts",
|
1117
|
+
"MD"=>"Maryland",
|
1118
|
+
"ME"=>"Maine",
|
1119
|
+
"MI"=>"Michigan",
|
1120
|
+
"MN"=>"Minnesota",
|
1121
|
+
"MO"=>"Missouri",
|
1122
|
+
"MS"=>"Mississippi",
|
1123
|
+
"MT"=>"Montana",
|
1124
|
+
"NC"=>"North Carolina",
|
1125
|
+
"ND"=>"North Dakota",
|
1126
|
+
"NE"=>"Nebraska",
|
1127
|
+
"NH"=>"New Hampshire",
|
1128
|
+
"NJ"=>"New Jersey",
|
1129
|
+
"NM"=>"New Mexico",
|
1130
|
+
"NV"=>"Nevada",
|
1131
|
+
"NY"=>"New York",
|
1132
|
+
"OH"=>"Ohio",
|
1133
|
+
"OK"=>"Oklahoma",
|
1134
|
+
"OR"=>"Oregon",
|
1135
|
+
"PA"=>"Pennsylvania",
|
1136
|
+
"PR"=>"Puerto Rico",
|
1137
|
+
"RI"=>"Rhode Island",
|
1138
|
+
"SC"=>"South Carolina",
|
1139
|
+
"SD"=>"South Dakota",
|
1140
|
+
"TN"=>"Tennessee",
|
1141
|
+
"TX"=>"Texas",
|
1142
|
+
"UT"=>"Utah",
|
1143
|
+
"VA"=>"Virginia",
|
1144
|
+
"VI"=>"Virgin Islands",
|
1145
|
+
"VT"=>"Vermont",
|
1146
|
+
"WA"=>"Washington",
|
1147
|
+
"WI"=>"Wisconsin",
|
1148
|
+
"WV"=>"West Virginia",
|
1149
|
+
"WY"=>"Wyoming"
|
1150
|
+
}
|
1151
|
+
|
1152
|
+
def state_code
|
1153
|
+
STATES.invert[state]
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
def state_code=(value)
|
1157
|
+
self.state = STATES[value]
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
def summary
|
1161
|
+
string_attributes = to_h.except(:billing_and_shipping)
|
1162
|
+
summary = string_attributes.values.map(&:to_s).reject(&:empty?).join(', ')
|
1163
|
+
summary += " (Billing & Shipping)" if billing_and_shipping
|
1164
|
+
summary
|
1165
|
+
end
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
@address = Address.new(
|
1169
|
+
street: '123 Main St',
|
1170
|
+
street2: 'Apartment 3C, 2nd door to the right',
|
1171
|
+
city: 'San Diego',
|
1172
|
+
state: 'California',
|
1173
|
+
zip_code: '91911',
|
1174
|
+
billing_and_shipping: true,
|
1175
|
+
)
|
1176
|
+
|
1177
|
+
include Glimmer
|
1178
|
+
|
1179
|
+
Document.ready? do
|
1180
|
+
div {
|
1181
|
+
div(style: 'display: grid; grid-auto-columns: 80px 260px;') { |address_div|
|
1182
|
+
label('Street: ', for: 'street-field')
|
1183
|
+
input(id: 'street-field') {
|
1184
|
+
# Bidirectional Data-Binding with <=> ensures input.value and @address.street
|
1185
|
+
# automatically stay in sync when either side changes
|
1186
|
+
value <=> [@address, :street]
|
1187
|
+
}
|
1188
|
+
|
1189
|
+
label('Street 2: ', for: 'street2-field')
|
1190
|
+
textarea(id: 'street2-field') {
|
1191
|
+
value <=> [@address, :street2]
|
1192
|
+
}
|
1193
|
+
|
1194
|
+
label('City: ', for: 'city-field')
|
1195
|
+
input(id: 'city-field') {
|
1196
|
+
value <=> [@address, :city]
|
1197
|
+
}
|
1198
|
+
|
1199
|
+
label('State: ', for: 'state-field')
|
1200
|
+
select(id: 'state-field') {
|
1201
|
+
Address::STATES.each do |state_code, state|
|
1202
|
+
option(value: state_code) { state }
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
value <=> [@address, :state_code]
|
1206
|
+
}
|
1207
|
+
|
1208
|
+
label('Zip Code: ', for: 'zip-code-field')
|
1209
|
+
input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
|
1210
|
+
# Bidirectional Data-Binding with <=> ensures input.value and @address.zip_code
|
1211
|
+
# automatically stay in sync when either side changes
|
1212
|
+
# on_write option specifies :to_s method to invoke on value before writing to model attribute
|
1213
|
+
# to ensure the numeric zip code value is stored as a String
|
1214
|
+
value <=> [@address, :zip_code,
|
1215
|
+
on_write: :to_s,
|
1216
|
+
]
|
1217
|
+
}
|
1218
|
+
|
1219
|
+
div(style: 'grid-column: 1 / span 2') {
|
1220
|
+
input(id: 'billing-and-shipping-field', type: 'checkbox') {
|
1221
|
+
checked <=> [@address, :billing_and_shipping]
|
1222
|
+
}
|
1223
|
+
label(for: 'billing-and-shipping-field') {
|
1224
|
+
'Use this address for both Billing & Shipping'
|
1225
|
+
}
|
1226
|
+
}
|
1227
|
+
|
1228
|
+
style {
|
1229
|
+
<<~CSS
|
1230
|
+
#{address_div.selector} * {
|
1231
|
+
margin: 5px;
|
1232
|
+
}
|
1233
|
+
#{address_div.selector} input, #{address_div.selector} select {
|
1234
|
+
grid-column: 2;
|
1235
|
+
}
|
1236
|
+
CSS
|
1237
|
+
}
|
1238
|
+
}
|
1239
|
+
|
1240
|
+
div(style: 'margin: 5px') {
|
1241
|
+
# Unidirectional Data-Binding is done with <= to ensure @address.summary changes
|
1242
|
+
# automatically update div.inner_text
|
1243
|
+
# (computed by changes to address attributes, meaning if street changes,
|
1244
|
+
# @address.summary is automatically recomputed.)
|
1245
|
+
inner_text <= [@address, :summary,
|
1246
|
+
computed_by: @address.members + ['state_code'],
|
1247
|
+
]
|
1248
|
+
}
|
1249
|
+
}.render
|
1250
|
+
end
|
1251
|
+
```
|
1252
|
+
|
1253
|
+
Screenshot:
|
1254
|
+
|
1255
|
+
![Hello, Data-Binding!](/images/glimmer-dsl-web-samples-hello-hello-data-binding.gif)
|
1256
|
+
|
1257
|
+
#### Hello, Input (Date/Time)!
|
1258
|
+
|
1259
|
+
Glimmer GUI code:
|
1260
|
+
|
1261
|
+
```ruby
|
1262
|
+
require 'glimmer-dsl-web'
|
1263
|
+
|
1264
|
+
class TimePresenter
|
1265
|
+
attr_accessor :date_time, :month_string, :week_string
|
1266
|
+
|
1267
|
+
def initialize
|
1268
|
+
@date_time = Time.now
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
def month_string
|
1272
|
+
@date_time&.strftime('%Y-%m')
|
1273
|
+
end
|
1274
|
+
|
1275
|
+
def month_string=(value)
|
1276
|
+
if value.match(/^\d{4}-\d{2}$/)
|
1277
|
+
year, month = value.split('-')
|
1278
|
+
self.date_time = Time.new(year, month, date_time.day, date_time.hour, date_time.min)
|
1279
|
+
end
|
1280
|
+
end
|
1281
|
+
|
1282
|
+
def week_string
|
1283
|
+
return nil if @date_time.nil?
|
1284
|
+
year = @date_time.year
|
1285
|
+
week = ((@date_time.yday / 7).to_i + 1).to_s.rjust(2, '0')
|
1286
|
+
"#{year}-W#{week}"
|
1287
|
+
end
|
1288
|
+
|
1289
|
+
def date_time_string
|
1290
|
+
@date_time&.strftime('%Y-%m-%dT%H:%M')
|
1291
|
+
end
|
1292
|
+
|
1293
|
+
def date_time_string=(value)
|
1294
|
+
if value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/)
|
1295
|
+
date_time_parts = value.split('T')
|
1296
|
+
date_parts = date_time_parts.first.split('-')
|
1297
|
+
time_parts = date_time_parts.last.split(':')
|
1298
|
+
self.date_time = Time.new(*date_parts, *time_parts)
|
1299
|
+
end
|
1300
|
+
end
|
1301
|
+
end
|
1302
|
+
|
1303
|
+
@time_presenter = TimePresenter.new
|
1304
|
+
|
1305
|
+
include Glimmer
|
1306
|
+
|
1307
|
+
Document.ready? do
|
1308
|
+
div {
|
1309
|
+
div(style: 'display: grid; grid-auto-columns: 130px 260px;') { |container_div|
|
1310
|
+
label('Date Time: ', for: 'date-time-field')
|
1311
|
+
input(id: 'date-time-field', type: 'datetime-local') {
|
1312
|
+
# Bidirectional Data-Binding with <=> ensures input.value and @time_presenter.date_time
|
1313
|
+
# automatically stay in sync when either side changes
|
1314
|
+
value <=> [@time_presenter, :date_time]
|
1315
|
+
}
|
1316
|
+
|
1317
|
+
label('Date: ', for: 'date-field')
|
1318
|
+
input(id: 'date-field', type: 'date') {
|
1319
|
+
value <=> [@time_presenter, :date_time]
|
1320
|
+
}
|
1321
|
+
|
1322
|
+
label('Time: ', for: 'time-field')
|
1323
|
+
input(id: 'time-field', type: 'time') {
|
1324
|
+
value <=> [@time_presenter, :date_time]
|
1325
|
+
}
|
1326
|
+
|
1327
|
+
label('Month: ', for: 'month-field')
|
1328
|
+
input(id: 'month-field', type: 'month') {
|
1329
|
+
value <=> [@time_presenter, :month_string, computed_by: :date_time]
|
1330
|
+
}
|
1331
|
+
|
1332
|
+
label('Week: ', for: 'week-field')
|
1333
|
+
input(id: 'week-field', type: 'week', disabled: true) {
|
1334
|
+
value <=> [@time_presenter, :week_string, computed_by: :date_time]
|
1335
|
+
}
|
1336
|
+
|
1337
|
+
label('Time String: ', for: 'time-string-field')
|
1338
|
+
input(id: 'time-string-field', type: 'text') {
|
1339
|
+
value <=> [@time_presenter, :date_time_string, computed_by: :date_time]
|
1340
|
+
}
|
1341
|
+
|
1342
|
+
style {
|
1343
|
+
<<~CSS
|
1344
|
+
#{container_div.selector} * {
|
1345
|
+
margin: 5px;
|
1346
|
+
}
|
1347
|
+
#{container_div.selector} label {
|
1348
|
+
grid-column: 1;
|
1349
|
+
}
|
1350
|
+
#{container_div.selector} input {
|
1351
|
+
grid-column: 2;
|
1352
|
+
}
|
1353
|
+
CSS
|
1354
|
+
}
|
1355
|
+
}
|
1356
|
+
}.render
|
1357
|
+
end
|
1358
|
+
```
|
1359
|
+
|
1360
|
+
Screenshot:
|
1361
|
+
|
1362
|
+
![Hello, Input (Date/Time)!](/images/glimmer-dsl-web-samples-hello-hello-input-date-time.gif)
|
1363
|
+
|
1009
1364
|
#### Button Counter
|
1010
1365
|
|
1011
1366
|
**UPCOMING (NOT RELEASED OR SUPPORTED YET)**
|
@@ -1136,7 +1491,7 @@ These features have been suggested. You might see them in a future version of Gl
|
|
1136
1491
|
|
1137
1492
|
[MIT](https://opensource.org/licenses/MIT)
|
1138
1493
|
|
1139
|
-
Copyright (c) 2023 - Andy Maleh.
|
1494
|
+
Copyright (c) 2023-2024 - Andy Maleh.
|
1140
1495
|
See [LICENSE.txt](LICENSE.txt) for further details.
|
1141
1496
|
|
1142
1497
|
--
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.7
|