tramway 0.5.0.1 → 0.5.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: 7a8b21470fd862c9a0fdbe1cb424c16826f572ca667609c01c04840420ade2fb
4
- data.tar.gz: eeb39f02b824342bd217b79b35ae80f4f87e5246d5e656495fd55d00fba59efa
3
+ metadata.gz: fbd54b0a803c53d12e50ec585a805ca39dc56b0551e12fa2de66514a9166b750
4
+ data.tar.gz: 7db6cfe3b24afaed951ac2ca2b52eb65d38bf0bdb49d6625f8508fa2dc594e52
5
5
  SHA512:
6
- metadata.gz: 40e0bfb98b80c7716120e793a1e1c0391176eff8f9a90ead5394a38c940177aa730292492ae9d65400eb1d0114d5e48de196347d93dfd294c8511938e094b264
7
- data.tar.gz: cbbd06e1b3d829a2a6aefa2983b06aa9abc7731909b7d32b706652b93d48fae25aaeafa32b726d012b565ead0dd076194bdebe285f122b4e063d82dca81af434
6
+ metadata.gz: a4d807fffc40d7e2cdfd9fec0b513f39f4a4338cd9ea1718d4ece1d0587bc1d86aa26112688ec9fb5e108ed1a04fac01f7f79ae7852f105fb132e4b9361550a3
7
+ data.tar.gz: 45ac2aec489c85b212938ae99456fe8e5fc54f804cc9525aee1f034c5586d0e080e3c58fd9593f3779aabce5f2c5941ff36198183e81880f23f5d3a4b871aa98
data/README.md CHANGED
@@ -486,6 +486,13 @@ eagerLoadControllersFrom("controllers", application)
486
486
  application.register('multiselect', Multiselect) // register Multiselect controller class as `multiselect` stimulus controller
