tramway 0.5.0.1 → 0.5.1.1
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 +4 -4
- data/README.md +72 -0
- data/app/assets/javascripts/tramway/multiselect_controller.js +27 -4
- data/app/components/tailwinds/form/builder.rb +1 -1
- data/app/components/tailwinds/form/multiselect_component.rb +19 -2
- data/app/components/tailwinds/navbar_component.html.haml +4 -4
- data/app/components/tailwinds/pagination/first_page_component.html.haml +9 -2
- data/app/components/tailwinds/pagination/gap_component.html.haml +1 -1
- data/app/components/tailwinds/pagination/last_page_component.html.haml +9 -2
- data/app/components/tailwinds/pagination/next_page_component.html.haml +11 -2
- data/app/components/tailwinds/pagination/page_component.html.haml +5 -1
- data/app/components/tailwinds/pagination/prev_page_component.html.haml +11 -2
- data/app/components/tailwinds/table/header_component.html.haml +1 -1
- data/app/components/tailwinds/table/row_component.html.haml +5 -3
- data/app/components/tailwinds/table/row_component.rb +1 -1
- data/app/controllers/tramway/entities_controller.rb +4 -0
- data/app/views/tramway/entities/index.html.haml +12 -9
- data/app/views/tramway/layouts/application.html.haml +26 -0
- data/config/tailwind.config.js +52 -0
- data/lib/tramway/base_decorator.rb +19 -0
- data/lib/tramway/decorators/class_helper.rb +19 -9
- data/lib/tramway/version.rb +1 -1
- metadata +21 -5
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: f4ba049579087665a532f06011144ca74a591cb7704769c8a2c69c99aca81aad
         | 
| 4 | 
            +
              data.tar.gz: 44b743f0aa80d9d6b074a325505b66a8c7a019f1c59103fd6ea79e5faf567ed8
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 88a6ba5eda48febc6aec0c5c15915ee276161ddedbaa281fed6a65e68b0dc55e4d04e53a6505d68fb9e9a5871249ea2b565ac9d828dbf2b186d8bd11368b663c
         | 
| 7 | 
            +
              data.tar.gz: 25ae018d2242c6adc460e1058489109c2fae3d151243b8240d925ed1a63e0d1e19beef90d3c23cf036d34d207816c316051c8db5d681628ea2bedb0743e1a33f
         | 
    
        data/README.md
    CHANGED
    
    | @@ -17,6 +17,8 @@ Add this line to your application's Gemfile: | |
| 17 17 |  | 
| 18 18 | 
             
            ```ruby
         | 
| 19 19 | 
             
            gem "tramway"
         | 
| 20 | 
            +
            gem "haml-rails"
         | 
| 21 | 
            +
            gem "kaminari"
         | 
| 20 22 | 
             
            gem "view_component"
         | 
| 21 23 | 
             
            ```
         | 
| 22 24 |  | 
| @@ -26,8 +28,71 @@ OR | |
| 26 28 | 
             
            bundle add tramway view_component
         | 
| 27 29 | 
             
            ```
         | 
| 28 30 |  | 
| 31 | 
            +
            ## Getting Started
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            **Step 1**
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            *config/initializers/tramway.rb*
         | 
| 36 | 
            +
            ```ruby
         | 
| 37 | 
            +
            Tramway.configure do |config|
         | 
| 38 | 
            +
              config.entities = [
         | 
| 39 | 
            +
                {
         | 
| 40 | 
            +
                  name: :user,
         | 
| 41 | 
            +
                  pages: [:index],
         | 
| 42 | 
            +
                }
         | 
| 43 | 
            +
              ]
         | 
| 44 | 
            +
            end
         | 
| 45 | 
            +
            ```
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            **Step 2**
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            *config/routes.rb*
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            ```ruby
         | 
| 52 | 
            +
            Rails.application.routes.draw do
         | 
| 53 | 
            +
              mount Tramway::Engine, at: '/'
         | 
| 54 | 
            +
            end
         | 
| 55 | 
            +
            ```
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            **Step 3**
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            ```ruby
         | 
| 60 | 
            +
            class UserDecorator < Tramway::BaseDecorator
         | 
| 61 | 
            +
              def self.list_attributes
         | 
| 62 | 
            +
                [:id, :email, :created_at]
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
            end
         | 
| 65 | 
            +
            ```
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            **Step 4**
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            Copy this [file](https://github.com/Purple-Magic/tramway/blob/main/config/tailwind.config.js) to config/tailwind.config.js
         | 
| 70 | 
            +
             | 
| 71 | 
            +
             | 
| 72 | 
            +
            **Step 5**
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            Run tailwincss-rails compiler
         | 
| 75 | 
            +
             | 
| 76 | 
            +
             | 
| 77 | 
            +
            ```bash
         | 
| 78 | 
            +
            bin/rails tailwindcss:build
         | 
| 79 | 
            +
            ```
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            **Step 6**
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            Run your server
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            ```bash
         | 
| 86 | 
            +
            bundle exec rails s
         | 
| 87 | 
            +
            ```
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            **Step 7**
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            Open [http://localhost:3000/users](http://localhost:3000/users)
         | 
| 92 | 
            +
             | 
| 29 93 | 
             
            ## Usage
         | 
| 30 94 |  | 
| 95 | 
            +
             | 
| 31 96 | 
             
            ### Tramway Entities
         | 
| 32 97 |  | 
| 33 98 | 
             
            Tramway is an entity-based framework. **Entity** is the class on whose objects actions be applied: _index, show, create, update, and destroy_. Tramway will support numerous classes as entities. For now, Entity could be only **ActiveRecord::Base** class.
         | 
| @@ -486,6 +551,13 @@ eagerLoadControllersFrom("controllers", application) | |
| 486 551 | 
             
            application.register('multiselect', Multiselect) // register Multiselect controller class as `multiselect` stimulus controller
         | 
| 487 552 | 
             
            ```
         | 
| 488 553 |  | 
| 554 | 
            +
            Use Stimulus `change` action with Tramway Multiselect
         | 
| 555 | 
            +
             | 
| 556 | 
            +
            ```ruby
         | 
| 557 | 
            +
            = tramway_form_for @user do |f|
         | 
| 558 | 
            +
              = f.multiselect :role, data: { action: 'change->user-form#updateForm' } # user-form is your Stimulus controller, updateForm is a method inside user-form Stimulus controller
         | 
| 559 | 
            +
            ```
         | 
| 560 | 
            +
             | 
| 489 561 | 
             
            ### Tailwind-styled pagination for Kaminari
         | 
| 490 562 |  | 
| 491 563 | 
             
            Tramway uses [Tailwind](https://tailwindcss.com/) by default. It has tailwind-styled pagination for [kaminari](https://github.com/kaminari/kaminari).
         | 
| @@ -12,7 +12,8 @@ export default class Multiselect extends Controller { | |
| 12 12 | 
             
                selectedItems: Array,
         | 
| 13 13 | 
             
                placeholder: String,
         | 
| 14 14 | 
             
                selectAsInput: String,
         | 
| 15 | 
            -
                value: Array
         | 
| 15 | 
            +
                value: Array,
         | 
| 16 | 
            +
                onChange: String
         | 
| 16 17 | 
             
              }
         | 
| 17 18 |  | 
| 18 19 | 
             
              connect() {
         | 
| @@ -26,9 +27,14 @@ export default class Multiselect extends Controller { | |
| 26 27 | 
             
                  }
         | 
| 27 28 | 
             
                });
         | 
| 28 29 |  | 
| 29 | 
            -
                const initialValues = this.element.dataset.value === undefined ? [] : this.element.dataset.value | 
| 30 | 
            -
             | 
| 31 | 
            -
                 | 
| 30 | 
            +
                const initialValues = this.element.dataset.value === undefined ? [] : JSON.parse(this.element.dataset.value);
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                initialValues.map((value) => {
         | 
| 33 | 
            +
                  const itemIndex = this.items.findIndex(x => x.value.toString() === value.toString());
         | 
| 34 | 
            +
                  this.items[itemIndex].selected = true;
         | 
| 35 | 
            +
                })
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                this.selectedItems = this.items.filter(item => item.selected);
         | 
| 32 38 |  | 
| 33 39 | 
             
                this.renderSelectedItems();
         | 
| 34 40 | 
             
              }
         | 
