tramway 0.5.4.1 → 0.5.5

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.
Files changed (24) hide show
  1. checksums.yaml +4 -4
  2. data/app/components/tailwind_component.rb +32 -0
  3. data/app/components/tailwinds/form/builder.rb +54 -15
  4. data/app/components/tailwinds/form/file_field_component.html.haml +6 -3
  5. data/app/components/tailwinds/form/multiselect/dropdown_container.html.haml +1 -1
  6. data/app/components/tailwinds/form/multiselect/item_container.html.haml +3 -3
  7. data/app/components/tailwinds/form/multiselect/select_as_input.html.haml +11 -1
  8. data/app/components/tailwinds/form/multiselect/select_as_input.rb +1 -0
  9. data/app/components/tailwinds/form/multiselect/selected_item_template.html.haml +1 -1
  10. data/app/components/tailwinds/form/multiselect_component.html.haml +3 -3
  11. data/app/components/tailwinds/form/multiselect_component.rb +8 -1
  12. data/app/components/tailwinds/form/number_field_component.html.haml +8 -0
  13. data/app/components/tailwinds/form/number_field_component.rb +9 -0
  14. data/app/components/tailwinds/form/select_component.html.haml +5 -1
  15. data/app/components/tailwinds/form/submit_button_component.html.haml +4 -1
  16. data/app/components/tailwinds/form/submit_button_component.rb +11 -3
  17. data/app/components/tailwinds/form/text_area_component.html.haml +4 -1
  18. data/app/components/tailwinds/form/text_field_component.html.haml +4 -1
  19. data/app/components/tailwinds/table/header_component.html.haml +1 -1
  20. data/app/components/tailwinds/table/header_component.rb +6 -1
  21. data/app/components/tailwinds/table_component.html.haml +1 -1
  22. data/lib/tramway/helpers/views_helper.rb +7 -2
  23. data/lib/tramway/version.rb +1 -1
  24. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b3a940800b0be153a5c1eb7655a1f4203ac977accf41579e43221766843c0e8
4
- data.tar.gz: ead462e416c64538748b393652233a4fb755f6c874dcf2d618e027ff4cfa77dc
3
+ metadata.gz: 21ffe76c2224bd46292479de272ccdb59f0095075043507af3dd6098d5559933
4
+ data.tar.gz: 6547f3ac892439dddb165dbf7d8a06dcb90d8adb6f12b65d921cb277ebf7754a
5
5
  SHA512:
6
- metadata.gz: 835993aedeccd2cd7873c3cb12416d5c7c90f45d0cb234926e544a251e6bbc7a889a4a68db36f9adec55b360d246f146701a37307875ad7729f2f029f0e829fe
7
- data.tar.gz: b78685a91ce1adbb6da37659972a5206b891cccde030a3fb18ee8ba559f49bc73144b5db0636fedc22ed2f9466cab67e74afe0192f0c162283f1009e85ed4886
6
+ metadata.gz: 93bdfec725bc98cb5ab5a045604fe00c6669eaf8d89eedb081e0750a4b1073fda7c7f99964a3e4b8bbfb4f668024ca9341f512c5fed77d17ea05eeb31de19f37
7
+ data.tar.gz: 6d6d161386768b114e82e031888ce118383bfb377c6b5e1551d9f8b7300f077af50eb6cb2bff3a6d4523ab0f43bbc77b69b3af180ed4895d7b8c24558c1876a4
@@ -10,4 +10,36 @@ class TailwindComponent < Tramway::Component::Base
10
10
  option :options
11
11
  option :label
12
12
  option :for
13
+ option :size, default: -> { :middle }
14
+
15
+ SIZE_CLASSES = {
16
+ small: {
17
+ text_input: 'text-sm px-2 py-1',
18
+ select_input: 'text-sm px-2 py-1',
19
+ file_button: 'text-sm px-3 py-1',
20
+ submit_button: 'text-sm px-3 py-1',
21
+ multiselect_input: 'text-sm px-2 py-1'
22
+ },
23
+ middle: {
24
+ text_input: 'text-base px-3 py-2',
25
+ select_input: 'text-base px-3 py-2',
26
+ file_button: 'text-base px-4 py-2',
27
+ submit_button: 'text-base px-4 py-2',
28
+ multiselect_input: 'text-base px-3 py-2'
29
+ },
30
+ large: {
31
+ text_input: 'text-lg px-4 py-3',
32
+ select_input: 'text-lg px-4 py-3',
33
+ file_button: 'text-lg px-5 py-3',
34
+ submit_button: 'text-lg px-5 py-3',
35
+ multiselect_input: 'text-lg px-4 py-3'
36
+ }
37
+ }.freeze
38
+
39
+ private
40
+
41
+ def size_class(key)
42
+ size_classes = SIZE_CLASSES.fetch(size) { SIZE_CLASSES[:middle] }
43
+ size_classes.fetch(key) { SIZE_CLASSES[:middle].fetch(key) }
44
+ end
13
45
  end
