bulma-rails-helpers 0.1.0
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 +7 -0
- data/app/helpers/bulma/form_helper.rb +254 -0
- data/app/javascript/controllers/bulma/file_input_display_controller.js +82 -0
- data/app/javascript/controllers/bulma/navigation_bar_controller.js +10 -0
- data/config/importmap.rb +1 -0
- data/lib/bulma/engine.rb +13 -0
- data/lib/bulma/version.rb +3 -0
- data/lib/bulma-rails-helpers.rb +4 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 778c3dafae213061ae5a31634952cb0c0afddec441ac6c47eb37584ae3ba782a
|
4
|
+
data.tar.gz: 2774b17df0e7ae39652bb62f5a715d029d0bd4782ee9a1f1e6b132ceb34ccc1b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: af60202790e1e7c9daf8e0819419c1972fbc227a7ad2166a4619bb2e5bbbc517b511f78308050c50008fd7cff0085f964839693cf9e94bb4768437e7a9d77f1d
|
7
|
+
data.tar.gz: 36c86bbbf37c810e9022252e8823493298ab4737eaa550e11334a9c25b915f052b0b04633f91d74d8f88645a7f1f6260405ebe6d04632b429e795cee2b584984
|
@@ -0,0 +1,254 @@
|
|
1
|
+
module Bulma
|
2
|
+
# # Bulma Form Helper
|
3
|
+
#
|
4
|
+
# Module Bulma FormHelper overrides the Rails form helpers to generate elements that are decorated with Bulma CSS
|
5
|
+
# classes. Here is an example of the generated html from the `text_field` helper (with a User model):
|
6
|
+
#
|
7
|
+
# f.text_field :name, class: "input"
|
8
|
+
#
|
9
|
+
# <div class="field">
|
10
|
+
# <label for="name" class="label">Name</label>
|
11
|
+
# <div class="control">
|
12
|
+
# <input id="user_name" class="input" type="text">
|
13
|
+
# </div>
|
14
|
+
# </div>
|
15
|
+
#
|
16
|
+
# In addition to the standard options, the following options are supported for inputs:
|
17
|
+
# - suppress_label: Do not render the label tag
|
18
|
+
# - icon_left: Add an icon to the left of the input field
|
19
|
+
# - icon_right: Add an icon to the right of the input field
|
20
|
+
#
|
21
|
+
# The two icon options should be a string that represents the icon class. For example, "fas fa-user" would render a user
|
22
|
+
# icon from Font Awesome.
|
23
|
+
module FormHelper
|
24
|
+
def text_field(object_name, method, options = {})
|
25
|
+
options = options.dup
|
26
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
27
|
+
end
|
28
|
+
|
29
|
+
def password_field(object_name, method, options = {})
|
30
|
+
options = options.dup
|
31
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
32
|
+
end
|
33
|
+
|
34
|
+
def telephone_field(object_name, method, options = {})
|
35
|
+
options = options.dup
|
36
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
37
|
+
end
|
38
|
+
|
39
|
+
def date_field(object_name, method, options = {})
|
40
|
+
options = options.dup
|
41
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
42
|
+
end
|
43
|
+
|
44
|
+
def time_field(object_name, method, options = {})
|
45
|
+
options = options.dup
|
46
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
47
|
+
end
|
48
|
+
|
49
|
+
def datetime_field(object_name, method, options = {})
|
50
|
+
options = options.dup
|
51
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
52
|
+
end
|
53
|
+
|
54
|
+
def month_field(object_name, method, options = {})
|
55
|
+
options = options.dup
|
56
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
57
|
+
end
|
58
|
+
|
59
|
+
def week_field(object_name, method, options = {})
|
60
|
+
options = options.dup
|
61
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
62
|
+
end
|
63
|
+
|
64
|
+
def url_field(object_name, method, options = {})
|
65
|
+
options = options.dup
|
66
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
67
|
+
end
|
68
|
+
|
69
|
+
def email_field(object_name, method, options = {})
|
70
|
+
options = options.dup
|
71
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
72
|
+
end
|
73
|
+
|
74
|
+
def number_field(object_name, method, options = {})
|
75
|
+
options = options.dup
|
76
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
77
|
+
end
|
78
|
+
|
79
|
+
def range_field(object_name, method, options = {})
|
80
|
+
options = options.dup
|
81
|
+
wrap_input_field(object_name, method, options) { super object_name, method, options }
|
82
|
+
end
|
83
|
+
|
84
|
+
def submit_tag(value = "Save", options = {})
|
85
|
+
return super if options.delete(:delivered)
|
86
|
+
|
87
|
+
append_classes_to_options(options, "button")
|
88
|
+
wrap_with_control { super }
|
89
|
+
end
|
90
|
+
|
91
|
+
def checkbox(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
|
92
|
+
tag.div(class: "field") do
|
93
|
+
tag.div(class: "control") do
|
94
|
+
label(object_name, method, class: "checkbox") do
|
95
|
+
concat super
|
96
|
+
concat " #{t(method, scope: [ :helpers, :label, object_name ])}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def checkbox_display(object_name, method, checked)
|
103
|
+
icon = checked ? "far fa-lg fa-square-check" : "fa-regular fa-square"
|
104
|
+
|
105
|
+
tag.div(class: "field") do
|
106
|
+
tag.div(class: "control") do
|
107
|
+
concat icon_span(icon, "is-medium")
|
108
|
+
concat tag.span t(method, scope: [ :helpers, :label, object_name ])
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def select(object, method, choices = nil, options = {}, html_options = {}, &block)
|
114
|
+
wrap_with_field_and_label(object, method, options, suppress_label: options.delete(:suppress_label)) do
|
115
|
+
wrap_with_control do
|
116
|
+
tag.div(class: class_names(:select, is_danger: attribute_has_errors?(options, method))) { super }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
|
122
|
+
wrap_with_field_and_label(object, method, options, suppress_label: options.delete(:suppress_label), column: options.delete(:column)) do
|
123
|
+
wrap_with_control do
|
124
|
+
tag.div(class: class_names(:select, is_danger: attribute_has_errors?(options, method))) { super }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def file_field(object_name, method, options = {})
|
130
|
+
append_classes_to_options(options, "file-input")
|
131
|
+
append_data_attributes_to_options(options, bulma__file_input_display_target: "fileInput", action: "bulma--file-input-display#show")
|
132
|
+
accepted_file_types = options.fetch(:accept, "").split(",").map(&:strip)
|
133
|
+
|
134
|
+
# [File Upload](https://bulma.io/documentation/form/file/)
|
135
|
+
tag.div(class: "file is-boxed", data: { controller: "bulma--file-input-display", bulma__file_input_display_accepted_file_types_value: accepted_file_types }) do
|
136
|
+
tag.label(class: "file-label") do
|
137
|
+
concat super
|
138
|
+
concat(tag.span(class: "file-cta") do
|
139
|
+
concat tag.span(class: "file-icon") { tag.i(class: "fas fa-upload") }
|
140
|
+
concat tag.span(class: "file-label") { t(method, scope: [ "helpers", "label", object_name ]) }
|
141
|
+
end)
|
142
|
+
concat tag.div(class: "content", data: { bulma__file_input_display_target: "fileList" }) { "No file(s) selected" }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def textarea(object_name, method, options = {})
|
148
|
+
append_classes_to_options(options, "textarea")
|
149
|
+
|
150
|
+
wrap_with_field_and_label(object_name, method, options, suppress_label: options.delete(:suppress_label), column: options.delete(:column)) do
|
151
|
+
wrap_with_control { super }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def text_area(object_name, method, options = {})
|
156
|
+
textarea(object_name, method, options)
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
# [Form Control](https://bulma.io/documentation/form/general/#form-control)
|
162
|
+
def wrap_with_control
|
163
|
+
tag.div(class: "control") { yield }
|
164
|
+
end
|
165
|
+
|
166
|
+
# [Form Input](https://bulma.io/documentation/form/general/#complete-form-example)
|
167
|
+
def wrap_input_field(object_name, method, options)
|
168
|
+
options.extend(InputOptionsParser)
|
169
|
+
append_classes_to_options(options, "input")
|
170
|
+
append_classes_to_options(options, "is-danger") if attribute_has_errors?(options, method)
|
171
|
+
|
172
|
+
wrap_with_field_and_label(object_name, method, options, suppress_label: options.suppress_label?, column: options.column?) do
|
173
|
+
tag.div(class: class_names(:control, options.icon_control_classes)) do
|
174
|
+
concat yield
|
175
|
+
concat icon_span(options.icon_left, "is-small is-left") if options.icon_left?
|
176
|
+
concat icon_span(options.icon_right, "is-small is-right") if options.icon_right?
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# [Form Field](https://bulma.io/documentation/form/general/#form-field)
|
182
|
+
# In addition to being able to add the "column" class by setting `column: true`, you can provide sizing for the
|
183
|
+
# column by passing in the class name as a string. For example, `column: "is-one-third"` will add the class
|
184
|
+
# "column is-one-third" to the field div.
|
185
|
+
def wrap_with_field_and_label(object_name, method, options, suppress_label: false, column: false)
|
186
|
+
tag.div(class: class_names(:field, column: column, column => column.is_a?(String))) do
|
187
|
+
concat label(object_name, method, class: "label") unless suppress_label
|
188
|
+
concat yield
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# [Icon](https://bulma.io/documentation/elements/icon/)
|
193
|
+
def icon_span(icon, additional_classes = nil)
|
194
|
+
tag.span(class: "icon #{additional_classes}".strip) do
|
195
|
+
tag.i(class: icon)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def append_classes_to_options(options, *additional_classes)
|
200
|
+
classes = options.fetch(:class, "")
|
201
|
+
adds = additional_classes.reject { classes.include?(it) }
|
202
|
+
options[:class] = "#{classes} #{adds.join(' ')}".strip
|
203
|
+
end
|
204
|
+
|
205
|
+
def append_data_attributes_to_options(options, **additional_attributes)
|
206
|
+
data = options.fetch(:data, {})
|
207
|
+
options[:data] = data.merge(additional_attributes)
|
208
|
+
end
|
209
|
+
|
210
|
+
def attribute_has_errors?(options, attribute_name)
|
211
|
+
object = options[:object]
|
212
|
+
object.respond_to?(:errors) && object.errors.key?(attribute_name)
|
213
|
+
end
|
214
|
+
|
215
|
+
# # Input Options Parser
|
216
|
+
#
|
217
|
+
# Module InputOptionsParser is mixed into the options hash to provide accessors and predicate methods for the custom
|
218
|
+
# options suppress_label, icon_left, and icon_right.
|
219
|
+
#
|
220
|
+
# Method `icon_control_classes` returns a hash suitable for passing to the `class_names` helper method.
|
221
|
+
#
|
222
|
+
# TODO: Handle options for [input modifiers color, size, and state](https://bulma.io/documentation/form/input/).
|
223
|
+
module InputOptionsParser
|
224
|
+
def self.extended(base)
|
225
|
+
attr_reader :icon_left, :icon_right
|
226
|
+
|
227
|
+
base.instance_variable_set(:@suppress_label, base.delete(:suppress_label))
|
228
|
+
base.instance_variable_set(:@icon_left, base.delete(:icon_left))
|
229
|
+
base.instance_variable_set(:@icon_right, base.delete(:icon_right))
|
230
|
+
base.instance_variable_set(:@column, base.delete(:column))
|
231
|
+
end
|
232
|
+
|
233
|
+
def suppress_label?
|
234
|
+
@suppress_label
|
235
|
+
end
|
236
|
+
|
237
|
+
def icon_left?
|
238
|
+
@icon_left.present?
|
239
|
+
end
|
240
|
+
|
241
|
+
def icon_right?
|
242
|
+
@icon_right.present?
|
243
|
+
end
|
244
|
+
|
245
|
+
def icon_control_classes
|
246
|
+
{ "has-icons-left": icon_left?, "has-icons-right": icon_right? }
|
247
|
+
end
|
248
|
+
|
249
|
+
def column?
|
250
|
+
@column
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["fileInput", "fileList"]
|
5
|
+
|
6
|
+
acceptValues = []
|
7
|
+
|
8
|
+
connect() {
|
9
|
+
this.show()
|
10
|
+
this.#parseAcceptAttribute()
|
11
|
+
}
|
12
|
+
|
13
|
+
show() {
|
14
|
+
const preview = this.fileListTarget;
|
15
|
+
this.#clear(preview)
|
16
|
+
|
17
|
+
const curFiles = this.fileInputTarget.files;
|
18
|
+
if (curFiles.length === 0) {
|
19
|
+
const para = document.createElement("p")
|
20
|
+
para.textContent = "No files currently selected for upload"
|
21
|
+
preview.appendChild(para)
|
22
|
+
} else {
|
23
|
+
const list = document.createElement("ul")
|
24
|
+
preview.appendChild(list)
|
25
|
+
|
26
|
+
for (const file of curFiles) {
|
27
|
+
const listItem = document.createElement("li")
|
28
|
+
listItem.textContent = this.#fileTextContent(file)
|
29
|
+
list.appendChild(listItem);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
#clear(preview) {
|
35
|
+
while (preview.firstChild) {
|
36
|
+
preview.removeChild(preview.firstChild);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
#fileTextContent(file) {
|
41
|
+
if (this.#validFileType(file)) {
|
42
|
+
return this.#fileInfo(file);
|
43
|
+
} else {
|
44
|
+
return `File name ${file.name}: Not a valid file type. Update your selection.`;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
#validFileType(file) {
|
49
|
+
// If no accept attribute is specified, accept all files
|
50
|
+
if (this.acceptValues.length === 0) {
|
51
|
+
return true
|
52
|
+
}
|
53
|
+
|
54
|
+
const fileExtension = `.${file.name.split('.').pop().toLowerCase()}`
|
55
|
+
const fileMimeType = file.type.toLowerCase()
|
56
|
+
|
57
|
+
// Check if the file matches any of the accept criteria
|
58
|
+
return this.acceptValues.some(acceptValue => {
|
59
|
+
if (acceptValue.startsWith('.')) {
|
60
|
+
// Check file extension (e.g. ".jpg")
|
61
|
+
return acceptValue.toLowerCase() === fileExtension
|
62
|
+
} else if (acceptValue.includes('/*')) {
|
63
|
+
// Check MIME type with wildcard (e.g. "image/*")
|
64
|
+
const acceptGroup = acceptValue.split('/')[0]
|
65
|
+
return fileMimeType.startsWith(`${acceptGroup}/`)
|
66
|
+
} else {
|
67
|
+
// Check specific MIME type (e.g. "image/jpeg")
|
68
|
+
return acceptValue === fileMimeType
|
69
|
+
}
|
70
|
+
})
|
71
|
+
}
|
72
|
+
|
73
|
+
#fileInfo(file) {
|
74
|
+
return `${file.name} (${file.size} bytes)`
|
75
|
+
}
|
76
|
+
|
77
|
+
#parseAcceptAttribute() {
|
78
|
+
if (this.fileInputTarget.accept) {
|
79
|
+
this.acceptValues = this.fileInputTarget.accept.split(',').map(type => type.trim())
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = [ "burger", "menu" ]
|
5
|
+
|
6
|
+
toggle(_event) {
|
7
|
+
this.burgerTarget.classList.toggle('is-active');
|
8
|
+
this.menuTarget.classList.toggle('is-active');
|
9
|
+
}
|
10
|
+
}
|
data/config/importmap.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pin_all_from File.expand_path("../app/javascript/controllers/bulma", __dir__), under: "controllers/bulma"
|
data/lib/bulma/engine.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "rails/engine"
|
2
|
+
|
3
|
+
module Bulma
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
initializer "bulma.importmap", before: "importmap" do |app|
|
6
|
+
app.config.importmap.paths << Engine.root.join("config/importmap.rb")
|
7
|
+
end
|
8
|
+
|
9
|
+
initializer "bulma.assets" do |app|
|
10
|
+
app.config.assets.paths << root.join("app/javascript")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bulma-rails-helpers
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Todd Kummer
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: rails
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 8.0.2
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 8.0.2
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: minitest-difftastic
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
description: Override the Rails tag helpers to generate HTML that plays well with
|
41
|
+
the Bulma CSS framework.
|
42
|
+
email:
|
43
|
+
- todd@rockridgesolutions.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- app/helpers/bulma/form_helper.rb
|
49
|
+
- app/javascript/controllers/bulma/file_input_display_controller.js
|
50
|
+
- app/javascript/controllers/bulma/navigation_bar_controller.js
|
51
|
+
- config/importmap.rb
|
52
|
+
- lib/bulma-rails-helpers.rb
|
53
|
+
- lib/bulma/engine.rb
|
54
|
+
- lib/bulma/version.rb
|
55
|
+
homepage: https://github.com/RockSolt/bulma-rails-helpers
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata:
|
59
|
+
rubygems_mfa_required: 'true'
|
60
|
+
homepage_uri: https://github.com/RockSolt/bulma-rails-helpers
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.4.0
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubygems_version: 3.6.7
|
76
|
+
specification_version: 4
|
77
|
+
summary: Build Rails forms with Bulma CSS framework.
|
78
|
+
test_files: []
|