Friday, August 30, 2024

Todo MVC Ruby Edition w/ Component Style Blocks, Inline-Style Data-Binding, and Class-Inclusion Data-Binding

Todo MVC Ruby Edition is a Rails sample app that was built with Glimmer DSL for Web using Frontend Ruby. It was initially documented in the blog post "Todo MVC Ruby Edition Is the One Todo MVC To Rule Them All". It has been refactored, simplified, and optimized significantly (all operations happen instantly now), relying on new Glimmer DSL for Web features, such as Component Style Blocks, Inline-Style Data-Binding, and Class-Inclusion Data-Binding.

You can access an online-hosted version of Todo MVC Ruby Edition here: 

https://sample-glimmer-dsl-web-rails7-app-black-sound-6793.fly.dev/


Glimmer DSL for Web 0.4.0 - 0.4.4 features:

1) Element Inline-Style Data-Binding: this enables simple data-binding of fine-grained CSS inline-styles (e.g. `background-color`) to Model/Presenter attributes (in the past, only the course-grained style attribute was data-bindable).

Example:

The code below is using Unidirectional Data-Binding (left-arrow) to declaratively bind the value of the style width for example to the width attribute on a button_model object.

button('Submit') {

  style(:width) <= [button_model, :width]

  style(:height) <= [button_model, :height]

  style(:font_size) <= [button_model, :font_size]

}

2) Element Class-Inclusion Data-Binding: this enables simple data-binding of fine-grained CSS classes (e.g. `active`) to Model/Presenter boolean attributes (in the past, only the course-grained class attribute was data-bindable).

Example:

The code below is using Unidirectional Data-Binding (left-arrow) to declaratively bind the value of the 'pushed' CSS class for example to the pushed attribute on a button_model object. In the second line, we add an on_read converter that would negate the value on read from the Model before writing to the View element 'pulled' CSS class.

button('Toggle') {

  class_name(:pushed) <= [button_model, :pushed]

  class_name(:pulled) <= [button_model, :pushed, on_read: :!]

}

3) Ability to specify element style attribute value as a hash of CSS properties instead of a plain String.

Example:

div(style: {display: :grid, grid_auto_columns: '80px 260px'})


4) Ability to specify CSS classes as an array of String's or Symbol's instead of a plain String.

Examples

li(class: [:completed, :editing, 'todo-item'])


5) Glimmer Web Component Style Block: a style block enables declaring common styles for a component that apply to all instances of the component, using Glimmer DSL for CSS. It is an optional feature that adopts the new recommendation to co-locate styles with components to speed up productivity and maintainability. It is similar to the Styled Components approach that React developers rely on, except it is implemented in Ruby, thus providing the unique benefit of not forcing developers to switch context between multiple languages, thus improving productivity/maintainability. But, if developers prefer relying on separate CSS/SCSS files, that is fully supported as well. When a style block is declared in a component, behind the scenes, Glimmer DSL for Web generates a <style> element that is automatically included in the <head> element of the page for better performance, and it is automatically removed from the head element upon removing the last instance of a component on a webpage. Since the style block applies to all instances of a component, it is evaluated against the component class, and can leverage class methods if needed.

Example:

class TodoMvc

  include Glimmer::Web::Component

  

  before_render do evaluated against the component instance

    @presenter = TodoPresenter.new

  end

  

  after_render do evaluated against the component instance

    @presenter.setup_filter_routes

  end

  

  markup evaluated against the component instance

    div has CSS class 'todo-mvc', derived from component class name by convention

      section(class: 'todoapp') {

        new_todo_form(presenter: @presenter)

        

        todo_list(presenter: @presenter)

        

        todo_filters(presenter: @presenter)

      }

      

      todo_mvc_footer

      

      on_remove do

        @presenter.unsetup_filter_routes

      end

    }

  }

  

  style { evaluated against the component class

    r('body, button, html') {

      margin 0

      padding 0

    }

    

    r('button') {

      _webkit_font_smoothing :antialiased

      _webkit_appearance :none

      appearance :none

      background :none

      border 0

      color :inherit

      font_family :inherit

      font_size '100%'

      font_weight :inherit

      vertical_align :baseline

    }

    

    r("#{component_element_selector.todoapp") # embeds .todo-mvc before .todoapp

      background '#fff'

      margin '130px 0 40px 0'

      position :relative

      box_shadow '0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1)'

    }

  

    media('screen and (-webkit-min-device-pixel-ratio:0)') {

      r('body') {

        font "14px 'Helvetica Neue', Helvetica, Arial, sans-serif"

        line_height 1.4.em

        background '#f5f5f5'

        color '#111111'

        min_width 230

        max_width 550

        margin '0 auto'

        _webkit_font_smoothing :antialiased

        font_weight '300'

      }

    }

  }

end



Below is the updated code of Todo MVC Ruby Edition. As mentioned before, it has been refactored, simplified, and optimized significantly. 


Happy learning!













No comments: