glimmer-dsl-web 0.6.0 → 0.6.1

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: b2c800cf33832acb3ab52d13d6ffa1db466d8c1c017bde29ce120c92c8e597e9
4
- data.tar.gz: 81e200ca9d1c20c62ed5a5b3e900b9723c08c54933d93e35c7d7ac114cda480f
3
+ metadata.gz: ff9463575ae6b368ccbde387628ed3267109d92ca7239eb893647b24d80fddd7
4
+ data.tar.gz: 6896f526bef25233083ad18e5fb2a8c668e2c88dfd91fe671de2bc3ad9e6d45e
5
5
  SHA512:
6
- metadata.gz: d86f6012cf574205d499aad0a684e33793ce547740917c186580cc7295da0c7b8e625ae20473e6cd49edb87fc8a7abe4d109ab2f98cbc52e2a7208608a76c3bd
7
- data.tar.gz: 9269348151f2ad4007a1dccae80ff9ae2565af008c70285a6e0ffbe6d22faa4878b414d287556621d8ced0b23e3424980af25ec3027f3f8b39633c288ceb4e55
6
+ metadata.gz: 204c573bae64566e947914a8862360eff803bd1be3b9f67a671c5053cf65cb07a880c0f176650db757ed9bb720cd9afeb4a1954eed865c9faa34e8540b21d4d0
7
+ data.tar.gz: ec311e00c136cbdce076b798735c02118f2c3459d8a3af929093325e70b3c5fbec8696868a02046cab9154e7745bb3356a77d83b05876a03517c82293c751693
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.6.1
4
+
5
+ - Hello, Component Listeners! Sample: `require 'glimmer-dsl-web/samples/hello/hello_component_listeners'`
6
+ - Upgrade to glimmer-dsl-css 1.5.2 to fix issue with `%` Number operator and support `rem`, `Q`, `pt`, `pc`, `ch`, `ex`, `vh`, `vw`, `vmin`, `vmax` Number methods
7
+ - Refactor Todo MVC sample
8
+ - Fix issue with components that have no events declared when nesting slot content within them
9
+
3
10
  ## 0.6.0
4
11
 
5
12
  - Support Component Custom Event Listeners (declare inside Component with `event :eventname` or `events :event1, :event2, ...` and then listen inside consumer code by adding `on_customeventname do; ...; end` listener inside content block of a consumed Glimmer Web Component)
data/README.md CHANGED
@@ -1,4 +1,4 @@
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.6.0 (Beta)
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.6.1 (Beta)
2
2
  ## Ruby-in-the-Browser Web Frontend Framework
3
3
  ### Finally, Ruby Developer Productivity, Happiness, and Fun in the Frontend!!!