| @@ -84,6 +90,23 @@ export default class Multiselect extends Controller { | |
| 84 90 | 
             
                if (this.dropdown()) {
         | 
| 85 91 | 
             
                  this.dropdown().remove();
         | 
| 86 92 | 
             
                }
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                const onChange = this.element.dataset.onChange;
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                if (onChange) {
         | 
| 97 | 
            +
                  const [controllerName, actionName] = onChange.split('#');
         | 
| 98 | 
            +
                  const controller = this.application.controllers.find(controller => controller.identifier === controllerName)
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  if (controller) {
         | 
| 101 | 
            +
                    if (typeof controller[actionName] === 'function') {
         | 
| 102 | 
            +
                      controller[actionName]({ target: this.element });
         | 
| 103 | 
            +
                    } else {
         | 
| 104 | 
            +
                      alert(`Action not found: ${actionName}`); // eslint-disable-line no-undef
         | 
| 105 | 
            +
                    }
         | 
| 106 | 
            +
                  } else {
         | 
| 107 | 
            +
                    alert(`Controller not found: ${controllerName}`); // eslint-disable-line no-undef
         | 
| 108 | 
            +
                  }
         | 
| 109 | 
            +
                }
         | 
| 87 110 | 
             
              }
         | 
| 88 111 |  | 
| 89 112 | 
             
              get template() {
         | 
| @@ -38,7 +38,7 @@ module Tailwinds | |
| 38 38 | 
             
                  def multiselect(attribute, collection, **options, &)
         | 
| 39 39 | 
             
                    render(Tailwinds::Form::MultiselectComponent.new(
         | 
| 40 40 | 
             
                             input: input(:text_field),
         | 
| 41 | 
            -
                             value: options[:value] || options[:selected] || object.public_send(attribute) | 
| 41 | 
            +
                             value: options[:value] || options[:selected] || object.public_send(attribute),
         | 
| 42 42 | 
             
                             collection:,
         | 
| 43 43 | 
             
                             **default_options(attribute, options)
         | 
| 44 44 | 
             
                           ), &)
         | 
| @@ -15,12 +15,15 @@ module Tailwinds | |
| 15 15 | 
             
                  def multiselect_hash
         | 
| 16 16 | 
             
                    {
         | 
| 17 17 | 
             
                      controller:, selected_item_template:, multiselect_selected_items_value:, dropdown_container:, item_container:,
         | 
| 18 | 
            -
                      items:, action:, select_as_input:, placeholder:, value:
         | 
| 18 | 
            +
                      items:, action:, select_as_input:, placeholder:, value:, on_change:
         | 
| 19 19 | 
             
                    }.transform_keys { |key| key.to_s.gsub('_', '-') }
         | 
| 20 20 | 
             
                  end
         | 
| 21 21 |  | 
| 22 22 | 
             
                  def controller
         | 
| 23 | 
            -
                    :multiselect
         | 
| 23 | 
            +
                    controllers = [:multiselect]
         | 
| 24 | 
            +
                    controllers << external_action.split('->').last.split('#').first if external_action
         | 
| 25 | 
            +
                    controllers += external_controllers
         | 
| 26 | 
            +
                    controllers.join(' ')
         | 
| 24 27 | 
             
                  end
         | 
| 25 28 |  | 
| 26 29 | 
             
                  private
         | 
| @@ -45,6 +48,20 @@ module Tailwinds | |
| 45 48 | 
             
                    render(Tailwinds::Form::Multiselect::SelectAsInput.new(options:, attribute:, input:))
         | 
| 46 49 | 
             
                  end
         | 
| 47 50 |  | 
| 51 | 
            +
                  def on_change
         | 
| 52 | 
            +
                    return unless external_action&.start_with?('change')
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    external_action.split('->').last
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def external_controllers
         | 
| 58 | 
            +
                    options[:controller]&.split || []
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def external_action
         | 
| 62 | 
            +
                    options.dig(:data, :action)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 48 65 | 
             
                  def method_missing(method_name, *, &)
         | 
| 49 66 | 
             
                    component = component_name(method_name)
         | 
| 50 67 |  | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            %nav.py- | 
| 1 | 
            +
            %nav.py-2.px-4.sm:px-8.flex.justify-between.items-center.dark:bg-gray-800{ class: "bg-#{@color}" }
         | 
| 2 2 | 
             
              .flex.justify-between.w-full
         | 
| 3 3 | 
             
                - if @title[:text].present? || @left_items.present?
         | 
| 4 4 | 
             
                  .flex.items-center
         | 
| @@ -7,16 +7,16 @@ | |
| 7 7 | 
             
                        .text-xl.text-white.font-bold
         | 
| 8 8 | 
             
                          = @title[:text]
         | 
| 9 9 | 
             
                    - if @left_items.present?
         | 
| 10 | 
            -
                      %ul. | 
| 10 | 
            +
                      %ul.block.flex.flex-row.items-center.space-x-4.ml-4
         | 
| 11 11 | 
             
                        - @left_items.each do |item|
         | 
| 12 12 | 
             
                          = item
         | 
| 13 13 |  | 
| 14 | 
            -
                . | 
| 14 | 
            +
                .hidden.sm:block
         | 
| 15 15 | 
             
                  %button#mobile-menu-button.text-white.focus:outline-none
         | 
| 16 16 | 
             
                    ☰
         | 
| 17 17 |  | 
| 18 18 | 
             
              - if @right_items.present?
         | 
| 19 | 
            -
                %ul. | 
| 19 | 
            +
                %ul.block.sm:flex.items-center.space-x-4
         | 
| 20 20 | 
             
                  - @right_items.each do |item|
         | 
| 21 21 | 
             
                    = item
         | 
| 22 22 |  | 
| @@ -1,3 +1,10 @@ | |
| 1 1 | 
             
            - unless current_page.first?
         | 
| 2 | 
            -
              = link_to helpers.t('views.pagination.first').html_safe, | 
| 3 | 
            -
             | 
| 2 | 
            +
              = link_to helpers.t('views.pagination.first').html_safe,
         | 
| 3 | 
            +
                url,
         | 
| 4 | 
            +
                remote:,
         | 
| 5 | 
            +
                class: pagination_classes(klass: 'first sm:hidden flex')
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              = link_to '⭰',
         | 
| 8 | 
            +
                url,
         | 
| 9 | 
            +
                remote:,
         | 
| 10 | 
            +
                class: pagination_classes(klass: 'first hidden sm:flex font-bold')
         | 
| @@ -1,2 +1,2 @@ | |
| 1 | 
            -
            %span.page.gap.px-3.py-2.text-sm.font-medium.text-purple-700.dark:text-white.sm: | 
| 1 | 
            +
            %span.page.gap.px-3.py-2.text-sm.font-medium.text-purple-700.dark:text-white.flex.sm:hidden
         | 
| 2 2 | 
             
              = helpers.t('views.pagination.truncate').html_safe
         | 
| @@ -1,3 +1,10 @@ | |
| 1 1 | 
             
            - unless current_page.last?
         | 
| 2 | 
            -
              = link_to helpers.t('views.pagination.last').html_safe, | 
| 3 | 
            -
             | 
| 2 | 
            +
              = link_to helpers.t('views.pagination.last').html_safe,
         | 
| 3 | 
            +
                url,
         | 
| 4 | 
            +
                remote:,
         | 
| 5 | 
            +
                class: pagination_classes(klass: 'last sm:hidden flex')
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              = link_to '⭲',
         | 
| 8 | 
            +
                url,
         | 
| 9 | 
            +
                remote:,
         | 
| 10 | 
            +
                class: pagination_classes(klass: 'last hidden sm:flex font-bold')
         | 
| @@ -1,3 +1,12 @@ | |
| 1 1 | 
             
            - unless current_page.last?
         | 
| 2 | 
            -
              = link_to helpers.t('views.pagination.next').html_safe, | 
| 3 | 
            -
             | 
| 2 | 
            +
              = link_to helpers.t('views.pagination.next').html_safe,
         | 
| 3 | 
            +
                url,
         | 
| 4 | 
            +
                rel: 'next',
         | 
| 5 | 
            +
                remote:,
         | 
| 6 | 
            +
                class: pagination_classes(klass: 'next sm:hidden flex')
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              = link_to '🠖',
         | 
| 9 | 
            +
                url,
         | 
| 10 | 
            +
                rel: 'next',
         | 
| 11 | 
            +
                remote:,
         | 
| 12 | 
            +
                class: pagination_classes(klass: 'next hidden sm:flex font-bold')
         | 
| @@ -2,4 +2,8 @@ | |
| 2 2 | 
             
              %span.px-3.py-2.font-medium.rounded-md.bg-purple-500.text-white.dark:text-gray-800.dark:bg-white
         | 
| 3 3 | 
             
                = page
         | 
| 4 4 | 
             
            - else
         | 
| 5 | 
            -
              = link_to page, | 
| 5 | 
            +
              = link_to page,
         | 
| 6 | 
            +
                url,
         | 
| 7 | 
            +
                remote:,
         | 
| 8 | 
            +
                rel: page.rel,
         | 
| 9 | 
            +
                class: pagination_classes(klass: 'sm:hidden flex')
         | 
| @@ -1,3 +1,12 @@ | |
| 1 1 | 
             
            - unless current_page.first?
         | 
| 2 | 
            -
              = link_to helpers.t('views.pagination.previous').html_safe, | 
| 3 | 
            -
             | 
| 2 | 
            +
              = link_to helpers.t('views.pagination.previous').html_safe,
         | 
| 3 | 
            +
                url,
         | 
| 4 | 
            +
                rel: 'prev',
         | 
| 5 | 
            +
                remote:,
         | 
| 6 | 
            +
                class: pagination_classes(klass: 'prev sm:hidden flex')
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              = link_to '🠔',
         | 
| 9 | 
            +
                url,
         | 
| 10 | 
            +
                rel: 'prev',
         | 
| 11 | 
            +
                remote:,
         | 
| 12 | 
            +
                class: pagination_classes(klass: 'prev hidden sm:flex font-bold')
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            - cols = headers.map { |item| "1fr" }.join(",")
         | 
| 2 2 |  | 
| 3 | 
            -
            .div-table-row. | 
| 3 | 
            +
            .div-table-row.block.grid.text-white.text-small.gap-4.bg-purple-700.dark:bg-gray-700.dark:text-gray-400{ class: "grid-cols-[#{cols}]" }
         | 
| 4 4 | 
             
              - headers.each do |header|
         | 
| 5 5 | 
             
                .div-table-cell.py-4.px-6
         | 
| 6 6 | 
             
                  = header
         | 
| @@ -2,12 +2,14 @@ | |
| 2 2 | 
             
            - if cells.any?
         | 
| 3 3 | 
             
              - cols = cells.count.times.map { |item| "1fr" }.join(",")
         | 
| 4 4 |  | 
| 5 | 
            -
               | 
| 5 | 
            +
              -# desktop view
         | 
| 6 | 
            +
              = row_tag class: "div-table-row block grid gap-4 bg-white border-b dark:bg-gray-800 dark:border-gray-700 cursor-pointer hover:bg-purple-100 grid-cols-[#{cols}]" do
         | 
| 6 7 | 
             
                - cells.each do |(_, value)|
         | 
| 7 | 
            -
                  .div-table-cell.px-6.py-4.font-medium.text-gray-900.whitespace-nowrap.dark:text-white.text-xs. | 
| 8 | 
            +
                  .div-table-cell.px-6.py-4.font-medium.text-gray-900.whitespace-nowrap.dark:text-white.sm:text-xs.text-base
         | 
| 8 9 | 
             
                    = value
         | 
| 9 10 |  | 
| 10 | 
            -
               | 
| 11 | 
            +
              -# mobile view
         | 
| 12 | 
            +
              .div-table-row.xl:hidden.border-b.dark:bg-gray-800.dark:border-gray-700.mb-2{ "data-action" => "click->preview#toggle", "data-controller" => "preview", "data-items" => cells.to_json }
         | 
| 11 13 | 
             
                .w-full.p-4.bg-purple-100.text-gray-700.dark:bg-gray-700.dark:text-gray-400
         | 
| 12 14 | 
             
                  = cells.values.first
         | 
| 13 15 |  | 
| @@ -9,7 +9,7 @@ module Tailwinds | |
| 9 9 |  | 
| 10 10 | 
             
                  def row_tag(**options, &)
         | 
| 11 11 | 
             
                    if href.present?
         | 
| 12 | 
            -
                      klass = "#{options[:class] || ''} cursor-pointer hover:bg-gray-700"
         | 
| 12 | 
            +
                      klass = "#{options[:class] || ''} cursor-pointer dark:hover:bg-gray-700"
         | 
| 13 13 |  | 
| 14 14 | 
             
                      link_to(href, options.merge(class: klass)) do
         | 
| 15 15 | 
             
                        yield if block_given?
         | 
| @@ -3,6 +3,10 @@ | |
| 3 3 | 
             
            module Tramway
         | 
| 4 4 | 
             
              # Main controller for entities pages
         | 
| 5 5 | 
             
              class EntitiesController < Tramway.config.application_controller.constantize
         | 
| 6 | 
            +
                prepend_view_path "#{Gem::Specification.find_by_name('tramway').gem_dir}/app/views"
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                layout 'tramway/layouts/application'
         | 
| 9 | 
            +
             | 
| 6 10 | 
             
                helper Tramway::ApplicationHelper
         | 
| 7 11 | 
             
                include Rails.application.routes.url_helpers
         | 
| 8 12 |  | 
| @@ -1,4 +1,7 @@ | |
| 1 | 
            -
            . | 
| 1 | 
            +
            - decorator = Tramway::Decorators::NameBuilder.default_decorator_class_name(@model_class)
         | 
| 2 | 
            +
            - list_attributes = decorator.constantize.list_attributes
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            .mt-8{ class: 'w-2/3' }
         | 
| 2 5 | 
             
              - content_for :title, page_title
         | 
| 3 6 |  | 
| 4 7 | 
             
              .flex.justify-between.items-center.md:mt-4.mt-2
         | 
| @@ -8,15 +11,15 @@ | |
| 8 11 | 
             
                - if Tramway.config.pagination[:enabled]
         | 
| 9 12 | 
             
                  = paginate @entities
         | 
| 10 13 |  | 
| 11 | 
            -
              - if  | 
| 14 | 
            +
              - if list_attributes.empty?
         | 
| 12 15 | 
             
                %p.text-center.mt-10
         | 
| 13 16 | 
             
                  You should fill class-level method `self.list_attributes` inside your
         | 
| 14 | 
            -
                  =  | 
| 17 | 
            +
                  = decorator
         | 
| 15 18 |  | 
| 16 | 
            -
            = component 'tailwinds/table' do
         | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 19 | 
            +
              = component 'tailwinds/table' do
         | 
| 20 | 
            +
                = component 'tailwinds/table/header', headers: list_attributes.map { |attribute| @model_class.human_attribute_name(attribute) }
         | 
| 21 | 
            +
                - @entities.each do |item|
         | 
| 22 | 
            +
                  = render 'entity', entity: item
         | 
| 20 23 |  | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 24 | 
            +
                .flex.mt-4
         | 
| 25 | 
            +
                  = paginate @entities
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            %html
         | 
| 2 | 
            +
              %head
         | 
| 3 | 
            +
                %title= content_for(:title) || "Tramway"
         | 
| 4 | 
            +
                %meta{name: "viewport", content: "width=device-width,initial-scale=1"}
         | 
| 5 | 
            +
                %meta{name: "apple-mobile-web-app-capable", content: "yes"}
         | 
| 6 | 
            +
                %meta{name: "mobile-web-app-capable", content: "yes"}
         | 
| 7 | 
            +
                = csrf_meta_tags
         | 
| 8 | 
            +
                = csp_meta_tag
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                = yield :head
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                / Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!)
         | 
| 13 | 
            +
                / = tag.link rel: "manifest", href: pwa_manifest_path(format: :json)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                %link{rel: "icon", href: "/icon.png", type: "image/png"}
         | 
| 16 | 
            +
                %link{rel: "icon", href: "/icon.svg", type: "image/svg+xml"}
         | 
| 17 | 
            +
                %link{rel: "apple-touch-icon", href: "/icon.png"}
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                / Includes all stylesheet files in app/assets/stylesheets
         | 
| 20 | 
            +
                = stylesheet_link_tag "tailwind", "data-turbo-track": "reload"
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              %body.bg-gray-100
         | 
| 23 | 
            +
                = tramway_navbar title: 'Tramway'
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                .container.mx-auto.p-4.flex.align-center.justify-center
         | 
| 26 | 
            +
                  = yield
         | 
| @@ -0,0 +1,52 @@ | |
| 1 | 
            +
            const defaultTheme = require('tailwindcss/defaultTheme');
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module.exports = {
         | 
| 4 | 
            +
              darkMode: 'class',
         | 
| 5 | 
            +
              content: [
         | 
| 6 | 
            +
                './public/*.html',
         | 
| 7 | 
            +
                './app/components/**/*.html.haml',
         | 
| 8 | 
            +
                './app/helpers/**/*.rb',
         | 
| 9 | 
            +
                './app/javascript/**/*.js',
         | 
| 10 | 
            +
                './app/views/**/*.{erb,haml,html,slim}'
         | 
| 11 | 
            +
              ],
         | 
| 12 | 
            +
              theme: {
         | 
| 13 | 
            +
                extend: {
         | 
| 14 | 
            +
                  screens: {
         | 
| 15 | 
            +
                    ...defaultTheme.screens,
         | 
| 16 | 
            +
                  },
         | 
| 17 | 
            +
                },
         | 
| 18 | 
            +
              },
         | 
| 19 | 
            +
              safelist: [
         | 
| 20 | 
            +
                'div-table',
         | 
| 21 | 
            +
                'div-table-row',
         | 
| 22 | 
            +
                'div-table-cell',
         | 
| 23 | 
            +
                'hidden',
         | 
| 24 | 
            +
                'text-xl',
         | 
| 25 | 
            +
                'text-4xl',
         | 
| 26 | 
            +
                'font-bold',
         | 
| 27 | 
            +
                'xl:hidden',
         | 
| 28 | 
            +
                'grid-cols-[1fr,1fr,1fr]',
         | 
| 29 | 
            +
                'text-right',
         | 
| 30 | 
            +
                'w-2/3',
         | 
| 31 | 
            +
                'flex',
         | 
| 32 | 
            +
                // multiselect styles
         | 
| 33 | 
            +
                'absolute', 'shadow', 'top-100', 'z-40', 'w-full', 'lef-0', 'rounded', 'max-h-select', 'overflow-y-auto', 'cursor-pointer', 'rounded-t', 'border-b', 'hover:bg-teal-100', 'items-center', 'border-transparent', 'border-l-2,', 'relative', 'hover:border-teal-100', 'leading-6', 'bg-transparent', 'appearance-none', 'outline-none', 'h-full' , 'justify-center', 'm-1', 'font-medium', 'rounded-full', 'text-teal-700', 'bg-teal-100', 'border', 'border-teal-300', 'text-xs', 'font-normal', 'leading-none', 'max-w-full', 'flex-initial', 'flex-auto', 'flex-row-reverse',, 'flex-col', 'min-w-96', 'w-fit', 'flex-wrap', 'w-8', 'border-l',
         | 
| 34 | 
            +
                {
         | 
| 35 | 
            +
                  pattern: /(text|bg|border)-(purple|blue|gray|yellow|red|white|green)-\d+/,
         | 
| 36 | 
            +
                  variants: ['dark', 'focus', 'hover', 'dark:hover'],
         | 
| 37 | 
            +
                },
         | 
| 38 | 
            +
                {
         | 
| 39 | 
            +
                  pattern: /(text|bg)-(purple|blue|gray|yellow|red|white)/,
         | 
| 40 | 
            +
                  variants: ['dark', 'focus', 'hover', 'dark:hover'],
         | 
| 41 | 
            +
                },
         | 
| 42 | 
            +
                {
         | 
| 43 | 
            +
                  pattern: /(m|p)(l|r|b|t|x|y)-\d+/
         | 
| 44 | 
            +
                },
         | 
| 45 | 
            +
              ],
         | 
| 46 | 
            +
              plugins: [
         | 
| 47 | 
            +
                require('@tailwindcss/forms'),
         | 
| 48 | 
            +
                require('@tailwindcss/aspect-ratio'),
         | 
| 49 | 
            +
                require('@tailwindcss/typography'),
         | 
| 50 | 
            +
                require('@tailwindcss/container-queries'),
         | 
| 51 | 
            +
              ],
         | 
| 52 | 
            +
            };
         | 
