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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9250414d9d8804f3af1ddf5cc083ce9e06ff87e3a57e1686c1e9bb7cb1ee847
4
- data.tar.gz: f9312dfb0e0a87a047b4b25216031587f965b70d8059a7de6d670105b2ce41ca
3
+ metadata.gz: 2615511253319f0227b782825764b4b009c0a3d4072b366d7409a3255f653d47
4
+ data.tar.gz: 0d09f5a38cd26cd6f8edad82aab884b89178ffa79043e5e7dc88fbd19f2e5958
5
5
  SHA512:
6
- metadata.gz: 05be5bd7202f612582819cf970eb8185e530c2dff83e90caae1e3d5b09ad18d1aad44cd622e5b1b6d17f5afd83eb61762af2a941ab7bc77ea39b409e81ce4e6c
7
- data.tar.gz: 6a979d023e5faf0b78656ca332bf6593ea33ed224ab8f1f06490767466dd62861e6a560fec63be25352500e0e2fde92cfa9d31e47089a6f5a629f7546816c283
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2023 Andy Maleh
1
+ Copyright (c) 2023-2024 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
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.5 (Early Alpha)
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 a pre-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.
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 a very early experimental and incomplete **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)
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.5'
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.5'
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
- [All HTML attributes](https://www.w3schools.com/html/html_attributes.asp).
841
+ ## Coming from Glimmer DSL for Opal
763
842
 
764
- [All HTML events](https://www.w3schools.com/tags/ref_eventattributes.asp).
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 only has text content:
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.5
1
+ 0.0.7