tramway 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc95797d7be71f61b7a8350b3ef71905d41c8281d3a134feefc4e8477b910b44
4
- data.tar.gz: 9fc4aa0e0bbec968e96620532082142b3ad48ab701e97ab52c2bd05a8236c047
3
+ metadata.gz: dee256141e588202061885354d7669ee2a5bb1ce99a57a709129031002ab13c8
4
+ data.tar.gz: 36b036fc2890c2710a12684f78ea36f9a2d8ae7e5f11f252973d81ae905f9e3a
5
5
  SHA512:
6
- metadata.gz: ef921bd83394ebde2404a8e5e1b7332af573713553ed73676850955ff8e32f9fc93cdddfa0124658ad40c096557fcd60ed420a55d252f5e5c3fb40ea34cae19e
7
- data.tar.gz: d0a2b72e6e35ea9a297a9af346e794cf1833e8b5922c2e09d98a5f8f06a2d283db49b33753510535a0594365b072aff666712ee50a0b2c9d6036aa530ab52e38
6
+ metadata.gz: b8ee38fd18865625ffdb1c9671cedbba4c780d001b47743f3b0be3657e525eb0d96c84529a9023dc6a68acc6507df1646ca712f4c26dd47df8caadc174c66aa3
7
+ data.tar.gz: 453471063d91beecf588b590008b37546a36b035fed4065499b87f4ef6182ea8fd2a551fcc124f88f1ee07a1ec4df501bfbbafa607a03ea05d1ca28925f5f7c9
data/README.md CHANGED
@@ -8,6 +8,7 @@ Unite Ruby on Rails brilliance. Streamline development with Tramway.
8
8
  * [Tramway Form](https://github.com/Purple-Magic/tramway#tramway-form)
9
9
  * [Tramway Navbar](https://github.com/Purple-Magic/tramway#tramway-navbar)
10
10
  * [Tailwind-styled forms](https://github.com/Purple-Magic/tramway#tailwind-styled-forms)
11
+ * [Tailwind-styled pagination](https://github.com/Purple-Magic/tramway#tailwind-styled-pagination)
11
12
 
12
13
  ## Installation
13
14
  Add this line to your application's Gemfile:
@@ -248,6 +249,24 @@ class Admin::UsersController < Admin::ApplicationController
248
249
  end
249
250
  ```
250
251
 
252
+ ### Form inheritance
253
+
254
+ Tramway Form supports inheritance of `properties`
255
+
256
+ **Example**
257
+
258
+ ```ruby
259
+ class UserForm < TramwayForm
260
+ properties :email, :password
261
+ end
262
+
263
+ class AdminForm < UserForm
264
+ properties :permissions
265
+ end
266
+
267
+ AdminForm.properties # returns [:email, :password, :permissions]
268
+ ```
269
+
251
270
  ### Make flexible and extendable forms
252
271
 
253
272
  Tramway Form properties are not mapped to a model. You're able to make extended forms.
@@ -362,6 +381,32 @@ Available form helpers:
362
381
  * select
363
382
  * submit
364
383
 
384
+ ### Tailwind-styled pagination for Kaminari
385
+
386
+ Tramway uses [Tailwind](https://tailwindcss.com/) by default. It has tailwind-styled pagination for [kaminari](https://github.com/kaminari/kaminari).
387
+
388
+ #### How to use
389
+
390
+ *Gemfile*
391
+ ```ruby
392
+ gem 'tramway'
393
+ gem 'kaminari'
394
+ ```
395
+
396
+ *config/initializers/tramway.rb*
397
+ ```ruby
398
+ Tramway.configure do |config|
399
+ config.pagination = { enabled: true } # enabled is false by default
400
+ end
401
+ ```
402
+
403
+ *app/views/users/index.html.haml*
404
+ ```haml
405
+ = paginate @users # it will render tailwind-styled pagination buttons by default
406
+ ```
407
+
408
+ Pagination buttons looks like [this](https://play.tailwindcss.com/mqgDS5l9oY)
409
+
365
410
  ## Contributing
366
411
 
367
412
  Install [lefthook](https://github.com/evilmartians/lefthook)
@@ -369,6 +414,7 @@ Install [lefthook](https://github.com/evilmartians/lefthook)
369
414
  ```
370
415
  bundle
371
416
  lefthook install
417
+ rspec
372
418
  ```
373
419
 
374
420
  ## License
@@ -6,9 +6,9 @@ require 'view_component'
6
6
  class TailwindComponent < ViewComponent::Base
7
7
  extend Dry::Initializer[undefined: false]
8
8
 
9
- option :template
9
+ option :input
10
10
  option :attribute
11
- option :object_name
11
+ option :value, optional: true
12
12
  option :options
13
13
  option :label
14
14
  option :for
@@ -6,19 +6,33 @@ module Tailwinds
6
6
  # :reek:InstanceVariableAssumption
7
7
  class Builder < Tramway::Views::FormBuilder
8
8
  def text_field(attribute, **options, &)
9
- render(Tailwinds::Form::TextFieldComponent.new(**default_options(attribute, options)), &)
9
+ render(Tailwinds::Form::TextFieldComponent.new(
10
+ input: input(:text_field),
11
+ value: get_value(attribute, options),
12
+ **default_options(attribute, options)
13
+ ), &)
10
14
  end
11
15
 
12
16
  def password_field(attribute, **options, &)
13
- render(Tailwinds::Form::TextFieldComponent.new(**default_options(attribute, options)), &)
17
+ render(Tailwinds::Form::TextFieldComponent.new(
18
+ input: input(:password_field),
19
+ **default_options(attribute, options)
20
+ ), &)
14
21
  end
15
22
 
16
23
  def file_field(attribute, **options, &)
17
- render(Tailwinds::Form::FileFieldComponent.new(**default_options(attribute, options)), &)
24
+ input = super(attribute, **options.merge(class: :hidden))
25
+
26
+ render(Tailwinds::Form::FileFieldComponent.new(input:, **default_options(attribute, options)), &)
18
27
  end
19
28
 
20
29
  def select(attribute, collection, **options, &)
21
- render(Tailwinds::Form::SelectComponent.new(**default_options(attribute, options).merge(collection:)), &)
30
+ render(Tailwinds::Form::SelectComponent.new(
31
+ input: input(:select),
32
+ value: options[:selected] || object.public_send(attribute),
33
+ collection:,
34
+ **default_options(attribute, options)
35
+ ), &)
22
36
  end
23
37
 
24
38
  def submit(action, **options, &)
@@ -27,11 +41,18 @@ module Tailwinds
27
41
 
28
42
  private
29
43
 
44
+ def input(method_name)
45
+ unbound_method = self.class.superclass.instance_method(method_name)
46
+ unbound_method.bind(self)
47
+ end
48
+
49
+ def get_value(attribute, options)
50
+ options[:value] || object.public_send(attribute)
51
+ end
52
+
30
53
  def default_options(attribute, options)
31
54
  {
32
- template: @template,
33
55
  attribute:,
34
- object_name:,
35
56
  label: label(attribute, options),
36
57
  for: for_id(attribute),
37
58
  options:
@@ -1,4 +1,4 @@
1
1
  .mb-4
2
2
  %label.inline-block.bg-blue-500.hover:bg-blue-700.text-white.font-bold.py-2.px-4.rounded.cursor-pointer.mt-4{ for: @for }
3
3
  = @label
4
- = @template.file_field @object_name, @attribute
4
+ = @input
@@ -4,6 +4,7 @@ module Tailwinds
4
4
  module Form
5
5
  # Tailwind-styled file_field input
6
6
  class FileFieldComponent < TailwindComponent
7
+ option :input
7
8
  end
8
9
  end
9
10
  end
@@ -1,4 +1,4 @@
1
1
  .mb-4
2
2
  %label.block.text-gray-700.text-sm.font-bold.mb-2{ for: @for }
3
3
  = @label
4
- = @template.select @object_name, @attribute, @collection, {}, @options.merge(class: 'bg-white border border-gray-300 text-gray-700 py-2 px-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent')
4
+ = @input.call(@attribute, @collection, { selected: @value }, @options.merge(class: 'bg-white border border-gray-300 text-gray-700 py-2 px-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent'))
@@ -1,4 +1,4 @@
1
1
  .mb-4
2
2
  %label.block.text-gray-700.text-sm.font-bold.mb-2{ for: @for }
3
3
  = @label
4
- = @template.text_field @object_name, @attribute, **@options.merge(class: 'w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:border-red-500')
4
+ = @input.call @attribute, **@options.merge(class: 'w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:border-red-500'), value: @value
@@ -0,0 +1,9 @@
1
+ -# Link to the "First" page
2
+ -# available local variables
3
+ -# url: url to the first page
4
+ -# current_page: a page object for the currently displayed page
5
+ -# total_pages: total number of pages
6
+ -# per_page: number of items to fetch per page
7
+ -# remote: data-remote
8
+ %span.first{ class: 'px-3 py-2 text-sm font-medium text-purple-700 bg-white rounded-md hover:bg-purple-100' }
9
+ = link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, remote: remote
@@ -0,0 +1,8 @@
1
+ -# Non-link tag that stands for skipped pages...
2
+ -# available local variables
3
+ -# current_page: a page object for the currently displayed page
4
+ -# total_pages: total number of pages
5
+ -# per_page: number of items to fetch per page
6
+ -# remote: data-remote
7
+ %span.page.gap{ class: 'px-3 py-2 text-sm font-medium text-purple-700' }
8
+ = t('views.pagination.truncate').html_safe
@@ -0,0 +1,9 @@
1
+ -# Link to the "Last" page
2
+ -# available local variables
3
+ -# url: url to the last page
4
+ -# current_page: a page object for the currently displayed page
5
+ -# total_pages: total number of pages
6
+ -# per_page: number of items to fetch per page
7
+ -# remote: data-remote
8
+ %span.last{ class: 'px-3 py-2 text-sm font-medium text-purple-700 bg-white rounded-md hover:bg-purple-100' }
9
+ = link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, remote: remote
@@ -0,0 +1,9 @@
1
+ -# Link to the "Next" page
2
+ -# available local variables
3
+ -# url: url to the next page
4
+ -# current_page: a page object for the currently displayed page
5
+ -# total_pages: total number of pages
6
+ -# per_page: number of items to fetch per page
7
+ -# remote: data-remote
8
+ %span.next{ class: 'px-3 py-2 text-sm font-medium text-purple-700 bg-white rounded-md hover:bg-purple-100' }
9
+ = link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, rel: 'next', remote: remote
@@ -0,0 +1,14 @@
1
+ -# Link showing page number
2
+ -# available local variables
3
+ -# page: a page object for "this" page
4
+ -# url: url to this page
5
+ -# current_page: a page object for the currently displayed page
6
+ -# total_pages: total number of pages
7
+ -# per_page: number of items to fetch per page
8
+ -# remote: data-remote
9
+ - if page.current?
10
+ %span{class: "px-3 py-2 font-medium rounded-md bg-purple-500 text-white" }
11
+ = page
12
+ - else
13
+ %span{class: "cursor px-3 py-2 font-medium text-purple-700 bg-white rounded-md hover:bg-purple-100"}
14
+ = link_to_unless page.current?, page, url, {remote: remote, rel: page.rel}
@@ -0,0 +1,12 @@
1
+ = paginator.render do
2
+ %nav.pagination.flex.items-center.justify-center.space-x-1
3
+ = first_page_tag unless current_page.first?
4
+ = prev_page_tag unless current_page.first?
5
+ - each_page do |page|
6
+ - if page.display_tag?
7
+ = page_tag page
8
+ - elsif !page.was_truncated?
9
+ = gap_tag
10
+ = next_page_tag unless current_page.last?
11
+ = last_page_tag unless current_page.last?
12
+
@@ -0,0 +1,9 @@
1
+ -# Link to the "Previous" page
2
+ -# available local variables
3
+ -# url: url to the previous page
4
+ -# current_page: a page object for the currently displayed page
5
+ -# total_pages: total number of pages
6
+ -# per_page: number of items to fetch per page
7
+ -# remote: data-remote
8
+ %span.prev{ class: 'px-3 py-2 text-sm font-medium text-purple-700 bg-white rounded-md hover:bg-purple-100' }
9
+ = link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, rel: 'prev', remote: remote
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :reek:ClassVariable { enabled: false }
3
4
  module Tramway
4
5
  # Provides form object for Tramway
5
6
  #
@@ -17,22 +18,40 @@ module Tramway
17
18
  end
18
19
 
19
20
  class << self
20
- def property(attribute, _proc_obj = nil)
21
- @properties ||= []
21
+ def inherited(subclass)
22
+ subclass.instance_variable_set(:@properties, [])
23
+
24
+ super
25
+ end
26
+
27
+ def property(attribute)
22
28
  @properties << attribute
23
29
 
24
30
  delegate attribute, to: :object
25
31
  end
26
32
 
27
33
  def properties(*attributes)
28
- if attributes.any?
29
- attributes.each do |attribute|
30
- property(attribute)
31
- end
32
- else
33
- @properties || []
34
+ attributes.any? ? __set_properties(attributes) : __properties
35
+ end
36
+
37
+ def __set_properties(attributes)
38
+ attributes.each do |attribute|
39
+ property(attribute)
34
40
  end
35
41
  end
42
+
43
+ def __properties
44
+ (__ancestor_properties + @properties).uniq
45
+ end
46
+
47
+ # :reek:ManualDispatch { enabled: false }
48
+ def __ancestor_properties(klass = superclass)
49
+ superklass = klass.superclass
50
+
51
+ return [] unless superklass.respond_to?(:properties)
52
+
53
+ klass.properties + __ancestor_properties(superklass)
54
+ end
36
55
  end
37
56
 
38
57
  def submit(params)
@@ -1,26 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'anyway'
3
4
  require 'singleton'
4
5
  require 'tramway/configs/entity'
5
6
 
6
7
  module Tramway
7
8
  # Basic configuration of Tramway
8
9
  #
9
- class Config
10
+ class Config < Anyway::Config
10
11
  include Singleton
11
12
 
12
- def initialize
13
- @entities = []
14
- end
13
+ attr_config(
14
+ pagination: { enabled: false },
15
+ entities: []
16
+ )
15
17
 
16
18
  def entities=(collection)
17
- @entities = collection.map do |entity|
19
+ super(collection.map do |entity|
18
20
  entity_options = entity.is_a?(Hash) ? entity : { name: entity }
19
21
 
20
22
  Tramway::Configs::Entity.new(**entity_options)
21
- end
23
+ end)
22
24
  end
23
-
24
- attr_reader :entities
25
25
  end
26
26
  end
@@ -7,29 +7,65 @@ module Tramway
7
7
  isolate_namespace Tramway
8
8
 
9
9
  initializer 'tramway.load_helpers' do
10
+ load_navbar_helper
11
+ load_views_helper
12
+ load_decorator_helper
13
+ load_form_helper
14
+ configure_pagination if Tramway.config.pagination[:enabled]
15
+ end
16
+
17
+ private
18
+
19
+ def load_navbar_helper
10
20
  ActiveSupport.on_load(:action_view) do |loaded_class|
11
21
  require 'tramway/helpers/navbar_helper'
12
22
 
13
23
  loaded_class.include Tramway::Helpers::NavbarHelper
14
24
  end
25
+ end
15
26
 
27
+ def load_views_helper
16
28
  ActiveSupport.on_load(:action_view) do |loaded_class|
17
29
  require 'tramway/helpers/views_helper'
18
30
 
19
31
  loaded_class.include Tramway::Helpers::ViewsHelper
20
32
  end
33
+ end
21
34
 
35
+ def load_decorator_helper
22
36
  ActiveSupport.on_load(:action_controller) do |loaded_class|
23
37
  require 'tramway/helpers/decorate_helper'
24
38
 
25
39
  loaded_class.include Tramway::Helpers::DecorateHelper
26
40
  end
41
+ end
27
42
 
43
+ def load_form_helper
28
44
  ActiveSupport.on_load(:action_controller) do |loaded_class|
29
45
  require 'tramway/helpers/form_helper'
30
46
 
31
47
  loaded_class.include Tramway::Helpers::FormHelper
32
48
  end
33
49
  end
50
+
51
+ # :reek:NestedIterators { enabled: false }
52
+ # :reek:TooManyStatements { enabled: false }
53
+ def configure_pagination
54
+ ActiveSupport.on_load(:action_controller) do
55
+ # Detecting tramway views path
56
+ tramway_spec = Gem.loaded_specs['tramway']
57
+ tramway_views_path = File.join(tramway_spec.full_gem_path, 'app/views')
58
+
59
+ paths = view_paths.to_ary
60
+
61
+ # Determine index to insert tramway views path
62
+ rails_views_index = paths.find_index { |path| path.to_s.ends_with?('app/views') }
63
+ insert_index = rails_views_index ? rails_views_index + 1 : 0
64
+
65
+ # Inserting tramway views path
66
+ paths.insert(insert_index, tramway_views_path)
67
+ self.view_paths = paths
68
+ end
69
+ end
34
70
  end
35
71
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tramway
4
- VERSION = '0.3.2'
4
+ VERSION = '0.4.1'
5
5
  end
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.3.2
4
+ version: 0.4.1
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: 2024-01-04 00:00:00.000000000 Z
12
+ date: 2024-02-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: dry-struct
@@ -106,6 +106,13 @@ files:
106
106
  - app/components/tailwinds/nav/item_component.rb
107
107
  - app/components/tailwinds/navbar_component.html.haml
108
108
  - app/components/tailwinds/navbar_component.rb
109
+ - app/views/kaminari/_first_page.html.haml
110
+ - app/views/kaminari/_gap.html.haml
111
+ - app/views/kaminari/_last_page.html.haml
112
+ - app/views/kaminari/_next_page.html.haml
113
+ - app/views/kaminari/_page.html.haml
114
+ - app/views/kaminari/_paginator.html.haml
115
+ - app/views/kaminari/_prev_page.html.haml
109
116
  - config/routes.rb
110
117
  - lib/rules/turbo_html_attributes_rules.rb
111
118
  - lib/tasks/tramway_tasks.rake