base_editing_bootstrap 1.13.0 → 1.15.0

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +37 -5
  4. data/app/assets/config/base_editing_bootstrap_manifest.js +1 -0
  5. data/app/assets/javascripts/nested_form_controller.js +48 -0
  6. data/app/controllers/base_editing_controller.rb +11 -0
  7. data/app/helpers/utilities/form_helper.rb +7 -3
  8. data/app/helpers/utilities/template_helper.rb +11 -3
  9. data/app/policies/base_model_policy.rb +21 -1
  10. data/app/views/base_editing/_form_field.html.erb +2 -3
  11. data/app/views/base_editing/_form_full_components.html.erb +2 -2
  12. data/app/views/base_editing/_nested_row_form.html.erb +1 -1
  13. data/app/views/base_editing/_nested_row_form_readonly.html.erb +8 -0
  14. data/app/views/base_editing/form_field/_accept_has_many_nested_field.html.erb +21 -1
  15. data/app/views/base_editing/form_field/_accept_has_many_nested_field_readonly.html.erb +22 -0
  16. data/app/views/base_editing/form_field/_accept_has_one_nested_field_readonly.html.erb +4 -0
  17. data/app/views/base_editing/form_field/_base_readonly.html.erb +8 -0
  18. data/app/views/base_editing/form_field/_belongs_to_select.html.erb +2 -2
  19. data/app/views/base_editing/form_field/_belongs_to_select_readonly.html.erb +6 -0
  20. data/app/views/base_editing/form_field/_boolean.html.erb +1 -1
  21. data/app/views/base_editing/form_field/_boolean_readonly.html.erb +2 -0
  22. data/app/views/base_editing/form_field/_date_readonly.html.erb +2 -0
  23. data/app/views/base_editing/form_field/_datetime_readonly.html.erb +2 -0
  24. data/app/views/base_editing/form_field/_decimal_readonly.html.erb +2 -0
  25. data/app/views/base_editing/form_field/_enum_readonly.html.erb +2 -0
  26. data/app/views/base_editing/form_field/_has_one_attachment.html.erb +16 -12
  27. data/app/views/base_editing/form_field/_has_one_attachment_readonly.html.erb +12 -0
  28. data/app/views/base_editing/form_field/_integer_readonly.html.erb +2 -0
  29. data/app/views/base_editing/form_field/_textarea_readonly.html.erb +7 -0
  30. data/config/importmap.rb +2 -0
  31. data/lib/base_editing_bootstrap/VERSION +1 -1
  32. data/lib/base_editing_bootstrap/engine.rb +8 -0
  33. data/lib/generators/base_editing_bootstrap/field_override/field_override_generator.rb +5 -2
  34. data/lib/generators/base_editing_bootstrap/install/install_generator.rb +3 -3
  35. data/spec/support/external_shared/pundit.rb +1 -0
  36. metadata +17 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03c295a72d7902af261c7c01405cc45a7b81d243c846f60c1974f4e07e5e648e
4
- data.tar.gz: 5281b24d0df35c81db386217c763e266bf29affa07ca33c34632527981f5be5b
3
+ metadata.gz: d69ef1800e2eece675666b76837ae189f47c6f054031c12990a4b36181fca951
4
+ data.tar.gz: e345a8d8545068e50128afe6e55d212fe5f6e87555309898fb72ed0add5e0d69
5
5
  SHA512:
6
- metadata.gz: cdd4c188cef1aba14fadc79888e24fbdf3dafdbcc204ec933a1558ef5fbbed0255f36924c4e327c244e8176e777452077404a3c0c23b3ebeb1c869af62bf9ae3
7
- data.tar.gz: 74c856a196196dd319d42446c474d5ba5449cefb2d01a7333d3e20bc2b4bcbb1273777b01363381e52a0fc8abbdd9bc9249a8cd5da4cd8dc60d2e470408994f3
6
+ metadata.gz: 37e059fd372aeb9ef27780523d13d336e4815c6afb8792990b6a375f1c60a1dfb951d48ea2231045a2715de8022138eb8732dfaa313b2e06d77ed23e1a4ee731
7
+ data.tar.gz: 4e8778eefe88514f17133dab5c96d5d8eaad61cb71bdf82b5ace11226af0f737a8e4ff15dd1609efd7555610aeaaec8e4783175103f4ad2e0323acd69ee6e2dd
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
  All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