@@ -5,59 +5,90 @@ module Tailwinds
5
5
  # Provides Tailwind-styled forms
6
6
  # :reek:InstanceVariableAssumption
7
7
  class Builder < Tramway::Views::FormBuilder
8
+ def initialize(object_name, object, template, options)
9
+ super
10
+
11
+ @form_size = options[:size] || options['size'] || :middle
12
+ end
13
+
8
14
  def text_field(attribute, **options, &)
15
+ sanitized_options = sanitize_options(options)
16
+
9
17
  render(Tailwinds::Form::TextFieldComponent.new(
10
18
  input: input(:text_field),
11
- value: get_value(attribute, options),
12
- **default_options(attribute, options)
19
+ value: get_value(attribute, sanitized_options),
20
+ **default_options(attribute, sanitized_options)
21
+ ), &)
22
+ end
23
+
24
+ def number_field(attribute, **options, &)
25
+ sanitized_options = sanitize_options(options)
26
+
27
+ render(Tailwinds::Form::NumberFieldComponent.new(
28
+ input: input(:number_field),
29
+ value: get_value(attribute, sanitized_options),
30
+ **default_options(attribute, sanitized_options)
13
31
  ), &)
14
32
  end
15
33
 
16
34
  def text_area(attribute, **options, &)
35
+ sanitized_options = sanitize_options(options)
36
+
17
37
  render(Tailwinds::Form::TextAreaComponent.new(
18
38
  input: input(:text_area),
19
- value: get_value(attribute, options),
20
- **default_options(attribute, options)
39
+ value: get_value(attribute, sanitized_options),
40
+ **default_options(attribute, sanitized_options)
21
41
  ), &)
22
42
  end
23
43
 
24
44
  def password_field(attribute, **options, &)
45
+ sanitized_options = sanitize_options(options)
46
+
25
47
  render(Tailwinds::Form::TextFieldComponent.new(
26
48
  input: input(:password_field),
27
- **default_options(attribute, options)
49
+ **default_options(attribute, sanitized_options)
28
50
  ), &)
29
51
  end
30
52
 
31
53
  def file_field(attribute, **options, &)
32
- input = super(attribute, **options.merge(class: :hidden))
54
+ sanitized_options = sanitize_options(options)
55
+ input = super(attribute, **sanitized_options.merge(class: :hidden))
33
56
 
34
- render(Tailwinds::Form::FileFieldComponent.new(input:, **default_options(attribute, options)), &)
57
+ render(Tailwinds::Form::FileFieldComponent.new(input:, **default_options(attribute, sanitized_options)), &)
35
58
  end
36
59
 
37
60
  def select(attribute, collection, **options, &)
61
+ sanitized_options = sanitize_options(options)
62
+
38
63
  render(Tailwinds::Form::SelectComponent.new(
39
64
  input: input(:select),
40
- value: options[:selected] || object.public_send(attribute),
41
- collection: explicitly_add_blank_option(collection, options),
42
- **default_options(attribute, options)
65
+ value: sanitized_options[:selected] || object.public_send(attribute),
66
+ collection: explicitly_add_blank_option(collection, sanitized_options),
67
+ **default_options(attribute, sanitized_options)
43
68
  ), &)
44
69
  end
45
70
 
46
71
  def multiselect(attribute, collection, **options, &)
72
+ sanitized_options = sanitize_options(options)
73
+
47
74
  render(Tailwinds::Form::MultiselectComponent.new(
48
75
  input: input(:text_field),
49
- value: options[:value] || options[:selected] || object.public_send(attribute),
76
+ value: sanitized_options[:value] || sanitized_options[:selected] || object.public_send(attribute),
50
77
  collection:,
51
- **default_options(attribute, options)
78
+ **default_options(attribute, sanitized_options)
52
79
  ), &)
53
80
  end
54
81
 
55
- def submit(action, **, &)
56
- render(Tailwinds::Form::SubmitButtonComponent.new(action, **), &)
82
+ def submit(action, **options, &)
83
+ sanitized_options = sanitize_options(options)
84
+
85
+ render(Tailwinds::Form::SubmitButtonComponent.new(action, size: form_size, **sanitized_options), &)
57
86
  end