| @@ -57,5 +57,24 @@ module Tramway | |
| 57 57 | 
             
                end
         | 
| 58 58 |  | 
| 59 59 | 
             
                def show_path = nil
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                # :reek:ManualDispatch { enabled: false } because there is the idea to manual dispatch
         | 
| 62 | 
            +
                def method_missing(method_name, *, &)
         | 
| 63 | 
            +
                  url_helpers = Rails.application.routes.url_helpers
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  if method_name.to_s.end_with?('_path', '_url')
         | 
| 66 | 
            +
                    return url_helpers.public_send(method_name, *, &) if url_helpers.respond_to?(method_name)
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    raise NoMethodError, "undefined method `#{method_name}` for #{self}" unless respond_to_missing?(method_name)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  super
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                # :reek:BooleanParameter { enabled: false } because it's a part of the duck-typing
         | 
| 76 | 
            +
                def respond_to_missing?(method_name, include_private = false)
         | 
| 77 | 
            +
                  method_name.to_s.end_with?('_path', '_url') || super
         | 
| 78 | 
            +
                end
         | 
| 60 79 | 
             
              end
         | 
| 61 80 | 
             
            end
         | 
| @@ -7,15 +7,15 @@ module Tramway | |
| 7 7 | 
             
                  module_function
         | 
