forma 0.0.0 → 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.
- data/README.md +40 -0
- data/Rakefile +8 -0
- data/lib/forma/action.rb +45 -0
- data/lib/forma/config.rb +68 -0
- data/lib/forma/field.rb +451 -0
- data/lib/forma/form.rb +257 -0
- data/lib/forma/helpers.rb +139 -0
- data/lib/forma/html.rb +163 -2
- data/lib/forma/railtie.rb +15 -0
- data/lib/forma/table.rb +115 -0
- data/lib/forma/utils.rb +24 -0
- data/lib/forma/version.rb +1 -1
- data/lib/forma.rb +8 -0
- data/spec/config_spec.rb +20 -0
- data/spec/field_spec.rb +3 -0
- data/spec/form_spec.rb +33 -0
- data/spec/helpers_spec.rb +18 -0
- data/spec/html_spec.rb +26 -0
- data/spec/spec_helper.rb +0 -6
- data/test/field_test.rb +63 -0
- data/test/form_test.rb +16 -0
- data/test/test_helper.rb +3 -0
- data/vendor/assets/images/ff-icons.png +0 -0
- data/vendor/assets/javascripts/forma.js +147 -0
- data/vendor/assets/stylesheets/forma-min.css +41 -1
- data/vendor/assets/stylesheets/forma.css +326 -6
- data/vendor/less/forma.less +138 -8
- metadata +28 -9
- data/.rspec +0 -1
- data/lib/forma/html/attributes.rb +0 -104
- data/lib/forma/html/element.rb +0 -74
- data/spec/html/attributes_spec.rb +0 -54
- data/spec/html/element_spec.rb +0 -21
data/README.md
CHANGED
@@ -1,3 +1,43 @@
|
|
1
1
|
# Forma
|
2
2
|
|
3
3
|
[](https://travis-ci.org/dimakura/forma)
|
4
|
+
|
5
|
+
`Forma` is usefull to easily create rich web forms with ruby web-frameworks.
|
6
|
+
|
7
|
+
Standard rails forms are great.
|
8
|
+
There are also nice libraries for creating some common elements, like SimpleForms.
|
9
|
+
|
10
|
+
`Forma` is intended for projects with huge amount of forms.
|
11
|
+
It scales easily and without a pain.
|
12
|
+
|
13
|
+
## Instalation and usage
|
14
|
+
|
15
|
+
Include
|
16
|
+
|
17
|
+
gem 'forma'
|
18
|
+
|
19
|
+
into your Gemfile. Or use
|
20
|
+
|
21
|
+
gem install forma
|
22
|
+
|
23
|
+
For proper functionality you should also include `jquery` (v > 1.9).
|
24
|
+
|
25
|
+
TODO: css & js inclusion ... etc.
|
26
|
+
|
27
|
+
## Examples
|
28
|
+
|
29
|
+
Below are some examples with `forma`:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
forma_for @user, title: 'Register', url: register_path do |f|
|
33
|
+
f.text_field :username
|
34
|
+
f.password_field :password
|
35
|
+
f.password_field :password_confirmation
|
36
|
+
f.text_field :first_name
|
37
|
+
f.text_field :last_name
|
38
|
+
f.text_field :mobile
|
39
|
+
f.web_field :email
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
TODO: more advanced usage of forms
|
data/Rakefile
CHANGED
data/lib/forma/action.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Forma
|
3
|
+
class Action
|
4
|
+
include Forma::Html
|
5
|
+
# attr_reader :label, :icon, :method, :confirm, :as
|
6
|
+
attr_reader :url
|
7
|
+
|
8
|
+
def initialize(h={})
|
9
|
+
h = h.symbolize_keys
|
10
|
+
@id = h[:id]
|
11
|
+
@label = h[:label]
|
12
|
+
@icon = h[:icon]
|
13
|
+
@url = h[:url]
|
14
|
+
@method = h[:method]
|
15
|
+
@confirm = h[:confirm]
|
16
|
+
@as = h[:as]
|
17
|
+
@tooltip = h[:tooltip]
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_html(model)
|
21
|
+
children = [ (el('img', attrs: { src: @icon }) if @icon.present?), el('span', text: eval_label(model)) ]
|
22
|
+
button = (@as.to_s == 'button')
|
23
|
+
el(
|
24
|
+
'a',
|
25
|
+
attrs: {
|
26
|
+
id: @id,
|
27
|
+
class: ['ff-action', ('btn' if button)],
|
28
|
+
href: eval_url(model), 'data-method' => @method, 'data-confirm' => @confirm,
|
29
|
+
'data-original-title' => @tooltip
|
30
|
+
},
|
31
|
+
children: children
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def eval_url(model)
|
38
|
+
@url.is_a?(Proc) ? @url.call(model) : @url.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def eval_label(model)
|
42
|
+
@label.is_a?(Proc) ? @label.call(model) : @label.to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/forma/config.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module Forma
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def config
|
8
|
+
Forma::Config.instance
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
class Config
|
15
|
+
include Singleton
|
16
|
+
|
17
|
+
def num
|
18
|
+
@num ||= NumberConfig.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def date
|
22
|
+
@date ||= DateConfig.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def texts
|
26
|
+
@texts ||= TextsConfig.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def map
|
30
|
+
@map ||= MapConfig.new
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class NumberConfig
|
35
|
+
attr_accessor :delimiter
|
36
|
+
attr_accessor :separator
|
37
|
+
def initialize
|
38
|
+
self.delimiter = ','
|
39
|
+
self.separator = '.'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class DateConfig
|
44
|
+
attr_accessor :formatter
|
45
|
+
def initialize
|
46
|
+
self.formatter = '%d-%b-%Y'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class TextsConfig
|
51
|
+
attr_accessor :empty, :table_empty
|
52
|
+
def initialize
|
53
|
+
self.empty = '(empty)'
|
54
|
+
self.table_empty = '(no data)'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class MapConfig
|
59
|
+
attr_accessor :default_latitude
|
60
|
+
attr_accessor :default_longitude
|
61
|
+
attr_accessor :zoom_level
|
62
|
+
def initialize
|
63
|
+
self.default_latitude = 41.711447
|
64
|
+
self.default_longitude = 44.754514
|
65
|
+
self.zoom_level = 17
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/forma/field.rb
ADDED
@@ -0,0 +1,451 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Forma
|
3
|
+
# General field interface.
|
4
|
+
class Field
|
5
|
+
include Forma::Utils
|
6
|
+
include Forma::Html
|
7
|
+
|
8
|
+
attr_reader :label, :hint, :i18n, :name, :tag
|
9
|
+
attr_reader :required, :autofocus, :readonly
|
10
|
+
attr_reader :width, :height
|
11
|
+
attr_reader :before, :after
|
12
|
+
attr_reader :url, :icon
|
13
|
+
attr_accessor :model, :value, :parent, :child_model_name
|
14
|
+
attr_writer :model_name
|
15
|
+
attr_reader :actions
|
16
|
+
|
17
|
+
def initialize(h = {})
|
18
|
+
h = h.symbolize_keys
|
19
|
+
@id = h[:id]; @label = h[:label]; @hint = h[:hint]; @i18n = h[:i18n]
|
20
|
+
@required = h[:required]; @autofocus = h[:autofocus]; @readonly = (not not h[:readonly])
|
21
|
+
@width = h[:width]; @height = h[:height]
|
22
|
+
@before = h[:before]; @after = h[:after]
|
23
|
+
@name = h[:name]; @value = h[:value]
|
24
|
+
@url = h[:url]; @icon = h[:icon]
|
25
|
+
@model = h[:model]; @parent = h[:parent]
|
26
|
+
@model_name = h[:model_name]; @child_model_name = h[:child_model_name]
|
27
|
+
@actions = h[:actions] || []
|
28
|
+
@tag = h[:tag]
|
29
|
+
end
|
30
|
+
|
31
|
+
def action(url, h={})
|
32
|
+
h[:url] = url
|
33
|
+
@actions << Action.new(h)
|
34
|
+
end
|
35
|
+
|
36
|
+
def name_as_chain
|
37
|
+
if self.parent and self.parent.respond_to?(:name_as_chain)
|
38
|
+
chain = self.parent.name_as_chain
|
39
|
+
chain << self.name
|
40
|
+
else
|
41
|
+
chain = [ self.model_name, self.name ]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def id
|
46
|
+
if @id then @id
|
47
|
+
else name_as_chain.flatten.join('_') end
|
48
|
+
end
|
49
|
+
|
50
|
+
def parameter_name
|
51
|
+
chain = name_as_chain; length = chain.length
|
52
|
+
p_name = ''
|
53
|
+
chain.reverse.each_with_index do |n, i|
|
54
|
+
if i == 0 then p_name = n
|
55
|
+
elsif i == length - 1 then p_name = "#{n}[#{p_name}]"
|
56
|
+
else p_name = "#{n}_attributes[#{p_name}]" end
|
57
|
+
end
|
58
|
+
p_name
|
59
|
+
end
|
60
|
+
|
61
|
+
# Convert this element into HTML.
|
62
|
+
def to_html(edit)
|
63
|
+
val = self.value
|
64
|
+
if edit and not readonly
|
65
|
+
edit = edit_element(val)
|
66
|
+
el('div', children: [ before_element, icon_element, edit, after_element, actions_element ])
|
67
|
+
else
|
68
|
+
if val.present? or val == false
|
69
|
+
view = view_element(val)
|
70
|
+
view = el('a', attrs: { href: eval_url }, children: [ view ]) if @url
|
71
|
+
el('div', children: [ before_element, icon_element, view, after_element, actions_element ])
|
72
|
+
else
|
73
|
+
empty = empty_element
|
74
|
+
el('div', children: [ empty, actions_element ])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns model name.
|
80
|
+
# Model name can be defined by user or determined automatically, based on model class.
|
81
|
+
def model_name
|
82
|
+
if @model_name then @model_name
|
83
|
+
elsif @parent then @parent.child_model_name
|
84
|
+
else singular_name(self.model)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def localization_key
|
89
|
+
if @i18n.present?
|
90
|
+
["models", self.model_name, @i18n].compact.join('.')
|
91
|
+
elsif self.respond_to?(:name)
|
92
|
+
["models", self.model_name, self.name].compact.join('.')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def localized_label
|
97
|
+
self.label.present? ? self.label : I18n.t(localization_key, default: self.name)
|
98
|
+
end
|
99
|
+
|
100
|
+
def localized_hint
|
101
|
+
self.hint.present? ? self.hint : I18n.t("#{localization_key}_hint", default: '')
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
def icon_element
|
107
|
+
el('img', attrs: { src: eval_icon, style: { 'margin-right' => '4px' } }) if @icon.present?
|
108
|
+
end
|
109
|
+
|
110
|
+
def before_element
|
111
|
+
el('span', text: before, attrs: { class: 'ff-field-before' }) if before.present?
|
112
|
+
end
|
113
|
+
|
114
|
+
def after_element
|
115
|
+
el('span', text: after, attrs: { class: 'ff-field-after' }) if after.present?
|
116
|
+
end
|
117
|
+
|
118
|
+
def empty_element
|
119
|
+
el('span', attrs: { class: 'ff-empty' }, text: Forma.config.texts.empty)
|
120
|
+
end
|
121
|
+
|
122
|
+
def actions_element
|
123
|
+
if @actions.any?
|
124
|
+
el('div', attrs: { class: 'ff-field-actions' }, children: @actions.map { |action| action.to_html(@model) })
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def eval_url
|
129
|
+
@url.is_a?(Proc) ? @url.call(self.model) : @url.to_s
|
130
|
+
end
|
131
|
+
|
132
|
+
def eval_icon
|
133
|
+
@icon.is_a?(Proc) ? @icon.call(self.model) : @icon.to_s
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Complex field.
|
138
|
+
class ComplexField < Field
|
139
|
+
include Forma::FieldHelper
|
140
|
+
attr_reader :fields
|
141
|
+
|
142
|
+
def initialize(h = {})
|
143
|
+
h = h.symbolize_keys
|
144
|
+
@fields = h[:fields] || []
|
145
|
+
super(h)
|
146
|
+
end
|
147
|
+
|
148
|
+
def add_field(f)
|
149
|
+
@fields << f
|
150
|
+
end
|
151
|
+
|
152
|
+
def value
|
153
|
+
val = super
|
154
|
+
if val then val
|
155
|
+
else
|
156
|
+
@fields.each { |f| f.model = self.model }
|
157
|
+
@fields.map { |f| f.value }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def edit_element(val)
|
162
|
+
el(
|
163
|
+
'div',
|
164
|
+
attrs: { class: 'ff-complex-field' },
|
165
|
+
children: @fields.zip(val).map { |fv|
|
166
|
+
el(
|
167
|
+
'div',
|
168
|
+
attrs: { class: 'ff-field' },
|
169
|
+
children: [ fv[0].edit_element(fv[1]) ]
|
170
|
+
)
|
171
|
+
}
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
def view_element(val)
|
176
|
+
el(
|
177
|
+
'div',
|
178
|
+
attrs: { class: 'ff-complex-field' },
|
179
|
+
children: @fields.zip(val).map { |fv|
|
180
|
+
el(
|
181
|
+
'div',
|
182
|
+
attrs: { class: 'ff-complex-part' },
|
183
|
+
children: [ fv[0].view_element(fv[1]) ]
|
184
|
+
)
|
185
|
+
}
|
186
|
+
)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Map field.
|
191
|
+
class MapField < Field
|
192
|
+
def value
|
193
|
+
val = super
|
194
|
+
if val then val
|
195
|
+
else
|
196
|
+
lat = extract_value(self.model, "#{self.name}_latitude") || Forma.config.map.default_latitude
|
197
|
+
long = extract_value(self.model, "#{self.name}_longitude") || Forma.config.map.default_longitude
|
198
|
+
[ lat, long ]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def width; @width || 500 end
|
203
|
+
def height; @height || 500 end
|
204
|
+
|
205
|
+
def view_element(val)
|
206
|
+
el('div', attrs: { style: { width: "#{self.width}px", height: "#{self.height}px", position: 'relative' } }, children: [
|
207
|
+
el('div', attrs: { id: self.id, class: 'ff-map' }),
|
208
|
+
# google_import,
|
209
|
+
map_display(val, false)
|
210
|
+
])
|
211
|
+
end
|
212
|
+
|
213
|
+
def edit_element(val)
|
214
|
+
el('div', attrs: { style: { width: "#{self.width}px", height: "#{self.height}px", position: 'relative' } }, children: [
|
215
|
+
el('div', attrs: { id: self.id, class: 'ff-map' }),
|
216
|
+
# google_import,
|
217
|
+
map_display(val, true),
|
218
|
+
el('input', attrs: { name: latitude_name, id: "#{self.id}_latitude", value: val[0], type: 'hidden' }),
|
219
|
+
el('input', attrs: { name: longitude_name, id: "#{self.id}_longitude", value: val[1], type: 'hidden' }),
|
220
|
+
])
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
def map_display(val, edit)
|
226
|
+
longLat = "{ latitude: #{val[0]}, longitude: #{val[1]} }"
|
227
|
+
zoom_level = Forma.config.map.zoom_level
|
228
|
+
el(
|
229
|
+
'script',
|
230
|
+
attrs: { type: 'text/javascript' },
|
231
|
+
html: %Q{ forma.registerGoogleMap('#{self.id}', #{zoom_level}, #{longLat}, [ #{longLat} ], #{edit}); }
|
232
|
+
)
|
233
|
+
end
|
234
|
+
|
235
|
+
def latitude_name
|
236
|
+
"#{name_as_chain[0]}[#{name_as_chain[1]}_latitude]"
|
237
|
+
end
|
238
|
+
|
239
|
+
def longitude_name
|
240
|
+
"#{name_as_chain[0]}[#{name_as_chain[1]}_longitude]"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# SimpleField gets it's value from it's name.
|
245
|
+
class SimpleField < Field
|
246
|
+
def initialize(h = {})
|
247
|
+
h = h.symbolize_keys
|
248
|
+
super(h)
|
249
|
+
end
|
250
|
+
|
251
|
+
def value
|
252
|
+
val = super
|
253
|
+
if val then val
|
254
|
+
else extract_value(self.model, self.name)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def errors
|
259
|
+
if self.model.respond_to?(:errors); self.model.errors.messages[name.to_sym] end || []
|
260
|
+
end
|
261
|
+
|
262
|
+
def has_errors?
|
263
|
+
errors.any?
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Subform!
|
268
|
+
class SubformField < SimpleField
|
269
|
+
include Forma::FieldHelper
|
270
|
+
attr_reader :form
|
271
|
+
|
272
|
+
def initialize(h = {})
|
273
|
+
h[:label] = false
|
274
|
+
@form = Form.new(collapsible: true)
|
275
|
+
super(h)
|
276
|
+
end
|
277
|
+
|
278
|
+
def edit_element(val)
|
279
|
+
init_forma_before_field_display(true)
|
280
|
+
@form.to_html
|
281
|
+
end
|
282
|
+
|
283
|
+
def view_element(val)
|
284
|
+
init_forma_before_field_display(false)
|
285
|
+
@form.to_html
|
286
|
+
end
|
287
|
+
|
288
|
+
private
|
289
|
+
|
290
|
+
def init_forma_before_field_display(edit)
|
291
|
+
@form.model = val
|
292
|
+
@form.parent_field = self
|
293
|
+
@form.edit = edit
|
294
|
+
@form.icon = eval_icon if @icon
|
295
|
+
@form.title = localized_label
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# Text field.
|
300
|
+
class TextField < SimpleField
|
301
|
+
attr_reader :password
|
302
|
+
|
303
|
+
def initialize(h = {})
|
304
|
+
h = h.symbolize_keys
|
305
|
+
@password = h[:password]
|
306
|
+
super(h)
|
307
|
+
end
|
308
|
+
|
309
|
+
def view_element(val)
|
310
|
+
el((@tag || 'span'), text: (password ? '******' : val.to_s), attrs: { id: self.id })
|
311
|
+
end
|
312
|
+
|
313
|
+
def edit_element(val)
|
314
|
+
el('input', attrs: {
|
315
|
+
id: self.id,
|
316
|
+
name: parameter_name,
|
317
|
+
type: (password ? 'password' : 'text'),
|
318
|
+
value: val.to_s,
|
319
|
+
autofocus: @autofocus,
|
320
|
+
style: { width: ("#{width}px" if width.present?) }
|
321
|
+
})
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Email field.
|
326
|
+
class EmailField < TextField
|
327
|
+
def view_element(val)
|
328
|
+
el('a', attrs: { href: "mailto:#{val}" }, text: val)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# Date feild.
|
333
|
+
class DateField < SimpleField
|
334
|
+
attr_reader :formatter
|
335
|
+
|
336
|
+
def initialize(h = {})
|
337
|
+
h = h.symbolize_keys
|
338
|
+
@formatter = h[:formatter]
|
339
|
+
super(h)
|
340
|
+
end
|
341
|
+
|
342
|
+
def view_element(val)
|
343
|
+
el('span', text: val.localtime.strftime(formatter || Forma.config.date.formatter))
|
344
|
+
end
|
345
|
+
|
346
|
+
def edit_element(val)
|
347
|
+
el('input', attrs: {
|
348
|
+
name: parameter_name,
|
349
|
+
type: 'text',
|
350
|
+
value: val.to_s,
|
351
|
+
autofocus: @autofocus,
|
352
|
+
style: { width: ("#{width}px" if width.present?) }
|
353
|
+
})
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# Boolean field.
|
358
|
+
class BooleanField < SimpleField
|
359
|
+
def view_element(val)
|
360
|
+
el('input', attrs: { type: 'checkbox', disabled: true, checked: ('checked' if val) })
|
361
|
+
end
|
362
|
+
|
363
|
+
def edit_element(val)
|
364
|
+
e1 = el('input', attrs: { type: 'hidden', name: parameter_name, value: "0"})
|
365
|
+
e2 = el('input', attrs: { type: 'checkbox', name: parameter_name, checked: ('checked' if val), value: "1"})
|
366
|
+
el('span', children: [ e1, e2 ])
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
# Image upload field.
|
371
|
+
class ImageField < SimpleField
|
372
|
+
def view_element(val)
|
373
|
+
el('img', attrs: { src: val.url } )
|
374
|
+
end
|
375
|
+
|
376
|
+
def edit_element(val)
|
377
|
+
el('input', attrs: {
|
378
|
+
name: parameter_name,
|
379
|
+
type: 'file',
|
380
|
+
})
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
# Number field.
|
385
|
+
class NumberField < TextField
|
386
|
+
def view_element(val)
|
387
|
+
el('code', text: "#{val}")
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# Combo field.
|
392
|
+
class ComboField < SimpleField
|
393
|
+
def initialize(h={})
|
394
|
+
h = h.symbolize_keys
|
395
|
+
@empty = h[:empty]
|
396
|
+
@default = h[:default]
|
397
|
+
@collection = (h[:collection] || [])
|
398
|
+
super(h)
|
399
|
+
end
|
400
|
+
|
401
|
+
def view_element(val)
|
402
|
+
data = normalize_data(@collection, false)
|
403
|
+
text = data.find{|text, value| val == value }[0].to_s rescue nil
|
404
|
+
el('span', text: text)
|
405
|
+
end
|
406
|
+
|
407
|
+
def edit_element(val)
|
408
|
+
data = normalize_data(@collection, @empty)
|
409
|
+
selection = val.present? ? val : @default
|
410
|
+
el('select', attrs: { name: parameter_name }, children: data.map { |text, value|
|
411
|
+
if value.nil? then el('option', attrs: { selected: selection.blank? }, text: text)
|
412
|
+
else el('option', attrs: { selected: (true if selection == value), value: value }, text: text)
|
413
|
+
end
|
414
|
+
})
|
415
|
+
end
|
416
|
+
|
417
|
+
private
|
418
|
+
|
419
|
+
def normalize_data(collection, empty)
|
420
|
+
if collection.is_a?(Hash) then data = collection.to_a
|
421
|
+
else data = collection.map { |x| [x.to_s, x.id] } end
|
422
|
+
if empty != false then data.insert[empty.to_s, nil] end
|
423
|
+
Hash[data]
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
# Selection field.
|
428
|
+
class SelectField < SimpleField
|
429
|
+
def initialize(h={})
|
430
|
+
h = h.symbolize_keys
|
431
|
+
@search_url = h[:search_url]
|
432
|
+
@search_width = h[:search_width] || 500
|
433
|
+
@search_height = h[:search_height] || 600
|
434
|
+
super(h)
|
435
|
+
end
|
436
|
+
|
437
|
+
def view_element(val)
|
438
|
+
el(@tag || 'span', text: val.to_s)
|
439
|
+
end
|
440
|
+
|
441
|
+
def edit_element(val)
|
442
|
+
el('div', attrs: { id: self.id, class: 'ff-select-field' }, children: [
|
443
|
+
el('input', attrs: { id: "#{self.id}_value", type: 'text', value: "#{val and val.id}" }),
|
444
|
+
el('span', attrs: { id: "#{self.id}_text" }, text: val.to_s),
|
445
|
+
el('a', attrs: { class: 'ff-select-link btn btn-mini', 'data-id' => self.id, 'data-url' => @search_url, 'data-width' => @search_width, 'data-height' => @search_height }, children: [
|
446
|
+
el('i', attrs: { class: 'icon icon-search' })
|
447
|
+
])
|
448
|
+
])
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|