glimmer-dsl-web 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d0e231339d0d3cd0a330fce53e852109df1b4b7b306194cff98e8e2eecbbd39
4
- data.tar.gz: 384766d64c13a9724402021ea1ce47fae393bf66ccb4c56011a6e14e4c3fdefd
3
+ metadata.gz: '028be0c05ee74f8e639b3b43adbdd52e7b90e7e5b52a0e7026fbf74e0d017300'
4
+ data.tar.gz: d2b8e3fc639dfa55655f05faf740fd36fcf518bc143eb8f08a874be3e34f72ec
5
5
  SHA512:
6
- metadata.gz: 1d127d5d4b7eed73f60940b8c669593fcace051c6eccd6a5e80bc9d759aedecec922ba2b701341b9fc043781d7d2e4d0e371cc967dd782f4c3ed228d7b68c748
7
- data.tar.gz: 9bb14fe78d3d4d6b7709429132c7b00bedb12d1183adcead609f88545b199174e7dda788241e41db63732551650064d77ec8794d3360c9c08c4c3719397a02a7
6
+ metadata.gz: e9a0add34e7db5cd7d0816e0006ed0426f9bc739402e4951f672195f6dcce472943b5f5c501213269b0ec84fb636aaea701fe35326639827bd580b94db370a5a
7
+ data.tar.gz: 19ccf1280735e472c6f643a21e105f0b1020fdacc53a66f63588be6f7b8faddb1e8391667094687a204c3d1363ee760c6b752c84866b86323437c17b8fdbe7d6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.0.8
4
+
5
+ - Validate that element keywords belong to the valid list of HTML element tag names to prevent rendering fake elements the keyword does not match a real HTML element tag
6
+ - Support inner_html/outer_html properties (it seems they did not convert to innerHTML/outerHTML correctly because of the all caps HTML)
7
+ - Support element properties via original JS names (e.g. innerHTML) to prevent logic from breaking if some do not convert correctly from underscored versions
8
+ - Package dependencies properly in the gem to avoid instructing users to add in Rails Gemfile manually
9
+ - Content Data-Binding support to regenerate element content based on changes to an observed model attribute.
10
+ Content Data-Binding Example:
11
+ content(*data_binding_options) { |data_binding_value|
12
+ li {
13
+ data_binding_value
14
+ }
15
+ }
16
+ - Ensuring removing data-binding model listeners when calling `element#remove` in addition to already removing standard HTML event listeners
17
+ - New Hello, Content Data-Binding! Sample: `require 'glimmer-dsl-web/samples/hello/hello_content_data_binding'`
18
+
19
+ ## 0.0.7
20
+
21
+ - Support input[type=number] value data-binding as a Ruby Numeric object (Integer or Float)
22
+ - Support input[type=range] value data-binding as a Ruby Numeric object (Integer or Float)
23
+ - Support input[type=datetime-local] value data-binding as a Ruby Time object
24
+ - Support input[type=date] value data-binding as a Ruby Date object
25
+ - Support input[type=time] value data-binding as a Ruby Time object
26
+ - Update Hello, Data-Binding! Sample to include a checkbox
27
+ - New Hello, Input (Date/Time)! Sample: `require 'glimmer-dsl-web/samples/hello/hello_input'`
28
+
3
29
  ## 0.0.6
4
30
 
5
31
  - Support attribute unidirectional/bidirectional data-binding
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,9 +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.6 (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.8 (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. 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)!
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, most straight-forward, and most productive 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 `<=>`). Dynamic rendering (and re-rendering) of HTML content is also supported via Content Data-Binding. 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
+ (the project plans to add component support very soon, albeit components are already supported by creating your own Ruby classes and having them render part of the GUI hierarchy)
7
9
 
8
10
  **Hello, World! Sample**
9
11
 
@@ -222,6 +224,8 @@ Screenshot:
222
224
 
223
225
  **Hello, Data-Binding!**
224
226
 