3
3
 
4
4
  - - -
5
+ ## 1.15.0 - 2026-04-09
6
+ #### Features
7
+ - Add Read only mode (#25) - (0ba3da2) - Marino Bonetti
8
+
9
+ - - -
10
+
11
+ ## 1.14.0 - 2026-03-19
12
+ #### Features
13
+ - Nested elements with limit from rails config (#24) - (f003ce4) - Marino Bonetti
14
+ #### Bug Fixes
15
+ - Correct nested_form when elements are marked for destruction - (9fd6544) - Marino Bonetti
16
+
17
+ - - -
18
+
5
19
  ## 1.13.0 - 2026-02-03
6
20
  #### Features
7
21
  - Form submit button inheritance value - (00b04b1) - Marino Bonetti
data/README.md CHANGED
@@ -35,16 +35,34 @@ $ bundle exec rails g base_editing_bootstrap:install
35
35
 
36
36
  **Si presume quindi che ActiveStorage sia correttamente installato, completo del javascript per il direct upload**
37
37
 
38
+ ### Upgrade Notes
39
+ - From <= 1.13:
40
+ In `app/javascript/controllers/application.js` replace:
41
+ ```js
42
+ import RailsNestedForm from '@stimulus-components/rails-nested-form'
43
+ application.register('nested-form', RailsNestedForm)
44
+ ```
45
+ with:
46
+ ```js
47
+ import NestedForm from 'nested_form_controller';
48
+ application.register('nested-form', NestedForm);
49
+ ```
50
+ in `config/importmap.rb` remove:
51
+ ```ruby
52
+ pin "@stimulus-components/rails-nested-form"
53
+ ```
54
+ and finaly remove in vendor/javascript the `@stimulus-components/rails-nested-form` downloaded dependencies
55
+
38
56
  ### Note for NestedAttributes
39
57
 
40
- Seguire le istruzioni per installare anche NestedAttributeForm Controller per stimulus:
58
+ Il controller nested attributes viene importato da importmaps dell'engine.
59
+ E' necessagio registrare il nuovo controller in app/javascript/application.js
41
60
 
42
- ```shell
43
- bin/importmap pin @stimulus-components/rails-nested-form
61
+ ```js
62
+ import NestedForm from 'nested_form_controller'; // Mappato con import maps locale
63
+ application.register('nested-form', NestedForm); // Utilizzato nell'interfaccia della form.
44
64
  ```
45
65
 
46
- e seguire installazione https://www.stimulus-components.com/docs/stimulus-rails-nested-form
47
-
48
66
  ### Generators
49
67
 
50
68
  Then Install dependency (if you run base_editing_bootstrap:install you are good to go):
@@ -274,6 +292,20 @@ Note:
274
292
 
275
293
  Fai riferimento all'implementazione di esempio del dummy `Company->addresses`
276
294
 
295
+ ### ReadOnly
296
+ E' possibile renderizzare i campi di un modello in sola lettura, andando a ridefinire nella policy
297
+ il metodo `attribute_is_readonly(attribute_name)`.
298
+ Per ogni campo abbiamo la versione NOME_CAMPO_readonly.html.erb che viene renderizzato quando il metodo
299
+ precedente restituisce true.
300
+ I nested attributes verranno renderizzati rispetto a quanto definito dal
301
+ campo padre.
302
+ La ricerca dei partial avviene nella stessa modalità dei campi sopra definiti.
303
+ Al generator per i campi standard basta aggiungere il parametro `--readonly` per avere la versione NOME_CAMPO_readonly.html.erb:
304
+ ```shell
305
+ rails g base_editing_bootstrap:field_override ModelName field1 field2:type --readonly
306
+ ```
307
+
308
+
277
309
  ### Translations
278
310
 
279
311
  #### Index buttons:
@@ -0,0 +1 @@
1
+ //= link nested_form_controller.js
@@ -0,0 +1,48 @@
1
+ import NestedForm from "@stimulus-components/rails-nested-form"
2
+
3
+ // Connects to data-controller="nested-form"
4
+ export default class extends NestedForm {
5
+
6
+ static values = {
7
+ limit: Number,
8
+ }
9
+
10
+ add(e) {
11
+ super.add(e);
12
+ this.check_for_enabling_add_button(e);
13
+ }
14
+
15
+ remove(e) {
16
+ super.remove(e);
17
+ this.check_for_enabling_add_button(e);
18
+ }
19
+
20
+ // Elenco elementi presenti visibili (e quindi non settati per essere cancellati)
21
+ total_active_elements() {
22
+ let count = 0;
23
+
24
+ this.targetTarget.parentElement.querySelectorAll(this.wrapperSelectorValue).forEach((ele) => {
25
+ if (ele.checkVisibility()) {
26
+ count += 1;
27
+ }
28
+ })
29
+ return count;
30
+ }
31
+
32
+ check_for_enabling_add_button(e) {
33
+ if (this.hasLimitValue) {
34
+ if (this.total_active_elements() >= this.limitValue) {
35
+ // Aggiungiamo classe disable sull'elemento che ha lanciato questo evento
36
+ this.add_button().disabled = "disabled";
37
+ } else {
38
+ this.add_button().removeAttribute("disabled");
39
+ }
40
+ }
41
+ }
42
+
43
+ add_button() {
44
+ return this.element.querySelector('[data-action="nested-form#add"]')
45
+ }
46
+
47
+
48
+ }
@@ -5,6 +5,7 @@ class BaseEditingController < RestrictedAreaController
5
5
  :edit_custom_polymorphic_path,
6
6
  :form_attributes,
7
7
  :form_builder,
8
+ :readonly_attribute?,
8
9
  :index_custom_polymorphic_path,
9
10
  :new_custom_polymorphic_path,
10
11
  :show_custom_polymorphic_path
@@ -131,6 +132,16 @@ class BaseEditingController < RestrictedAreaController
131
132
  policy.public_send(method_name)
132
133
  end
133
134
 
135
+ def readonly_attribute?(attribute, model = base_class.new, action = override_pundit_action_name)
136
+ policy = policy(model)
137
+ method_name = if policy.respond_to?("attribute_is_readonly_for_#{action}")
138
+ "attribute_is_readonly_for_#{action}"
139
+ else
140
+ "attribute_is_readonly"
141
+ end
142
+ policy.public_send(method_name, attribute)
143
+ end
144
+
134
145
  def load_object
135
146
  @object = base_class.find(params[:id])
136
147
 
@@ -19,10 +19,13 @@ module Utilities
19
19
  #
20
20
  # @param [Forms::Base] form
21
21
  # @param [Symbol] field
22
- def form_print_field(form, field)
22
+ # @param [Boolean] readonly -> rende possibile nelle nested form, nel caso arrivi da un field padre che definisce
23
+ # il campo come readonly di non controllare nemmeno la policy(il padre ha priorità su figlio)
24
+ # @return [ActiveSupport::SafeBuffer]
25
+ def form_print_field(form, field, readonly: nil)
23
26
  locals = {form:, field:}
24
27
  if form.object.class.respond_to?(:field_to_form_partial) and (generic_field = form.object.class.field_to_form_partial(field))
25
- type= :custom
28
+ type = :custom
26
29
  elsif form.object.class.respond_to?(:defined_enums) && form.object.class.defined_enums.key?(field.to_s)
27
30
  type = :enum
28
31
  generic_field = "enum"
@@ -89,7 +92,8 @@ module Utilities
89
92
  form.object,
90
93
  field,
91
94
  "form_field",
92
- generic_field
95
+ generic_field,
96
+ readonly: (readonly.nil? ? readonly_attribute?(field, form.object) : readonly)
93
97
  )
94
98
  bs_logger.debug do
95
99
  <<~TEXT
@@ -12,7 +12,8 @@ module Utilities::TemplateHelper
12
12
  # @param [Symbol] field
13
13
  # @param [String] base_path
14
14
  # @param [String] generic_field
15
- def find_template_with_fallbacks(obj, field, base_path, generic_field)
15
+ # @param [Boolean] readonly #aggiunge nella ricerca del template la versione readonly
16
+ def find_template_with_fallbacks(obj, field, base_path, generic_field, readonly: false)
16
17
  # nei casi in cui passiamo la classe e non l'oggetto, dobbiamo utilizzare un metodo interno a rails per
17
18
  # avere la partial_path
18
19
 
@@ -31,7 +32,7 @@ module Utilities::TemplateHelper
31
32
  end
32
33
 
33
34
  bs_logger.tagged(field) do
34
- [
35
+ casistiche = [
35
36
  # Precedenza modello e campo specifico
36
37
  ["Campo SPECIFICO + inheritance tra modelli", field, obj_base_paths],
37
38
  # cerco tramite nome modello semplice, con namespace della risorsa (cell_field,header_field,form_field) e nome del campo specifico
@@ -42,7 +43,14 @@ module Utilities::TemplateHelper
42
43
  ["Campo GENERICO + inheritance controllers", "#{base_path}/#{generic_field}", lookup_context.prefixes],
43
44
  ["Campo GENERICO + inheritance tra modelli", generic_field, obj_base_paths],
44
45
  ["Default BaseEditingController", "base_editing/#{base_path}/#{generic_field}", []],
45
- ].each do |desc, partial, prefixes|
46
+ ]
47
+ # In caso di readonly andremo a ricercare solamente la versione di quel tipo
48
+ if readonly
49
+ casistiche = casistiche.collect do |desc, partial, prefixes|
50
+ [desc, "#{partial}_readonly", prefixes]
51
+ end
52
+ end
53
+ casistiche.each do |desc, partial, prefixes|
46
54
  bs_logger.debug { "#{desc} - partial:`#{partial}` in #{prefixes.inspect}" }
47
55
  if lookup_context.exists?(partial, prefixes, true)
48
56
  return lookup_context.find(partial, prefixes, true)
@@ -9,13 +9,33 @@ class BaseModelPolicy < ApplicationPolicy
9
9
 
10
10
  def show? = general_rule
11
11
 
12
- # Questo metodo può essere anche scritto specifico per azione:
12
+ # Questo metodo può essere richiamato specifico per azione:
13
13
  # - permitted_attributes_for_create
14
14
  # - permitted_attributes_for_update
15
+ # - permitted_attributes_for_ACTION_NAME
16
+ # Quindi nella policy possiamo differenziare le due situazioni
15
17
  def permitted_attributes = []
16
18
 
19
+ # Questo metodo può essere richiamato specifico per azione:
20
+ # - editable_attributes_for_create
21
+ # - editable_attributes_for_update
22
+ # - editable_attributes_for_ACTION_NAME
23
+ # Quindi nella policy possiamo differenziare le due situazioni
17
24
  def editable_attributes = []
18
25
 
26
+ ##
27
+ # Permette di specificare se un attributo è di sola lettura durante il rendering della form.
28
+ # Oltre alla versione standard è possibile definire il metodo specificando il tipo di azione con il
29
+ # medesimo formato utilizzato negli altri metodi:
30
+ # - attribute_is_readonly_for_create?
31
+ # - attribute_is_readonly_for_update?
32
+ # - attribute_is_readonly_for_ACTION_NAME?
33
+ #
34
+ # @param attribute [Symbol] nome dell'attributo
35
+ # @param action_name [String] nome dell'azione
36
+ # @return [Boolean] true se l'attributo è di sola lettura, false altrimenti
37
+ def attribute_is_readonly(_attribute) = false
38
+
19
39
  def permitted_attributes_for_ransack
20
40
  record.class.column_names + record.class._ransackers.keys
21
41
  end
@@ -4,11 +4,10 @@
4
4
  # - form -> FormBuilder
5
5
  # - form_field -> String
6
6
  %>
7
- <%# locals: (form:, form_field:) -%>
7
+ <%# locals: (form:, form_field:,readonly:nil) -%>
8
8
  <%
9
9
  input_group_classes = ["mb-1"]
10
-
11
- content = form_print_field(form, form_field)
10
+ content = form_print_field(form, form_field, readonly: readonly)
12
11
  unless content.match "checkbox"
13
12
  input_group_classes << "input-group"
14
13
  end
@@ -2,13 +2,13 @@
2
2
  Questo partial è il raggruppamento di tutti gli elementi tranne il footer del form.
3
3
  Serve per riutilizzarlo anche nel nested attributes come base per costruire tutti gli elementi
4
4
  %>
5
- <%# locals: (form:) -%>
5
+ <%# locals: (form:,readonly:nil) -%>
6
6
  <%= render layout: "form_body_container" do %>
7
7
  <%= render partial: "form_field_header", locals: {form:} %>
8
8
  <%= render partial: "form_base_errors", locals: {form:} if form.object.errors.key?(:base) %>
9
9
  <%= render layout: "form_fields_container" do %>
10
10
  <%= render collection: form_attributes(form.object),
11
11
  layout: "form_field_container",
12
- partial: "form_field", locals: {form:} %>
12
+ partial: "form_field", locals: {form:, readonly: readonly} %>
13
13
  <% end %>
14
14
  <% end %>
@@ -1,4 +1,4 @@
1
- <tr class="nested-form-wrapper" data-new-record="<%= form.object.new_record? %>" id="<%= dom_id(form.object) %>">
1
+ <tr class="nested-form-wrapper <%= (form.object.marked_for_destruction? ? "d-none" : "") %>" data-new-record="<%= form.object.new_record? %>" id="<%= dom_id(form.object) %>">
2
2
 
3
3
  <% policy(form.object).editable_attributes.each do |field| %>
4
4
  <td>
@@ -0,0 +1,8 @@
1
+ <tr id="<%= dom_id(form.object) %>">
2
+ <% policy(form.object).editable_attributes.each do |field| %>
3
+ <td>
4
+ <%= form_print_field(form, field, readonly: true) %>
5
+ <%= error_messages_for(form.object, field) %>
6
+ </td>
7
+ <% end %>
8
+ </tr>
@@ -1,5 +1,25 @@
1
1
  <%# locals: (form:, field:,new_object:) -%>
2
- <%= content_tag :table, class: "table", data: {controller: 'nested-form'} do %>
2
+ <%
3
+
4
+ ##
5
+ # Costruisco le opzioni per limitare tramite interfaccia (ricevuta da configurazione di Rails nested_attributes_for)
6
+ # il numero di nested elements
7
+ controller_data = {controller: 'nested-form'}
8
+ if (limit = form.object.class.nested_attributes_options[field].dig(:limit))
9
+ controller_data["nested-form-limit-value"] = \
10
+ case limit
11
+ when Symbol #TODO add tests
12
+ form.object.class.send(limit)
13
+ when Proc #TODO add tests
14
+ limit.call
15
+ else
16
+ limit
17
+ end
18
+ end
19
+
20
+ %>
21
+
22
+ <%= content_tag :table, class: "table", data: controller_data do %>
3
23
 
4
24
  <thead>
5
25
  <tr>
@@ -0,0 +1,22 @@
1
+ <%# locals: (form:, field:,new_object:) -%>
2
+ <%= content_tag :table, class: "table read-only" do %>
3
+ <thead>
4
+ <tr>
5
+ <% policy(new_object).editable_attributes.each do |field| %>
6
+ <th>
7
+ <%= new_object.class.human_attribute_name(field) %>
8
+ </th>
9
+ <% end %>
10
+ </tr>
11
+ </thead>
12
+
13
+ <tbody>
14
+
15
+ <%= form.fields_for field do |form_for_sections| %>
16
+ <%= render "nested_row_form_readonly", form: form_for_sections %>
17
+ <% end %>
18
+
19
+ </tbody>
20
+
21
+ <% end %>
22
+
@@ -0,0 +1,4 @@
1
+ <%# locals: (form:, field:) -%>
2
+ <%= form.fields_for field do |form_for_sections| %>
3
+ <%= render partial: "form_full_components", locals: {form: form_for_sections, readonly: true} %>
4
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%
2
+ ##
3
+ # Template per il rendering del vero campo input
4
+ # eseguire override nei casi in cui si voglia renderizzare qualcosa di differente
5
+ %>
6
+ <%# locals: (form:, field:) -%>
7
+ <%= form.text_field(field, readonly: true, disabled: true, name: "READONLY") %>
8
+
@@ -1,6 +1,6 @@
1
1
  <%# locals: (form:, field:,relation_class:,foreign_key:,value_method: :id, label_method: :option_label) -%>
2
2
  <%= form.select(foreign_key, options_from_collection_for_select(policy_scope(relation_class),
3
- value_method, label_method,
4
- selected: form.object.send(foreign_key)),
3
+ value_method, label_method,
4
+ selected: form.object.send(foreign_key)),
5
5
  include_blank: true
6
6
  ) %>
@@ -0,0 +1,6 @@
1
+ <%# locals: (form:, field:,relation_class:,foreign_key:,value_method: :id, label_method: :option_label) -%>
2
+ <%= form.select(foreign_key, options_from_collection_for_select(policy_scope(relation_class),
3
+ value_method, label_method,
4
+ selected: form.object.send(foreign_key)),
5
+ {include_blank: true}, {disabled: true, readonly: true, name: "READONLY"}
6
+ ) %>
@@ -1,2 +1,2 @@
1
1
  <%# locals: (form:, field:) -%>
2
- <%= form.switch_box(field,label: field) %>
2
+ <%= form.switch_box(field, label: field) %>
@@ -0,0 +1,2 @@
1
+ <%# locals: (form:, field:) -%>
2
+ <%= form.switch_box(field, label: field, disabled: true, name: "READONLY") %>
@@ -0,0 +1,2 @@
1
+ <%# locals: (form:, field:) -%>
2
+ <%= form.date_field(field, disabled: true, readonly: true, name: "READONLY") %>
@@ -0,0 +1,2 @@
1
+ <%# locals: (form:, field:) -%>
2
+ <%= form.datetime_field(field, disabled: true, readonly: true, name: "READONLY") %>
@@ -0,0 +1,2 @@
1
+ <%# locals: (form:, field:, scale:2) -%>
2
+ <%= form.number_field(field, step: "0.#{"0" * (scale - 1)}1", disabled: true, readonly: true, name: "READONLY") %>
@@ -0,0 +1,2 @@
1
+ <%# locals: (form:, field:) -%>
2
+ <%= form.select(field, enum_collection(form.object.class, field, :form_field), {}, {disabled: true, readonly: true, name: "READONLY"}) %>
@@ -13,19 +13,23 @@
13
13
  JAVASCRIPT
14
14
 
15
15
  %>
16
- <%= form.hidden_field field, value: form.object.send(field).signed_id, id: hidden_field_id if is_attached %>
16
+ <div class="d-flex">
17
+ <%= form.hidden_field field, value: form.object.send(field).signed_id, id: hidden_field_id if is_attached %>
17
18
 
18
- <%= form.file_field field, direct_upload: true %>
19
+ <%= form.file_field field, direct_upload: true %>
19
20
 
20
- <% if is_attached %>
21
- <%= content_tag :span, form.object.send(field).attachment.blob.filename, class: "input-group-text", id: filename_id %>
22
- <%= content_tag :button, icon(:trash),
23
- onclick: javascript_clear_event,
24
- class: "btn btn-outline-secondary rounded-0" %>
25
- <%= link_to icon(:download), form.object.send(field), class: "btn btn-outline-secondary", target: :_blank %>
26
- <% if form.object.send(field).representable? %>
27
- <% content_for :form_field_ending,flush:true do %>
28
- <%= content_tag :div, image_tag(form.object.send(field).representation(resize_to_limit: [100, 100])), id: preview_image_id %>
21
+ <% if is_attached %>
22
+
23
+ <%= content_tag :span, form.object.send(field).attachment.blob.filename, class: "input-group-text flex-grow-1", id: filename_id %>
24
+ <%= content_tag :button, icon(:trash),
25
+ onclick: javascript_clear_event,
26
+ class: "btn btn-outline-secondary rounded-0" %>
27
+ <%= link_to icon(:download), form.object.send(field), class: "btn btn-outline-secondary", target: :_blank %>
28
+
29
+ <% if form.object.send(field).representable? %>
30
+ <% content_for :form_field_ending, flush: true do %>
31
+ <%= content_tag :div, image_tag(form.object.send(field).representation(resize_to_limit: [100, 100])), id: preview_image_id %>
32
+ <% end %>
29
33
  <% end %>
30
34
  <% end %>
31
- <% end %>
35
+ </div>
@@ -0,0 +1,12 @@
1
+ <%# locals: (form:, field:) -%>
2
+ <% if form.object.send(field).attached? %>
3
+ <div class="d-flex">
4
+ <%= content_tag :span, form.object.send(field).attachment.blob.filename, class: "input-group-text flex-grow-1" %>
5
+ <%= link_to icon(:download), form.object.send(field), class: "btn btn-outline-secondary ms-1", target: :_blank %>
6
+ </div>
7
+ <% if form.object.send(field).representable? %>
8
+ <% content_for :form_field_ending, flush: true do %>
9
+ <%= content_tag :div, image_tag(form.object.send(field).representation(resize_to_limit: [100, 100])) %>
10
+ <% end %>
11
+ <% end %>
12
+ <% end %>
@@ -0,0 +1,2 @@
1
+ <%# locals: (form:, field:) -%>
2
+ <%= form.number_field(field, step: "1", readonly: true) %>
@@ -0,0 +1,7 @@
1
+ <%
2
+ ##
3
+ # Template per il rendering del vero campo input
4
+ # eseguire override nei casi in cui si voglia renderizzare qualcosa di differente
5
+ %>
6
+ <%# locals: (form:, field:) -%>
7
+ <%= form.text_area(field, readonly: true) %>
@@ -0,0 +1,2 @@
1
+ pin "@stimulus-components/rails-nested-form", to: "https://ga.jspm.io/npm:@stimulus-components/rails-nested-form@5.0.0/dist/stimulus-rails-nested-form.mjs" # @5.0.0
2
+ pin_all_from File.expand_path("../app/assets/javascripts", __dir__)
@@ -1 +1 @@
1
- 1.13.0
1
+ 1.15.0
@@ -5,5 +5,13 @@ module BaseEditingBootstrap
5
5
  app.deprecators[:base_editing_bootstrap] = BaseEditingBootstrap.deprecator
6
6
  end
7
7
 
8
+ initializer "base_editing_bootstrap.importmap", before: "importmap" do |app|
9
+ app.config.importmap.paths << Engine.root.join("config/importmap.rb")
10
+ end
11
+
12
+ initializer "base_editing_bootstrap.assets.precompile" do |app|
13
+ app.config.assets.precompile << "nested_form_controller.js"
14
+ end
15
+
8
16
  end
9
17
  end
@@ -6,7 +6,8 @@ module BaseEditingBootstrap
6
6
  include BaseEditingBootstrap::GeneratorsHelpers
7
7
  source_root File.expand_path("../../../../app/views/base_editing", __dir__)
8
8
  argument :name, type: :string, banner: "Post", required: true
9
- argument :attributes, type: :array, default: [], banner: "field field:type"
9
+ argument :attributes, type: :array, default: [], banner: "field field[:type]"
10
+ class_option :readonly, type: :boolean, default: false, banner: "--readonly"
10
11
 
11
12
  TYPES = %i[base date datetime decimal integer enum boolean]
12
13
 
@@ -24,13 +25,15 @@ module BaseEditingBootstrap
24
25
 
25
26
  base_path = class_to_view_path(name)
26
27
 
28
+ readonly_suffix = options[:readonly] ? "_readonly" : ""
29
+
27
30
  attributes.each do |a|
28
31
  attr_name, type = a.split(":")
29
32
 
30
33
  type = :base if type.nil?
31
34
  type = type.to_sym
32
35
  raise "Type #{type} not found in #{TYPES}" unless TYPES.include?(type)
33
- copy_file "form_field/_#{type}.html.erb", File.join(*base_path,"form_field", "_#{attr_name}.html.erb")
36
+ copy_file "form_field/_#{type}#{readonly_suffix}.html.erb", File.join(*base_path,"form_field", "_#{attr_name}#{readonly_suffix}.html.erb")
34
37
  end
35
38
  end
36
39
 
@@ -35,9 +35,9 @@ module BaseEditingBootstrap
35
35
  # attualmente penso sia più sensato semplicemente scrivere a video i passaggi necessari, dato che
36
36
  # potrebbe essere già presente importmap, nested_attribute_controller e le varie configurazioni
37
37
 
38
- say "Install dependencies for nested attributes:"
39
- say " bin/importmap pin @stimulus-components/rails-nested-form"
40
- say "Attiva quindi come spiegato qua: https://www.stimulus-components.com/docs/stimulus-rails-nested-form il controller"
38
+ say "Install nested attributes controller, add to application.js:"
39
+ say " import NestedForm from 'nested_form_controller' \n
40
+ application.register('nested-form', NestedForm)"
41
41
 
42
42
  end
43
43
  end
@@ -28,6 +28,7 @@ RSpec.shared_examples "a standard base model policy" do |factory, check_default_
28
28
  [:permitted_scopes_for_ransack],
29
29
  [:editable_attributes],
30
30
  [:permitted_attributes],
31
+ [:attribute_is_readonly],
31
32
  ]
