effective_bootstrap 0.9.23 → 0.9.28
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/images/icons/grip-lines.svg +1 -0
- data/app/assets/images/icons/reorder.svg +10 -0
- data/app/assets/javascripts/effective_bootstrap.js +1 -0
- data/app/assets/javascripts/effective_bootstrap/tabs.js +7 -0
- data/app/assets/javascripts/effective_has_many/initialize.js.coffee +10 -9
- data/app/assets/javascripts/effective_has_many/input.js +2 -1
- data/app/assets/javascripts/effective_has_many/sortable-jquery.js +76 -0
- data/app/assets/javascripts/effective_has_many/sortable.js +3722 -0
- data/app/assets/javascripts/effective_rich_text_area/extensions.js +171 -0
- data/app/assets/javascripts/effective_rich_text_area/initialize.js +20 -0
- data/app/assets/javascripts/effective_rich_text_area/input.js +2 -0
- data/app/assets/stylesheets/effective_has_many/input.scss +5 -30
- data/app/assets/stylesheets/effective_rich_text_area/extensions.scss +78 -0
- data/app/assets/stylesheets/effective_rich_text_area/input.scss +2 -25
- data/app/assets/stylesheets/effective_rich_text_area/rich_text_area.scss +25 -0
- data/app/models/effective/form_inputs/has_many.rb +30 -8
- data/app/views/action_text/attachables/_content_attachment.html.erb +3 -0
- data/app/views/action_text/attachables/content_attachments/_horizontal_rule.html.erb +1 -0
- data/lib/effective_bootstrap/engine.rb +6 -0
- data/lib/effective_bootstrap/version.rb +1 -1
- metadata +14 -4
- data/app/assets/javascripts/effective_has_many/jquery.sortable.js +0 -696
@@ -0,0 +1,171 @@
|
|
1
|
+
// https://github.com/lazaronixon/trix-extensions/blob/master/app/javascript/richtext.js
|
2
|
+
|
3
|
+
if(window.Trix) {
|
4
|
+
|
5
|
+
addHeadingAttributes()
|
6
|
+
addForegroundColorAttributes()
|
7
|
+
addBackgroundColorAttributes()
|
8
|
+
|
9
|
+
addEventListener('trix-initialize', function (event) { new RichText(event.target) })
|
10
|
+
|
11
|
+
addEventListener('trix-action-invoke', function (event) {
|
12
|
+
if (event.actionName == 'x-horizontal-rule') insertHorizontalRule()
|
13
|
+
|
14
|
+
function insertHorizontalRule() {
|
15
|
+
event.target.editor.insertAttachment(buildHorizontalRule())
|
16
|
+
}
|
17
|
+
|
18
|
+
function buildHorizontalRule() {
|
19
|
+
return new Trix.Attachment({ content: '<hr>', contentType: 'vnd.rubyonrails.horizontal-rule.html' })
|
20
|
+
}
|
21
|
+
})
|
22
|
+
|
23
|
+
class RichText {
|
24
|
+
constructor(element) {
|
25
|
+
this.element = element
|
26
|
+
|
27
|
+
this.insertHeadingElements()
|
28
|
+
this.insertDividerElements()
|
29
|
+
this.insertColorElements()
|
30
|
+
}
|
31
|
+
|
32
|
+
insertHeadingElements() {
|
33
|
+
this.removeOriginalHeadingButton()
|
34
|
+
this.insertNewHeadingButton()
|
35
|
+
this.insertHeadingDialog()
|
36
|
+
}
|
37
|
+
|
38
|
+
removeOriginalHeadingButton() {
|
39
|
+
this.buttonGroupBlockTools.removeChild(this.originalHeadingButton)
|
40
|
+
}
|
41
|
+
|
42
|
+
insertNewHeadingButton() {
|
43
|
+
this.buttonGroupBlockTools.insertAdjacentHTML("afterbegin", this.headingButtonTemplate)
|
44
|
+
}
|
45
|
+
|
46
|
+
insertHeadingDialog() {
|
47
|
+
this.dialogsElement.insertAdjacentHTML("beforeend", this.dialogHeadingTemplate)
|
48
|
+
}
|
49
|
+
|
50
|
+
insertDividerElements() {
|
51
|
+
this.quoteButton.insertAdjacentHTML("afterend", this.horizontalButtonTemplate)
|
52
|
+
}
|
53
|
+
|
54
|
+
insertColorElements() {
|
55
|
+
this.insertColorButton()
|
56
|
+
this.insertDialogColor()
|
57
|
+
}
|
58
|
+
|
59
|
+
insertColorButton() {
|
60
|
+
this.buttonGroupTextTools.insertAdjacentHTML("beforeend", this.colorButtonTemplate)
|
61
|
+
}
|
62
|
+
|
63
|
+
insertDialogColor() {
|
64
|
+
this.dialogsElement.insertAdjacentHTML("beforeend", this.dialogColorTemplate)
|
65
|
+
}
|
66
|
+
|
67
|
+
get buttonGroupBlockTools() {
|
68
|
+
return this.toolbarElement.querySelector("[data-trix-button-group=block-tools]")
|
69
|
+
}
|
70
|
+
|
71
|
+
get buttonGroupTextTools() {
|
72
|
+
return this.toolbarElement.querySelector("[data-trix-button-group=text-tools]")
|
73
|
+
}
|
74
|
+
|
75
|
+
get dialogsElement() {
|
76
|
+
return this.toolbarElement.querySelector("[data-trix-dialogs]")
|
77
|
+
}
|
78
|
+
|
79
|
+
get originalHeadingButton() {
|
80
|
+
return this.toolbarElement.querySelector("[data-trix-attribute=heading1]")
|
81
|
+
}
|
82
|
+
|
83
|
+
get quoteButton() {
|
84
|
+
return this.toolbarElement.querySelector("[data-trix-attribute=quote]")
|
85
|
+
}
|
86
|
+
|
87
|
+
get toolbarElement() {
|
88
|
+
return this.element.toolbarElement
|
89
|
+
}
|
90
|
+
|
91
|
+
get horizontalButtonTemplate() {
|
92
|
+
return '<button type="button" class="trix-button trix-button--icon trix-button--icon-horizontal-rule" data-trix-action="x-horizontal-rule" tabindex="-1" title="Divider">Divider</button>'
|
93
|
+
}
|
94
|
+
|
95
|
+
get headingButtonTemplate() {
|
96
|
+
return '<button type="button" class="trix-button trix-button--icon trix-button--icon-heading-1" data-trix-action="x-heading" title="Heading" tabindex="-1">Heading</button>'
|
97
|
+
}
|
98
|
+
|
99
|
+
get colorButtonTemplate() {
|
100
|
+
return '<button type="button" class="trix-button trix-button--icon trix-button--icon-color" data-trix-action="x-color" title="Color" tabindex="-1">Color</button>'
|
101
|
+
}
|
102
|
+
|
103
|
+
get dialogHeadingTemplate() {
|
104
|
+
return `
|
105
|
+
<div class="trix-dialog trix-dialog--heading" data-trix-dialog="x-heading" data-trix-dialog-attribute="x-heading">
|
106
|
+
<div class="trix-dialog__link-fields">
|
107
|
+
<input type="text" name="x-heading" class="trix-dialog-hidden__input" data-trix-input>
|
108
|
+
<div class="trix-button-group">
|
109
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="heading1">H1</button>
|
110
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="heading2">H2</button>
|
111
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="heading3">H3</button>
|
112
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="heading4">H4</button>
|
113
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="heading5">H5</button>
|
114
|
+
</div>
|
115
|
+
</div>
|
116
|
+
</div>
|
117
|
+
`
|
118
|
+
}
|
119
|
+
|
120
|
+
get dialogColorTemplate() {
|
121
|
+
return `
|
122
|
+
<div class="trix-dialog trix-dialog--color" data-trix-dialog="x-color" data-trix-dialog-attribute="x-color">
|
123
|
+
<div class="trix-dialog__link-fields">
|
124
|
+
<input type="text" name="x-color" class="trix-dialog-hidden__input" data-trix-input>
|
125
|
+
<div class="trix-button-group">
|
126
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="fgColor1" data-trix-method="hideDialog"></button>
|
127
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="fgColor2" data-trix-method="hideDialog"></button>
|
128
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="fgColor3" data-trix-method="hideDialog"></button>
|
129
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="fgColor4" data-trix-method="hideDialog"></button>
|
130
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="fgColor5" data-trix-method="hideDialog"></button>
|
131
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="fgColor6" data-trix-method="hideDialog"></button>
|
132
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="fgColor7" data-trix-method="hideDialog"></button>
|
133
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="fgColor8" data-trix-method="hideDialog"></button>
|
134
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="fgColor9" data-trix-method="hideDialog"></button>
|
135
|
+
</div>
|
136
|
+
<div class="trix-button-group">
|
137
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="bgColor1" data-trix-method="hideDialog"></button>
|
138
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="bgColor2" data-trix-method="hideDialog"></button>
|
139
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="bgColor3" data-trix-method="hideDialog"></button>
|
140
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="bgColor4" data-trix-method="hideDialog"></button>
|
141
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="bgColor5" data-trix-method="hideDialog"></button>
|
142
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="bgColor6" data-trix-method="hideDialog"></button>
|
143
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="bgColor7" data-trix-method="hideDialog"></button>
|
144
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="bgColor8" data-trix-method="hideDialog"></button>
|
145
|
+
<button type="button" class="trix-button trix-button--dialog" data-trix-attribute="bgColor9" data-trix-method="hideDialog"></button>
|
146
|
+
</div>
|
147
|
+
</div>
|
148
|
+
</div>
|
149
|
+
`
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
function addHeadingAttributes() {
|
154
|
+
Array.from(["h1", "h2", "h3", "h4", "h5"]).forEach((tagName, i) => {
|
155
|
+
Trix.config.blockAttributes[`heading${(i + 1)}`] = { tagName: tagName, terminal: true, breakOnReturn: true, group: false }
|
156
|
+
})
|
157
|
+
}
|
158
|
+
|
159
|
+
function addForegroundColorAttributes() {
|
160
|
+
Array.from(["rgb(136, 118, 38)", "rgb(185, 94, 6)", "rgb(207, 0, 0)", "rgb(216, 28, 170)", "rgb(144, 19, 254)", "rgb(5, 98, 185)", "rgb(17, 138, 15)", "rgb(148, 82, 22)", "rgb(102, 102, 102)"]).forEach((color, i) => {
|
161
|
+
Trix.config.textAttributes[`fgColor${(i + 1)}`] = { style: { color: color }, inheritable: true, parser: e => e.style.color == color }
|
162
|
+
})
|
163
|
+
}
|
164
|
+
|
165
|
+
function addBackgroundColorAttributes() {
|
166
|
+
Array.from(["rgb(250, 247, 133)", "rgb(255, 240, 219)", "rgb(255, 229, 229)", "rgb(255, 228, 247)", "rgb(242, 237, 255)", "rgb(225, 239, 252)", "rgb(228, 248, 226)", "rgb(238, 226, 215)", "rgb(242, 242, 242)"]).forEach((color, i) => {
|
167
|
+
Trix.config.textAttributes[`bgColor${(i + 1)}`] = { style: { backgroundColor: color }, inheritable: true, parser: e => e.style.backgroundColor == color }
|
168
|
+
})
|
169
|
+
}
|
170
|
+
|
171
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
if(window.Trix) {
|
2
|
+
|
3
|
+
window.Trix.config.blockAttributes.default.tagName = 'p';
|
4
|
+
window.Trix.config.blockAttributes.default.breakOnReturn = true;
|
5
|
+
|
6
|
+
window.Trix.Block.prototype.breaksOnReturn = function() {
|
7
|
+
const attr = this.getLastAttribute();
|
8
|
+
const config = Trix.getBlockConfig(attr ? attr : 'default');
|
9
|
+
return config ? config.breakOnReturn : false;
|
10
|
+
};
|
11
|
+
|
12
|
+
window.Trix.LineBreakInsertion.prototype.shouldInsertBlockBreak = function() {
|
13
|
+
if(this.block.hasAttributes() && this.block.isListItem() && !this.block.isEmpty()) {
|
14
|
+
return this.startLocation.offset > 0
|
15
|
+
} else {
|
16
|
+
return !this.shouldBreakFormattedBlock() ? this.breaksOnReturn : false;
|
17
|
+
}
|
18
|
+
};
|
19
|
+
|
20
|
+
}
|
@@ -1,42 +1,17 @@
|
|
1
|
-
body.dragging,
|
2
|
-
body.dragging * {
|
3
|
-
cursor: move !important;
|
4
|
-
}
|
5
|
-
|
6
1
|
.form-has-many {
|
7
|
-
.has-many-
|
8
|
-
position: relative;
|
9
|
-
height: 2rem;
|
10
|
-
|
11
|
-
&:before {
|
12
|
-
position: absolute;
|
13
|
-
content: '';
|
14
|
-
background-image: asset-data-url('icons/arrow-right-circle.svg');
|
15
|
-
background-repeat: no-repeat;
|
16
|
-
height: 2rem;
|
17
|
-
width: 2rem;
|
18
|
-
}
|
19
|
-
}
|
20
|
-
|
21
|
-
.has-many-fields.dragged {
|
22
|
-
position: absolute;
|
23
|
-
opacity: 0;
|
24
|
-
z-index: 2000;
|
25
|
-
.has-many-move { display: none; }
|
26
|
-
}
|
27
|
-
|
28
|
-
.has-many-move svg { margin-top: 6px; }
|
2
|
+
.has-many-fields.sortable-ghost { border-top: solid 3px #212529; }
|
29
3
|
.has-many-move { display: none; }
|
4
|
+
.has-many-remove-disabled { opacity: 0; cursor: default !important; }
|
30
5
|
|
31
|
-
.has-many-remove { margin-top:
|
6
|
+
.has-many-remove { margin-top: 1rem; }
|
32
7
|
.has-many-move { margin-top: 1.5rem; }
|
33
8
|
}
|
34
9
|
|
35
10
|
.form-has-many.reordering {
|
36
|
-
.has-many-move { display: inline-block; }
|
11
|
+
.has-many-move { display: inline-block; cursor: grab; }
|
37
12
|
}
|
38
13
|
|
39
14
|
.form-has-many.tight {
|
40
15
|
.has-many-remove { margin-top: 0; }
|
41
|
-
.has-many-move { margin-top: 0; }
|
16
|
+
.has-many-move { margin-top: 0.5rem; }
|
42
17
|
}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
trix-toolbar {
|
2
|
+
// http://www.asiteaboutnothing.net/c_decode-url.html
|
3
|
+
// background-image: url("data:image/svg+xml,***encoded data***");
|
4
|
+
|
5
|
+
.trix-button--icon-horizontal-rule::before { background-image: url("data:image/svg+xml,%3Csvg%20enable-background%3D%22new%200%200%2024%2024%22%20viewBox%3D%220%200%2024%2024%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20fill%3D%22%23000%22%3E%3Cpath%20d%3D%22m0%2013h24v-2h-24z%22%2F%3E%3Cpath%20d%3D%22m5%208.5h14v-3h-14z%22%2F%3E%3Cpath%20d%3D%22m5%2018.5h14v-3h-14z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"); }
|
6
|
+
.trix-button--icon-color::before { background-image: url("data:image/svg+xml,%3Csvg%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20width%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22m16.56%2011.94-8.94-8.94-1.41%201.41%202.38%202.38-5.15%205.15c-.59.59-.59%201.54%200%202.12l5.5%205.5c.29.29.68.44%201.06.44s.77-.15%201.06-.44l5.5-5.5c.59-.58.59-1.53%200-2.12zm-11.35%201.06%204.79-4.79%204.79%204.79zm13.79%202.5s-2%202.17-2%203.5c0%201.1.9%202%202%202s2-.9%202-2c0-1.33-2-3.5-2-3.5z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E%0A"); }
|
7
|
+
|
8
|
+
.trix-dialog--heading { max-width: 160px; }
|
9
|
+
|
10
|
+
.trix-dialog--color {
|
11
|
+
max-width: 265px;
|
12
|
+
|
13
|
+
.trix-dialog__link-fields { flex-direction: column; }
|
14
|
+
|
15
|
+
.trix-button-group {
|
16
|
+
margin: 1px;
|
17
|
+
|
18
|
+
button {
|
19
|
+
width: 28px;
|
20
|
+
&:after { content: "Ab"; }
|
21
|
+
&.trix-active::after { content: "✓"; }
|
22
|
+
}
|
23
|
+
|
24
|
+
[data-trix-attribute=fgColor1] { color: rgb(136, 118, 38) }
|
25
|
+
[data-trix-attribute=fgColor2] { color: rgb(136, 118, 38) }
|
26
|
+
[data-trix-attribute=fgColor3] { color: rgb(207, 0, 0) }
|
27
|
+
[data-trix-attribute=fgColor4] { color: rgb(216, 28, 170) }
|
28
|
+
[data-trix-attribute=fgColor5] { color: rgb(144, 19, 254) }
|
29
|
+
[data-trix-attribute=fgColor6] { color: rgb(5, 98, 185) }
|
30
|
+
[data-trix-attribute=fgColor7] { color: rgb(17, 138, 15) }
|
31
|
+
[data-trix-attribute=fgColor8] { color: rgb(148, 82, 22) }
|
32
|
+
[data-trix-attribute=fgColor9] { color: rgb(102, 102, 102) }
|
33
|
+
|
34
|
+
[data-trix-attribute=bgColor1] { background-color: rgb(250, 247, 133) }
|
35
|
+
[data-trix-attribute=bgColor2] { background-color: rgb(255, 240, 219) }
|
36
|
+
[data-trix-attribute=bgColor3] { background-color: rgb(255, 229, 229) }
|
37
|
+
[data-trix-attribute=bgColor4] { background-color: rgb(255, 228, 247) }
|
38
|
+
[data-trix-attribute=bgColor5] { background-color: rgb(242, 237, 255) }
|
39
|
+
[data-trix-attribute=bgColor6] { background-color: rgb(225, 239, 252) }
|
40
|
+
[data-trix-attribute=bgColor7] { background-color: rgb(228, 248, 226) }
|
41
|
+
[data-trix-attribute=bgColor8] { background-color: rgb(238, 226, 215) }
|
42
|
+
[data-trix-attribute=bgColor9] { background-color: rgb(242, 242, 242) }
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
.trix-dialog {
|
47
|
+
padding: 5px;
|
48
|
+
|
49
|
+
.trix-dialog-hidden__input {
|
50
|
+
position: absolute;
|
51
|
+
z-index: -1;
|
52
|
+
opacity: 0;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
trix-editor {
|
58
|
+
[data-trix-mutable].attachment[data-trix-content-type~="vnd.rubyonrails.horizontal-rule.html"] {
|
59
|
+
box-shadow: 0 0 0 2px highlight;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
.trix-content {
|
64
|
+
.attachment { max-width: 100%; }
|
65
|
+
|
66
|
+
.attachment--content.attachment--horizontal-rule,
|
67
|
+
.attachment--content[data-trix-content-type~='vnd.rubyonrails.horizontal-rule.html'] {
|
68
|
+
padding: 1.5em 0 0.5em !important;
|
69
|
+
margin-bottom: 0.5em
|
70
|
+
}
|
71
|
+
|
72
|
+
.attachment--content.attachment--horizontal-rule hr,
|
73
|
+
.attachment--content[data-trix-content-type~='vnd.rubyonrails.horizontal-rule.html'] hr {
|
74
|
+
margin: 0;
|
75
|
+
width: 20%;
|
76
|
+
border-color: currentColor
|
77
|
+
}
|
78
|
+
}
|
@@ -1,25 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
border-color: #ccc;
|
4
|
-
|
5
|
-
&:hover,
|
6
|
-
&:active,
|
7
|
-
&:focus, {
|
8
|
-
border-color: #ccc;
|
9
|
-
outline: 0px !important;
|
10
|
-
-webkit-appearance: none;
|
11
|
-
box-shadow: none !important;
|
12
|
-
}
|
13
|
-
}
|
14
|
-
|
15
|
-
// Bootstrap 4 Feedback client side
|
16
|
-
.was-validated .form-control:invalid ~ .trix-content, { border-color: #dc3545 !important; }
|
17
|
-
.was-validated .form-control:valid ~ .trix-content { border-color: #28a745 !important; }
|
18
|
-
.was-validated .form-control:invalid ~ trix-toolbar, { border-color: #dc3545 !important; }
|
19
|
-
.was-validated .form-control:valid ~ trix-toolbar { border-color: #28a745 !important; }
|
20
|
-
|
21
|
-
// Bootstrap 4 Server side feedback
|
22
|
-
.form-control.is-invalid ~ .trix-content { border-color: #dc3545 !important; }
|
23
|
-
.form-control.is-invalid ~ trix-toolbar { border-color: #dc3545 !important; }
|
24
|
-
.form-control.is-valid ~ .trix-content { border-color: #28a745 !important; }
|
25
|
-
.form-control.is-valid ~ trix-toolbar { border-color: #28a745 !important; }
|
1
|
+
@import 'rich_text_area';
|
2
|
+
@import 'extensions';
|
@@ -0,0 +1,25 @@
|
|
1
|
+
.effective_rich_text_area {
|
2
|
+
height: 100%;
|
3
|
+
border-color: #ccc;
|
4
|
+
|
5
|
+
&:hover,
|
6
|
+
&:active,
|
7
|
+
&:focus, {
|
8
|
+
border-color: #ccc;
|
9
|
+
outline: 0px !important;
|
10
|
+
-webkit-appearance: none;
|
11
|
+
box-shadow: none !important;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
// Bootstrap 4 Feedback client side
|
16
|
+
.was-validated .form-control:invalid ~ .trix-content, { border-color: #dc3545 !important; }
|
17
|
+
.was-validated .form-control:valid ~ .trix-content { border-color: #28a745 !important; }
|
18
|
+
.was-validated .form-control:invalid ~ trix-toolbar, { border-color: #dc3545 !important; }
|
19
|
+
.was-validated .form-control:valid ~ trix-toolbar { border-color: #28a745 !important; }
|
20
|
+
|
21
|
+
// Bootstrap 4 Server side feedback
|
22
|
+
.form-control.is-invalid ~ .trix-content { border-color: #dc3545 !important; }
|
23
|
+
.form-control.is-invalid ~ trix-toolbar { border-color: #dc3545 !important; }
|
24
|
+
.form-control.is-valid ~ .trix-content { border-color: #28a745 !important; }
|
25
|
+
.form-control.is-valid ~ trix-toolbar { border-color: #28a745 !important; }
|
@@ -7,8 +7,9 @@ module Effective
|
|
7
7
|
object.send(name).build() if build? && collection.blank?
|
8
8
|
|
9
9
|
errors = (@builder.error(name) if errors?) || BLANK
|
10
|
+
can_remove_method
|
10
11
|
|
11
|
-
errors + content_tag(:div, options[:input]) do
|
12
|
+
errors + content_tag(:div, options[:input].except(:collection)) do
|
12
13
|
has_many_fields_for(block) + has_many_links_for(block)
|
13
14
|
end
|
14
15
|
end
|
@@ -76,6 +77,11 @@ module Effective
|
|
76
77
|
end
|
77
78
|
end
|
78
79
|
|
80
|
+
def can_remove_method
|
81
|
+
return @can_remove_method unless @can_remove_method.nil?
|
82
|
+
@can_remove_method = (options[:input].delete(:can_remove_method) || false)
|
83
|
+
end
|
84
|
+
|
79
85
|
# reorder: true
|
80
86
|
def reorder?
|
81
87
|
return @reorder unless @reorder.nil?
|
@@ -107,19 +113,23 @@ module Effective
|
|
107
113
|
|
108
114
|
def render_resource(resource, block)
|
109
115
|
remove = BLANK
|
116
|
+
reorder = BLANK
|
117
|
+
can_remove = (can_remove_method.blank? || !!resource.send(can_remove_method))
|
110
118
|
|
111
119
|
content = @builder.fields_for(name, resource) do |form|
|
112
120
|
fields = block.call(form)
|
113
121
|
|
114
|
-
remove += form.super_hidden_field(:_destroy) if remove? && resource.persisted?
|
115
|
-
|
122
|
+
remove += form.super_hidden_field(:_destroy) if remove? && can_remove && resource.persisted?
|
123
|
+
reorder += form.super_hidden_field(:position) if reorder? && !fields.include?('][position]')
|
116
124
|
|
117
125
|
fields
|
118
126
|
end
|
119
127
|
|
120
|
-
|
128
|
+
if remove?
|
129
|
+
remove += (can_remove || resource.new_record?) ? link_to_remove(resource) : disabled_link_to_remove(resource)
|
130
|
+
end
|
121
131
|
|
122
|
-
content_tag(:div, render_fields(content, remove), class: 'has-many-fields')
|
132
|
+
content_tag(:div, render_fields(content, (remove + reorder)), class: 'has-many-fields')
|
123
133
|
end
|
124
134
|
|
125
135
|
def render_fields(content, remove)
|
@@ -169,7 +179,7 @@ module Effective
|
|
169
179
|
def link_to_reorder(block)
|
170
180
|
content_tag(
|
171
181
|
:button,
|
172
|
-
icon('
|
182
|
+
icon('reorder') + 'Reorder',
|
173
183
|
class: 'has-many-reorder btn btn-secondary',
|
174
184
|
title: 'Reorder',
|
175
185
|
data: {
|
@@ -181,7 +191,7 @@ module Effective
|
|
181
191
|
def link_to_remove(resource)
|
182
192
|
content_tag(
|
183
193
|
:button,
|
184
|
-
icon('trash-2')
|
194
|
+
icon('trash-2'),
|
185
195
|
class: 'has-many-remove btn btn-danger',
|
186
196
|
title: 'Remove',
|
187
197
|
data: {
|
@@ -191,8 +201,20 @@ module Effective
|
|
191
201
|
)
|
192
202
|
end
|
193
203
|
|
204
|
+
def disabled_link_to_remove(resource)
|
205
|
+
content_tag(
|
206
|
+
:button,
|
207
|
+
icon('trash-2'),
|
208
|
+
class: 'has-many-remove-disabled btn btn-danger',
|
209
|
+
title: 'Remove',
|
210
|
+
data: {
|
211
|
+
'effective-form-has-many-remove-disabled': true,
|
212
|
+
}
|
213
|
+
)
|
214
|
+
end
|
215
|
+
|
194
216
|
def has_many_move
|
195
|
-
@has_many_move ||= content_tag(:span, icon('
|
217
|
+
@has_many_move ||= content_tag(:span, icon('grip-lines'), class: 'has-many-move')
|
196
218
|
end
|
197
219
|
|
198
220
|
def build_resource
|