4
4
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-web.svg)](http://badge.fury.io/rb/glimmer-dsl-web)
@@ -12,7 +12,7 @@
12
12
 
13
13
  You can finally have Ruby developer happiness and productivity in the Frontend! No more wasting time splitting your resources across multiple languages, using badly engineered, over-engineered, or premature-optimization-obsessed JavaScript libraries, fighting JavaScript build issues (e.g. webpack), or rewriting Ruby Backend code in Frontend JavaScript. With [Ruby in the Browser](https://www.youtube.com/watch?v=4AdcfbI6A4c), you can have an exponential jump in development productivity (2x or higher), time-to-release (1/2 or less time), cost (1/2 or cheaper), and maintainability (~50% the code that is simpler and more readable) over JavaScript libraries like React, Angular, Ember, Vue, and Svelte, while being able to reuse Backend Ruby code as is in the Frontend for faster interactions when needed. Also, with Frontend Ruby, companies can cut their hiring budget in half by having Backend Ruby Software Engineers do Frontend Development in Ruby! [Ruby in the Browser](https://www.youtube.com/watch?v=4AdcfbI6A4c) finally fulfills every smart highly-productive Rubyist's dream by bringing Ruby productivity fun to Frontend Development, the same productivity fun you had for years and decades in Backend Development.
14
14
 
15
- [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for Web enables building Web 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 supports Rails' principle of the One Person Framework by not requiring any extra developers with JavaScript expertise, yet enabling Ruby (Backend) Software Engineers to develop the Frontend with Ruby code that is better than any JavaScript code produced by JS developers. It aims at providing the simplest, most intuitive, most straight-forward, and most productive frontend framework in existence. The framework 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)) in building Isomorphic Ruby on Rails Applications. It provides a Ruby [HTML DSL](#usage) (including full support for [SVG](#hello-svg)), which uniquely enables writing both structure code and logic code in one language. It supports both Unidirectional (One-Way) [Data-Binding](#hello-data-binding) (using `<=`) and Bidirectional (Two-Way) [Data-Binding](#hello-data-binding) (using `<=>`). Dynamic rendering (and re-rendering) of HTML content is also supported via [Content Data-Binding](#hello-content-data-binding). Modular design is supported with [Glimmer Web Components](#hello-component), [Component Slots](#hello-component-slots), and Component Custom Event Listeners. And, a Ruby CSS DSL is supported with the included [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css). To automatically convert legacy HTML & CSS code to Glimmer DSL Ruby code, Software Engineers could use the included [`html_to_glimmer`](https://github.com/AndyObtiva/glimmer-dsl-xml#html-to-glimmer-converter) and [`css_to_glimmer`](https://github.com/AndyObtiva/glimmer-dsl-css#css-to-glimmer-converter) commands. Many [samples](#samples) are demonstrated in the [Rails sample app](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app) (there is a very minimal [Standalone [No Rails] static site sample app](https://github.com/Largo/glimmer-dsl-web-standalone-demo) too). 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)!
15
+ [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for Web enables building Web 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 supports Rails' principle of the One Person Framework by not requiring any extra developers with JavaScript expertise, yet enabling Ruby (Backend) Software Engineers to develop the Frontend with Ruby code that is better than any JavaScript code produced by JS developers. It aims at providing the simplest, most intuitive, most straight-forward, and most productive frontend framework in existence. The framework 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)) in building Isomorphic Ruby on Rails Applications. It provides a Ruby [HTML DSL](#usage) (including full support for [SVG](#hello-svg)), which uniquely enables writing both structure code and logic code in one language. It supports both Unidirectional (One-Way) [Data-Binding](#hello-data-binding) (using `<=`) and Bidirectional (Two-Way) [Data-Binding](#hello-data-binding) (using `<=>`). Dynamic rendering (and re-rendering) of HTML content is also supported via [Content Data-Binding](#hello-content-data-binding). Modular design is supported with [Glimmer Web Components](#hello-component), [Component Slots](#hello-component-slots), and [Component Custom Event Listeners](#hello-component-listeners). And, a Ruby CSS DSL is supported with the included [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css). To automatically convert legacy HTML & CSS code to Glimmer DSL Ruby code, Software Engineers could use the included [`html_to_glimmer`](https://github.com/AndyObtiva/glimmer-dsl-xml#html-to-glimmer-converter) and [`css_to_glimmer`](https://github.com/AndyObtiva/glimmer-dsl-css#css-to-glimmer-converter) commands. Many [samples](#samples) are demonstrated in the [Rails sample app](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app) (there is a very minimal [Standalone [No Rails] static site sample app](https://github.com/Largo/glimmer-dsl-web-standalone-demo) too). 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)!
16
16
 
17
17
  [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) aims to be a very simple Ruby-based drop-in replacement for your existing JavaScript Frontend library (e.g. React, Angular, Vue, Ember, Svelte) or your JavaScript Frontend layer in general. It does not change how your Frontend interacts with the Backend, meaning you can continue to write Rails Backend API endpoints as needed and make HTTP/Ajax requests or read data embedded in elements, but from [Ruby in the Browser](https://www.youtube.com/watch?v=4AdcfbI6A4c). Whatever is possible in JavaScript is possible when using Glimmer DSL for Web as it integrates with any existing JavaScript library. The [Rails sample app](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app) demonstrates how to [make HTTP calls](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app/blob/master/app/assets/opal/sample_selector/models/sample_api.rb) and how to [integrate with a JavaScript library](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app/blob/master/app/views/layouts/application.html.erb) (highlightjs) that performs [code syntax highlighting](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app/blob/master/app/assets/opal/sample_selector.rb).
18
18
 
@@ -1344,6 +1344,7 @@ Learn more about the differences between various [Glimmer](https://github.com/An
1344
1344
  - [Hello, Content Data-Binding!](#hello-content-data-binding)
1345
1345
  - [Hello, Component!](#hello-compoent)
1346
1346
  - [Hello, Component Slots!](#hello-component-slots)
1347
+ - [Hello, Component Listeners!](#hello-compoent-listeners)
1347
1348
  - [Hello, glimmer_component Rails Helper!](#hello-glimmer_component-rails-helper)
1348
1349
  - [Hello, Paragraph!](#hello-paragraph)
1349
1350
  - [Hello, Style!](#hello-style)
@@ -1400,7 +1401,7 @@ rails new glimmer_app_server
1400
1401
  Add the following to `Gemfile`:
1401
1402
 
1402
1403
  ```
1403
- gem 'glimmer-dsl-web', '~> 0.6.0'
1404
+ gem 'glimmer-dsl-web', '~> 0.6.1'
1404
1405
  ```
1405
1406
 
1406
1407
  Run:
@@ -1629,7 +1630,7 @@ Disable the `webpacker` gem line in `Gemfile`:
1629
1630
  Add the following to `Gemfile`:
1630
1631
 
1631
1632
  ```ruby
1632
- gem 'glimmer-dsl-web', '~> 0.6.0'
1633
+ gem 'glimmer-dsl-web', '~> 0.6.1'
1633
1634
  ```
1634
1635
 
1635
1636
  Run:
@@ -3087,6 +3088,393 @@ Screenshot:
3087
3088
 
3088
3089
  ![Hello, Component Slots!](/images/glimmer-dsl-web-samples-hello-hello-component-slots.png)
3089
3090
 
3091
+ #### Hello, Component Listeners!
3092
+
3093
+ Component listeners enable consumers of components to listen to any custom events generated by components. Component supported events are declared with class method invocations `events :eventname1, :eventname2, ...` or `event :eventname`. Consumers can listen to those events by simply nesting `on_eventname do; ... end` matching the name of the declared event inside the component.
3094
+
3095
+ For example, an `AccordionSection` component might generate events `:expanded` and `:collapsed` when a user clicks on the section title to expand it or collapse it. Consumers can then use `on_expanded` and `on_collapsed` to listen to those events.
3096
+
3097
+ [lib/glimmer-dsl-web/samples/hello/hello_component_listeners.rb](/lib/glimmer-dsl-web/samples/hello/hello_component_listeners.rb)
3098
+
3099
+ Glimmer HTML DSL Ruby code in the frontend:
3100
+
3101
+ ```ruby
3102
+ require 'glimmer-dsl-web'
3103
+
3104
+ unless Object.const_defined?(:Address)
3105
+ Address = Struct.new(:full_name, :street, :street2, :city, :state, :zip_code, keyword_init: true) do
3106
+ STATES = {
3107
+ "AK"=>"Alaska",
3108
+ "AL"=>"Alabama",
3109
+ "AR"=>"Arkansas",
3110
+ "AS"=>"American Samoa",
3111
+ "AZ"=>"Arizona",
3112
+ "CA"=>"California",
3113
+ "CO"=>"Colorado",
3114
+ "CT"=>"Connecticut",
3115
+ "DC"=>"District of Columbia",
3116
+ "DE"=>"Delaware",
3117
+ "FL"=>"Florida",
3118
+ "GA"=>"Georgia",
3119
+ "GU"=>"Guam",
3120
+ "HI"=>"Hawaii",
3121
+ "IA"=>"Iowa",
3122
+ "ID"=>"Idaho",
3123
+ "IL"=>"Illinois",
3124
+ "IN"=>"Indiana",
3125
+ "KS"=>"Kansas",
3126
+ "KY"=>"Kentucky",
3127
+ "LA"=>"Louisiana",
3128
+ "MA"=>"Massachusetts",
3129
+ "MD"=>"Maryland",
3130
+ "ME"=>"Maine",
3131
+ "MI"=>"Michigan",
3132
+ "MN"=>"Minnesota",
3133
+ "MO"=>"Missouri",
3134
+ "MS"=>"Mississippi",
3135
+ "MT"=>"Montana",
3136
+ "NC"=>"North Carolina",
3137
+ "ND"=>"North Dakota",
3138
+ "NE"=>"Nebraska",
3139
+ "NH"=>"New Hampshire",
3140
+ "NJ"=>"New Jersey",
3141
+ "NM"=>"New Mexico",
3142
+ "NV"=>"Nevada",
3143
+ "NY"=>"New York",
3144
+ "OH"=>"Ohio",
3145
+ "OK"=>"Oklahoma",
3146
+ "OR"=>"Oregon",
3147
+ "PA"=>"Pennsylvania",
3148
+ "PR"=>"Puerto Rico",
3149
+ "RI"=>"Rhode Island",
3150
+ "SC"=>"South Carolina",
3151
+ "SD"=>"South Dakota",
3152
+ "TN"=>"Tennessee",
3153
+ "TX"=>"Texas",
3154
+ "UT"=>"Utah",
3155
+ "VA"=>"Virginia",
3156
+ "VI"=>"Virgin Islands",
3157
+ "VT"=>"Vermont",
3158
+ "WA"=>"Washington",
3159
+ "WI"=>"Wisconsin",
3160
+ "WV"=>"West Virginia",
3161
+ "WY"=>"Wyoming"
3162
+ }
3163
+
3164
+ def state_code
3165
+ STATES.invert[state]
3166
+ end
3167
+
3168
+ def state_code=(value)
3169
+ self.state = STATES[value]
3170
+ end
3171
+
3172
+ def summary
3173
+ to_h.values.map(&:to_s).reject(&:empty?).join(', ')
3174
+ end
3175
+ end
3176
+ end
3177
+
3178
+ unless Object.const_defined?(:AddressForm)
3179
+ # AddressForm Glimmer Web Component (View component)
3180
+ #
3181
+ # Including Glimmer::Web::Component makes this class a View component and automatically
3182
+ # generates a new Glimmer HTML DSL keyword that matches the lowercase underscored version
3183
+ # of the name of the class. AddressForm generates address_form keyword, which can be used
3184
+ # elsewhere in Glimmer HTML DSL code as done inside HelloComponentListeners below.
3185
+ class AddressForm
3186
+ include Glimmer::Web::Component
3187
+
3188
+ option :address
3189
+
3190
+ markup {
3191
+ div {
3192
+ div(style: {display: :grid, grid_auto_columns: '80px 260px'}) { |address_div|
3193
+ label('Full Name: ', for: 'full-name-field')
3194
+ input(id: 'full-name-field') {
3195
+ value <=> [address, :full_name]
3196
+ }
3197
+
3198
+ label('Street: ', for: 'street-field')
3199
+ input(id: 'street-field') {
3200
+ value <=> [address, :street]
3201
+ }
3202
+
3203
+ label('Street 2: ', for: 'street2-field')
3204
+ textarea(id: 'street2-field') {
3205
+ value <=> [address, :street2]
3206
+ }
3207
+
3208
+ label('City: ', for: 'city-field')
3209
+ input(id: 'city-field') {
3210
+ value <=> [address, :city]
3211
+ }
3212
+
3213
+ label('State: ', for: 'state-field')
3214
+ select(id: 'state-field') {
3215
+ Address::STATES.each do |state_code, state|
3216
+ option(value: state_code) { state }
3217
+ end
3218
+
3219
+ value <=> [address, :state_code]
3220
+ }
3221
+
3222
+ label('Zip Code: ', for: 'zip-code-field')
3223
+ input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
3224
+ value <=> [address, :zip_code,
3225
+ on_write: :to_s,
3226
+ ]
3227
+ }
3228
+
3229
+ style {
3230
+ r("#{address_div.selector} *") {
3231
+ margin '5px'
3232
+ }
3233
+ r("#{address_div.selector} input, #{address_div.selector} select") {
3234
+ grid_column '2'
3235
+ }
3236
+ }
3237
+ }
3238
+
3239
+ div(style: {margin: 5}) {
3240
+ inner_text <= [address, :summary,
3241
+ computed_by: address.members + ['state_code'],
3242
+ ]
3243
+ }
3244
+ }
3245
+ }
3246
+ end
3247
+ end
3248
+
3249
+ unless Object.const_defined?(:AccordionSection)
3250
+ class AccordionSection
3251
+ class Presenter
3252
+ attr_accessor :collapsed, :instant_transition
3253
+
3254
+ def toggle_collapsed(instant: false)
3255
+ self.instant_transition = instant
3256
+ self.collapsed = !collapsed
3257
+ end
3258
+
3259
+ def expand(instant: false)
3260
+ self.instant_transition = instant
3261
+ self.collapsed = false
3262
+ end
3263
+
3264
+ def collapse(instant: false)
3265
+ self.instant_transition = instant
3266
+ self.collapsed = true
3267
+ end
3268
+ end
3269
+
3270
+ include Glimmer::Web::Component
3271
+
3272
+ events :expanded, :collapsed
3273
+
3274
+ option :title
3275
+
3276
+ attr_reader :presenter
3277
+
3278
+ before_render do
3279
+ @presenter = Presenter.new
3280
+ end
3281
+
3282
+ markup {
3283
+ section {
3284
+ # Unidirectionally data-bind the class inclusion of 'collapsed' to the @presenter.collapsed boolean attribute,
3285
+ # meaning if @presenter.collapsed changes to true, the CSS class 'collapsed' is included on the element,
3286
+ # and if it changes to false, the CSS class 'collapsed' is removed from the element.
3287
+ class_name(:collapsed) <= [@presenter, :collapsed]
3288
+ class_name(:instant_transition) <= [@presenter, :instant_transition]
3289
+
3290
+ header(title, class: 'accordion-section-title') {
3291
+ onclick do |event|
3292
+ @presenter.toggle_collapsed
3293
+ if @presenter.collapsed
3294
+ notify_listeners(:collapsed)
3295
+ else
3296
+ notify_listeners(:expanded)
3297
+ end
3298
+ end
3299
+ }
3300
+
3301
+ div(slot: :section_content, class: 'accordion-section-content')
3302
+ }
3303
+ }
3304
+
3305
+ style {
3306
+ r('.accordion-section-title') {
3307
+ font_size 2.em
3308
+ font_weight :bold
3309
+ cursor :pointer
3310
+ padding_left 20
3311
+ position :relative
3312
+ margin_block_start 0.33.em
3313
+ margin_block_end 0.33.em
3314
+ }
3315
+
3316
+ r('.accordion-section-title::before') {
3317
+ content '"▼"'
3318
+ position :absolute
3319
+ font_size 0.5.em
3320
+ top 10
3321
+ left 0
3322
+ }
3323
+
3324
+ r('.accordion-section-content') {
3325
+ height 246
3326
+ overflow :hidden
3327
+ transition 'height 0.5s linear'
3328
+ }
3329
+
3330
+ r("#{component_element_selector}.instant_transition .accordion-section-content") {
3331
+ transition 'initial'
3332
+ }
3333
+
3334
+ r("#{component_element_selector}.collapsed .accordion-section-title::before") {
3335
+ content '"►"'
3336
+ }
3337
+
3338
+ r("#{component_element_selector}.collapsed .accordion-section-content") {
3339
+ height 0
3340
+ }
3341
+ }
3342
+ end
3343
+ end
3344
+
3345
+ unless Object.const_defined?(:Accordion)
3346
+ class Accordion
3347
+ include Glimmer::Web::Component
3348
+
3349
+ events :accordion_section_expanded, :accordion_section_collapsed
3350
+
3351
+ markup {
3352
+ # given that no slots are specified, nesting content under the accordion component
3353
+ # in consumer code adds content directly inside the markup root div.
3354
+ div { |accordion|
3355
+ # on render, all accordion sections would have been added by consumers already, so we can
3356
+ # attach listeners to all of them by re-opening their content with `.content { ... }` block
3357
+ on_render do
3358
+ accordion_section_elements = accordion.children
3359
+ accordion_sections = accordion_section_elements.map(&:component)
3360
+ accordion_sections.each_with_index do |accordion_section, index|
3361
+ accordion_section_number = index + 1
3362
+
3363
+ # ensure only the first section is expanded
3364
+ accordion_section.presenter.collapse(instant: true) if accordion_section_number != 1
3365
+
3366
+ accordion_section.content {
3367
+ on_expanded do
3368
+ other_accordion_sections = accordion_sections.reject {|other_accordion_section| other_accordion_section == accordion_section }
3369
+ other_accordion_sections.each { |other_accordion_section| other_accordion_section.presenter.collapse }
3370
+ notify_listeners(:accordion_section_expanded, accordion_section_number)
3371
+ end
3372
+
3373
+ on_collapsed do
3374
+ notify_listeners(:accordion_section_collapsed, accordion_section_number)
3375
+ end
3376
+ }
3377
+ end
3378
+ end
3379
+ }
3380
+ }
3381
+ end
3382
+ end
3383
+
3384
+ unless Object.const_defined?(:HelloComponentListeners)
3385
+ # HelloComponentListeners Glimmer Web Component (View component)
3386
+ #
3387
+ # This View component represents the main page being rendered,
3388
+ # as done by its `render` class method below
3389
+ class HelloComponentListeners
3390
+ class Presenter
3391
+ attr_accessor :status_message
3392
+
3393
+ def initialize
3394
+ @status_message = "Accordion section 1 is expanded!"
3395
+ end
3396
+ end
3397
+
3398
+ include Glimmer::Web::Component
3399
+
3400
+ before_render do
3401
+ @presenter = Presenter.new
3402
+ @shipping_address = Address.new(
3403
+ full_name: 'Johnny Doe',
3404
+ street: '3922 Park Ave',
3405
+ street2: 'PO BOX 8382',
3406
+ city: 'San Diego',
3407
+ state: 'California',
3408
+ zip_code: '91913',
3409
+ )
3410
+ @billing_address = Address.new(
3411
+ full_name: 'John C Doe',
3412
+ street: '123 Main St',
3413
+ street2: 'Apartment 3C',
3414
+ city: 'San Diego',
3415
+ state: 'California',
3416
+ zip_code: '91911',
3417
+ )
3418
+ @emergency_address = Address.new(
3419
+ full_name: 'Mary Doe',
3420
+ street: '2038 Ipswitch St',
3421
+ street2: 'Suite 300',
3422
+ city: 'San Diego',
3423
+ state: 'California',
3424
+ zip_code: '91912',
3425
+ )
3426
+ end
3427
+
3428
+ markup {
3429
+ div {
3430
+ h1(style: {font_style: :italic}) {
3431
+ inner_html <= [@presenter, :status_message]
3432
+ }
3433
+
3434
+ accordion { # any content nested under component directly is added under its markup root div element
3435
+ accordion_section(title: 'Shipping Address') {
3436
+ section_content { # contribute elements to section_content slot declared in AccordionSection component
3437
+ address_form(address: @shipping_address)
3438
+ }
3439
+ }
3440
+
3441
+ accordion_section(title: 'Billing Address') {
3442
+ section_content {
3443
+ address_form(address: @billing_address)
3444
+ }
3445
+ }
3446
+
3447
+ accordion_section(title: 'Emergency Address') {
3448
+ section_content {
3449
+ address_form(address: @emergency_address)
3450
+ }
3451
+ }
3452
+
3453
+ # on_accordion_section_expanded listener matches event :accordion_section_expanded declared in Accordion component
3454
+ on_accordion_section_expanded { |accordion_section_number|
3455
+ @presenter.status_message = "Accordion section #{accordion_section_number} is expanded!"
3456
+ }
3457
+
3458
+ on_accordion_section_collapsed { |accordion_section_number|
3459
+ @presenter.status_message = "Accordion section #{accordion_section_number} is collapsed!"
3460
+ }
3461
+ }
3462
+ }
3463
+ }
3464
+ end
3465
+ end
3466
+
3467
+ Document.ready? do
3468
+ # renders a top-level (root) HelloComponentListeners component
3469
+ HelloComponentListeners.render
3470
+ end
3471
+ ```
3472
+
3473
+ Screenshot:
3474
+
3475
+ ![Hello, Component Listeners!](/images/glimmer-dsl-web-samples-hello-hello-component-listeners.gif)
3476
+
3477
+
3090
3478
  #### Hello, glimmer_component Rails Helper!
3091
3479
 
3092
3480
  You may insert a Glimmer component anywhere into a Rails View using
@@ -3465,9 +3853,9 @@ class StyledButton
3465
3853
  class_name(:pushed) <= [button_model, :pushed]
3466
3854
  class_name(:pulled) <= [button_model, :pushed, on_read: :!]
3467
3855
 
3468
- style(:width) <= [button_model, :width, on_read: :px]
3469
- style(:height) <= [button_model, :height, on_read: :px]
3470
- style(:font_size) <= [button_model, :font_size, on_read: :px]
3856
+ style(:width) <= [button_model, :width]
3857
+ style(:height) <= [button_model, :height]
3858
+ style(:font_size) <= [button_model, :font_size]
3471
3859
  style(:background_color) <= [button_model, :background_color]
3472
3860
  style(:border_color) <= [button_model, :border_color, computed_by: :background_color]
3473
3861
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.6.1
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: glimmer-dsl-web 0.6.0 ruby lib
5
+ # stub: glimmer-dsl-web 0.6.1 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-web".freeze
9
- s.version = "0.6.0"
9
+ s.version = "0.6.1"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Andy Maleh".freeze]
14
- s.date = "2024-08-24"
14
+ s.date = "2024-09-02"
15
15
  s.description = "Glimmer DSL for Web (Ruby in the Browser Web Frontend Framework) enables building Web Frontends using Ruby in the Browser, as per Matz's recommendation in his RubyConf 2022 keynote speech to replace JavaScript with Ruby. It aims at providing the simplest, most intuitive, most straight-forward, and most productive frontend framework in existence. The framework follows the Ruby way (with DSLs and TIMTOWTDI) and the Rails way (Convention over Configuration) in building Isomorphic Ruby on Rails Applications. It provides a Ruby HTML DSL, which uniquely enables writing both structure code and logic code in one language. It supports 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. Modular design is supported with Glimmer Web Components, Component Slots, and Component Custom Event Listeners. And, a Ruby CSS DSL is supported with the included Glimmer DSL for CSS. Many samples are demonstrated in the Rails sample app (there is a very minimal Standalone [No Rails] sample app too). You can finally live in pure Rubyland on the Web in both the frontend and backend with Glimmer DSL for Web! This gem relies on Opal Ruby.".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.extra_rdoc_files = [
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
33
33
  "lib/glimmer-dsl-web/ext/kernel.rb",
34
34
  "lib/glimmer-dsl-web/samples/hello/hello_button.rb",
35
35
  "lib/glimmer-dsl-web/samples/hello/hello_component.rb",
36
+ "lib/glimmer-dsl-web/samples/hello/hello_component_listeners.rb",
36
37
  "lib/glimmer-dsl-web/samples/hello/hello_component_slots.rb",
37
38
  "lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb",
38
39
  "lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb",
@@ -102,7 +103,7 @@ Gem::Specification.new do |s|
102
103
 
103
104
  s.add_runtime_dependency(%q<glimmer>.freeze, ["~> 2.8.0"])
104
105
  s.add_runtime_dependency(%q<glimmer-dsl-xml>.freeze, ["~> 1.4.0"])
105
- s.add_runtime_dependency(%q<glimmer-dsl-css>.freeze, ["~> 1.5.1"])
106
+ s.add_runtime_dependency(%q<glimmer-dsl-css>.freeze, ["~> 1.5.2"])
106
107
  s.add_runtime_dependency(%q<opal>.freeze, ["= 1.8.2"])
107
108
  s.add_runtime_dependency(%q<opal-rails>.freeze, ["= 2.0.3"])
108
109
  s.add_runtime_dependency(%q<opal-async>.freeze, ["~> 1.4.1"])
@@ -89,17 +89,18 @@ module Glimmer
89
89
  @after_render = block
90
90
  end
91
91
 
92
- def event(event)
92
+ def event(event_name)
93
93
  @events ||= []
94
- event = event.to_sym
95
- @events << event unless @events.include?(event)
94
+ event_name = event_name.to_sym
95
+ @events << event_name unless @events.include?(event_name)
96
96
  end
97
97
 
98
- def events(*events)
99
- if events.empty?
98
+ def events(*event_names)
99
+ @events ||= []
100
+ if event_names.empty?
100
101
  @events
101
102
  else
102
- events.each { |event| event(event) }
103
+ event_names.each { |event| event(event) }
103
104
  end
104
105
  end
105
106
 
@@ -286,7 +287,7 @@ module Glimmer
286
287
  @args = args
287
288
  options ||= {}
288
289
  @options = self.class.options.merge(options)
289
- @events = self.class.instance_variable_get("@events")
290
+ @events = self.class.instance_variable_get("@events") || []
290
291
  @content = Util::ProcTracker.new(content) if content
291
292
  # @style_blocks = {} # TODO enable when doing bulk head rendering in the future
292
293
  execute_hooks('before_render')
@@ -0,0 +1,390 @@
1
+ # Copyright (c) 2023-2024 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
+ unless Object.const_defined?(:Address)
25
+ Address = Struct.new(:full_name, :street, :street2, :city, :state, :zip_code, keyword_init: true) do
26
+ STATES = {
27
+ "AK"=>"Alaska",
28
+ "AL"=>"Alabama",
29
+ "AR"=>"Arkansas",
30
+ "AS"=>"American Samoa",
31
+ "AZ"=>"Arizona",
32
+ "CA"=>"California",
33
+ "CO"=>"Colorado",
34
+ "CT"=>"Connecticut",
35
+ "DC"=>"District of Columbia",
36
+ "DE"=>"Delaware",
37
+ "FL"=>"Florida",
38
+ "GA"=>"Georgia",
39
+ "GU"=>"Guam",
40
+ "HI"=>"Hawaii",
41
+ "IA"=>"Iowa",
42
+ "ID"=>"Idaho",
43
+ "IL"=>"Illinois",
44
+ "IN"=>"Indiana",
45
+ "KS"=>"Kansas",
46
+ "KY"=>"Kentucky",
47
+ "LA"=>"Louisiana",
48
+ "MA"=>"Massachusetts",
49
+ "MD"=>"Maryland",
50
+ "ME"=>"Maine",
51
+ "MI"=>"Michigan",
52
+ "MN"=>"Minnesota",
53
+ "MO"=>"Missouri",
54
+ "MS"=>"Mississippi",
55
+ "MT"=>"Montana",
56
+ "NC"=>"North Carolina",
57
+ "ND"=>"North Dakota",
58
+ "NE"=>"Nebraska",
59
+ "NH"=>"New Hampshire",
60
+ "NJ"=>"New Jersey",
61
+ "NM"=>"New Mexico",
62
+ "NV"=>"Nevada",
63
+ "NY"=>"New York",
64
+ "OH"=>"Ohio",
65
+ "OK"=>"Oklahoma",
66
+ "OR"=>"Oregon",
67
+ "PA"=>"Pennsylvania",
68
+ "PR"=>"Puerto Rico",
69
+ "RI"=>"Rhode Island",
70
+ "SC"=>"South Carolina",
71
+ "SD"=>"South Dakota",
72
+ "TN"=>"Tennessee",
73
+ "TX"=>"Texas",
74
+ "UT"=>"Utah",
75
+ "VA"=>"Virginia",
76
+ "VI"=>"Virgin Islands",
77
+ "VT"=>"Vermont",
78
+ "WA"=>"Washington",
79
+ "WI"=>"Wisconsin",
80
+ "WV"=>"West Virginia",
81
+ "WY"=>"Wyoming"
82
+ }
83
+
84
+ def state_code
85
+ STATES.invert[state]
86
+ end
87
+
88
+ def state_code=(value)
89
+ self.state = STATES[value]
90
+ end
91
+
92
+ def summary
93
+ to_h.values.map(&:to_s).reject(&:empty?).join(', ')
94
+ end
95
+ end
96
+ end
97
+
98
+ unless Object.const_defined?(:AddressForm)
99
+ # AddressForm Glimmer Web Component (View component)
100
+ #
101
+ # Including Glimmer::Web::Component makes this class a View component and automatically
102
+ # generates a new Glimmer HTML DSL keyword that matches the lowercase underscored version
103
+ # of the name of the class. AddressForm generates address_form keyword, which can be used
104
+ # elsewhere in Glimmer HTML DSL code as done inside HelloComponentListeners below.
105
+ class AddressForm
106
+ include Glimmer::Web::Component
107
+
108
+ option :address
109
+
110
+ markup {
111
+ div {
112
+ div(style: {display: :grid, grid_auto_columns: '80px 260px'}) { |address_div|
113
+ label('Full Name: ', for: 'full-name-field')
114
+ input(id: 'full-name-field') {
115
+ value <=> [address, :full_name]
116
+ }
117
+
118
+ label('Street: ', for: 'street-field')
119
+ input(id: 'street-field') {
120
+ value <=> [address, :street]
121
+ }
122
+
123
+ label('Street 2: ', for: 'street2-field')
124
+ textarea(id: 'street2-field') {
125
+ value <=> [address, :street2]
126
+ }
127
+
128
+ label('City: ', for: 'city-field')
129
+ input(id: 'city-field') {
130
+ value <=> [address, :city]
131
+ }
132
+
133
+ label('State: ', for: 'state-field')
134
+ select(id: 'state-field') {
135
+ Address::STATES.each do |state_code, state|
136
+ option(value: state_code) { state }
137
+ end
138
+
139
+ value <=> [address, :state_code]
140
+ }
141
+
142
+ label('Zip Code: ', for: 'zip-code-field')
143
+ input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
144
+ value <=> [address, :zip_code,
145
+ on_write: :to_s,
146
+ ]
147
+ }
148
+
149
+ style {
150
+ r("#{address_div.selector} *") {
151
+ margin '5px'
152
+ }
153
+ r("#{address_div.selector} input, #{address_div.selector} select") {
154
+ grid_column '2'
155
+ }
156
+ }
157
+ }
158
+
159
+ div(style: {margin: 5}) {
160
+ inner_text <= [address, :summary,
161
+ computed_by: address.members + ['state_code'],
162
+ ]
163
+ }
164
+ }
165
+ }
166
+ end
167
+ end
168
+
169
+ unless Object.const_defined?(:AccordionSection)
170
+ class AccordionSection
171
+ class Presenter
172
+ attr_accessor :collapsed, :instant_transition
173
+
174
+ def toggle_collapsed(instant: false)
175
+ self.instant_transition = instant
176
+ self.collapsed = !collapsed
177
+ end
178
+
179
+ def expand(instant: false)
180
+ self.instant_transition = instant
181
+ self.collapsed = false
182
+ end
183
+
184
+ def collapse(instant: false)
185
+ self.instant_transition = instant
186
+ self.collapsed = true
187
+ end
188
+ end
189
+
190
+ include Glimmer::Web::Component
191
+
192
+ events :expanded, :collapsed
193
+
194
+ option :title
195
+
196
+ attr_reader :presenter
197
+
198
+ before_render do
199
+ @presenter = Presenter.new
200
+ end
201
+
202
+ markup {
203
+ section {
204
+ # Unidirectionally data-bind the class inclusion of 'collapsed' to the @presenter.collapsed boolean attribute,
205
+ # meaning if @presenter.collapsed changes to true, the CSS class 'collapsed' is included on the element,
206
+ # and if it changes to false, the CSS class 'collapsed' is removed from the element.
207
+ class_name(:collapsed) <= [@presenter, :collapsed]
208
+ class_name(:instant_transition) <= [@presenter, :instant_transition]
209
+
210
+ header(title, class: 'accordion-section-title') {
211
+ onclick do |event|
212
+ @presenter.toggle_collapsed
213
+ if @presenter.collapsed
214
+ notify_listeners(:collapsed)
215
+ else
216
+ notify_listeners(:expanded)
217
+ end
218
+ end
219
+ }
220
+
221
+ div(slot: :section_content, class: 'accordion-section-content')
222
+ }
223
+ }
224
+
225
+ style {
226
+ r('.accordion-section-title') {
227
+ font_size 2.em
228
+ font_weight :bold
229
+ cursor :pointer
230
+ padding_left 20
231
+ position :relative
232
+ margin_block_start 0.33.em
233
+ margin_block_end 0.33.em
234
+ }
235
+
236
+ r('.accordion-section-title::before') {
237
+ content '"▼"'
238
+ position :absolute
239
+ font_size 0.5.em
240
+ top 10
241
+ left 0
242
+ }
243
+
244
+ r('.accordion-section-content') {
245
+ height 246
246
+ overflow :hidden
247
+ transition 'height 0.5s linear'
248
+ }
249
+
250
+ r("#{component_element_selector}.instant_transition .accordion-section-content") {
251
+ transition 'initial'
252
+ }
253
+
254
+ r("#{component_element_selector}.collapsed .accordion-section-title::before") {
255
+ content '"►"'
256
+ }
257
+
258
+ r("#{component_element_selector}.collapsed .accordion-section-content") {
259
+ height 0
260
+ }
261
+ }
262
+ end
263
+ end
264
+
265
+ unless Object.const_defined?(:Accordion)
266
+ class Accordion
267
+ include Glimmer::Web::Component
268
+
269
+ events :accordion_section_expanded, :accordion_section_collapsed
270
+
271
+ markup {
272
+ # given that no slots are specified, nesting content under the accordion component
273
+ # in consumer code adds content directly inside the markup root div.
274
+ div { |accordion|
275
+ # on render, all accordion sections would have been added by consumers already, so we can
276
+ # attach listeners to all of them by re-opening their content with `.content { ... }` block
277
+ on_render do
278
+ accordion_section_elements = accordion.children
279
+ accordion_sections = accordion_section_elements.map(&:component)
280
+ accordion_sections.each_with_index do |accordion_section, index|
281
+ accordion_section_number = index + 1
282
+
283
+ # ensure only the first section is expanded
284
+ accordion_section.presenter.collapse(instant: true) if accordion_section_number != 1
285
+
286
+ accordion_section.content {
287
+ on_expanded do
288
+ other_accordion_sections = accordion_sections.reject {|other_accordion_section| other_accordion_section == accordion_section }
289
+ other_accordion_sections.each { |other_accordion_section| other_accordion_section.presenter.collapse }
290
+ notify_listeners(:accordion_section_expanded, accordion_section_number)
291
+ end
292
+
293
+ on_collapsed do
294
+ notify_listeners(:accordion_section_collapsed, accordion_section_number)
295
+ end
296
+ }
297
+ end
298
+ end
299
+ }
300
+ }
301
+ end
302
+ end
303
+
304
+ unless Object.const_defined?(:HelloComponentListeners)
305
+ # HelloComponentListeners Glimmer Web Component (View component)
306
+ #
307
+ # This View component represents the main page being rendered,
308
+ # as done by its `render` class method below
309
+ class HelloComponentListeners
310
+ class Presenter
311
+ attr_accessor :status_message
312
+
313
+ def initialize
314
+ @status_message = "Accordion section 1 is expanded!"
315
+ end
316
+ end
317
+
318
+ include Glimmer::Web::Component
319
+
320
+ before_render do
321
+ @presenter = Presenter.new
322
+ @shipping_address = Address.new(
323
+ full_name: 'Johnny Doe',
324
+ street: '3922 Park Ave',
325
+ street2: 'PO BOX 8382',
326
+ city: 'San Diego',
327
+ state: 'California',
328
+ zip_code: '91913',
329
+ )
330
+ @billing_address = Address.new(
331
+ full_name: 'John C Doe',
332
+ street: '123 Main St',
333
+ street2: 'Apartment 3C',
334
+ city: 'San Diego',
335
+ state: 'California',
336
+ zip_code: '91911',
337
+ )
338
+ @emergency_address = Address.new(
339
+ full_name: 'Mary Doe',
340
+ street: '2038 Ipswitch St',
341
+ street2: 'Suite 300',
342
+ city: 'San Diego',
343
+ state: 'California',
344
+ zip_code: '91912',
345
+ )
346
+ end
347
+
348
+ markup {
349
+ div {
350
+ h1(style: {font_style: :italic}) {
351
+ inner_html <= [@presenter, :status_message]
352
+ }
353
+
354
+ accordion { # any content nested under component directly is added under its markup root div element
355
+ accordion_section(title: 'Shipping Address') {
356
+ section_content { # contribute elements to section_content slot declared in AccordionSection component
357
+ address_form(address: @shipping_address)
358
+ }
359
+ }
360
+
361
+ accordion_section(title: 'Billing Address') {
362
+ section_content {
363
+ address_form(address: @billing_address)
364
+ }
365
+ }
366
+
367
+ accordion_section(title: 'Emergency Address') {
368
+ section_content {
369
+ address_form(address: @emergency_address)
370
+ }
371
+ }
372
+
373
+ # on_accordion_section_expanded listener matches event :accordion_section_expanded declared in Accordion component
374
+ on_accordion_section_expanded { |accordion_section_number|
375
+ @presenter.status_message = "Accordion section #{accordion_section_number} is expanded!"
376
+ }
377
+
378
+ on_accordion_section_collapsed { |accordion_section_number|
379
+ @presenter.status_message = "Accordion section #{accordion_section_number} is collapsed!"
380
+ }
381
+ }
382
+ }
383
+ }
384
+ end
385
+ end
386
+
387
+ Document.ready? do
388
+ # renders a top-level (root) HelloComponentListeners component
389
+ HelloComponentListeners.render
390
+ end
@@ -86,9 +86,9 @@ class StyledButton
86
86
  class_name(:pushed) <= [button_model, :pushed]
87
87
  class_name(:pulled) <= [button_model, :pushed, on_read: :!]
88
88
 
89
- style(:width) <= [button_model, :width, on_read: :px]
90
- style(:height) <= [button_model, :height, on_read: :px]
91
- style(:font_size) <= [button_model, :font_size, on_read: :px]
89
+ style(:width) <= [button_model, :width]
90
+ style(:height) <= [button_model, :height]
91
+ style(:font_size) <= [button_model, :font_size]
92
92
  style(:background_color) <= [button_model, :background_color]
93
93
  style(:border_color) <= [button_model, :border_color, computed_by: :background_color]
94
94
 
@@ -24,7 +24,7 @@ class NewTodoForm
24
24
  _moz_text_rendering :optimizeLegibility
25
25
  text_rendering :optimizeLegibility
26
26
  top -140
27
- width '100%'
27
+ width 100.%
28
28
  }
29
29
  }
30
30
  end
@@ -7,7 +7,7 @@ class TodoInput
7
7
  r(component_element_selector) {
8
8
  position :relative
9
9
  margin 0
10
- width '100%'
10
+ width 100.%
11
11
  font_size 24
12
12
  font_family :inherit
13
13
  font_weight :inherit
@@ -46,11 +46,11 @@ class TodoList
46
46
 
47
47
  r('.toggle-all') {
48
48
  border :none
49
- bottom '100%'
49
+ bottom 100.%
50
50
  height 1
51
51
  opacity 0
52
52
  position :absolute
53
- right '100%'
53
+ right 100.%
54
54
  width 1
55
55
  }
56
56
 
@@ -14,8 +14,6 @@ class TodoListItem
14
14
  end
15
15
 
16
16
  markup {
17
-
18
-
19
17
  li {
20
18
  # Data-bind inclusion of `completed` in `li` `class` attribute unidirectionally to `todo` `completed` attribute,
21
19
  # meaning inclusion/exclusion of `completed` class happens automatically when `todo.completed` boolean value changes.
@@ -149,7 +147,7 @@ class TodoListItem
149
147
  r('.todo-list li .destroy:after') {
150
148
  content '"×"'
151
149
  display :block
152
- height '100%'
150
+ height 100.%
153
151
  line_height '1.1'
154
152
  }
155
153
 
@@ -19,7 +19,7 @@ class TodoMvc
19
19
  end
20
20
 
21
21
  markup {
22
- div(class: 'todomvc') {
22
+ div {
23
23
  section(class: 'todoapp') {
24
24
  new_todo_form(presenter: @presenter)
25
25
 
@@ -50,7 +50,7 @@ class TodoMvc
50
50
  border 0
51
51
  color :inherit
52
52
  font_family :inherit
53
- font_size '100%'
53
+ font_size 100.%
54
54
  font_weight :inherit
55
55
  vertical_align :baseline
56
56
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-24 00:00:00.000000000 Z
11
+ date: 2024-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.5.1
47
+ version: 1.5.2
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.5.1
54
+ version: 1.5.2
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: opal
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -267,6 +267,7 @@ files:
267
267
  - lib/glimmer-dsl-web/ext/kernel.rb
268
268
  - lib/glimmer-dsl-web/samples/hello/hello_button.rb
269
269
  - lib/glimmer-dsl-web/samples/hello/hello_component.rb
270
+ - lib/glimmer-dsl-web/samples/hello/hello_component_listeners.rb
270
271
  - lib/glimmer-dsl-web/samples/hello/hello_component_slots.rb
271
272
  - lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb
272
273
  - lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb