tramway 0.5.5 → 0.6
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 +78 -6
 - data/app/components/tailwinds/back_button_component.html.haml +2 -0
 - data/app/components/tailwinds/back_button_component.rb +7 -0
 - data/app/components/tailwinds/base_component.rb +7 -0
 - data/app/components/tailwinds/button_component.html.haml +12 -0
 - data/app/components/tailwinds/button_component.rb +60 -0
 - data/app/components/tailwinds/containers/narrow_component.html.haml +3 -0
 - data/app/components/tailwinds/containers/narrow_component.rb +10 -0
 - data/app/components/tailwinds/form/builder.rb +19 -26
 - data/app/components/tailwinds/form/file_field_component.html.haml +6 -6
 - data/app/components/tailwinds/form/multiselect/select_as_input.html.haml +1 -8
 - data/app/components/tailwinds/table/row_component.rb +2 -6
 - data/app/controllers/tramway/entities_controller.rb +11 -5
 - data/config/routes.rb +4 -4
 - data/config/tailwind.config.js +105 -25
 - data/lib/generators/tramway/install/install_generator.rb +133 -0
 - data/lib/tramway/base_form.rb +1 -1
 - data/lib/tramway/decorators/association.rb +1 -1
 - data/lib/tramway/decorators/class_helper.rb +18 -16
 - data/lib/tramway/forms/properties.rb +6 -10
 - data/lib/tramway/helpers/views_helper.rb +39 -0
 - data/lib/tramway/navbar.rb +5 -5
 - data/lib/tramway/version.rb +1 -1
 - metadata +10 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: ff761d41cd4748bf3d7e1451825da24764f69077b842e92f7be6baf0f14dff55
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: d9c3b25519ab3eb4dfe9c7d2e26d8dffb339a2743b6c5c494495bd4c5b71fc6c
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: bb0c9c5121dc9a8213a0011c07265b2c42a486218966725df559c31f48b1f1e0cbbb50dc7fd10ddaac13f00a14d55ae57be42e121a1c1d44b1560822425f54af
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 4488ab5046c8fee6150ee80459df3859f0a2d8b62948cfb91af93052dce2f0788aa067dde8261c9df0708ad31c853c522adc65c91c907cc71b4418c17bfc4244
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -14,22 +14,33 @@ Unite Ruby on Rails brilliance. Streamline development with Tramway. 
     | 
|
| 
       14 
