para 0.12.1 → 0.12.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
});
|