para 0.12.1 → 0.12.3
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 +4 -4
- data/app/assets/stylesheets/para/admin/src/_sorting.scss +5 -2
- data/app/javascripts/para/admin/job-tracker.js +2 -2
- data/app/javascripts/para/controllers/index.js +3 -0
- data/app/javascripts/para/{inputs/nested_many.js → controllers/nested_many_input_controller.js} +70 -53
- data/app/javascripts/para/index.js +0 -4
- data/app/views/para/inputs/_nested_many.html.haml +1 -1
- data/lib/para/attribute_field/base.rb +7 -4
- data/lib/para/model_field_parsers/relations.rb +36 -27
- data/lib/para/version.rb +1 -1
- metadata +4 -6
- data/app/javascripts/para/inputs/material-input.js +0 -7
- data/app/javascripts/para/lib/page-loading.js +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2e8f3dd97232094cbf6840ad5ec462068c43019dec82f3c872d48bb5f0d8ffa
|
4
|
+
data.tar.gz: 2b3d6cd3dbfa31daef6cdca4e3e040843f10e350f02c9687dec51f1c641de12c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c5d0d01332d290463ac6b3c856e2260ee62625ceb9a9e76ea0887f13188ce7df691b23be95e40b20b271a19c5650975844944c6bf1f50262b8677eaa7189d86
|
7
|
+
data.tar.gz: 29ad0aba0dc5ac675f601e8891641aa28e38649550202a03858c46a9703e2a257a52e09bf5ee21ce74f6ca21e877c141888f081ffdba6b88c69fb1bafa4cadd3
|
@@ -31,12 +31,15 @@
|
|
31
31
|
content: "\f0dc";
|
32
32
|
opacity: .3;
|
33
33
|
}
|
34
|
+
|
34
35
|
.sort_link.asc:before {
|
35
|
-
content: "\
|
36
|
+
content: "\f0d8";
|
36
37
|
}
|
38
|
+
|
37
39
|
.sort_link.desc:before {
|
38
|
-
content: "\
|
40
|
+
content: "\f0d7";
|
39
41
|
}
|
42
|
+
|
40
43
|
.sort_link.asc.disabled:before,
|
41
44
|
.sort_link.desc.disabled:before {
|
42
45
|
opacity: .1;
|
@@ -22,11 +22,11 @@ function trackProgressFor(element, jobStatusUrl) {
|
|
22
22
|
}
|
23
23
|
|
24
24
|
document.documentElement.addEventListener('turbo:frame-load', function(e) {
|
25
|
-
if (e.target.id
|
25
|
+
if (e.target.id !== 'para_admin_modal') return;
|
26
26
|
|
27
27
|
loadedElement = e.target.childNodes[0];
|
28
28
|
|
29
|
-
var jobStatusUrl = loadedElement
|
29
|
+
var jobStatusUrl = loadedElement?.dataset?.jobStatusUrl;
|
30
30
|
if (!jobStatusUrl) return;
|
31
31
|
|
32
32
|
trackProgressFor(loadedElement, jobStatusUrl);
|
@@ -8,3 +8,6 @@ application.register("para-admin-flash-message", ParaAdminFlashMessageController
|
|
8
8
|
|
9
9
|
import SelectizeFieldController from "./selectize_field_controller";
|
10
10
|
application.register("selectize-field", SelectizeFieldController);
|
11
|
+
|
12
|
+
import NestedManyInputController from "./nested_many_input_controller";
|
13
|
+
application.register("nested-many-input", NestedManyInputController);
|
data/app/javascripts/para/{inputs/nested_many.js → controllers/nested_many_input_controller.js}
RENAMED
@@ -1,52 +1,70 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
connect() {
|
3
5
|
this.stoppingPropagation = this.stoppingPropagation.bind(this);
|
4
6
|
this.afterInsertField = this.afterInsertField.bind(this);
|
5
7
|
this.beforeRemoveField = this.beforeRemoveField.bind(this);
|
6
|
-
|
8
|
+
this.handleOrderingUpdated = this.handleOrderingUpdated.bind(this);
|
7
9
|
this.afterRemoveField = this.afterRemoveField.bind(this);
|
8
10
|
this.collapseShown = this.collapseShown.bind(this);
|
9
|
-
|
11
|
+
|
12
|
+
this.$field = $(this.element);
|
10
13
|
this.$fieldsList = this.$field.find('.fields-list');
|
11
|
-
this.
|
14
|
+
this.initializeSortable();
|
12
15
|
this.initializeCocoon();
|
13
16
|
this.$field.on('shown.bs.collapse', this.stoppingPropagation(this.collapseShown));
|
14
17
|
}
|
15
18
|
|
16
|
-
|
17
|
-
this.
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
initializeSortable() {
|
20
|
+
this.isSortable = this.$field.hasClass('orderable');
|
21
|
+
|
22
|
+
if (!this.isSortable) return;
|
23
|
+
|
21
24
|
return this.$fieldsList.sortable({
|
22
25
|
handle: '.order-anchor',
|
23
26
|
animation: 150,
|
24
|
-
onUpdate:
|
27
|
+
onUpdate: this.handleOrderingUpdated
|
25
28
|
});
|
26
29
|
}
|
27
30
|
|
28
31
|
handleOrderingUpdated() {
|
29
32
|
var formFields;
|
30
33
|
formFields = [];
|
34
|
+
|
31
35
|
return this.$fieldsList.find('.form-fields:visible').each(function(_i, el) {
|
32
|
-
|
36
|
+
let $parent, isNestedField, j, len;
|
37
|
+
|
33
38
|
for (j = 0, len = formFields.length; j < len; j++) {
|
34
39
|
$parent = formFields[j];
|
35
40
|
isNestedField = $parent.find(el).length;
|
36
41
|
}
|
42
|
+
|
37
43
|
if (isNestedField) {
|
38
44
|
return;
|
39
45
|
}
|
40
|
-
|
46
|
+
|
47
|
+
const $el = $(el);
|
41
48
|
$el.find('.resource-position-field:eq(0)').val(formFields.length);
|
42
49
|
return formFields.push($el);
|
43
50
|
});
|
44
51
|
}
|
45
52
|
|
46
53
|
initializeCocoon() {
|
47
|
-
this.$fieldsList.on(
|
48
|
-
|
49
|
-
|
54
|
+
this.$fieldsList.on(
|
55
|
+
'cocoon:after-insert',
|
56
|
+
this.stoppingPropagation(this.afterInsertField)
|
57
|
+
);
|
58
|
+
|
59
|
+
this.$fieldsList.on(
|
60
|
+
'cocoon:before-remove',
|
61
|
+
this.stoppingPropagation(this.beforeRemoveField)
|
62
|
+
);
|
63
|
+
|
64
|
+
return this.$fieldsList.on(
|
65
|
+
'cocoon:after-remove',
|
66
|
+
this.stoppingPropagation(this.afterRemoveField)
|
67
|
+
);
|
50
68
|
}
|
51
69
|
|
52
70
|
stoppingPropagation(callback) {
|
@@ -61,9 +79,9 @@ Para.NestedManyField = class NestedManyField {
|
|
61
79
|
if (($collapsible = $element.find('[data-open-on-insert="true"]')).length) {
|
62
80
|
this.openInsertedField($collapsible);
|
63
81
|
}
|
64
|
-
if (this.
|
82
|
+
if (this.isSortable) {
|
65
83
|
this.$fieldsList.sortable('destroy');
|
66
|
-
this.
|
84
|
+
this.initializeSortable();
|
67
85
|
this.handleOrderingUpdated();
|
68
86
|
}
|
69
87
|
return $element.simpleForm();
|
@@ -90,22 +108,24 @@ Para.NestedManyField = class NestedManyField {
|
|
90
108
|
}
|
91
109
|
|
92
110
|
collapseShown(e) {
|
93
|
-
|
94
|
-
|
95
|
-
if (
|
96
|
-
return this.initializeCollapseContent(
|
111
|
+
const { target } = e;
|
112
|
+
|
113
|
+
if (target.dataset.isRendered) {
|
114
|
+
return this.initializeCollapseContent(target);
|
97
115
|
} else {
|
98
|
-
this.loadCollapseContent(
|
99
|
-
return this.scrollToTarget(
|
116
|
+
this.loadCollapseContent(target);
|
117
|
+
return this.scrollToTarget(target);
|
100
118
|
}
|
101
119
|
}
|
102
120
|
|
103
|
-
initializeCollapseContent(
|
104
|
-
this.scrollToTarget(
|
105
|
-
return this.
|
121
|
+
initializeCollapseContent(target) {
|
122
|
+
this.scrollToTarget(target);
|
123
|
+
return this.focusFirstVisibleInputInside(target);
|
106
124
|
}
|
107
125
|
|
108
|
-
scrollToTarget(
|
126
|
+
scrollToTarget(target) {
|
127
|
+
const $target = $(target);
|
128
|
+
|
109
129
|
var $affixNavTabs, $field, scrollOffset;
|
110
130
|
$field = this.$field.find(`[data-toggle='collapse'][href='#${$target.attr('id')}']`);
|
111
131
|
scrollOffset = -($('[data-header]').outerHeight() + 30);
|
@@ -117,35 +137,32 @@ Para.NestedManyField = class NestedManyField {
|
|
117
137
|
});
|
118
138
|
}
|
119
139
|
|
120
|
-
|
121
|
-
|
140
|
+
focusFirstVisibleInputInside(target) {
|
141
|
+
setTimeout(() => {
|
142
|
+
target.querySelector('input:not([type="hidden"]), textarea, select')?.focus();
|
143
|
+
}, 100);
|
122
144
|
}
|
123
145
|
|
124
|
-
loadCollapseContent(
|
125
|
-
|
126
|
-
targetUrl =
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
data = {
|
131
|
-
|
132
|
-
|
133
|
-
|
146
|
+
loadCollapseContent(target) {
|
147
|
+
const $target = $(target);
|
148
|
+
const targetUrl = target.dataset.renderPath;
|
149
|
+
|
150
|
+
if (!targetUrl) return;
|
151
|
+
|
152
|
+
const data = {
|
153
|
+
id: target.dataset.id,
|
154
|
+
object_name: target.dataset.objectName,
|
155
|
+
model_name: target.dataset.modelName
|
134
156
|
};
|
135
|
-
|
136
|
-
|
137
|
-
$content = $(resp);
|
138
|
-
$target.find(
|
157
|
+
|
158
|
+
$.get(targetUrl, data).then((resp) => {
|
159
|
+
const $content = $(resp);
|
160
|
+
$target.find('[data-nested-form-container]:eq(0)').html($content);
|
139
161
|
$content.simpleForm();
|
140
|
-
|
141
|
-
|
162
|
+
|
163
|
+
this.focusFirstVisibleInputInside(target);
|
164
|
+
|
165
|
+
target.dataset.isRendered = true;
|
142
166
|
});
|
143
167
|
}
|
144
|
-
|
145
168
|
};
|
146
|
-
|
147
|
-
$.simpleForm.onDomReady(function($document) {
|
148
|
-
return $document.find('.nested-many-field').each(function(i, el) {
|
149
|
-
return new Para.NestedManyField($(el));
|
150
|
-
});
|
151
|
-
});
|
@@ -14,11 +14,7 @@ import "./vendor/jquery.sortable";
|
|
14
14
|
|
15
15
|
import "./application";
|
16
16
|
|
17
|
-
import "./lib/page-loading";
|
18
|
-
|
19
|
-
import "./inputs/material-input";
|
20
17
|
import "./inputs/multi-select-input";
|
21
|
-
import "./inputs/nested_many";
|
22
18
|
|
23
19
|
import "./admin/async-progress";
|
24
20
|
import "./admin/filters-form";
|
@@ -1,4 +1,4 @@
|
|
1
|
-
.nested-many-field{ class: [('orderable' if orderable), ('nested-many-field-inset' if inset)] }
|
1
|
+
.nested-many-field{ class: [('orderable' if orderable), ('nested-many-field-inset' if inset)], data: { controller: "nested-many-input" } }
|
2
2
|
.fields-list{ id: dom_identifier }
|
3
3
|
= form.simple_fields_for attribute_name, resources, nested_attribute_name: attribute_name, orderable: orderable, track_attribute_mappings: render_partial do |nested_form|
|
4
4
|
= render partial: find_partial_for(model, 'nested_many/container', partial_dir: 'inputs'), locals: { form: nested_form, model: nested_form.object.class, subclass: subclass, allow_destroy_if: allow_destroy_if, nested_locals: nested_locals, inset: inset, uncollapsed: uncollapsed, render_partial: render_partial, remote_partial_params: remote_partial_params }
|
@@ -56,6 +56,10 @@ module Para
|
|
56
56
|
|
57
57
|
reference = model.reflect_on_all_associations.find do |association|
|
58
58
|
association.foreign_key == name
|
59
|
+
rescue ArgumentError
|
60
|
+
# This can happen when the association is polymorphic and the foreign key can't
|
61
|
+
# be determined, in this case, we just ignore the association.
|
62
|
+
false
|
59
63
|
end
|
60
64
|
|
61
65
|
if reference
|
@@ -75,12 +79,11 @@ module Para
|
|
75
79
|
true
|
76
80
|
end
|
77
81
|
|
78
|
-
#
|
79
82
|
def searchable?
|
80
83
|
options[:searchable] != false && (
|
81
|
-
[
|
84
|
+
%i[string text].include?(type.to_sym) && !name.match(/password/)
|
82
85
|
) && (
|
83
|
-
!model.respond_to?(:ransackable_attributes) ||
|
86
|
+
!model.respond_to?(:ransackable_attributes) ||
|
84
87
|
model.ransackable_attributes.include?(name.to_s)
|
85
88
|
)
|
86
89
|
end
|
@@ -94,7 +97,7 @@ module Para
|
|
94
97
|
def field_options
|
95
98
|
self.class._field_options.each_with_object({}) do |params, hash|
|
96
99
|
value = send(params[:method_name])
|
97
|
-
hash[params[:key]] = value if value
|
100
|
+
hash[params[:key]] = value if !value.nil? || params[:options][:allow_nil]
|
98
101
|
end
|
99
102
|
end
|
100
103
|
|
@@ -42,8 +42,10 @@ module Para
|
|
42
42
|
# attributes mappings above
|
43
43
|
next if AttributeField::RelationField == fields_hash[name]
|
44
44
|
|
45
|
-
|
46
|
-
|
45
|
+
unless through_polymorphic_reflection?(reflection)
|
46
|
+
# Remove foreign key, if existing, from fields
|
47
|
+
fields_hash.delete(reflection.foreign_key.to_s)
|
48
|
+
end
|
47
49
|
|
48
50
|
# Do not process polymorphic belongs to for now ...
|
49
51
|
if reflection.options[:polymorphic] == true
|
@@ -52,27 +54,25 @@ module Para
|
|
52
54
|
end
|
53
55
|
|
54
56
|
if model.nested_attributes_options[name]
|
55
|
-
if reflection.collection?
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
remove_counter_cache_column!(name, reflection)
|
57
|
+
fields_hash[name] = if reflection.collection?
|
58
|
+
AttributeField::NestedManyField.new(
|
59
|
+
model, name: name, type: 'has_many', field_type: 'nested_many'
|
60
|
+
)
|
61
|
+
else
|
62
|
+
AttributeField::NestedOneField.new(
|
63
|
+
model, name: name, type: 'belongs_to', field_type: 'nested_one'
|
64
|
+
)
|
65
|
+
end
|
66
|
+
elsif reflection.collection?
|
67
|
+
remove_counter_cache_column!(name, reflection)
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
69
|
+
fields_hash[name] = AttributeField::HasManyField.new(
|
70
|
+
model, name: name, type: 'has_many', field_type: 'multi_select'
|
71
|
+
)
|
72
|
+
elsif !reflection.options[:through]
|
73
|
+
fields_hash[name] = AttributeField::BelongsToField.new(
|
74
|
+
model, name: name, type: 'belongs_to', field_type: 'selectize'
|
75
|
+
)
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
@@ -84,14 +84,23 @@ module Para
|
|
84
84
|
return unless (inverse_relation = reflection.inverse_of)
|
85
85
|
return unless (counter_name = inverse_relation.options[:counter_cache])
|
86
86
|
|
87
|
-
counter_name = if String
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
87
|
+
counter_name = if counter_name.is_a?(String)
|
88
|
+
counter_name
|
89
|
+
else
|
90
|
+
"#{name}_count"
|
91
|
+
end
|
92
92
|
|
93
93
|
fields_hash.delete(counter_name)
|
94
94
|
end
|
95
|
+
|
96
|
+
def through_polymorphic_reflection?(reflection)
|
97
|
+
reflection.through_reflection? && (
|
98
|
+
(
|
99
|
+
reflection.through_reflection.options[:polymorphic] &&
|
100
|
+
!reflection.through_reflection.options[:source_type]
|
101
|
+
) || through_polymorphic_reflection?(reflection.through_reflection)
|
102
|
+
)
|
103
|
+
end
|
95
104
|
end
|
96
105
|
end
|
97
106
|
end
|
data/lib/para/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: para
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.12.
|
4
|
+
version: 0.12.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Valentin Ballestrino
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_decorator
|
@@ -479,15 +479,13 @@ files:
|
|
479
479
|
- app/javascripts/para/application.js
|
480
480
|
- app/javascripts/para/controllers/application.js
|
481
481
|
- app/javascripts/para/controllers/index.js
|
482
|
+
- app/javascripts/para/controllers/nested_many_input_controller.js
|
482
483
|
- app/javascripts/para/controllers/para_admin_flash_message_controller.js
|
483
484
|
- app/javascripts/para/controllers/para_admin_modal_controller.js
|
484
485
|
- app/javascripts/para/controllers/selectize_field_controller.js
|
485
486
|
- app/javascripts/para/index.js
|
486
|
-
- app/javascripts/para/inputs/material-input.js
|
487
487
|
- app/javascripts/para/inputs/multi-select-input.js
|
488
|
-
- app/javascripts/para/inputs/nested_many.js
|
489
488
|
- app/javascripts/para/lib/fetch.js
|
490
|
-
- app/javascripts/para/lib/page-loading.js
|
491
489
|
- app/javascripts/para/plugins-includes.js.erb
|
492
490
|
- app/javascripts/para/simple_form_extension/colorpicker.js
|
493
491
|
- app/javascripts/para/simple_form_extension/datetimepicker.js
|
@@ -794,7 +792,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
794
792
|
- !ruby/object:Gem::Version
|
795
793
|
version: '0'
|
796
794
|
requirements: []
|
797
|
-
rubygems_version: 3.
|
795
|
+
rubygems_version: 3.5.17
|
798
796
|
signing_key:
|
799
797
|
specification_version: 4
|
800
798
|
summary: Rails admin engine
|
@@ -1,7 +0,0 @@
|
|
1
|
-
$(document).on('page:change turbo:load turbo:frame-load', function() {
|
2
|
-
return $('body').on('focusin focusout', 'input, select, textarea', function(e) {
|
3
|
-
var focused;
|
4
|
-
focused = e.type === 'focusin';
|
5
|
-
return $(e.target).closest('.form-group').toggleClass('focused', focused);
|
6
|
-
});
|
7
|
-
});
|
@@ -1,42 +0,0 @@
|
|
1
|
-
Para.PageLoading = class PageLoading {
|
2
|
-
constructor() {
|
3
|
-
this.start = this.start.bind(this);
|
4
|
-
this.stop = this.stop.bind(this);
|
5
|
-
}
|
6
|
-
|
7
|
-
start() {
|
8
|
-
return this.addLoadingMarkup();
|
9
|
-
}
|
10
|
-
|
11
|
-
stop() {
|
12
|
-
return this.removeLoadingMarkup();
|
13
|
-
}
|
14
|
-
|
15
|
-
addLoadingMarkup() {
|
16
|
-
$('<div/>', {
|
17
|
-
class: 'loading-overlay',
|
18
|
-
'data-loading-overlay': true
|
19
|
-
}).prependTo('body');
|
20
|
-
|
21
|
-
$('<div/>', {
|
22
|
-
class: 'loading-spinner',
|
23
|
-
'data-loading-spinner': true
|
24
|
-
}).prependTo('body');
|
25
|
-
}
|
26
|
-
|
27
|
-
removeLoadingMarkup() {
|
28
|
-
$('[data-loading-overlay]').remove();
|
29
|
-
return $('[data-loading-spinner]').remove();
|
30
|
-
}
|
31
|
-
|
32
|
-
};
|
33
|
-
|
34
|
-
// Global loading manager allowing to
|
35
|
-
Para.loadingManager = new Para.PageLoading();
|
36
|
-
|
37
|
-
$(document).on('turbo:before-fetch-request', Para.loadingManager.start);
|
38
|
-
|
39
|
-
$(document).on('turbo:load turbo:frame-load turbo:before-stream-render turbo:frame-missing turbo:fetch-request-error', function() {
|
40
|
-
Para.loadingManager.stop();
|
41
|
-
return $('body').on('submit', '[data-para-form]:not([data-remote])', Para.loadingManager.start);
|
42
|
-
});
|