227
+ [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.
228
+
225
229
  Glimmer GUI code:
226
230
 
227
231
  ```ruby
@@ -239,7 +243,10 @@ Address = Struct.new(:street, :street2, :city, :state, :zip_code, keyword_init:
239
243
  end
240
244
 
241
245
  def summary
242
- values.map(&:to_s).reject(&:empty?).join(', ')
246
+ string_attributes = to_h.except(:billing_and_shipping)
247
+ summary = string_attributes.values.map(&:to_s).reject(&:empty?).join(', ')
248
+ summary += " (Billing & Shipping)" if billing_and_shipping
249
+ summary
243
250
  end
244
251
  end
245
252
 
@@ -248,14 +255,15 @@ end
248
255
  street2: 'Apartment 3C, 2nd door to the right',
249
256
  city: 'San Diego',
250
257
  state: 'California',
251
- zip_code: '91911'
258
+ zip_code: '91911',
259
+ billing_and_shipping: true,
252
260
  )
253
261
 
254
262
  include Glimmer
255
263
 
256
264
  Document.ready? do
257
265
  div {
258
- form(style: 'display: grid; grid-auto-columns: 80px 200px;') { |address_form|
266
+ div(style: 'display: grid; grid-auto-columns: 80px 260px;') { |address_div|
259
267
  label('Street: ', for: 'street-field')
260
268
  input(id: 'street-field') {
261
269
  # Bidirectional Data-Binding with <=> ensures input.value and @address.street
@@ -289,16 +297,25 @@ Document.ready? do
289
297
  # on_write option specifies :to_s method to invoke on value before writing to model attribute
290
298
  # to ensure the numeric zip code value is stored as a String
291
299
  value <=> [@address, :zip_code,
292
- on_write: :to_s
300
+ on_write: :to_s,
293
301
  ]
294
302
  }
295
303
 
304
+ div(style: 'grid-column: 1 / span 2') {
305
+ input(id: 'billing-and-shipping-field', type: 'checkbox') {
306
+ checked <=> [@address, :billing_and_shipping]
307
+ }
308
+ label(for: 'billing-and-shipping-field') {
309
+ 'Use this address for both Billing & Shipping'
310
+ }
311
+ }
312
+
296
313
  style {
297
314
  <<~CSS
298
- .#{address_form.element_id} * {
315
+ #{address_div.selector} * {
299
316
  margin: 5px;
300
317
  }
301
- .#{address_form.element_id} input, .#{address_form.element_id} select {
318
+ #{address_div.selector} input, #{address_div.selector} select {
302
319
  grid-column: 2;
303
320
  }
304
321
  CSS
@@ -306,10 +323,12 @@ Document.ready? do
306
323
  }
307
324
 
308
325
  div(style: 'margin: 5px') {
309
- # Unidirectional Data-Binding is done with <= to ensure @address.summary changes update div.inner_text
310
- # as computed by changes to the address member attributes + state_code address custom attribute
326
+ # Unidirectional Data-Binding is done with <= to ensure @address.summary changes
327
+ # automatically update div.inner_text
328
+ # (computed by changes to address attributes, meaning if street changes,
329
+ # @address.summary is automatically recomputed.)
311
330
  inner_text <= [@address, :summary,
312
- computed_by: @address.members + ['state_code']
331
+ computed_by: @address.members + ['state_code'],
313
332
  ]
314
333
  }
315
334
  }.render
@@ -320,6 +339,137 @@ Screenshot:
320
339
 
321
340
  ![Hello, Data-Binding!](/images/glimmer-dsl-web-samples-hello-hello-data-binding.gif)
322
341
 
342
+ **Hello, Content Data-Binding!**
343
+
344
+ If you need to regenerate HTML element content dynamically, you can use Content Data-Binding to effortlessly
345
+ rebuild HTML elements based on changes in a Model attribute that provides the source data.
346
+ In this example, we generate multiple address forms based on the number of addresses the user has.
347
+
348
+ Glimmer GUI code:
349
+
350
+ ```ruby
351
+ require 'glimmer-dsl-web'
352
+
353
+ class Address
354
+ attr_accessor :text
355
+ attr_reader :name, :street, :city, :state, :zip
356
+
357
+ def name=(value)
358
+ @name = value
359
+ update_text
360
+ end
361
+
362
+ def street=(value)
363
+ @street = value
364
+ update_text
365
+ end
366
+
367
+ def city=(value)
368
+ @city = value
369
+ update_text
370
+ end
371
+
372
+ def state=(value)
373
+ @state = value
374
+ update_text
375
+ end
376
+
377
+ def zip=(value)
378
+ @zip = value
379
+ update_text
380
+ end
381
+
382
+ private
383
+
384
+ def update_text
385
+ self.text = [name, street, city, state, zip].compact.reject(&:empty?).join(', ')
386
+ end
387
+ end
388
+
389
+ class User
390
+ attr_accessor :addresses
391
+ attr_reader :address_count
392
+
393
+ def initialize
394
+ @address_count = 1
395
+ @addresses = []
396
+ update_addresses
397
+ end
398
+
399
+ def address_count=(value)
400
+ value = [[1, value.to_i].max, 3].min
401
+ @address_count = value
402
+ update_addresses
403
+ end
404
+
405
+ private
406
+
407
+ def update_addresses
408
+ address_count_change = address_count - addresses.size
409
+ if address_count_change > 0
410
+ address_count_change.times { addresses << Address.new }
411
+ else
412
+ address_count_change.abs.times { addresses.pop }
413
+ end
414
+ end
415
+ end
416
+
417
+ @user = User.new
418
+
419
+ div {
420
+ div {
421
+ label('Number of addresses: ', for: 'address-count-field')
422
+ input(id: 'address-count-field', type: 'number', min: 1, max: 3) {
423
+ value <=> [@user, :address_count]
424
+ }
425
+ }
426
+
427
+ div {
428
+ # Content Data-Binding is used to dynamically (re)generate content of div
429
+ # based on changes to @user.addresses, replacing older content on every change
430
+ content(@user, :addresses) do
431
+ @user.addresses.each do |address|
432
+ div {
433
+ div(style: 'display: grid; grid-auto-columns: 80px 280px;') { |address_div|
434
+ [:name, :street, :city, :state, :zip].each do |attribute|
435
+ label(attribute.to_s.capitalize, for: "#{attribute}-field")
436
+ input(id: "#{attribute}-field", type: 'text') {
437
+ value <=> [address, attribute]
438
+ }
439
+ end
440
+
441
+ div(style: 'grid-column: 1 / span 2;') {
442
+ inner_text <= [address, :text]
443
+ }
444
+
445
+ style {
446
+ <<~CSS
447
+ #{address_div.selector} {
448
+ margin: 10px 0;
449
+ }
450
+ #{address_div.selector} * {
451
+ margin: 5px;
452
+ }
453
+ #{address_div.selector} label {
454
+ grid-column: 1;
455
+ }
456
+ #{address_div.selector} input, #{address_div.selector} select {
457
+ grid-column: 2;
458
+ }
459
+ CSS
460
+ }
461
+ }
462
+ }
463
+ end
464
+ end
465
+ }
466
+ }.render
467
+ ```
468
+
469
+ Screenshot:
470
+
471
+ ![Hello, Content Data-Binding!](/images/glimmer-dsl-web-samples-hello-hello-content-data-binding.gif)
472
+
323
473
  **Button Counter Sample**
324
474
 
325
475
  **UPCOMING (NOT RELEASED OR SUPPORTED YET)**
@@ -424,6 +574,8 @@ Learn more about the differences between various [Glimmer](https://github.com/An
424
574
  - [Hello, Button!](#hello-button)
425
575
  - [Hello, Form!](#hello-form)
426
576
  - [Hello, Data-Binding!](#hello-data-binding)
577
+ - [Hello, Content Data-Binding!](#hello-content-data-binding)
578
+ - [Hello, Input (Date/Time)!](#hello-input-datetime)
427
579
  - [Button Counter](#button-counter)
428
580
  - [Glimmer Process](#glimmer-process)
429
581
  - [Help](#help)
@@ -446,7 +598,7 @@ Learn more about the differences between various [Glimmer](https://github.com/An
446
598
 
447
599
  ## Setup
448
600
 
449
- (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)
601
+ (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)
450
602
 
451
603
  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.
452
604
 
@@ -469,13 +621,7 @@ rails new glimmer_app_server
469
621
  Add the following to `Gemfile`:
470
622
 
471
623
  ```
472
- gem 'opal', '1.4.1'
473
- gem 'opal-rails', '2.0.2'
474
- gem 'opal-async', '~> 1.4.0'
475
- gem 'opal-jquery', '~> 0.4.6'
476
- gem 'glimmer-dsl-web', '~> 0.0.6'
477
- gem 'glimmer-dsl-xml', '~> 1.3.1', require: false
478
- gem 'glimmer-dsl-css', '~> 1.2.1', require: false
624
+ gem 'glimmer-dsl-web', '~> 0.0.8'
479
625
  ```
480
626
 
481
627
  Run:
@@ -614,13 +760,7 @@ Disable the `webpacker` gem line in `Gemfile`:
614
760
  Add the following to `Gemfile`:
615
761
 
616
762
  ```ruby
617
- gem 'opal', '1.4.1'
618
- gem 'opal-rails', '2.0.2'
619
- gem 'opal-async', '~> 1.4.0'
620
- gem 'opal-jquery', '~> 0.4.6'
621
- gem 'glimmer-dsl-web', '~> 0.0.6'
622
- gem 'glimmer-dsl-xml', '~> 1.3.1', require: false
623
- gem 'glimmer-dsl-css', '~> 1.2.1', require: false
763
+ gem 'glimmer-dsl-web', '~> 0.0.8'
624
764
  ```
625
765
 
626
766
  Run:
@@ -1140,7 +1280,10 @@ Address = Struct.new(:street, :street2, :city, :state, :zip_code, keyword_init:
1140
1280
  end
1141
1281
 
1142
1282
  def summary
1143
- values.map(&:to_s).reject(&:empty?).join(', ')
1283
+ string_attributes = to_h.except(:billing_and_shipping)
1284
+ summary = string_attributes.values.map(&:to_s).reject(&:empty?).join(', ')
1285
+ summary += " (Billing & Shipping)" if billing_and_shipping
1286
+ summary
1144
1287
  end
1145
1288
  end
1146
1289
 
@@ -1149,14 +1292,15 @@ end
1149
1292
  street2: 'Apartment 3C, 2nd door to the right',
1150
1293
  city: 'San Diego',
1151
1294
  state: 'California',
1152
- zip_code: '91911'
1295
+ zip_code: '91911',
1296
+ billing_and_shipping: true,
1153
1297
  )
1154
1298
 
1155
1299
  include Glimmer
1156
1300
 
1157
1301
  Document.ready? do
1158
1302
  div {
1159
- form(style: 'display: grid; grid-auto-columns: 80px 200px;') { |address_form|
1303
+ div(style: 'display: grid; grid-auto-columns: 80px 260px;') { |address_div|
1160
1304
  label('Street: ', for: 'street-field')
1161
1305
  input(id: 'street-field') {
1162
1306
  # Bidirectional Data-Binding with <=> ensures input.value and @address.street
@@ -1190,16 +1334,25 @@ Document.ready? do
1190
1334
  # on_write option specifies :to_s method to invoke on value before writing to model attribute
1191
1335
  # to ensure the numeric zip code value is stored as a String
1192
1336
  value <=> [@address, :zip_code,
1193
- on_write: :to_s
1337
+ on_write: :to_s,
1194
1338
  ]
1195
1339
  }
1196
1340
 
1341
+ div(style: 'grid-column: 1 / span 2') {
1342
+ input(id: 'billing-and-shipping-field', type: 'checkbox') {
1343
+ checked <=> [@address, :billing_and_shipping]
1344
+ }
1345
+ label(for: 'billing-and-shipping-field') {
1346
+ 'Use this address for both Billing & Shipping'
1347
+ }
1348
+ }
1349
+
1197
1350
  style {
1198
1351
  <<~CSS
1199
- .#{address_form.element_id} * {
1352
+ #{address_div.selector} * {
1200
1353
  margin: 5px;
1201
1354
  }
1202
- .#{address_form.element_id} input, .#{address_form.element_id} select {
1355
+ #{address_div.selector} input, #{address_div.selector} select {
1203
1356
  grid-column: 2;
1204
1357
  }
1205
1358
  CSS
@@ -1207,10 +1360,12 @@ Document.ready? do
1207
1360
  }
1208
1361
 
1209
1362
  div(style: 'margin: 5px') {
1210
- # Unidirectional Data-Binding is done with <= to ensure @address.summary changes update div.inner_text
1211
- # as computed by changes to the address member attributes + state_code address custom attribute
1363
+ # Unidirectional Data-Binding is done with <= to ensure @address.summary changes
1364
+ # automatically update div.inner_text
1365
+ # (computed by changes to address attributes, meaning if street changes,
1366
+ # @address.summary is automatically recomputed.)
1212
1367
  inner_text <= [@address, :summary,
1213
- computed_by: @address.members + ['state_code']
1368
+ computed_by: @address.members + ['state_code'],
1214
1369
  ]
1215
1370
  }
1216
1371
  }.render
@@ -1221,6 +1376,240 @@ Screenshot:
1221
1376
 
1222
1377
  ![Hello, Data-Binding!](/images/glimmer-dsl-web-samples-hello-hello-data-binding.gif)
1223
1378
 
1379
+ #### Hello, Content Data-Binding!
1380
+
1381
+ Glimmer GUI code:
1382
+
1383
+ ```ruby
1384
+ require 'glimmer-dsl-web'
1385
+
1386
+ class Address
1387
+ attr_accessor :text
1388
+ attr_reader :name, :street, :city, :state, :zip
1389
+
1390
+ def name=(value)
1391
+ @name = value
1392
+ update_text
1393
+ end
1394
+
1395
+ def street=(value)
1396
+ @street = value
1397
+ update_text
1398
+ end
1399
+
1400
+ def city=(value)
1401
+ @city = value
1402
+ update_text
1403
+ end
1404
+
1405
+ def state=(value)
1406
+ @state = value
1407
+ update_text
1408
+ end
1409
+
1410
+ def zip=(value)
1411
+ @zip = value
1412
+ update_text
1413
+ end
1414
+
1415
+ private
1416
+
1417
+ def update_text
1418
+ self.text = [name, street, city, state, zip].compact.reject(&:empty?).join(', ')
1419
+ end
1420
+ end
1421
+
1422
+ class User
1423
+ attr_accessor :addresses
1424
+ attr_reader :address_count
1425
+
1426
+ def initialize
1427
+ @address_count = 1
1428
+ @addresses = []
1429
+ update_addresses
1430
+ end
1431
+
1432
+ def address_count=(value)
1433
+ value = [[1, value.to_i].max, 3].min
1434
+ @address_count = value
1435
+ update_addresses
1436
+ end
1437
+
1438
+ private
1439
+
1440
+ def update_addresses
1441
+ address_count_change = address_count - addresses.size
1442
+ if address_count_change > 0
1443
+ address_count_change.times { addresses << Address.new }
1444
+ else
1445
+ address_count_change.abs.times { addresses.pop }
1446
+ end
1447
+ end
1448
+ end
1449
+
1450
+ @user = User.new
1451
+
1452
+ div {
1453
+ div {
1454
+ label('Number of addresses: ', for: 'address-count-field')
1455
+ input(id: 'address-count-field', type: 'number', min: 1, max: 3) {
1456
+ value <=> [@user, :address_count]
1457
+ }
1458
+ }
1459
+
1460
+ div {
1461
+ # Content Data-Binding is used to dynamically (re)generate content of div
1462
+ # based on changes to @user.addresses, replacing older content on every change
1463
+ content(@user, :addresses) do
1464
+ @user.addresses.each do |address|
1465
+ div {
1466
+ div(style: 'display: grid; grid-auto-columns: 80px 280px;') { |address_div|
1467
+ [:name, :street, :city, :state, :zip].each do |attribute|
1468
+ label(attribute.to_s.capitalize, for: "#{attribute}-field")
1469
+ input(id: "#{attribute}-field", type: 'text') {
1470
+ value <=> [address, attribute]
1471
+ }
1472
+ end
1473
+
1474
+ div(style: 'grid-column: 1 / span 2;') {
1475
+ inner_text <= [address, :text]
1476
+ }
1477
+
1478
+ style {
1479
+ <<~CSS
1480
+ #{address_div.selector} {
1481
+ margin: 10px 0;
1482
+ }
1483
+ #{address_div.selector} * {
1484
+ margin: 5px;
1485
+ }
1486
+ #{address_div.selector} label {
1487
+ grid-column: 1;
1488
+ }
1489
+ #{address_div.selector} input, #{address_div.selector} select {
1490
+ grid-column: 2;
1491
+ }
1492
+ CSS
1493
+ }
1494
+ }
1495
+ }
1496
+ end
1497
+ end
1498
+ }
1499
+ }.render
1500
+ ```
1501
+
1502
+ Screenshot:
1503
+
1504
+ ![Hello, Content Data-Binding!](/images/glimmer-dsl-web-samples-hello-hello-content-data-binding.gif)
1505
+
1506
+ #### Hello, Input (Date/Time)!
1507
+
1508
+ Glimmer GUI code:
1509
+
1510
+ ```ruby
1511
+ require 'glimmer-dsl-web'
1512
+
1513
+ class TimePresenter
1514
+ attr_accessor :date_time, :month_string, :week_string
1515
+
1516
+ def initialize
1517
+ @date_time = Time.now
1518
+ end
1519
+
1520
+ def month_string
1521
+ @date_time&.strftime('%Y-%m')
1522
+ end
1523
+
1524
+ def month_string=(value)
1525
+ if value.match(/^\d{4}-\d{2}$/)
1526
+ year, month = value.split('-')
1527
+ self.date_time = Time.new(year, month, date_time.day, date_time.hour, date_time.min)
1528
+ end
1529
+ end
1530
+
1531
+ def week_string
1532
+ return nil if @date_time.nil?
1533
+ year = @date_time.year
1534
+ week = ((@date_time.yday / 7).to_i + 1).to_s.rjust(2, '0')
1535
+ "#{year}-W#{week}"
1536
+ end
1537
+
1538
+ def date_time_string
1539
+ @date_time&.strftime('%Y-%m-%dT%H:%M')
1540
+ end
1541
+
1542
+ def date_time_string=(value)
1543
+ if value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/)
1544
+ date_time_parts = value.split('T')
1545
+ date_parts = date_time_parts.first.split('-')
1546
+ time_parts = date_time_parts.last.split(':')
1547
+ self.date_time = Time.new(*date_parts, *time_parts)
1548
+ end
1549
+ end
1550
+ end
1551
+
1552
+ @time_presenter = TimePresenter.new
1553
+
1554
+ include Glimmer
1555
+
1556
+ Document.ready? do
1557
+ div {
1558
+ div(style: 'display: grid; grid-auto-columns: 130px 260px;') { |container_div|
1559
+ label('Date Time: ', for: 'date-time-field')
1560
+ input(id: 'date-time-field', type: 'datetime-local') {
1561
+ # Bidirectional Data-Binding with <=> ensures input.value and @time_presenter.date_time
1562
+ # automatically stay in sync when either side changes
1563
+ value <=> [@time_presenter, :date_time]
1564
+ }
1565
+
1566
+ label('Date: ', for: 'date-field')
1567
+ input(id: 'date-field', type: 'date') {
1568
+ value <=> [@time_presenter, :date_time]
1569
+ }
1570
+
1571
+ label('Time: ', for: 'time-field')
1572
+ input(id: 'time-field', type: 'time') {
1573
+ value <=> [@time_presenter, :date_time]
1574
+ }
1575
+
1576
+ label('Month: ', for: 'month-field')
1577
+ input(id: 'month-field', type: 'month') {
1578
+ value <=> [@time_presenter, :month_string, computed_by: :date_time]
1579
+ }
1580
+
1581
+ label('Week: ', for: 'week-field')
1582
+ input(id: 'week-field', type: 'week', disabled: true) {
1583
+ value <=> [@time_presenter, :week_string, computed_by: :date_time]
1584
+ }
1585
+
1586
+ label('Time String: ', for: 'time-string-field')
1587
+ input(id: 'time-string-field', type: 'text') {
1588
+ value <=> [@time_presenter, :date_time_string, computed_by: :date_time]
1589
+ }
1590
+
1591
+ style {
1592
+ <<~CSS
1593
+ #{container_div.selector} * {
1594
+ margin: 5px;
1595
+ }
1596
+ #{container_div.selector} label {
1597
+ grid-column: 1;
1598
+ }
1599
+ #{container_div.selector} input {
1600
+ grid-column: 2;
1601
+ }
1602
+ CSS
1603
+ }
1604
+ }
1605
+ }.render
1606
+ end
1607
+ ```
1608
+
1609
+ Screenshot:
1610
+
1611
+ ![Hello, Input (Date/Time)!](/images/glimmer-dsl-web-samples-hello-hello-input-date-time.gif)
1612
+
1224
1613
  #### Button Counter
1225
1614
 
1226
1615
  **UPCOMING (NOT RELEASED OR SUPPORTED YET)**
@@ -1351,7 +1740,7 @@ These features have been suggested. You might see them in a future version of Gl
1351
1740
 
1352
1741
  [MIT](https://opensource.org/licenses/MIT)
1353
1742
 
1354
- Copyright (c) 2023 - Andy Maleh.
1743
+ Copyright (c) 2023-2024 - Andy Maleh.
1355
1744
  See [LICENSE.txt](LICENSE.txt) for further details.
1356
1745
 
1357
1746
  --
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.6
1
+ 0.0.8