58
87
 
59
88
  private
60
89
 
90
+ attr_reader :form_size
91
+
61
92
  def input(method_name)
62
93
  unbound_method = self.class.superclass.instance_method(method_name)
63
94
  unbound_method.bind(self)
@@ -72,7 +103,8 @@ module Tailwinds
72
103
  attribute:,
73
104
  label: label_build(attribute, options),
74
105
  for: for_id(attribute),
75
- options:
106
+ options:,
107
+ size: form_size
76
108
  }
77
109
  end
78
110
 
@@ -88,6 +120,13 @@ module Tailwinds
88
120
  "#{object_name}_#{attribute}"
89
121
  end
90
122
 
123
+ def sanitize_options(options)
124
+ options.dup.tap do |opts|
125
+ opts.delete(:size)
126
+ opts.delete('size')
127
+ end
128
+ end
129
+
91
130
  # REMOVE IT. WE MUST UNDERSTAND WHY INCLUDE_BLANK DOES NOT WORK
92
131
  # :reek:UtilityFunction
93
132
  def explicitly_add_blank_option(collection, options)
@@ -1,5 +1,8 @@
1
1
  .mb-4
2
- - if @label
3
- %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 }
4
- = @label
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
5
8
  = @input
@@ -1,3 +1,3 @@
1
- #dropdown.absolute.shadow.top-11.bg-white.z-40.w-full.lef-0.rounded.max-h-select.overflow-y-auto{ data: { action: "click@window->multiselect#closeOnClickOutside" } }
1
+ #dropdown.absolute.shadow.top-11.bg-white.z-40.w-full.lef-0.rounded.max-h-select.overflow-y-auto.dark:bg-gray-800.dark:shadow-lg.dark:ring-1.dark:ring-gray-700{ data: { action: "click@window->multiselect#closeOnClickOutside" } }
2
2
  .flex.flex-col.w-full
3
3
  {{content}}
@@ -1,5 +1,5 @@
1
- .cursor-pointer.w-full.border-gray-100.rounded-t.border-b.hover:bg-teal-100{ data: { action: "click->multiselect#toggleItem", text: "{{text}}", value: "{{value}}" } }
2
- .flex.w-full.items-center.p-2.pl-2.border-transparent.border-l-2.relative.hover:border-teal-100
3
- .w-full.items-center.flex
1
+ .cursor-pointer.w-full.border-gray-100.rounded-t.border-b.hover:bg-teal-100.dark:border-gray-700.dark:hover:bg-gray-700{ data: { action: "click->multiselect#toggleItem", text: "{{text}}", value: "{{value}}" } }
2
+ .flex.w-full.items-center.p-2.pl-2.border-transparent.border-l-2.relative.hover:border-teal-100.dark:hover:border-gray-600
3
+ .w-full.items-center.flex.dark:text-gray-100
4
4
  .mx-2.leading-6
5
5
  {{text}}
@@ -1,2 +1,12 @@
1
1
  .flex-1
2
- = @input.call(@attribute, @options.merge(placeholder: "{{placeholder}}", class: "bg-transparent p-1 px-2 appearance-none outline-none h-full w-full text-gray-800 hidden", data: { 'multiselect-target' => 'hiddenInput' }))
2
+ - base_classes = 'bg-transparent appearance-none outline-none h-full w-full text-gray-800 hidden '
3
+ - base_classes += 'dark:text-white dark:placeholder-white'
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
+ )
@@ -10,6 +10,7 @@ module Tailwinds
10
10
  option :options
11
11
  option :attribute
12
12
  option :input
13
+ option :size_class
13
14
  end
14
15
  end
15
16
  end
@@ -1,4 +1,4 @@
1
- .flex.justify-center.items-center.m-1.font-medium.py-1.px-2.bg-white.rounded-full.text-teal-700.bg-teal-100.border.border-teal-300
1
+ .flex.justify-center.items-center.m-1.font-medium.py-1.px-2.bg-white.rounded-full.text-teal-700.bg-teal-100.border.border-teal-300.dark:bg-teal-900.dark:text-teal-100.dark:border-teal-700
2
2
  .text-xs.font-normal.leading-none.max-w-full.flex-initial
3
3
  {{text}}
4
4
  .flex.flex-auto.flex-row-reverse
@@ -2,9 +2,9 @@
2
2
  - if @label
3
3
  = component('tailwinds/form/label', for: @for) do
4
4
  = @label
5
- .flex.flex-col.relative{ data: multiselect_hash, id: "#{@for}_multiselect" }
5
+ .flex.flex-col.relative.dark:text-gray-100{ data: multiselect_hash, id: "#{@for}_multiselect" }
6
6
  .min-w-96.w-fit