14 
     | 
    
         
             
              * [Tailwind-styled pagination](https://github.com/Purple-Magic/tramway?tab=readme-ov-file#tailwind-styled-pagination-for-kaminari)
         
     | 
| 
       15 
15 
     | 
    
         
             
            * [Articles](https://github.com/Purple-Magic/tramway#usage)
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
      
 17 
     | 
    
         
            +
            ## Compatibility
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            Tramway is actively verified against the following Ruby and Rails versions.
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            | Ruby \ Rails | 7.1 | 7.2 | 8.0 | 8.1 |
         
     | 
| 
      
 22 
     | 
    
         
            +
            | ------------- | --- | --- | --- | --- |
         
     | 
| 
      
 23 
     | 
    
         
            +
            | 3.2           | ✅  | ✅  | ✅  | ✅  |
         
     | 
| 
      
 24 
     | 
    
         
            +
            | 3.3           | ✅  | ✅  | ✅  | ✅  |
         
     | 
| 
      
 25 
     | 
    
         
            +
            | 3.4           | ✅  | ✅  | ✅  | ✅  |
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
       17 
27 
     | 
    
         
             
            ## Installation
         
     | 
| 
       18 
28 
     | 
    
         
             
            Add this line to your application's Gemfile:
         
     | 
| 
       19 
29 
     | 
    
         | 
| 
       20 
30 
     | 
    
         
             
            ```ruby
         
     | 
| 
       21 
31 
     | 
    
         
             
            gem "tramway"
         
     | 
| 
       22 
     | 
    
         
            -
            gem "haml-rails"
         
     | 
| 
       23 
     | 
    
         
            -
            gem "kaminari"
         
     | 
| 
       24 
     | 
    
         
            -
            gem "view_component"
         
     | 
| 
       25 
32 
     | 
    
         
             
            ```
         
     | 
| 
       26 
33 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 34 
     | 
    
         
            +
            Then install Tramway and its dependencies:
         
     | 
| 
       28 
35 
     | 
    
         | 
| 
       29 
36 
     | 
    
         
             
            ```shell
         
     | 
| 
       30 
     | 
    
         
            -
            bundle  
     | 
| 
      
 37 
     | 
    
         
            +
            bundle install
         
     | 
| 
      
 38 
     | 
    
         
            +
            bin/rails g tramway:install
         
     | 
| 
       31 
39 
     | 
    
         
             
            ```
         
     | 
| 
       32 
40 
     | 
    
         | 
| 
      
 41 
     | 
    
         
            +
            The install generator adds the required gems (`haml-rails`, `kaminari`, `view_component`, and `dry-initializer`) to your
         
     | 
| 
      
 42 
     | 
    
         
            +
            application's Gemfile—if they are not present—and appends the Tailwind safelist configuration Tramway ships with.
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
       33 
44 
     | 
    
         
             
            ## Getting Started
         
     | 
| 
       34 
45 
     | 
    
         | 
| 
       35 
46 
     | 
    
         
             
            **Step 1**
         
     | 
| 
         @@ -68,7 +79,9 @@ end 
     | 
|
| 
       68 
79 
     | 
    
         | 
| 
       69 
80 
     | 
    
         
             
            **Step 4**
         
     | 
| 
       70 
81 
     | 
    
         | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
      
 82 
     | 
    
         
            +
            If you ran `bin/rails g tramway:install`, the Tailwind safelist was already appended to `config/tailwind.config.js`.
         
     | 
| 
      
 83 
     | 
    
         
            +
            Otherwise, copy this [file](https://github.com/Purple-Magic/tramway/blob/main/config/tailwind.config.js) to
         
     | 
| 
      
 84 
     | 
    
         
            +
            `config/tailwind.config.js`.
         
     | 
| 
       72 
85 
     | 
    
         | 
| 
       73 
86 
     | 
    
         | 
| 
       74 
87 
     | 
    
         
             
            **Step 5**
         
     | 
| 
         @@ -528,6 +541,38 @@ Tramway provides a responsive, tailwind-styled table with light and dark themes. 
     | 
|
| 
       528 
541 
     | 
    
         
             
                  Another
         
     | 
| 
       529 
542 
     | 
    
         
             
            ```
         
     | 
| 
       530 
543 
     | 
    
         | 
| 
      
 544 
     | 
    
         
            +
            `Tailwinds::TableComponent` accepts an optional `options` hash that is merged into the outer `.div-table` element. The hash is
         
     | 
| 
      
 545 
     | 
    
         
            +
            forwarded as HTML attributes, so you can pass things like `id`, `data` attributes, or additional classes. If you do not supply
         
     | 
| 
      
 546 
     | 
    
         
            +
            your own width utility (e.g. a class that starts with `w-`), the component automatically appends `w-full` to keep the table
         
     | 
| 
      
 547 
     | 
    
         
            +
            responsive. This allows you to extend the default styling without losing the sensible defaults provided by the component.
         
     | 
| 
      
 548 
     | 
    
         
            +
             
     | 
| 
      
 549 
     | 
    
         
            +
            ```haml
         
     | 
| 
      
 550 
     | 
    
         
            +
            = component 'tailwinds/table', options: { class: 'max-w-3xl border border-gray-200', data: { controller: 'table' } } do
         
     | 
| 
      
 551 
     | 
    
         
            +
              = component 'tailwinds/table/header', headers: ['Name', 'Email']
         
     | 
| 
      
 552 
     | 
    
         
            +
              = component 'tailwinds/table/row' do
         
     | 
| 
      
 553 
     | 
    
         
            +
                = component 'tailwinds/table/cell' do
         
     | 
| 
      
 554 
     | 
    
         
            +
                  = user.name
         
     | 
| 
      
 555 
     | 
    
         
            +
                = component 'tailwinds/table/cell' do
         
     | 
| 
      
 556 
     | 
    
         
            +
                  = user.email
         
     | 
| 
      
 557 
     | 
    
         
            +
            ```
         
     | 
| 
      
 558 
     | 
    
         
            +
             
     | 
| 
      
 559 
     | 
    
         
            +
            When you render a header you can either pass the `headers:` array, as in the examples above, or render custom header content in
         
     | 
| 
      
 560 
     | 
    
         
            +
            the block. `Tailwinds::Table::HeaderComponent` uses the length of the `headers` array to build the grid if the array is present.
         
     | 
| 
      
 561 
     | 
    
         
            +
            If you omit the array and provide custom content, pass the `columns:` argument so the component knows how many grid columns to
         
     | 
| 
      
 562 
     | 
    
         
            +
            generate.
         
     | 
| 
      
 563 
     | 
    
         
            +
             
     | 
| 
      
 564 
     | 
    
         
            +
            ```haml
         
     | 
| 
      
 565 
     | 
    
         
            +
            = component 'tailwinds/table/header', columns: 4 do
         
     | 
| 
      
 566 
     | 
    
         
            +
              = component 'tailwinds/table/cell' do
         
     | 
| 
      
 567 
     | 
    
         
            +
                Custom header cell
         
     | 
| 
      
 568 
     | 
    
         
            +
              = component 'tailwinds/table/cell' do
         
     | 
| 
      
 569 
     | 
    
         
            +
                Another header cell
         
     | 
| 
      
 570 
     | 
    
         
            +
              / ...
         
     | 
| 
      
 571 
     | 
    
         
            +
            ```
         
     | 
| 
      
 572 
     | 
    
         
            +
             
     | 
| 
      
 573 
     | 
    
         
            +
            With this approach you control the header layout while still benefiting from the default Tailwind grid classes that the header
         
     | 
| 
      
 574 
     | 
    
         
            +
            component applies.
         
     | 
| 
      
 575 
     | 
    
         
            +
             
     | 
| 
       531 
576 
     | 
    
         
             
            ### Tailwind-styled forms
         
     | 
| 
       532 
577 
     | 
    
         | 
| 
       533 
578 
     | 
    
         
             
            Tramway uses [Tailwind](https://tailwindcss.com/) by default. All UI helpers are implemented with [ViewComponent](https://github.com/viewcomponent/view_component).
         
     | 
| 
         @@ -539,6 +584,7 @@ Tramway provides `tramway_form_for` helper that renders Tailwind-styled forms by 
     | 
|
| 
       539 
584 
     | 
    
         
             
            ```ruby
         
     | 
| 
       540 
585 
     | 
    
         
             
            = tramway_form_for @user do |f|
         
     | 
| 
       541 
586 
     | 
    
         
             
              = f.text_field :text
         
     | 
| 
      
 587 
     | 
    
         
            +
              = f.email_field :email
         
     | 
| 
       542 
588 
     | 
    
         
             
              = f.password_field :password
         
     | 
| 
       543 
589 
     | 
    
         
             
              = f.select :role, [:admin, :user]
         
     | 
| 
       544 
590 
     | 
    
         
             
              = f.multiselect :permissions, [['Create User', 'create_user'], ['Update user', 'update_user']]
         
     | 
| 
         @@ -550,12 +596,38 @@ will render [this](https://play.tailwindcss.com/xho3LfjKkK) 
     | 
|
| 
       550 
596 
     | 
    
         | 
| 
       551 
597 
     | 
    
         
             
            Available form helpers:
         
     | 
| 
       552 
598 
     | 
    
         
             
            * text_field
         
     | 
| 
      
 599 
     | 
    
         
            +
            * email_field
         
     | 
| 
       553 
600 
     | 
    
         
             
            * password_field
         
     | 
| 
       554 
601 
     | 
    
         
             
            * file_field
         
     | 
| 
       555 
602 
     | 
    
         
             
            * select
         
     | 
| 
       556 
603 
     | 
    
         
             
            * multiselect ([Stimulus-based](https://github.com/Purple-Magic/tramway#stimulus-based-inputs))
         
     | 
| 
       557 
604 
     | 
    
         
             
            * submit
         
     | 
| 
       558 
605 
     | 
    
         | 
| 
      
 606 
     | 
    
         
            +
            **Examples**
         
     | 
| 
      
 607 
     | 
    
         
            +
             
     | 
| 
      
 608 
     | 
    
         
            +
            1. Sign In Form for `devise` authentication
         
     | 
| 
      
 609 
     | 
    
         
            +
             
     | 
| 
      
 610 
     | 
    
         
            +
            *app/views/devise/sessions/new.html.haml*
         
     | 
| 
      
 611 
     | 
    
         
            +
            ```haml
         
     | 
| 
      
 612 
     | 
    
         
            +
              = tramway_form_for(resource, as: resource_name, url: session_path(resource_name), class: 'bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4') do |f|
         
     | 
| 
      
 613 
     | 
    
         
            +
                = component 'forms/errors', record: resource
         
     | 
| 
      
 614 
     | 
    
         
            +
             
     | 
| 
      
 615 
     | 
    
         
            +
                = f.text_field :email, placeholder: "Your email"
         
     | 
| 
      
 616 
     | 
    
         
            +
                = f.password_field :password, placeholder: "Your password"
         
     | 
| 
      
 617 
     | 
    
         
            +
             
     | 
| 
      
 618 
     | 
    
         
            +
                = f.submit "Sign In"
         
     | 
| 
      
 619 
     | 
    
         
            +
            ```
         
     | 
| 
      
 620 
     | 
    
         
            +
             
     | 
| 
      
 621 
     | 
    
         
            +
            2. Sign In Form for Rails authorization
         
     | 
| 
      
 622 
     | 
    
         
            +
             
     | 
| 
      
 623 
     | 
    
         
            +
            *app/views/sessions/new.html.haml*
         
     | 
| 
      
 624 
     | 
    
         
            +
            ```haml
         
     | 
| 
      
 625 
     | 
    
         
            +
            = form_with url: login_path, scope: :session, local: true, builder: Tailwinds::Form::Builder do |form|
         
     | 
| 
      
 626 
     | 
    
         
            +
                = form.email_field :email
         
     | 
| 
      
 627 
     | 
    
         
            +
                = form.password_field :password
         
     | 
| 
      
 628 
     | 
    
         
            +
                = form.submit "Log in"
         
     | 
| 
      
 629 
     | 
    
         
            +
            ```
         
     | 
| 
      
 630 
     | 
    
         
            +
             
     | 
| 
       559 
631 
     | 
    
         
             
            #### Stimulus-based inputs
         
     | 
| 
       560 
632 
     | 
    
         | 
| 
       561 
633 
     | 
    
         
             
            `tramway_form_for` provides Tailwind-styled Stimulus-based custom inputs.
         
     | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            - if text.present?
         
     | 
| 
      
 2 
     | 
    
         
            +
              - if method == :get
         
     | 
| 
      
 3 
     | 
    
         
            +
                = link_to text, path, class: classes, **options.except(:class)
         
     | 
| 
      
 4 
     | 
    
         
            +
              - else
         
     | 
| 
      
 5 
     | 
    
         
            +
                = button_to text, path, method:, class: classes, **options.except(:class)
         
     | 
| 
      
 6 
     | 
    
         
            +
            - else
         
     | 
| 
      
 7 
     | 
    
         
            +
              - if method == :get
         
     | 
| 
      
 8 
     | 
    
         
            +
                = link_to path, class: classes, **options.except(:class) do
         
     | 
| 
      
 9 
     | 
    
         
            +
                  = content
         
     | 
| 
      
 10 
     | 
    
         
            +
              - else
         
     | 
| 
      
 11 
     | 
    
         
            +
                = button_to path, method:, class: classes, **options.except(:class) do
         
     | 
| 
      
 12 
     | 
    
         
            +
                  = content
         
     | 
| 
         @@ -0,0 +1,60 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Tailwinds
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Default Tramway button
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              class ButtonComponent < BaseComponent
         
     | 
| 
      
 7 
     | 
    
         
            +
                option :text, optional: true, default: -> {}
         
     | 
| 
      
 8 
     | 
    
         
            +
                option :path
         
     | 
| 
      
 9 
     | 
    
         
            +
                option :color, default: -> { :blue }
         
     | 
| 
      
 10 
     | 
    
         
            +
                option :type, default: -> { :default }
         
     | 
| 
      
 11 
     | 
    
         
            +
                option :size, default: -> { :middle }
         
     | 
| 
      
 12 
     | 
    
         
            +
                option :method, optional: true, default: -> { :get }
         
     | 
| 
      
 13 
     | 
    
         
            +
                option :options, optional: true, default: -> { {} }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def size_classes
         
     | 
| 
      
 16 
     | 
    
         
            +
                  case size
         
     | 
| 
      
 17 
     | 
    
         
            +
                  when :small
         
     | 
| 
      
 18 
     | 
    
         
            +
                    'text-sm py-1 px-1'
         
     | 
| 
      
 19 
     | 
    
         
            +
                  when :middle
         
     | 
| 
      
 20 
     | 
    
         
            +
                    'py-2 px-4'
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def classes
         
     | 
| 
      
 25 
     | 
    
         
            +
                  (default_classes +
         
     | 
| 
      
 26 
     | 
    
         
            +
                    light_mode_classes +
         
     | 
| 
      
 27 
     | 
    
         
            +
                    dark_mode_classes +
         
     | 
| 
      
 28 
     | 
    
         
            +
                    (method == :get ? %w[px-1 h-fit] : ['cursor-pointer'])).compact.join(' ')
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def default_classes
         
     | 
| 
      
 32 
     | 
    
         
            +
                  [
         
     | 
| 
      
 33 
     | 
    
         
            +
                    'btn',
         
     | 
| 
      
 34 
     | 
    
         
            +
                    'btn-primary',
         
     | 
| 
      
 35 
     | 
    
         
            +
                    'font-bold',
         
     | 
| 
      
 36 
     | 
    
         
            +
                    'rounded-sm',
         
     | 
| 
      
 37 
     | 
    
         
            +
                    'flex',
         
     | 
| 
      
 38 
     | 
    
         
            +
                    'flex-row',
         
     | 
| 
      
 39 
     | 
    
         
            +
                    size_classes.to_s,
         
     | 
| 
      
 40 
     | 
    
         
            +
                    options[:class].to_s
         
     | 
| 
      
 41 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def light_mode_classes
         
     | 
| 
      
 45 
     | 
    
         
            +
                  [
         
     | 
| 
      
 46 
     | 
    
         
            +
                    "bg-#{color}-500",
         
     | 
| 
      
 47 
     | 
    
         
            +
                    "hover:bg-#{color}-700",
         
     | 
| 
      
 48 
     | 
    
         
            +
                    'text-white'
         
     | 
| 
      
 49 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def dark_mode_classes
         
     | 
| 
      
 53 
     | 
    
         
            +
                  [
         
     | 
| 
      
 54 
     | 
    
         
            +
                    "dark:bg-#{color}-600",
         
     | 
| 
      
 55 
     | 
    
         
            +
                    "dark:hover:bg-#{color}-800",
         
     | 
| 
      
 56 
     | 
    
         
            +
                    'dark:text-gray-300'
         
     | 
| 
      
 57 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -11,34 +11,33 @@ module Tailwinds 
     | 
|
| 
       11 
11 
     | 
    
         
             
                    @form_size = options[:size] || options['size'] || :middle
         
     | 
| 
       12 
12 
     | 
    
         
             
                  end
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
                  def  
     | 
| 
      
 14 
     | 
    
         
            +
                  def common_field(component_name, input_method, attribute, **options, &)
         
     | 
| 
       15 
15 
     | 
    
         
             
                    sanitized_options = sanitize_options(options)
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
                     
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
                    component_class = "Tailwinds::Form::#{component_name.to_s.camelize}Component".constantize
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    render(component_class.new(
         
     | 
| 
      
 20 
     | 
    
         
            +
                             input: input(input_method),
         
     | 
| 
       19 
21 
     | 
    
         
             
                             value: get_value(attribute, sanitized_options),
         
     | 
| 
       20 
22 
     | 
    
         
             
                             **default_options(attribute, sanitized_options)
         
     | 
| 
       21 
     | 
    
         
            -
                           ), 
     | 
| 
      
 23 
     | 
    
         
            +
                           ),
         
     | 
| 
      
 24 
     | 
    
         
            +
                           &)
         
     | 
| 
       22 
25 
     | 
    
         
             
                  end
         
     | 
| 
       23 
26 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                  def  
     | 
| 
       25 
     | 
    
         
            -
                     
     | 
| 
      
 27 
     | 
    
         
            +
                  def text_field(attribute, **, &)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    common_field(:text_field, :text_field, attribute, **, &)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
       26 
30 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
                             value: get_value(attribute, sanitized_options),
         
     | 
| 
       30 
     | 
    
         
            -
                             **default_options(attribute, sanitized_options)
         
     | 
| 
       31 
     | 
    
         
            -
                           ), &)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def email_field(attribute, **, &)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    common_field(:text_field, :email_field, attribute, **, &)
         
     | 
| 
       32 
33 
     | 
    
         
             
                  end
         
     | 
| 
       33 
34 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
                  def  
     | 
| 
       35 
     | 
    
         
            -
                     
     | 
| 
      
 35 
     | 
    
         
            +
                  def number_field(attribute, **, &)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    common_field(:number_field, :number_field, attribute, **, &)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
       36 
38 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
                             value: get_value(attribute, sanitized_options),
         
     | 
| 
       40 
     | 
    
         
            -
                             **default_options(attribute, sanitized_options)
         
     | 
| 
       41 
     | 
    
         
            -
                           ), &)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  def text_area(attribute, **, &)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    common_field(:text_area, :text_area, attribute, **, &)
         
     | 
| 
       42 
41 
     | 
    
         
             
                  end
         
     | 
| 
       43 
42 
     | 
    
         | 
| 
       44 
43 
     | 
    
         
             
                  def password_field(attribute, **options, &)
         
     | 
| 
         @@ -95,17 +94,11 @@ module Tailwinds 
     | 
|
| 
       95 
94 
     | 
    
         
             
                  end
         
     | 
| 
       96 
95 
     | 
    
         | 
| 
       97 
96 
     | 
    
         
             
                  def get_value(attribute, options)
         
     | 
| 
       98 
     | 
    
         
            -
                    options[:value] || object.public_send(attribute)
         
     | 
| 
      
 97 
     | 
    
         
            +
                    options[:value] || (object.present? ? object.public_send(attribute) : nil)
         
     | 
| 
       99 
98 
     | 
    
         
             
                  end
         
     | 
| 
       100 
99 
     | 
    
         | 
| 
       101 
100 
     | 
    
         
             
                  def default_options(attribute, options)
         
     | 
| 
       102 
     | 
    
         
            -
                    {
         
     | 
| 
       103 
     | 
    
         
            -
                      attribute:,
         
     | 
| 
       104 
     | 
    
         
            -
                      label: label_build(attribute, options),
         
     | 
| 
       105 
     | 
    
         
            -
                      for: for_id(attribute),
         
     | 
| 
       106 
     | 
    
         
            -
                      options:,
         
     | 
| 
       107 
     | 
    
         
            -
                      size: form_size
         
     | 
| 
       108 
     | 
    
         
            -
                    }
         
     | 
| 
      
 101 
     | 
    
         
            +
                    { attribute:, label: label_build(attribute, options), for: for_id(attribute), options:, size: form_size }
         
     | 
| 
       109 
102 
     | 
    
         
             
                  end
         
     | 
| 
       110 
103 
     | 
    
         | 
| 
       111 
104 
     | 
    
         
             
                  # :reek:UtilityFunction
         
     | 
| 
         @@ -1,8 +1,8 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            .mb-4
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
      
 2 
     | 
    
         
            +
              - if @label
         
     | 
| 
      
 3 
     | 
    
         
            +
                - base_classes = 'inline-block bg-blue-500 hover:bg-blue-700 text-white font-bold rounded cursor-pointer mt-4 '
         
     | 
| 
      
 4 
     | 
    
         
            +
                - base_classes += 'dark:bg-blue-600 dark:hover:bg-blue-500'
         
     | 
| 
      
 5 
     | 
    
         
            +
                - classes = "#{size_class(:file_button)} #{base_classes}"
         
     | 
| 
      
 6 
     | 
    
         
            +
                %label{ for: @for, class: classes }
         
     | 
| 
      
 7 
     | 
    
         
            +
                  = @label
         
     | 
| 
       8 
8 
     | 
    
         
             
              = @input
         
     | 
| 
         @@ -2,11 +2,4 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
              - base_classes = 'bg-transparent appearance-none outline-none h-full w-full text-gray-800 hidden '
         
     | 
| 
       3 
3 
     | 
    
         
             
              - base_classes += 'dark:text-white dark:placeholder-white'
         
     | 
| 
       4 
4 
     | 
    
         
             
              - classes = "#{@size_class} #{base_classes}"
         
     | 
| 
       5 
     | 
    
         
            -
              = @input.call(
         
     | 
| 
       6 
     | 
    
         
            -
                @attribute,
         
     | 
| 
       7 
     | 
    
         
            -
                @options.merge(
         
     | 
| 
       8 
     | 
    
         
            -
                  placeholder: "{{placeholder}}",
         
     | 
| 
       9 
     | 
    
         
            -
                  class: classes,
         
     | 
| 
       10 
     | 
    
         
            -
                  data: { 'multiselect-target' => 'hiddenInput' }
         
     | 
| 
       11 
     | 
    
         
            -
                )
         
     | 
| 
       12 
     | 
    
         
            -
              )
         
     | 
| 
      
 5 
     | 
    
         
            +
              = @input.call(@attribute, @options.merge(placeholder: "{{placeholder}}", class: classes, data: { 'multiselect-target' => 'hiddenInput' }))
         
     | 
| 
         @@ -12,26 +12,22 @@ module Tailwinds 
     | 
|
| 
       12 
12 
     | 
    
         
             
                    default_attributes = { role: :row }
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
                    if href.present?
         
     | 
| 
       15 
     | 
    
         
            -
                       
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                      link_to(href, options.merge(class: klass, **default_attributes)) do
         
     | 
| 
      
 15 
     | 
    
         
            +
                      link_to(href, options.merge(class: "#{options[:class] || ''} #{link_row_classes}", **default_attributes)) do
         
     | 
| 
       18 
16 
     | 
    
         
             
                        yield if block_given?
         
     | 
| 
       19 
17 
     | 
    
         
             
                      end
         
     | 
| 
       20 
18 
     | 
    
         
             
                    else
         
     | 
| 
       21 
     | 
    
         
            -
                      tag.div(**options 
     | 
| 
      
 19 
     | 
    
         
            +
                      tag.div(**options, **default_attributes) do
         
     | 
| 
       22 
20 
     | 
    
         
             
                        yield if block_given?
         
     | 
| 
       23 
21 
     | 
    
         
             
                      end
         
     | 
| 
       24 
22 
     | 
    
         
             
                    end
         
     | 
| 
       25 
23 
     | 
    
         
             
                  end
         
     | 
| 
       26 
24 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
                  # :reek:UtilityFunction { enabled: false }
         
     | 
| 
       28 
25 
     | 
    
         
             
                  def desktop_row_classes(cells_count)
         
     | 
| 
       29 
26 
     | 
    
         
             
                    [
         
     | 
| 
       30 
27 
     | 
    
         
             
                      'div-table-row', 'grid', 'gap-4', 'bg-white', 'border-b', 'last:border-b-0', 'dark:bg-gray-800',
         
     | 
| 
       31 
28 
     | 
    
         
             
                      'dark:border-gray-700', "grid-cols-#{cells_count}"
         
     | 
| 
       32 
29 
     | 
    
         
             
                    ].join(' ')
         
     | 
| 
       33 
30 
     | 
    
         
             
                  end
         
     | 
| 
       34 
     | 
    
         
            -
                  # :reek:UtilityFunction { enabled: true }
         
     | 
| 
       35 
31 
     | 
    
         | 
| 
       36 
32 
     | 
    
         
             
                  def link_row_classes
         
     | 
| 
       37 
33 
     | 
    
         
             
                    'cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700'
         
     | 
| 
         @@ -11,11 +11,13 @@ module Tramway 
     | 
|
| 
       11 
11 
     | 
    
         
             
                include Rails.application.routes.url_helpers
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
                def index
         
     | 
| 
       14 
     | 
    
         
            -
                   
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
      
 14 
     | 
    
         
            +
                  if index_scope.present?
         
     | 
| 
      
 15 
     | 
    
         
            +
                    model_class.public_send(index_scope)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  else
         
     | 
| 
      
 17 
     | 
    
         
            +
                    model_class.order(id: :desc)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end.page(params[:page]) => entities
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  @entities = entities
         
     | 
| 
       19 
21 
     | 
    
         | 
| 
       20 
22 
     | 
    
         
             
                  @namespace = entity.route&.namespace
         
     | 
| 
       21 
23 
     | 
    
         
             
                end
         
     | 
| 
         @@ -29,5 +31,9 @@ module Tramway 
     | 
|
| 
       29 
31 
     | 
    
         
             
                def entity
         
     | 
| 
       30 
32 
     | 
    
         
             
                  @entity ||= Tramway.config.entities.find { |e| e.name == params[:entity][:name] }
         
     | 
| 
       31 
33 
     | 
    
         
             
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def index_scope
         
     | 
| 
      
 36 
     | 
    
         
            +
                  entity.page(:index).scope
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
       32 
38 
     | 
    
         
             
              end
         
     | 
| 
       33 
39 
     | 
    
         
             
            end
         
     | 
    
        data/config/routes.rb
    CHANGED
    
    | 
         @@ -3,13 +3,13 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            Tramway::Engine.routes.draw do
         
     | 
| 
       4 
4 
     | 
    
         
             
              Tramway.config.entities.each do |entity|
         
     | 
| 
       5 
5 
     | 
    
         
             
                segments      = entity.name.split('/')
         
     | 
| 
       6 
     | 
    
         
            -
                resource_name = segments.pop 
     | 
| 
      
 6 
     | 
    
         
            +
                resource_name = segments.pop
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                define_resource = proc do
         
     | 
| 
       9 
9 
     | 
    
         
             
                  resources resource_name.pluralize.to_sym,
         
     | 
| 
       10 
     | 
    
         
            -
                            only: 
     | 
| 
       11 
     | 
    
         
            -
                            controller:'/tramway/entities',
         
     | 
| 
       12 
     | 
    
         
            -
                            defaults: 
     | 
| 
      
 10 
     | 
    
         
            +
                            only: [:index],
         
     | 
| 
      
 11 
     | 
    
         
            +
                            controller: '/tramway/entities',
         
     | 
| 
      
 12 
     | 
    
         
            +
                            defaults: { entity: entity }
         
     | 
| 
       13 
13 
     | 
    
         
             
                end
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
                if segments.empty?
         
     | 
    
        data/config/tailwind.config.js
    CHANGED
    
    | 
         @@ -1,13 +1,25 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module.exports = {
         
     | 
| 
       2 
2 
     | 
    
         
             
              safelist: [
         
     | 
| 
      
 3 
     | 
    
         
            +
                // === Navbar ===
         
     | 
| 
      
 4 
     | 
    
         
            +
                'ml-4',
         
     | 
| 
      
 5 
     | 
    
         
            +
                
         
     | 
| 
      
 6 
     | 
    
         
            +
                // === Custom table layout utilities ===
         
     | 
| 
       3 
7 
     | 
    
         
             
                'div-table',
         
     | 
| 
       4 
8 
     | 
    
         
             
                'div-table-row',
         
     | 
| 
       5 
9 
     | 
    
         
             
                'div-table-cell',
         
     | 
| 
      
 10 
     | 
    
         
            +
                'sm:text-base',
         
     | 
| 
      
 11 
     | 
    
         
            +
                'last:border-b-0',
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                // === Visibility and typography helpers ===
         
     | 
| 
       6 
14 
     | 
    
         
             
                'hidden',
         
     | 
| 
      
 15 
     | 
    
         
            +
                'xl:hidden',
         
     | 
| 
       7 
16 
     | 
    
         
             
                'text-xl',
         
     | 
| 
       8 
17 
     | 
    
         
             
                'text-4xl',
         
     | 
| 
       9 
18 
     | 
    
         
             
                'font-bold',
         
     | 
| 
       10 
     | 
    
         
            -
                ' 
     | 
| 
      
 19 
     | 
    
         
            +
                'text-right',
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                // === Grid templates used for configurable layouts ===
         
     | 
| 
      
 22 
     | 
    
         
            +
                'grid',
         
     | 
| 
       11 
23 
     | 
    
         
             
                'grid-cols-1',
         
     | 
| 
       12 
24 
     | 
    
         
             
                'grid-cols-2',
         
     | 
| 
       13 
25 
     | 
    
         
             
                'grid-cols-3',
         
     | 
| 
         @@ -18,44 +30,112 @@ module.exports = { 
     | 
|
| 
       18 
30 
     | 
    
         
             
                'grid-cols-8',
         
     | 
| 
       19 
31 
     | 
    
         
             
                'grid-cols-9',
         
     | 
| 
       20 
32 
     | 
    
         
             
                'grid-cols-10',
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                 
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                // === Page container and alignment helpers ===
         
     | 
| 
      
 35 
     | 
    
         
            +
                'container',
         
     | 
| 
      
 36 
     | 
    
         
            +
                'mx-auto',
         
     | 
| 
      
 37 
     | 
    
         
            +
                'align-center',
         
     | 
| 
      
 38 
     | 
    
         
            +
                'justify-center',
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                // === Flexbox layout utilities ===
         
     | 
| 
       23 
41 
     | 
    
         
             
                'flex',
         
     | 
| 
       24 
     | 
    
         
            -
                ' 
     | 
| 
      
 42 
     | 
    
         
            +
                'flex-col',
         
     | 
| 
      
 43 
     | 
    
         
            +
                'flex-wrap',
         
     | 
| 
      
 44 
     | 
    
         
            +
                'flex-row-reverse',
         
     | 
| 
      
 45 
     | 
    
         
            +
                'flex-auto',
         
     | 
| 
      
 46 
     | 
    
         
            +
                'flex-initial',
         
     | 
| 
      
 47 
     | 
    
         
            +
                'justify-between',
         
     | 
| 
      
 48 
     | 
    
         
            +
                'justify-end',
         
     | 
| 
      
 49 
     | 
    
         
            +
                'space-x-1',
         
     | 
| 
      
 50 
     | 
    
         
            +
                'items-center',
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                // === Responsive visibility helpers ===
         
     | 
| 
      
 53 
     | 
    
         
            +
                'sm:hidden',
         
     | 
| 
      
 54 
     | 
    
         
            +
                'sm:flex',
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                // === Sizing utilities ===
         
     | 
| 
      
 57 
     | 
    
         
            +
                'w-2/3',
         
     | 
| 
      
 58 
     | 
    
         
            +
                'w-full',
         
     | 
| 
      
 59 
     | 
    
         
            +
                'w-fit',
         
     | 
| 
      
 60 
     | 
    
         
            +
                'w-8',
         
     | 
| 
      
 61 
     | 
    
         
            +
                'min-w-96',
         
     | 
| 
      
 62 
     | 
    
         
            +
                'max-w-full',
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                // === Spacing utilities ===
         
     | 
| 
      
 65 
     | 
    
         
            +
                'p-4',
         
     | 
| 
       25 
66 
     | 
    
         
             
                'px-6',
         
     | 
| 
       26 
     | 
    
         
            -
                'px-3',
         
     | 
| 
       27 
67 
     | 
    
         
             
                'px-4',
         
     | 
| 
      
 68 
     | 
    
         
            +
                'px-3',
         
     | 
| 
      
 69 
     | 
    
         
            +
                'py-8',
         
     | 
| 
       28 
70 
     | 
    
         
             
                'py-4',
         
     | 
| 
       29 
71 
     | 
    
         
             
                'py-2',
         
     | 
| 
       30 
72 
     | 
    
         
             
                'mb-2',
         
     | 
| 
       31 
     | 
    
         
            -
                ' 
     | 
| 
      
 73 
     | 
    
         
            +
                'mt-8',
         
     | 
| 
      
 74 
     | 
    
         
            +
                'mt-2',
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                // === Pagination styles ===
         
     | 
| 
      
 77 
     | 
    
         
            +
                'bg-white',
         
     | 
| 
      
 78 
     | 
    
         
            +
                'rounded-md',
         
     | 
| 
      
 79 
     | 
    
         
            +
                'hover:bg-purple-100',
         
     | 
| 
      
 80 
     | 
    
         
            +
                'hover:bg-gray-100',
         
     | 
| 
      
 81 
     | 
    
         
            +
                'hover:bg-gray-300',
         
     | 
| 
      
 82 
     | 
    
         
            +
                'hover:text-gray-800',
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                // === Dark mode pagination styles ===
         
     | 
| 
       32 
85 
     | 
    
         
             
                'dark:text-white',
         
     | 
| 
       33 
86 
     | 
    
         
             
                'dark:bg-gray-800',
         
     | 
| 
       34 
     | 
    
         
            -
                'dark:bg-gray-900',
         
     | 
| 
       35 
87 
     | 
    
         
             
                'dark:hover:bg-gray-700',
         
     | 
| 
      
 88 
     | 
    
         
            +
                'dark:bg-gray-900',
         
     | 
| 
       36 
89 
     | 
    
         
             
                'dark:bg-gray-700',
         
     | 
| 
       37 
90 
     | 
    
         
             
                'dark:text-gray-400',
         
     | 
| 
       38 
     | 
    
         
            -
                'dark:hover:bg-gray-700',
         
     | 
| 
       39 
91 
     | 
    
         
             
                'dark:hover:text-gray-400',
         
     | 
| 
       40 
     | 
    
         
            -
                ' 
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                 
     | 
| 
       43 
     | 
    
         
            -
                ' 
     | 
| 
       44 
     | 
    
         
            -
                ' 
     | 
| 
       45 
     | 
    
         
            -
                ' 
     | 
| 
       46 
     | 
    
         
            -
                ' 
     | 
| 
       47 
     | 
    
         
            -
                ' 
     | 
| 
       48 
     | 
    
         
            -
                ' 
     | 
| 
       49 
     | 
    
         
            -
                ' 
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                 
     | 
| 
       52 
     | 
    
         
            -
                'mt-2',
         
     | 
| 
      
 92 
     | 
    
         
            +
                'dark:placeholder-gray-400',
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                // === Button and badge helpers ===
         
     | 
| 
      
 95 
     | 
    
         
            +
                'bg-purple-700',
         
     | 
| 
      
 96 
     | 
    
         
            +
                'rounded',
         
     | 
| 
      
 97 
     | 
    
         
            +
                'rounded-full',
         
     | 
| 
      
 98 
     | 
    
         
            +
                'rounded-t',
         
     | 
| 
      
 99 
     | 
    
         
            +
                'font-medium',
         
     | 
| 
      
 100 
     | 
    
         
            +
                'font-normal',
         
     | 
| 
      
 101 
     | 
    
         
            +
                'text-xs',
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                // === Form state helpers ===
         
     | 
| 
       53 
104 
     | 
    
         
             
                'disabled:bg-gray-100',
         
     | 
| 
       54 
105 
     | 
    
         
             
                'disabled:text-gray-400',
         
     | 
| 
       55 
106 
     | 
    
         
             
                'disabled:cursor-not-allowed',
         
     | 
| 
       56 
     | 
    
         
            -
                 
     | 
| 
       57 
     | 
    
         
            -
                'bg- 
     | 
| 
       58 
     | 
    
         
            -
                 
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
      
 107 
     | 
    
         
            +
                'mb-4',
         
     | 
| 
      
 108 
     | 
    
         
            +
                'bg-red-500',
         
     | 
| 
      
 109 
     | 
    
         
            +
                'hover:bg-red-700',
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                // === Multiselect dropdown positioning ===
         
     | 
| 
      
 112 
     | 
    
         
            +
                'absolute',
         
     | 
| 
      
 113 
     | 
    
         
            +
                'relative',
         
     | 
| 
      
 114 
     | 
    
         
            +
                'shadow',
         
     | 
| 
      
 115 
     | 
    
         
            +
                'top-11',
         
     | 
| 
      
 116 
     | 
    
         
            +
                'z-40',
         
     | 
| 
      
 117 
     | 
    
         
            +
                'left-0',
         
     | 
| 
      
 118 
     | 
    
         
            +
                'max-h-select',
         
     | 
| 
      
 119 
     | 
    
         
            +
                'overflow-y-auto',
         
     | 
| 
      
 120 
     | 
    
         
            +
                'cursor-pointer',
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                // === Multiselect option styling ===
         
     | 
| 
      
 123 
     | 
    
         
            +
                'border-b',
         
     | 
| 
      
 124 
     | 
    
         
            +
                'border',
         
     | 
| 
      
 125 
     | 
    
         
            +
                'border-l',
         
     | 
| 
      
 126 
     | 
    
         
            +
                'border-l-2',
         
     | 
| 
      
 127 
     | 
    
         
            +
                'border-transparent',
         
     | 
| 
      
 128 
     | 
    
         
            +
                'border-teal-300',
         
     | 
| 
      
 129 
     | 
    
         
            +
                'hover:border-teal-100',
         
     | 
| 
      
 130 
     | 
    
         
            +
                'bg-teal-100',
         
     | 
| 
      
 131 
     | 
    
         
            +
                'bg-transparent',
         
     | 
| 
      
 132 
     | 
    
         
            +
                'text-teal-700',
         
     | 
| 
      
 133 
     | 
    
         
            +
                'leading-6',
         
     | 
| 
      
 134 
     | 
    
         
            +
                'leading-none',
         
     | 
| 
      
 135 
     | 
    
         
            +
                'appearance-none',
         
     | 
| 
      
 136 
     | 
    
         
            +
                'outline-none',
         
     | 
| 
      
 137 
     | 
    
         
            +
                'h-full',
         
     | 
| 
      
 138 
     | 
    
         
            +
                'm-1',
         
     | 
| 
      
 139 
     | 
    
         
            +
                'hover:bg-teal-100',
         
     | 
| 
       60 
140 
     | 
    
         
             
              ],
         
     | 
| 
       61 
141 
     | 
    
         
             
            }
         
     | 
| 
         @@ -0,0 +1,133 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'rails/generators'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Tramway
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Generators
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Running `rails generate tramway:install` will invoke this generator
         
     | 
| 
      
 9 
     | 
    
         
            +
                #
         
     | 
| 
      
 10 
     | 
    
         
            +
                class InstallGenerator < Rails::Generators::Base
         
     | 
| 
      
 11 
     | 
    
         
            +
                  desc 'Installs Tramway dependencies and Tailwind safelist configuration.'
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def ensure_dependencies
         
     | 
| 
      
 14 
     | 
    
         
            +
                    missing_dependencies = gem_dependencies.reject do |dependency|
         
     | 
| 
      
 15 
     | 
    
         
            +
                      gemfile_contains?(dependency[:name])
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    return if missing_dependencies.empty?
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    append_to_file 'Gemfile', <<~GEMS
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                      # Tramway dependencies
         
     | 
| 
      
 23 
     | 
    
         
            +
                      #{missing_dependencies.pluck(:declaration).join("\n")}
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    GEMS
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def ensure_tailwind_safelist
         
     | 
| 
      
 29 
     | 
    
         
            +
                    return create_tailwind_config unless File.exist?(tailwind_config_path)
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    source_entries = extract_safelist_entries(File.read(gem_tailwind_config_path))
         
     | 
| 
      
 32 
     | 
    
         
            +
                    target_content = File.read(tailwind_config_path)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    target_entries = extract_safelist_entries(target_content)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    missing_entries = source_entries - target_entries
         
     | 
| 
      
 36 
     | 
    
         
            +
                    return if missing_entries.empty?
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    File.write(tailwind_config_path, insert_entries(target_content, missing_entries))
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  def ensure_tailwind_application_stylesheet
         
     | 
| 
      
 42 
     | 
    
         
            +
                    path = tailwind_application_stylesheet_path
         
     | 
| 
      
 43 
     | 
    
         
            +
                    FileUtils.mkdir_p(File.dirname(path))
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    return create_file(path, "#{tailwind_css_import_line}\n") unless File.exist?(path)
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    content = File.read(path)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    return if content.include?(tailwind_css_import_line)
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    File.open(path, 'a') do |file|
         
     | 
| 
      
 51 
     | 
    
         
            +
                      file.write("\n") unless content.empty? || content.end_with?("\n")
         
     | 
| 
      
 52 
     | 
    
         
            +
                      file.write("#{tailwind_css_import_line}\n")
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  private
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def gem_dependencies
         
     | 
| 
      
 59 
     | 
    
         
            +
                    @gem_dependencies ||= [
         
     | 
| 
      
 60 
     | 
    
         
            +
                      { name: 'haml-rails', declaration: 'gem "haml-rails"' },
         
     | 
| 
      
 61 
     | 
    
         
            +
                      { name: 'kaminari', declaration: 'gem "kaminari"' },
         
     | 
| 
      
 62 
     | 
    
         
            +
                      { name: 'view_component', declaration: 'gem "view_component"' },
         
     | 
| 
      
 63 
     | 
    
         
            +
                      { name: 'dry-initializer', declaration: "gem 'dry-initializer'" }
         
     | 
| 
      
 64 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  def gemfile_path
         
     | 
| 
      
 68 
     | 
    
         
            +
                    @gemfile_path ||= File.join(destination_root, 'Gemfile')
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  def gemfile_contains?(name)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    return false unless File.exist?(gemfile_path)
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    content = File.read(gemfile_path)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    content.match?(/^\s*gem ['"]#{Regexp.escape(name)}['"]/)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  def tailwind_config_path
         
     | 
| 
      
 79 
     | 
    
         
            +
                    @tailwind_config_path ||= File.join(destination_root, 'config/tailwind.config.js')
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def gem_tailwind_config_path
         
     | 
| 
      
 83 
     | 
    
         
            +
                    @gem_tailwind_config_path ||= File.expand_path('../../../../config/tailwind.config.js', __dir__)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                  def tailwind_application_stylesheet_path
         
     | 
| 
      
 87 
     | 
    
         
            +
                    @tailwind_application_stylesheet_path ||= File.join(destination_root, 'app/assets/tailwind/application.css')
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  def tailwind_css_import_line
         
     | 
| 
      
 91 
     | 
    
         
            +
                    '@import "tailwindcss";'
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                  def create_tailwind_config
         
     | 
| 
      
 95 
     | 
    
         
            +
                    create_file tailwind_config_path, File.read(gem_tailwind_config_path)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                  def extract_safelist_entries(content)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    section = safelist_section(content)
         
     | 
| 
      
 100 
     | 
    
         
            +
                    return [] if section.nil?
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                    section.scan(/'([^']+)'/).flatten.map(&:strip).reject(&:empty?).uniq
         
     | 
| 
      
 103 
     | 
    
         
            +
                  end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                  def safelist_section(content)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    match = content.match(/safelist\s*:\s*\[(.*?)\]\s*,?/m)
         
     | 
| 
      
 107 
     | 
    
         
            +
                    match&.[](1)
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  def insert_entries(content, entries)
         
     | 
| 
      
 111 
     | 
    
         
            +
                    match = content.match(/safelist\s*:\s*\[(.*?)\](\s*,?)/m)
         
     | 
| 
      
 112 
     | 
    
         
            +
                    return content unless match
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                    closing_index = match.begin(0) + match[0].rindex(']')
         
     | 
| 
      
 115 
     | 
    
         
            +
                    indentation = closing_indentation(content, closing_index)
         
     | 
| 
      
 116 
     | 
    
         
            +
                    formatted_entries = entries.map { |entry| "#{indentation}'#{entry}',\n" }.join
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                    needs_leading_newline = content[closing_index - 1] != "\n"
         
     | 
| 
      
 119 
     | 
    
         
            +
                    insertion = String.new
         
     | 
| 
      
 120 
     | 
    
         
            +
                    insertion << "\n" if needs_leading_newline
         
     | 
| 
      
 121 
     | 
    
         
            +
                    insertion << formatted_entries
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                    content.dup.insert(closing_index, insertion)
         
     | 
| 
      
 124 
     | 
    
         
            +
                  end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                  def closing_indentation(content, index)
         
     | 
| 
      
 127 
     | 
    
         
            +
                    line_start = content.rindex("\n", index - 1) || 0
         
     | 
| 
      
 128 
     | 
    
         
            +
                    line = content[line_start..index]
         
     | 
| 
      
 129 
     | 
    
         
            +
                    line[/^\s*/] || '  '
         
     | 
| 
      
 130 
     | 
    
         
            +
                  end
         
     | 
| 
      
 131 
     | 
    
         
            +
                end
         
     | 
| 
      
 132 
     | 
    
         
            +
              end
         
     | 
| 
      
 133 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/tramway/base_form.rb
    CHANGED
    
    
| 
         @@ -20,26 +20,19 @@ module Tramway 
     | 
|
| 
       20 
20 
     | 
    
         
             
                  end
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                  def decorator_class_name(object_or_array_or_class, namespace)
         
     | 
| 
       23 
     | 
    
         
            -
                     
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
      
 23 
     | 
    
         
            +
                    if Tramway::Decorators::CollectionDecorators.collection?(object_or_array_or_class)
         
     | 
| 
      
 24 
     | 
    
         
            +
                      object_or_array_or_class.first.class
         
     | 
| 
      
 25 
     | 
    
         
            +
                    elsif object_or_array_or_class.is_a?(Class)
         
     | 
| 
      
 26 
     | 
    
         
            +
                      object_or_array_or_class
         
     | 
| 
      
 27 
     | 
    
         
            +
                    else
         
     | 
| 
      
 28 
     | 
    
         
            +
                      object_or_array_or_class.class
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end => klass
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         
             
                    base_class_name = Tramway::Decorators::NameBuilder.default_decorator_class_name(klass)
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
                     
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                    if klass_name.safe_constantize
         
     | 
| 
       36 
     | 
    
         
            -
                      klass_name
         
     | 
| 
       37 
     | 
    
         
            -
                    else
         
     | 
| 
       38 
     | 
    
         
            -
                      raise NameError, "You should define #{klass_name} decorator class in app/decorators/ folder."
         
     | 
| 
       39 
     | 
    
         
            -
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                    build_klass_name(base_class_name, namespace)
         
     | 
| 
       40 
34 
     | 
    
         
             
                  end
         
     | 
| 
       41 
35 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                  # :reek:NilCheck { enabled: false }
         
     | 
| 
       43 
36 
     | 
    
         
             
                  def raise_error_if_object_empty(object_or_array, decorator)
         
     | 
| 
       44 
37 
     | 
    
         
             
                    return unless object_or_array.blank? && decorator.nil?
         
     | 
| 
       45 
38 
     | 
    
         | 
| 
         @@ -47,7 +40,16 @@ module Tramway 
     | 
|
| 
       47 
40 
     | 
    
         | 
| 
       48 
41 
     | 
    
         
             
                    raise ArgumentError, text
         
     | 
| 
       49 
42 
     | 
    
         
             
                  end
         
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  def build_klass_name(base_class_name, namespace)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    klass_name = namespace.present? ? "#{namespace.to_s.camelize}::#{base_class_name}" : base_class_name
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    unless klass_name.safe_constantize
         
     | 
| 
      
 48 
     | 
    
         
            +
                      raise NameError, "You should define #{klass_name} decorator class in app/decorators/ folder."
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    klass_name
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
       51 
53 
     | 
    
         
             
                end
         
     | 
| 
       52 
54 
     | 
    
         
             
              end
         
     | 
| 
       53 
55 
     | 
    
         
             
            end
         
     | 
| 
         @@ -10,21 +10,17 @@ module Tramway 
     | 
|
| 
       10 
10 
     | 
    
         
             
                      @properties << attribute
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                      define_method(attribute) do
         
     | 
| 
       13 
     | 
    
         
            -
                         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
                         
     | 
| 
       16 
     | 
    
         
            -
                          raise NoMethodError, "#{self.class}##{attribute} is not defined"
         
     | 
| 
       17 
     | 
    
         
            -
                        end
         
     | 
| 
      
 13 
     | 
    
         
            +
                        raise NoMethodError, "#{self.class}##{attribute} is not defined" unless object.respond_to?(attribute)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                        object.public_send(attribute)
         
     | 
| 
       18 
16 
     | 
    
         
             
                      end
         
     | 
| 
       19 
17 
     | 
    
         | 
| 
       20 
18 
     | 
    
         
             
                      set_method = "#{attribute}="
         
     | 
| 
       21 
19 
     | 
    
         | 
| 
       22 
20 
     | 
    
         
             
                      define_method(set_method) do |value|
         
     | 
| 
       23 
     | 
    
         
            -
                         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                         
     | 
| 
       26 
     | 
    
         
            -
                          raise NoMethodError, "#{self.class}##{set_method} is not defined"
         
     | 
| 
       27 
     | 
    
         
            -
                        end
         
     | 
| 
      
 21 
     | 
    
         
            +
                        raise NoMethodError, "#{self.class}##{set_method} is not defined" unless object.respond_to?(set_method)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                        object.public_send(set_method, value)
         
     | 
| 
       28 
24 
     | 
    
         
             
                      end
         
     | 
| 
       29 
25 
     | 
    
         
             
                    end
         
     | 
| 
       30 
26 
     | 
    
         | 
| 
         @@ -12,6 +12,45 @@ module Tramway 
     | 
|
| 
       12 
12 
     | 
    
         
             
                      &
         
     | 
| 
       13 
13 
     | 
    
         
             
                    )
         
     | 
| 
       14 
14 
     | 
    
         
             
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def tramway_table(**options, &)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    component 'tailwinds/table', options:, &
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def tramway_row(**options, &)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    component 'tailwinds/table/row',
         
     | 
| 
      
 22 
     | 
    
         
            +
                              cells: options.delete(:cells),
         
     | 
| 
      
 23 
     | 
    
         
            +
                              href: options.delete(:href),
         
     | 
| 
      
 24 
     | 
    
         
            +
                              options:,
         
     | 
| 
      
 25 
     | 
    
         
            +
                              &
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def tramway_cell(&)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    component 'tailwinds/table/cell', &
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def tramway_button(path:, text: nil, method: :get, **options)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    component 'tailwinds/button',
         
     | 
| 
      
 34 
     | 
    
         
            +
                              text:,
         
     | 
| 
      
 35 
     | 
    
         
            +
                              path:,
         
     | 
| 
      
 36 
     | 
    
         
            +
                              method:,
         
     | 
| 
      
 37 
     | 
    
         
            +
                              color: options.delete(:color),
         
     | 
| 
      
 38 
     | 
    
         
            +
                              type: options.delete(:type),
         
     | 
| 
      
 39 
     | 
    
         
            +
                              size: options.delete(:size),
         
     | 
| 
      
 40 
     | 
    
         
            +
                              options:
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def tramway_back_button
         
     | 
| 
      
 44 
     | 
    
         
            +
                    component 'tailwinds/back_button'
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  def tramway_container(id: nil, &)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    if id.present?
         
     | 
| 
      
 49 
     | 
    
         
            +
                      component 'tailwinds/containers/narrow', id: id, &
         
     | 
| 
      
 50 
     | 
    
         
            +
                    else
         
     | 
| 
      
 51 
     | 
    
         
            +
                      component 'tailwinds/containers/narrow', &
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
       15 
54 
     | 
    
         
             
                end
         
     | 
| 
       16 
55 
     | 
    
         
             
              end
         
     | 
| 
       17 
56 
     | 
    
         
             
            end
         
     | 
    
        data/lib/tramway/navbar.rb
    CHANGED
    
    | 
         @@ -10,13 +10,13 @@ module Tramway 
     | 
|
| 
       10 
10 
     | 
    
         
             
                  @items = { left: [], right: [] }
         
     | 
| 
       11 
11 
     | 
    
         
             
                  @filling = nil
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                   
     | 
| 
       14 
     | 
    
         
            -
                    entities = Tramway.config.entities
         
     | 
| 
      
 13 
     | 
    
         
            +
                  return unless with_entities
         
     | 
| 
       15 
14 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
      
 15 
     | 
    
         
            +
                  entities = Tramway.config.entities
         
     | 
| 
       17 
16 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
                  return unless entities.any?
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  preset_left entities
         
     | 
| 
       20 
20 
     | 
    
         
             
                end
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                def left
         
     | 
    
        data/lib/tramway/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: tramway
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: '0.6'
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - kalashnikovisme
         
     | 
| 
         @@ -9,7 +9,7 @@ authors: 
     | 
|
| 
       9 
9 
     | 
    
         
             
            autorequire:
         
     | 
| 
       10 
10 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       11 
11 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       12 
     | 
    
         
            -
            date: 2025-10- 
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2025-10-25 00:00:00.000000000 Z
         
     | 
| 
       13 
13 
     | 
    
         
             
            dependencies:
         
     | 
| 
       14 
14 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       15 
15 
     | 
    
         
             
              name: anyway_config
         
     | 
| 
         @@ -143,6 +143,13 @@ files: 
     | 
|
| 
       143 
143 
     | 
    
         
             
            - app/assets/javascripts/tramway/table_row_preview_controller.js
         
     | 
| 
       144 
144 
     | 
    
         
             
            - app/components/tailwind_component.html.haml
         
     | 
| 
       145 
145 
     | 
    
         
             
            - app/components/tailwind_component.rb
         
     | 
| 
      
 146 
     | 
    
         
            +
            - app/components/tailwinds/back_button_component.html.haml
         
     | 
| 
      
 147 
     | 
    
         
            +
            - app/components/tailwinds/back_button_component.rb
         
     | 
| 
      
 148 
     | 
    
         
            +
            - app/components/tailwinds/base_component.rb
         
     | 
| 
      
 149 
     | 
    
         
            +
            - app/components/tailwinds/button_component.html.haml
         
     | 
| 
      
 150 
     | 
    
         
            +
            - app/components/tailwinds/button_component.rb
         
     | 
| 
      
 151 
     | 
    
         
            +
            - app/components/tailwinds/containers/narrow_component.html.haml
         
     | 
| 
      
 152 
     | 
    
         
            +
            - app/components/tailwinds/containers/narrow_component.rb
         
     | 
| 
       146 
153 
     | 
    
         
             
            - app/components/tailwinds/form/builder.rb
         
     | 
| 
       147 
154 
     | 
    
         
             
            - app/components/tailwinds/form/file_field_component.html.haml
         
     | 
| 
       148 
155 
     | 
    
         
             
            - app/components/tailwinds/form/file_field_component.rb
         
     | 
| 
         @@ -214,6 +221,7 @@ files: 
     | 
|
| 
       214 
221 
     | 
    
         
             
            - app/views/tramway/layouts/application.html.haml
         
     | 
| 
       215 
222 
     | 
    
         
             
            - config/routes.rb
         
     | 
| 
       216 
223 
     | 
    
         
             
            - config/tailwind.config.js
         
     | 
| 
      
 224 
     | 
    
         
            +
            - lib/generators/tramway/install/install_generator.rb
         
     | 
| 
       217 
225 
     | 
    
         
             
            - lib/kaminari/helpers/tag.rb
         
     | 
| 
       218 
226 
     | 
    
         
             
            - lib/rules/turbo_html_attributes_rules.rb
         
     | 
| 
       219 
227 
     | 
    
         
             
            - lib/tasks/tramway_tasks.rake
         
     |