487
487
  ```
488
488
 
489
+ Use Stimulus `change` action with Tramway Multiselect
490
+
491
+ ```ruby
492
+ = tramway_form_for @user do |f|
493
+ = f.multiselect :role, data: { action: 'change->user-form#updateForm' } # user-form is your Stimulus controller, updateForm is a method inside user-form Stimulus controller
494
+ ```
495
+
489
496
  ### Tailwind-styled pagination for Kaminari
490
497
 
491
498
  Tramway uses [Tailwind](https://tailwindcss.com/) by default. It has tailwind-styled pagination for [kaminari](https://github.com/kaminari/kaminari).
@@ -12,7 +12,8 @@ export default class Multiselect extends Controller {
12
12
  selectedItems: Array,
13
13
  placeholder: String,
14
14
  selectAsInput: String,
15
- value: Array
15
+ value: Array,
16
+ onChange: String
16
17
  }
17
18
 
18
19
  connect() {
@@ -26,9 +27,14 @@ export default class Multiselect extends Controller {
26
27
  }
27
28
  });
28
29
 
29
- const initialValues = this.element.dataset.value === undefined ? [] : this.element.dataset.value.split(',')
30
- this.selectedItems = this.items.filter(item => initialValues.includes(item.value));
31
- this.items = this.items.filter(item => !initialValues.includes(item.value));
30
+ const initialValues = this.element.dataset.value === undefined ? [] : JSON.parse(this.element.dataset.value);
31
+
32
+ initialValues.map((value) => {
33
+ const itemIndex = this.items.findIndex(x => x.value.toString() === value.toString());
34
+ this.items[itemIndex].selected = true;
35
+ })
36
+
37
+ this.selectedItems = this.items.filter(item => item.selected);
32
38
 
33
39
  this.renderSelectedItems();
34
40
  }
@@ -84,6 +90,23 @@ export default class Multiselect extends Controller {
84
90
  if (this.dropdown()) {
85
91
  this.dropdown().remove();
86
92
  }
93
+
94
+ const onChange = this.element.dataset.onChange;
95
+
96
+ if (onChange) {
97
+ const [controllerName, actionName] = onChange.split('#');
98
+ const controller = this.application.controllers.find(controller => controller.identifier === controllerName)
99
+
100
+ if (controller) {
101
+ if (typeof controller[actionName] === 'function') {
102
+ controller[actionName]({ target: this.element });
103
+ } else {
104
+ alert(`Action not found: ${actionName}`); // eslint-disable-line no-undef
105
+ }
106
+ } else {
107
+ alert(`Controller not found: ${controllerName}`); // eslint-disable-line no-undef
108
+ }
109
+ }
87
110
  }
88
111
 
89
112
  get template() {
@@ -38,7 +38,7 @@ module Tailwinds
38
38
  def multiselect(attribute, collection, **options, &)
39
39
  render(Tailwinds::Form::MultiselectComponent.new(
40
40
  input: input(:text_field),
41
- value: options[:value] || options[:selected] || object.public_send(attribute)&.first,
41
+ value: options[:value] || options[:selected] || object.public_send(attribute),
42
42
  collection:,
43
43
  **default_options(attribute, options)
44
44
  ), &)
@@ -15,12 +15,15 @@ module Tailwinds
15
15
  def multiselect_hash
16
16
  {
17
17
  controller:, selected_item_template:, multiselect_selected_items_value:, dropdown_container:, item_container:,
18
- items:, action:, select_as_input:, placeholder:, value:
18
+ items:, action:, select_as_input:, placeholder:, value:, on_change:
19
19
  }.transform_keys { |key| key.to_s.gsub('_', '-') }
20
20
  end
21
21
 
22
22
  def controller
23
- :multiselect
23
+ controllers = [:multiselect]
24
+ controllers << external_action.split('->').last.split('#').first if external_action
25
+ controllers += external_controllers
26
+ controllers.join(' ')
24
27
  end
25
28
 
26
29
  private
@@ -45,6 +48,20 @@ module Tailwinds
45
48
  render(Tailwinds::Form::Multiselect::SelectAsInput.new(options:, attribute:, input:))
46
49
  end
47
50
 
51
+ def on_change
52
+ return unless external_action&.start_with?('change')
53
+
54
+ external_action.split('->').last
55
+ end
56
+
57
+ def external_controllers
58
+ options[:controller]&.split || []
59
+ end
60
+
61
+ def external_action
62
+ options.dig(:data, :action)
63
+ end
64
+
48
65
  def method_missing(method_name, *, &)
49
66
  component = component_name(method_name)
50
67
 
@@ -1,4 +1,4 @@
1
- %nav.py-4.px-4.sm:px-8.flex.justify-between.items-center.dark:bg-gray-800{ class: "bg-#{@color}" }
1
+ %nav.py-2.px-4.sm:px-8.flex.justify-between.items-center.dark:bg-gray-800{ class: "bg-#{@color}" }
2
2
  .flex.justify-between.w-full
3
3
  - if @title[:text].present? || @left_items.present?
4
4
  .flex.items-center
@@ -7,16 +7,16 @@
7
7
  .text-xl.text-white.font-bold
8
8
  = @title[:text]
9
9
  - if @left_items.present?
10
- %ul.hidden.sm:flex.items-center.space-x-4.ml-4
10
+ %ul.block.flex.flex-row.items-center.space-x-4.ml-4
11
11
  - @left_items.each do |item|
12
12
  = item
13
13
 
14
- .block.sm:hidden
14
+ .hidden.sm:block
15
15
  %button#mobile-menu-button.text-white.focus:outline-none
16
16
 
17
17
 
18
18
  - if @right_items.present?
19
- %ul.hidden.sm:flex.items-center.space-x-4
19
+ %ul.block.sm:flex.items-center.space-x-4
20
20
  - @right_items.each do |item|
21
21
  = item
22
22
 
@@ -1,3 +1,10 @@
1
1
  - unless current_page.first?
2
- = link_to helpers.t('views.pagination.first').html_safe, url, remote:, class: pagination_classes(klass: 'first hidden sm:flex')
3
- = link_to '⭰', url, remote:, class: pagination_classes(klass: 'first sm:hidden font-bold')
2
+ = link_to helpers.t('views.pagination.first').html_safe,
3
+ url,
4
+ remote:,
5
+ class: pagination_classes(klass: 'first sm:hidden flex')
6
+
7
+ = link_to '⭰',
8
+ url,
9
+ remote:,
10
+ class: pagination_classes(klass: 'first hidden sm:flex font-bold')
@@ -1,2 +1,2 @@
1
- %span.page.gap.px-3.py-2.text-sm.font-medium.text-purple-700.dark:text-white.sm:flex.hidden
1
+ %span.page.gap.px-3.py-2.text-sm.font-medium.text-purple-700.dark:text-white.flex.sm:hidden
2
2
  = helpers.t('views.pagination.truncate').html_safe
@@ -1,3 +1,10 @@
1
1
  - unless current_page.last?
2
- = link_to helpers.t('views.pagination.last').html_safe, url, remote:, class: pagination_classes(klass: 'last hidden sm:flex')
3
- = link_to '⭲', url, remote:, class: pagination_classes(klass: 'last sm:hidden font-bold')
2
+ = link_to helpers.t('views.pagination.last').html_safe,
3
+ url,
4
+ remote:,
5
+ class: pagination_classes(klass: 'last sm:hidden flex')
6
+
7
+ = link_to '⭲',
8
+ url,
9
+ remote:,
10
+ class: pagination_classes(klass: 'last hidden sm:flex font-bold')
@@ -1,3 +1,12 @@
1
1
  - unless current_page.last?
2
- = link_to helpers.t('views.pagination.next').html_safe, url, rel: 'next', remote:, class: pagination_classes(klass: 'next hidden sm:flex')
3
- = link_to '🠖', url, rel: 'next', remote:, class: pagination_classes(klass: 'next sm:hidden font-bold')
2
+ = link_to helpers.t('views.pagination.next').html_safe,
3
+ url,
4
+ rel: 'next',
5
+ remote:,
6
+ class: pagination_classes(klass: 'next sm:hidden flex')
7
+
8
+ = link_to '🠖',
9
+ url,
10
+ rel: 'next',
11
+ remote:,
12
+ class: pagination_classes(klass: 'next hidden sm:flex font-bold')
@@ -2,4 +2,8 @@
2
2
  %span.px-3.py-2.font-medium.rounded-md.bg-purple-500.text-white.dark:text-gray-800.dark:bg-white
3
3
  = page
4
4
  - else
5
- = link_to page, url, remote:, rel: page.rel, class: pagination_classes(klass: 'hidden sm:flex')
5
+ = link_to page,
6
+ url,
7
+ remote:,
8
+ rel: page.rel,
9
+ class: pagination_classes(klass: 'sm:hidden flex')
@@ -1,3 +1,12 @@
1
1
  - unless current_page.first?
2
- = link_to helpers.t('views.pagination.previous').html_safe, url, rel: 'prev', remote:, class: pagination_classes(klass: 'prev hidden sm:flex')
3
- = link_to '🠔', url, rel: 'prev', remote:, class: pagination_classes(klass: 'prev sm:hidden font-bold')
2
+ = link_to helpers.t('views.pagination.previous').html_safe,
3
+ url,
4
+ rel: 'prev',
5
+ remote:,
6
+ class: pagination_classes(klass: 'prev sm:hidden flex')
7
+
8
+ = link_to '🠔',
9
+ url,
10
+ rel: 'prev',
11
+ remote:,
12
+ class: pagination_classes(klass: 'prev hidden sm:flex font-bold')
@@ -1,6 +1,6 @@
1
1
  - cols = headers.map { |item| "1fr" }.join(",")
2
2
 
3
- .div-table-row.hidden.md:grid.text-white.text-small.gap-4.bg-purple-700.dark:bg-gray-700.dark:text-gray-400{ class: "grid-cols-[#{cols}]" }
3
+ .div-table-row.block.grid.text-white.text-small.gap-4.bg-purple-700.dark:bg-gray-700.dark:text-gray-400{ class: "grid-cols-[#{cols}]" }
4
4
  - headers.each do |header|
5
5
  .div-table-cell.py-4.px-6
6
6
  = header
@@ -2,12 +2,14 @@
2
2
  - if cells.any?
3
3
  - cols = cells.count.times.map { |item| "1fr" }.join(",")
4
4
 
5
- = row_tag class: "div-table-row hidden md:grid gap-4 bg-white border-b dark:bg-gray-800 dark:border-gray-700 grid-cols-[#{cols}]" do
5
+ -# desktop view
6
+ = row_tag class: "div-table-row block grid gap-4 bg-white border-b dark:bg-gray-800 dark:border-gray-700 grid-cols-[#{cols}]" do
6
7
  - cells.each do |(_, value)|
7
- .div-table-cell.px-6.py-4.font-medium.text-gray-900.whitespace-nowrap.dark:text-white.text-xs.sm:text-base
8
+ .div-table-cell.px-6.py-4.font-medium.text-gray-900.whitespace-nowrap.dark:text-white.sm:text-xs.text-base
8
9
  = value
9
10
 
10
- .div-table-row.border-b.dark:bg-gray-800.dark:border-gray-700.md:hidden.mb-2{ "data-action" => "click->preview#toggle", "data-controller" => "preview", "data-items" => cells.to_json }
11
+ -# mobile view
12
+ .div-table-row.xl:hidden.border-b.dark:bg-gray-800.dark:border-gray-700.mb-2{ "data-action" => "click->preview#toggle", "data-controller" => "preview", "data-items" => cells.to_json }
11
13
  .w-full.p-4.bg-purple-100.text-gray-700.dark:bg-gray-700.dark:text-gray-400
12
14
  = cells.values.first
13
15
 
@@ -9,7 +9,7 @@ module Tailwinds
9
9
 
10
10
  def row_tag(**options, &)
11
11
  if href.present?
12
- klass = "#{options[:class] || ''} cursor-pointer hover:bg-gray-700"
12
+ klass = "#{options[:class] || ''} cursor-pointer dark:hover:bg-gray-700"
13
13
 
14
14
  link_to(href, options.merge(class: klass)) do
15
15
  yield if block_given?
@@ -1,3 +1,6 @@
1
+ - decorator = Tramway::Decorators::NameBuilder.default_decorator_class_name(@model_class)
2
+ - list_attributes = decorator.constantize.list_attributes
3
+
1
4
  .w-full
2
5
  - content_for :title, page_title
3
6
 
@@ -8,13 +11,13 @@
8
11
  - if Tramway.config.pagination[:enabled]
9
12
  = paginate @entities
10
13
 
11
- - if decorator_class(@entities).list_attributes.empty?
14
+ - if list_attributes.empty?
12
15
  %p.text-center.mt-10
13
16
  You should fill class-level method `self.list_attributes` inside your
14
- = decorator_class_name(@entities)
17
+ = decorator
15
18
 
16
19
  = component 'tailwinds/table' do
17
- = component 'tailwinds/table/header', headers: decorator_class(@entities).list_attributes.map { |attribute| @model_class.human_attribute_name(attribute) }
20
+ = component 'tailwinds/table/header', headers: list_attributes.map { |attribute| @model_class.human_attribute_name(attribute) }
18
21
  - @entities.each do |item|
19
22
  = render 'entity', entity: item
20
23
 
@@ -7,15 +7,15 @@ module Tramway
7
7
  module_function
8
8
 
9
9
  def decorator_class(object_or_array, decorator = nil)
10
- if decorator.present?
11
- decorator
12
- else
13
- begin
14
- class_name = decorator_class_name(object_or_array)
15
- class_name.constantize
16
- rescue NameError
17
- raise NameError, "You should define #{class_name} decorator class."
18
- end
10
+ raise_error_if_object_empty object_or_array, decorator
11
+
12
+ return decorator if decorator.present?
13
+
14
+ begin
15
+ class_name = decorator_class_name(object_or_array)
16
+ class_name.constantize
17
+ rescue NameError
18
+ raise NameError, "You should define #{class_name} decorator class."
19
19
  end
20
20
  end
21
21
 
@@ -28,6 +28,16 @@ module Tramway
28
28
 
29
29
  Tramway::Decorators::NameBuilder.default_decorator_class_name(klass)
30
30
  end
31
+
32
+ # :reek:NilCheck { enabled: false }
33
+ def raise_error_if_object_empty(object_or_array, decorator)
34
+ return unless object_or_array.blank? && decorator.nil?
35
+
36
+ text = 'You should pass object or array that is not empty OR provide a decorator class as a second argument'
37
+
38
+ raise ArgumentError, text
39
+ end
40
+ # :reek:NilCheck { enabled: true }
31
41
  end
32
42
  end
33
43
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tramway
4
- VERSION = '0.5.0.1'
4
+ VERSION = '0.5.1'
5
5
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tramway
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0.1
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - kalashnikovisme
8
8
  - moshiaan
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-12-09 00:00:00.000000000 Z
12
+ date: 2025-01-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: anyway_config
@@ -213,7 +213,7 @@ metadata:
213
213
  homepage_uri: https://github.com/purple-magic/tramway
214
214
  source_code_uri: https://github.com/purple-magic/tramway
215
215
  changelog_uri: https://github.com/purple-magic/tramway
216
- post_install_message:
216
+ post_install_message:
217
217
  rdoc_options: []
218
218
  require_paths:
219
219
  - lib
@@ -229,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
229
229
  version: '0'
230
230
  requirements: []
231
231
  rubygems_version: 3.4.6
232
- signing_key:
232
+ signing_key:
233
233
  specification_version: 4
234
234
  summary: Tramway Rails Engine
235
235
  test_files: []