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.
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ //= require ./initialize
2
+ //= require ./extensions
@@ -1,42 +1,17 @@
1
- body.dragging,
2
- body.dragging * {
3
- cursor: move !important;
4
- }
5
-
6
1
  .form-has-many {
7
- .has-many-placeholder {
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: 1.5rem; }
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
- .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; }
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
- remove += form.super_hidden_field(:position) if reorder? && !fields.include?('][position]')
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
- remove += link_to_remove(resource) if (remove? || resource.new_record?)
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('list') + 'Reorder',
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') + 'Remove',
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('move'), class: 'has-many-move')
217
+ @has_many_move ||= content_tag(:span, icon('grip-lines'), class: 'has-many-move')
196
218
  end
197
219
 
198
220
  def build_resource