| 8 8 |  | 
| 9 9 | 
             
                  def decorator_class(object_or_array, decorator = nil)
         | 
| 10 | 
            -
                     | 
| 11 | 
            -
             | 
| 12 | 
            -
                     | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
                       | 
| 17 | 
            -
             | 
| 18 | 
            -
                       | 
| 10 | 
            +
                    raise_error_if_object_empty object_or_array, decorator
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    return decorator if decorator.present?
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    begin
         | 
| 15 | 
            +
                      class_name = decorator_class_name(object_or_array)
         | 
| 16 | 
            +
                      class_name.constantize
         | 
| 17 | 
            +
                    rescue NameError
         | 
| 18 | 
            +
                      raise NameError, "You should define #{class_name} decorator class."
         | 
| 19 19 | 
             
                    end
         | 
| 20 20 | 
             
                  end
         | 
| 21 21 |  | 
| @@ -28,6 +28,16 @@ module Tramway | |
| 28 28 |  | 
| 29 29 | 
             
                    Tramway::Decorators::NameBuilder.default_decorator_class_name(klass)
         | 
| 30 30 | 
             
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  # :reek:NilCheck { enabled: false }
         | 
| 33 | 
            +
                  def raise_error_if_object_empty(object_or_array, decorator)
         | 
| 34 | 
            +
                    return unless object_or_array.blank? && decorator.nil?
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    text = 'You should pass object or array that is not empty OR provide a decorator class as a second argument'
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    raise ArgumentError, text
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                  # :reek:NilCheck { enabled: true }
         | 
