effective_bootstrap 0.9.23 → 0.9.28

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.
@@ -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