32
33
  end
33
34
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: base_editing_bootstrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.0
4
+ version: 1.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marino Bonetti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-03 00:00:00.000000000 Z
11
+ date: 2026-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -320,6 +320,7 @@ files:
320
320
  - README.md
321
321
  - Rakefile
322
322
  - app/assets/config/base_editing_bootstrap_manifest.js
323
+ - app/assets/javascripts/nested_form_controller.js
323
324
  - app/controllers/.keep
324
325
  - app/controllers/base_editing_controller.rb
325
326
  - app/controllers/restricted_area_controller.rb
@@ -352,6 +353,7 @@ files:
352
353
  - app/views/base_editing/_index_title_header.html.erb
353
354
  - app/views/base_editing/_navbar.html.erb
354
355
  - app/views/base_editing/_nested_row_form.html.erb
356
+ - app/views/base_editing/_nested_row_form_readonly.html.erb
355
357
  - app/views/base_editing/_new_page_title_header.html.erb
356
358
  - app/views/base_editing/_search.html.erb
357
359
  - app/views/base_editing/_search_field.erb
@@ -366,17 +368,29 @@ files:
366
368
  - app/views/base_editing/cell_field/_timestamps.html.erb
367
369
  - app/views/base_editing/edit.html.erb
368
370
  - app/views/base_editing/form_field/_accept_has_many_nested_field.html.erb