| 31 41 | 
             
                end
         | 
| 32 42 | 
             
              end
         | 
| 33 43 | 
             
            end
         | 
    
        data/lib/tramway/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,15 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: tramway
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.5. | 
| 4 | 
            +
              version: 0.5.1.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - kalashnikovisme
         | 
| 8 8 | 
             
            - moshiaan
         | 
| 9 | 
            -
            autorequire: | 
| 9 | 
            +
            autorequire:
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date:  | 
| 12 | 
            +
            date: 2025-01-20 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: anyway_config
         | 
| @@ -67,6 +67,20 @@ dependencies: | |
| 67 67 | 
             
                - - ">="
         | 
| 68 68 | 
             
                  - !ruby/object:Gem::Version
         | 
| 69 69 | 
             
                    version: '0'
         | 
| 70 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 71 | 
            +
              name: kaminari
         | 
| 72 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 73 | 
            +
                requirements:
         | 
| 74 | 
            +
                - - ">="
         | 
| 75 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 76 | 
            +
                    version: '0'
         | 
| 77 | 
            +
              type: :runtime
         | 
| 78 | 
            +
              prerelease: false
         | 
| 79 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 80 | 
            +
                requirements:
         | 
| 81 | 
            +
                - - ">="
         | 
| 82 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 83 | 
            +
                    version: '0'
         | 