7
- .p-1.flex.border.border-gray-200.bg-white.rounded{ data: { "multiselect-target" => "dropdown" } }
7
+ .p-1.flex.border.border-gray-200.bg-white.rounded.dark:border-gray-600.dark:bg-gray-800{ data: { "multiselect-target" => "dropdown" } }
8
8
  .flex.flex-auto.flex-wrap{ data: { "multiselect-target" => "showSelectedArea" } }
9
- .text-gray-300.w-8.py-1.pl-2.pr-1.border-l.flex.items-center.border-gray-200
9
+ .text-gray-300.w-8.py-1.pl-2.pr-1.border-l.flex.items-center.border-gray-200.dark:text-gray-500.dark:border-gray-600
10
10
  ^
@@ -45,7 +45,14 @@ module Tailwinds
45
45
  end
46
46
 
47
47
  def select_as_input
48
- render(Tailwinds::Form::Multiselect::SelectAsInput.new(options:, attribute:, input:))
48
+ render(
49
+ Tailwinds::Form::Multiselect::SelectAsInput.new(
50
+ options:,
51
+ attribute:,
52
+ input:,
53
+ size_class: size_class(:multiselect_input)
54
+ )
55
+ )
49
56
  end
50
57
 
51
58
  def on_change
@@ -0,0 +1,8 @@
1
+ .mb-4
2
+ - if @label
3
+ = component('tailwinds/form/label', for: @for) do
4
+ = @label
5
+ - base_classes = 'w-full bg-white border border-gray-300 rounded focus:outline-none focus:border-red-500 '
6
+ - base_classes += 'dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:focus:border-red-400 dark:placeholder-white'
7
+ - classes = "#{size_class(:text_input)} #{base_classes}"
8
+ = @input.call @attribute, **@options.merge(class: classes), value: @value
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tailwinds
4
+ module Form
5
+ # Tailwind-styled numeric field
6
+ class NumberFieldComponent < TailwindComponent
7
+ end
8
+ end
9
+ end
@@ -2,4 +2,8 @@
2
2
  - if @label
3
3
  = component('tailwinds/form/label', for: @for) do
4
4
  = @label
5
- = @input.call(@attribute, @collection, { selected: @value }, @options.merge(class: 'bg-white border border-gray-300 text-gray-700 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent py-2 px-3 disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed'))
5
+ - base_classes = 'bg-white border border-gray-300 text-gray-700 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 '
6
+ - base_classes += 'focus:border-transparent disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed '
7
+ - base_classes += 'dark:bg-gray-800 dark:border-gray-600 dark:text-gray-100 dark:focus:ring-red-400 dark:disabled:bg-gray-800 dark:disabled:text-gray-500'
8
+ - classes = "#{size_class(:select_input)} #{base_classes}"
9
+ = @input.call(@attribute, @collection, { selected: @value }, @options.merge(class: classes))
@@ -1,4 +1,7 @@
1
1
  .flex.items-center.justify-between
2
- %button.bg-red-500.hover:bg-red-700.text-white.font-bold.py-2.px-4.rounded.focus:outline-none.focus:shadow-outline.cursor-pointer.dark:text-white{ type: :submit, name: :commit, **@options }
2
+ - base_classes = 'bg-red-500 hover:bg-red-700 text-white font-bold rounded focus:outline-none focus:shadow-outline cursor-pointer '
3
+ - base_classes += 'dark:text-white dark:bg-red-600 dark:hover:bg-red-500 dark:focus:ring-2 dark:focus:ring-red-400'
4
+ - classes = "#{size_class(:submit_button)} #{base_classes}"
5
+ %button{ type: :submit, name: :commit, class: classes, **@options }
3
6
  = @text
4
7
  = @content
@@ -4,10 +4,18 @@ module Tailwinds
4
4
  module Form
5
5
  # Tailwind-styled submit button
6
6
  class SubmitButtonComponent < TailwindComponent
7
- def initialize(action, **options)
8
- @options = options.except :type
9
-
7
+ def initialize(action, size: :middle, **options)
10
8
  @text = action.is_a?(String) ? action : action.to_s.capitalize
9
+
10
+ super(
11
+ input: nil,
12
+ attribute: nil,
13
+ value: nil,
14
+ options: options.except(:type),
15
+ label: nil,
16
+ for: nil,
17
+ size:
18
+ )
11
19
  end
12
20
  end
13
21
  end
@@ -2,4 +2,7 @@
2
2
  - if @label