371
+ - app/views/base_editing/form_field/_accept_has_many_nested_field_readonly.html.erb
369
372
  - app/views/base_editing/form_field/_accept_has_one_nested_field.html.erb
373
+ - app/views/base_editing/form_field/_accept_has_one_nested_field_readonly.html.erb
370
374
  - app/views/base_editing/form_field/_base.html.erb
375
+ - app/views/base_editing/form_field/_base_readonly.html.erb
371
376
  - app/views/base_editing/form_field/_belongs_to_select.html.erb
377
+ - app/views/base_editing/form_field/_belongs_to_select_readonly.html.erb
372
378
  - app/views/base_editing/form_field/_boolean.html.erb
379
+ - app/views/base_editing/form_field/_boolean_readonly.html.erb
373
380
  - app/views/base_editing/form_field/_date.html.erb
381
+ - app/views/base_editing/form_field/_date_readonly.html.erb
374
382
  - app/views/base_editing/form_field/_datetime.html.erb
383
+ - app/views/base_editing/form_field/_datetime_readonly.html.erb
375
384
  - app/views/base_editing/form_field/_decimal.html.erb
385
+ - app/views/base_editing/form_field/_decimal_readonly.html.erb
376
386
  - app/views/base_editing/form_field/_enum.html.erb
387
+ - app/views/base_editing/form_field/_enum_readonly.html.erb
377
388
  - app/views/base_editing/form_field/_has_one_attachment.html.erb
389
+ - app/views/base_editing/form_field/_has_one_attachment_readonly.html.erb
378
390
  - app/views/base_editing/form_field/_integer.html.erb
391
+ - app/views/base_editing/form_field/_integer_readonly.html.erb
379
392
  - app/views/base_editing/form_field/_textarea.html.erb
393
+ - app/views/base_editing/form_field/_textarea_readonly.html.erb
380
394
  - app/views/base_editing/header_field/_base.html.erb
381
395
  - app/views/base_editing/index.html.erb
382
396
  - app/views/base_editing/new.html.erb
@@ -389,6 +403,7 @@ files:
389
403
  - app/views/kaminari/_paginator.html.erb
390
404
  - app/views/kaminari/_prev_page.html.erb
391
405
  - base_editing_bootstrap.gemspec
406
+ - config/importmap.rb
392
407
  - config/initializers/base_field_error_proc.rb
393
408
  - config/locales/it.yml
394
409
  - config/routes.rb