| 70 84 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 71 85 | 
             
              name: rails
         | 
| 72 86 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -177,7 +191,9 @@ files: | |
| 177 191 | 
             
            - app/views/kaminari/_prev_page.html.haml
         | 
| 178 192 | 
             
            - app/views/tramway/entities/_entity.html.haml
         | 
| 179 193 | 
             
            - app/views/tramway/entities/index.html.haml
         | 
| 194 | 
            +
            - app/views/tramway/layouts/application.html.haml
         | 
| 180 195 | 
             
            - config/routes.rb
         | 
| 196 | 
            +
            - config/tailwind.config.js
         | 
| 181 197 | 
             
            - lib/rules/turbo_html_attributes_rules.rb
         | 
| 182 198 | 
             
            - lib/tasks/tramway_tasks.rake
         | 
| 183 199 | 
             
            - lib/tramway.rb
         | 
| @@ -213,7 +229,7 @@ metadata: | |
| 213 229 | 
             
              homepage_uri: https://github.com/purple-magic/tramway
         | 
| 214 230 | 
             
              source_code_uri: https://github.com/purple-magic/tramway
         | 
| 215 231 | 
             
              changelog_uri: https://github.com/purple-magic/tramway
         | 
| 216 | 
            -
            post_install_message: | 
| 232 | 
            +
            post_install_message:
         | 
| 217 233 | 
             
            rdoc_options: []
         | 
| 218 234 | 
             
            require_paths:
         | 
| 219 235 | 
             
            - lib
         | 
| @@ -229,7 +245,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 229 245 | 
             
                  version: '0'
         | 
| 230 246 | 
             
            requirements: []
         | 
| 231 247 | 
             
            rubygems_version: 3.4.6
         | 
| 232 | 
            -
            signing_key: | 
| 248 | 
            +
            signing_key:
         | 
| 233 249 | 
             
            specification_version: 4
         | 
| 234 250 | 
             
            summary: Tramway Rails Engine
         | 
| 235 251 | 
             
            test_files: []
         |