3
3
  = component('tailwinds/form/label', for: @for) do
4
4
  = @label
5
- = @input.call @attribute, **@options.merge(class: 'w-full bg-white px-3 py-2 border border-gray-300 rounded focus:outline-none focus:border-red-500 dark:placeholder-gray-400'), value: @value
5
+ - base_classes = 'w-full bg-white border border-gray-300 rounded focus:outline-none focus:border-red-500 '
6
+ - base_classes += 'dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:focus:border-red-400 dark:placeholder-white'
7
+ - classes = "#{size_class(:text_input)} #{base_classes}"
8
+ = @input.call @attribute, **@options.merge(class: classes), value: @value
@@ -2,4 +2,7 @@
2
2
  - if @label
3
3
  = component('tailwinds/form/label', for: @for) do
4
4
  = @label
5
- = @input.call @attribute, **@options.merge(class: 'w-full bg-white px-3 py-2 border border-gray-300 rounded focus:outline-none focus:border-red-500 dark:placeholder-gray-400'), value: @value
5
+ - base_classes = 'w-full bg-white border border-gray-300 rounded focus:outline-none focus:border-red-500 '
6
+ - base_classes += 'dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:focus:border-red-400 dark:placeholder-white'
7
+ - classes = "#{size_class(:text_input)} #{base_classes}"
8
+ = @input.call @attribute, **@options.merge(class: classes), value: @value
@@ -1,4 +1,4 @@
1
- .div-table-row.grid.text-white.text-small.gap-4.bg-purple-700.dark:bg-gray-700.dark:text-gray-400{ class: "grid-cols-#{headers.count}", aria: { label: "Table Header" }, role: "row" }
1
+ .div-table-row.grid.text-white.text-small.gap-4.bg-purple-700.dark:bg-gray-700.dark:text-gray-400{ class: "grid-cols-#{columns_count}", aria: { label: "Table Header" }, role: "row" }
2
2
  - if headers.any?
3
3
  - headers.each do |header|
4
4
  .div-table-cell.py-4.px-6
@@ -4,7 +4,12 @@ module Tailwinds
4
4
  module Table
5
5
  # Component for rendering a header in a table
6
6
  class HeaderComponent < Tramway::Component::Base
7
- option :headers
7
+ option :headers, optional: true, default: -> { [] }
8
+ option :columns, optional: true, default: -> { 3 }
9
+
10
+ def columns_count
11
+ headers.present? ? headers.size : columns
12
+ end
8
13
  end
9
14
  end
10
15
  end
@@ -1,5 +1,5 @@
1
1
  = helpers.component 'tailwinds/table/row/preview'
2
2
 
3
3
  - width_class = options[:class]&.include?('w-') ? '' : 'w-full'
4
- .div-table.text-left.rtl:text-right.text-gray-500.dark:text-gray-400.mt-4{ class: "#{options[:class]} #{width_class}", **options.except(:class) }
4
+ .div-table.text-left.rtl:text-right.text-gray-500.dark:text-gray-400{ class: "#{options[:class]} #{width_class}", **options.except(:class) }
5
5
  = content
@@ -4,8 +4,13 @@ module Tramway
4
4
  module Helpers
5
5
  # Provides view-oriented helpers for ActionView
6
6
  module ViewsHelper
7
- def tramway_form_for(object, *, **options, &)
8
- form_for(object, *, **options.merge(builder: Tailwinds::Form::Builder), &)
7
+ def tramway_form_for(object, *, size: :middle, **options, &)
8
+ form_for(
9
+ object,
10
+ *,
11
+ **options.merge(builder: Tailwinds::Form::Builder, size:),
12
+ &
13
+ )
9
14
  end
10
15
  end
11
16
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tramway
4
- VERSION = '0.5.4.1'
4
+ VERSION = '0.5.5'
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.5.4.1
4
+ version: 0.5.5
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-07-11 00:00:00.000000000 Z
12
+ date: 2025-10-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: anyway_config
@@ -158,6 +158,8 @@ files:
158
158
  - app/components/tailwinds/form/multiselect/selected_item_template.rb
159
159
  - app/components/tailwinds/form/multiselect_component.html.haml
160
160
  - app/components/tailwinds/form/multiselect_component.rb
161
+ - app/components/tailwinds/form/number_field_component.html.haml
162
+ - app/components/tailwinds/form/number_field_component.rb
161
163
  - app/components/tailwinds/form/select_component.html.haml
162
164
  - app/components/tailwinds/form/select_component.rb
163
165
  - app/components/tailwinds/form/submit_button_component.html.haml