tramway 0.5.0.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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: []