forma 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 16aaf06c56a52180e7b4aea01508cb4bdc9fcb74
4
+ data.tar.gz: 4e1a5437ff778c502118d0cebc20fc97c03f2e8c
5
+ SHA512:
6
+ metadata.gz: 84ae57199011563b025ca909d30b6047bd3865e25a54dd8d606732b8631de098411743b58b60b6e002ec615f52c8206032320ffd27a44afdbd5b764e5b2ad18f
7
+ data.tar.gz: c07481957ed0302a27a3325491e364fabf6effb634438ba6c15e6175e307fe23ddfa2057783942062486263444234e80a0b97d9e55c4b70eaac7c650bc7006bb
data/lib/forma/action.rb CHANGED
@@ -15,31 +15,56 @@ module Forma
15
15
  @confirm = h[:confirm]
16
16
  @as = h[:as]
17
17
  @tooltip = h[:tooltip]
18
+ @select = h[:select]
19
+ @condition = h[:condition]
18
20
  end
19
21
 
20
22
  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
- )
23
+ if eval_condition(model)
24
+ if @select
25
+ el(
26
+ 'a',
27
+ attrs: {
28
+ id: @id, class: ['ff-action', 'btn', 'btn-mini', 'ff-select-action', 'btn-xs', 'btn-default'],
29
+ href: '#', 'data-original-title' => @tooltip,
30
+ 'data-value-id' => model.id, 'data-value-type' => model.class.name, 'data-value-text' => model.to_s
31
+ },
32
+ children: [ el('i', attrs: { class: 'icon icon-download fa fa-hand-o-left' }) ]
33
+ )
34
+ else
35
+ children = [ (el('img', attrs: { src: eval_icon(model) }) if @icon.present?), el('span', text: eval_label(model)) ]
36
+ button = (@as.to_s == 'button')
37
+ el(
38
+ 'a',
39
+ attrs: {
40
+ id: @id,
41
+ class: ['ff-action', ('btn btn-default' if button)],
42
+ href: eval_url(model), 'data-method' => @method, 'data-confirm' => @confirm,
43
+ 'data-original-title' => @tooltip
44
+ },
45
+ children: children
46
+ )
47
+ end
48
+ end
33
49
  end
34
50
 
35
51
  private
36
52
 
37
53
  def eval_url(model)
38
- @url.is_a?(Proc) ? @url.call(model) : @url.to_s
54
+ @url.is_a?(Proc) ? @url.call(model).to_s : @url.to_s
39
55
  end
40
56
 
41
57
  def eval_label(model)
42
- @label.is_a?(Proc) ? @label.call(model) : @label.to_s
58
+ @label.is_a?(Proc) ? @label.call(model).to_s : @label.to_s
59
+ end
60
+
61
+ def eval_icon(model)
62
+ @icon.is_a?(Proc) ? @icon.call(model).to_s : @icon.to_s
63
+ end
64
+
65
+ def eval_condition(model)
66
+ if @condition then @condition.is_a?(Proc) ? @condition.call(model) : @condition
67
+ else true end
43
68
  end
44
69
  end
45
70
  end
data/lib/forma/config.rb CHANGED
@@ -34,9 +34,13 @@ module Forma
34
34
  class NumberConfig
35
35
  attr_accessor :delimiter
36
36
  attr_accessor :separator
37
+ attr_accessor :max_digits
38
+ attr_accessor :min_digits
37
39
  def initialize
38
40
  self.delimiter = ','
39
41
  self.separator = '.'
42
+ self.max_digits = 3
43
+ self.min_digits = 2
40
44
  end
41
45
  end
42
46
 
data/lib/forma/field.rb CHANGED
@@ -5,7 +5,7 @@ module Forma
5
5
  include Forma::Utils
6
6
  include Forma::Html
7
7
 
8
- attr_reader :label, :hint, :i18n, :name, :tag
8
+ attr_reader :label, :hint, :i18n, :name, :tag, :inline_hint
9
9
  attr_reader :required, :autofocus, :readonly
10
10
  attr_reader :width, :height
11
11
  attr_reader :before, :after
@@ -17,6 +17,7 @@ module Forma
17
17
  def initialize(h = {})
18
18
  h = h.symbolize_keys
19
19
  @id = h[:id]; @label = h[:label]; @hint = h[:hint]; @i18n = h[:i18n]
20
+ @inline_hint = h[:inline_hint]
20
21
  @required = h[:required]; @autofocus = h[:autofocus]; @readonly = (not not h[:readonly])
21
22
  @width = h[:width]; @height = h[:height]
22
23
  @before = h[:before]; @after = h[:after]
@@ -26,6 +27,9 @@ module Forma
26
27
  @model_name = h[:model_name]; @child_model_name = h[:child_model_name]
27
28
  @actions = h[:actions] || []
28
29
  @tag = h[:tag]
30
+ @empty = h[:empty]
31
+ @force_nonempty = h[:force_nonempty]
32
+ @class = h[:class]
29
33
  end
30
34
 
31
35
  def action(url, h={})
@@ -40,22 +44,16 @@ module Forma
40
44
  else
41
45
  chain = [ self.model_name, self.name ]
42
46
  end
47
+ chain.map { |x| x.to_s.gsub '.', '_' }
43
48
  end
44
49
 
45
50
  def id
46
51
  if @id then @id
47
- else name_as_chain.flatten.join('_') end
52
+ else name_as_chain.select{ |x| x.present? }.join('_') end
48
53
  end
49
54
 
50
55
  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
56
+ parameter_name_from_chain(name_as_chain)
59
57
  end
60
58
 
61
59
  # Convert this element into HTML.
@@ -63,15 +61,15 @@ module Forma
63
61
  val = self.value
64
62
  if edit and not readonly
65
63
  edit = edit_element(val)
66
- el('div', children: [ before_element, icon_element, edit, after_element, actions_element ])
64
+ el('div', children: [ before_element, icon_element, edit, after_element, actions_element, inline_hint_element ])
67
65
  else
68
- if val.present? or val == false
66
+ if val.present? or val == false or @force_nonempty
69
67
  view = view_element(val)
70
68
  view = el('a', attrs: { href: eval_url }, children: [ view ]) if @url
71
- el('div', children: [ before_element, icon_element, view, after_element, actions_element ])
69
+ el('div', attrs: { class: (@class ? eval_with_model(@class) : nil) },
70
+ children: [ before_element, icon_element, view, after_element, actions_element, inline_hint_element ])
72
71
  else
73
- empty = empty_element
74
- el('div', children: [ empty, actions_element ])
72
+ el('div', children: [ empty_element, actions_element ])
75
73
  end
76
74
  end
77
75
  end
@@ -94,7 +92,9 @@ module Forma
94
92
  end
95
93
 
96
94
  def localized_label
97
- self.label.present? ? self.label : I18n.t(localization_key, default: self.name)
95
+ unless self.label == false
96
+ self.label.present? ? self.label : I18n.t(localization_key, default: self.name)
97
+ end
98
98
  end
99
99
 
100
100
  def localized_hint
@@ -103,35 +103,28 @@ module Forma
103
103
 
104
104
  protected
105
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) })
106
+ def parameter_name_from_chain(chain)
107
+ length = chain.length
108
+ p_name = ''
109
+ chain.select{ |x| x.present? }.reverse.each_with_index do |n, i|
110
+ if i == 0 then p_name = n
111
+ elsif i == length - 1 then p_name = "#{n}[#{p_name}]"
112
+ else p_name = "#{n}_attributes[#{p_name}]" end
125
113
  end
114
+ p_name
126
115
  end
127
116
 
128
- def eval_url
129
- @url.is_a?(Proc) ? @url.call(self.model) : @url.to_s
130
- end
117
+ def icon_element; el('img', attrs: { src: eval_icon, style: { 'margin-right' => '4px' } }) if @icon.present? end
118
+ def before_element; el('span', text: eval_with_model(before), attrs: { class: 'ff-field-before' }) if before.present? end
119
+ def after_element; el('span', text: eval_with_model(after), attrs: { class: 'ff-field-after' }) if after.present? end
120
+ def empty_element; el('span', attrs: { class: 'ff-empty' }, text: Forma.config.texts.empty) unless @empty == false end
121
+ def actions_element; el('div', attrs: { class: 'ff-field-actions' }, children: @actions.map { |action| action.to_html(@model) }) if @actions.any? end
122
+ def inline_hint_element; el('div', attrs: { class: 'ff-inline-hint' }, text: @inline_hint) if @inline_hint.present? end
123
+ def eval_url; eval_with_model(@url) end
124
+ def eval_icon; eval_with_model(@icon) end
131
125
 
132
- def eval_icon
133
- @icon.is_a?(Proc) ? @icon.call(self.model) : @icon.to_s
134
- end
126
+ private
127
+ def eval_with_model(val, h={}); val.is_a?(Proc) ? val.call(h[:model] || self.model) : val.to_s end
135
128
  end
136
129
 
137
130
  # Complex field.
@@ -150,24 +143,17 @@ module Forma
150
143
  end
151
144
 
152
145
  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
146
+ @model
159
147
  end
160
148
 
161
149
  def edit_element(val)
162
150
  el(
163
151
  'div',
164
152
  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
- )
153
+ children: @fields.map { |f|
154
+ f.model = self.model
155
+ f.model_name = self.model_name
156
+ el('div', attrs: { class: 'ff-complex-part' }, children: [ f.to_html(true) ])
171
157
  }
172
158
  )
173
159
  end
@@ -176,15 +162,20 @@ module Forma
176
162
  el(
177
163
  'div',
178
164
  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
- )
165
+ children: @fields.map { |f|
166
+ f.model = self.model
167
+ el('div', attrs: { class: 'ff-complex-part' }, children: [ f.to_html(false) ])
185
168
  }
186
169
  )
187
170
  end
171
+
172
+ def errors
173
+ @fields.map { |f| f.model = @model; f.errors }.flatten
174
+ end
175
+
176
+ def has_errors?
177
+ errors.any?
178
+ end
188
179
  end
189
180
 
190
181
  # Map field.
@@ -336,21 +327,36 @@ module Forma
336
327
  def initialize(h = {})
337
328
  h = h.symbolize_keys
338
329
  @formatter = h[:formatter]
330
+ @@date_counter ||= 0
331
+ @@date_counter += 1
332
+ @date_counter = @@date_counter
339
333
  super(h)
340
334
  end
341
335
 
342
336
  def view_element(val)
343
- el('span', text: val.localtime.strftime(formatter || Forma.config.date.formatter))
337
+ val = val.respond_to?(:localtime) ? val.localtime : val
338
+ el('span', text: val.strftime(formatter || Forma.config.date.formatter))
344
339
  end
345
340
 
346
341
  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
- })
342
+ input_id = "ff-date-#{@date_counter}"
343
+ val = Date.strptime(val) if (val.present? and val.is_a?(String)) rescue nil
344
+ el('div', children: [
345
+ el('input', attrs: {
346
+ id: input_id,
347
+ name: parameter_name,
348
+ value: val.to_s,
349
+ type: 'hidden'
350
+ }),
351
+ el('input', attrs: {
352
+ class: 'ff-date',
353
+ type: 'text',
354
+ value: (val.strftime('%d-%b-%Y') if val.present?),
355
+ autofocus: @autofocus,
356
+ style: { width: "#{width || 100}px" },
357
+ 'data-altfield' => input_id,
358
+ })
359
+ ])
354
360
  end
355
361
  end
356
362
 
@@ -369,22 +375,59 @@ module Forma
369
375
 
370
376
  # Image upload field.
371
377
  class ImageField < SimpleField
378
+ def initialize(h = {})
379
+ h = h.symbolize_keys
380
+ @popover = h[:popover]
381
+ @height = h[:height] || 200
382
+ @width = h[:width] || 200
383
+ super(h)
384
+ end
385
+
372
386
  def view_element(val)
373
- el('img', attrs: { src: val.url } )
387
+ popover_data = {}
388
+ image_url = val.respond_to?(:url) ? val.url : val.to_s
389
+ if @popover
390
+ popover_data['data-original-title'] = eval_with_model(@popover[:title])
391
+ url = eval_with_model(@popover[:url])
392
+ popover_data['data-content'] = %Q{<div style="height: #{@height}px; width: #{@width}px;"><img src="#{url}"></img></div>}
393
+ popover_data['data-html'] = 'true'
394
+ popover_data['data-placement'] = @popover[:placement]
395
+ el('img', attrs: { src: image_url, class: 'ff-popover' }.merge(popover_data))
396
+ else
397
+ el('img', attrs: { src: image_url })
398
+ end
374
399
  end
375
400
 
376
401
  def edit_element(val)
377
- el('input', attrs: {
378
- name: parameter_name,
379
- type: 'file',
380
- })
402
+ el('input', attrs: { name: parameter_name, type: 'file' })
403
+ end
404
+ end
405
+
406
+ # Image upload field.
407
+ class FileField < SimpleField
408
+ def view_element(val)
409
+ el('div', text: 'NO IMPLEMENTATION')
410
+ end
411
+
412
+ def edit_element(val)
413
+ el('input', attrs: { name: parameter_name, type: 'file' })
381
414
  end
382
415
  end
383
416
 
384
417
  # Number field.
385
418
  class NumberField < TextField
419
+ include Forma::Utils
420
+ def initialize(h = {})
421
+ h = h.symbolize_keys
422
+ @min_digits = h[:min_digits] || Forma.config.num.min_digits
423
+ @max_digits = h[:max_digits] || Forma.config.num.max_digits
424
+ @separator = h[:separator] || Forma.config.num.separator
425
+ @delimiter = h[:delimiter] || Forma.config.num.delimiter
426
+ super(h)
427
+ end
428
+
386
429
  def view_element(val)
387
- el('code', text: "#{val}")
430
+ el('code', text: "#{number_format(val.to_f, max_digits: @max_digits, min_digits: @min_digits)}")
388
431
  end
389
432
  end
390
433
 
@@ -408,8 +451,8 @@ module Forma
408
451
  data = normalize_data(@collection, @empty)
409
452
  selection = val.present? ? val : @default
410
453
  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)
454
+ if value.nil? then el('option', attrs: { selected: selection.blank? , value: ""}, text: text)
455
+ else el('option', attrs: { selected: (true if selection.to_s == value.to_s), value: value }, text: text)
413
456
  end
414
457
  })
415
458
  end
@@ -419,11 +462,64 @@ module Forma
419
462
  def normalize_data(collection, empty)
420
463
  if collection.is_a?(Hash) then data = collection.to_a
421
464
  else data = collection.map { |x| [x.to_s, x.id] } end
422
- if empty != false then data.insert[empty.to_s, nil] end
465
+ if empty != false then data = ([[empty.to_s, nil]] + data) end
423
466
  Hash[data]
424
467
  end
425
468
  end
426
469
 
470
+ # Array field.
471
+ class ArrayField < SimpleField
472
+ def initialize(h={})
473
+ h = h.symbolize_keys
474
+ @item_actions = h[:item_actions] || []
475
+ @item_url = h[:item_url]
476
+ super(h)
477
+ end
478
+
479
+ def view_element(val)
480
+ el('div', attrs: { class: ['ff-array-field'] }, children: val.map { |x|
481
+ el('div', attrs: { class: 'ff-array-part' }, children: [
482
+ if @item_url then el('a', attrs: { href: eval_with_model(@item_url, model: x) }, text: x.to_s) else el('span', text: x.to_s) end,
483
+ (el('span', attrs: { class: 'ff-actions' }, children: @item_actions.map { |a| a.to_html(x) } ) if @item_actions.any?)
484
+ ])
485
+ })
486
+ end
487
+
488
+ def edit_element(val)
489
+ el('div', text: 'NO IMPLEMENTATION')
490
+ end
491
+
492
+ def item_action(url, h={})
493
+ h[:url] = url
494
+ @item_actions << Action.new(h)
495
+ end
496
+ end
497
+
498
+ # Table field.
499
+ class TableField < SimpleField
500
+ def initialize(h={})
501
+ h = h.symbolize_keys
502
+ h[:label] = false
503
+ h[:force_nonempty] = true
504
+ @table = Forma::Table.new(h[:table] || {})
505
+ super(h)
506
+ end
507
+
508
+ def view_element(val)
509
+ @table.models = val
510
+ @table.to_html
511
+ end
512
+
513
+ def edit_element(val)
514
+ el('div', text: 'NO IMPLEMENTATION')
515
+ end
516
+
517
+ def table
518
+ yield @table if block_given?
519
+ @table
520
+ end
521
+ end
522
+
427
523
  # Selection field.
428
524
  class SelectField < SimpleField
429
525
  def initialize(h={})
@@ -431,21 +527,46 @@ module Forma
431
527
  @search_url = h[:search_url]
432
528
  @search_width = h[:search_width] || 500
433
529
  @search_height = h[:search_height] || 600
530
+ @polymorphic = h[:polymorphic]
434
531
  super(h)
435
532
  end
436
533
 
437
- def view_element(val)
438
- el(@tag || 'span', text: val.to_s)
534
+ def id_field_name
535
+ chain = name_as_chain
536
+ chain[chain.length - 1] = "#{chain.last}_id"
537
+ parameter_name_from_chain(chain)
538
+ end
539
+
540
+ def type_field_name
541
+ chain = name_as_chain
542
+ chain[chain.length - 1] = "#{chain.last}_type"
543
+ parameter_name_from_chain(chain)
439
544
  end
440
545
 
546
+ def view_element(val); el(@tag || 'span', text: val.to_s) end
547
+
441
548
  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' })
549
+ inputs = [ el('input', attrs: { id: "#{self.id}_id_value", type: 'hidden', value: "#{val and val.id}", name: id_field_name }) ]
550
+ if @polymorphic
551
+ inputs << el('input',
552
+ attrs: { id: "#{self.id}_type_value", type: 'hidden', value: "#{val and val.class.to_s}", name: type_field_name }
553
+ )
554
+ end
555
+ text_element = el(
556
+ 'span',
557
+ attrs: { id: "#{self.id}_text", class: ['ff-select-label', ('ff-empty' if val.blank?)] },
558
+ text: (val.present? ? val.to_s : Forma.config.texts.empty)
559
+ )
560
+ buttons = el('div', attrs: { class: 'btn-group' }, children: [
561
+ el('a', attrs: { class: 'ff-select-link btn btn-mini btn-default btn-xs', 'data-id' => self.id, 'data-url' => @search_url, 'data-width' => @search_width, 'data-height' => @search_height }, children: [
562
+ el('i', attrs: { class: 'icon icon-search fa fa-search' })
563
+ ]),
564
+ el('a', attrs: { class: 'ff-clear-selection-action btn btn-mini btn-default btn-xs', 'data-id' => self.id }, children: [
565
+ el('i', attrs: { class: 'icon icon-trash fa fa-trash-o' })
447
566
  ])
448
567
  ])
568
+ children = inputs + [ text_element, buttons ]
569
+ el('div', attrs: { id: self.id, class: 'ff-select-field' }, children: children)
449
570
  end
450
571
  end
451
572
  end
data/lib/forma/form.rb CHANGED
@@ -109,7 +109,7 @@ module Forma
109
109
  end
110
110
 
111
111
  def auth_token_element
112
- if @auth_token.present?
112
+ if @auth_token.present? and @method != 'get'
113
113
  el('div', attrs: { style: {padding: 0, margin: 0, height: 0, width: 0, display: 'inline'} }, children: [
114
114
  el('input', attrs: { type: 'hidden', name: 'authenticity_token', value: @auth_token })
115
115
  ])
@@ -138,7 +138,10 @@ module Forma
138
138
  ]
139
139
  )
140
140
  end
141
- value_element = el('div', attrs: { class: (fld.required ? ['ff-value', 'ff-required'] : ['ff-value']) }, children: [
141
+ value_class = ['ff-value']
142
+ value_class << 'ff-required' if fld.required
143
+ value_class << 'ff-value-no-label' if fld.label == false
144
+ value_element = el('div', attrs: { class: value_class }, children: [
142
145
  fld.to_html(@edit), (field_error_element(fld.errors) if has_errors),
143
146
  ])
144
147
  el(
data/lib/forma/helpers.rb CHANGED
@@ -92,6 +92,13 @@ module Forma
92
92
  add_field(field)
93
93
  end
94
94
 
95
+ def file_field(name, opts={})
96
+ opts[:name] = name
97
+ field = Forma::FileField.new(opts)
98
+ yield field if block_given?
99
+ add_field(field)
100
+ end
101
+
95
102
  def number_field(name, opts = {})
96
103
  opts[:name] = name
97
104
  field = Forma::NumberField.new(opts)
@@ -113,6 +120,20 @@ module Forma
113
120
  yield field if block_given?
114
121
  add_field(field)
115
122
  end
123
+
124
+ def array_field(name, opts = {})
125
+ opts[:name] = name
126
+ field = Forma::ArrayField.new(opts)
127
+ yield field if block_given?
128
+ add_field(field)
129
+ end
130
+
131
+ def table_field(name, opts = {})
132
+ opts[:name] = name
133
+ field = Forma::TableField.new(opts)
134
+ yield field if block_given?
135
+ add_field(field)
136
+ end
116
137
  end
117
138
 
118
139
  module WithTitleElement
data/lib/forma/html.rb CHANGED
@@ -44,7 +44,7 @@ module Forma::Html
44
44
  module_function :attr
45
45
  module_function :el
46
46
 
47
- private
47
+ # private
48
48
 
49
49
  class Attr; end
50
50
 
@@ -57,8 +57,10 @@ module Forma::Html
57
57
  end
58
58
 
59
59
  def to_s
60
- if @name.present? and @value.present?
61
- %Q{#{@name}="#{@value}"}
60
+ if @name == 'value'
61
+ %Q{#{@name}="#{ERB::Util.html_escape(@value)}"}
62
+ elsif @name.present? and @value.present?
63
+ %Q{#{@name}="#{ERB::Util.html_escape(@value)}"}
62
64
  end
63
65
  end
64
66
  end
data/lib/forma/table.rb CHANGED
@@ -6,6 +6,7 @@ module Forma
6
6
  include Forma::WithTitleElement
7
7
  attr_reader :collapsible, :collapsed, :icon, :title
8
8
  attr_reader :title_actions
9
+ attr_accessor :models
9
10
 
10
11
  def initialize(h = {})
11
12
  h = h.symbolize_keys
@@ -108,7 +109,14 @@ module Forma
108
109
  if @paginate and @context
109
110
  self_from_block = eval("self", @context.binding)
110
111
  s = self_from_block.send(:will_paginate, @models, @paginate_options)
111
- el('div', html: s.to_s)
112
+ paginate = el('div', html: s.to_s)
113
+ el('div', attrs: { class: 'ff-paginate' }, children: [
114
+ paginate,
115
+ (el('div', attrs: { class: 'ff-totals' }, children: [
116
+ el('code', text: "#{@models.total_entries}"),
117
+ el('span', text: @paginate_options[:records] || 'records')
118
+ ]) if @models.total_entries > 0)
119
+ ])
112
120
  end
113
121
  end
114
122
  end
data/lib/forma/utils.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ require 'action_controller'
3
+
2
4
  module Forma
3
5
  module Utils
6
+ include ActionView::Helpers::NumberHelper
7
+
4
8
  def singular_name(model)
5
9
  if model.respond_to?(:model_name); model.model_name.singular_route_key # Mongoid
6
10
  elsif model.class.respond_to?(:table_name); model.class.table_name.singularize # ActiveModel
@@ -18,7 +22,25 @@ module Forma
18
22
  val
19
23
  end
20
24
 
25
+ def number_format(num, h = {})
26
+ max_digits = h[:max_digits] || 2
27
+ max_digits = 0 if max_digits < 0
28
+ min_digits = h[:min_digits] || 0
29
+ min_digits = max_digits if min_digits > max_digits
30
+ separator = h[:separator] || '.'
31
+ delimiter = h[:delimiter] || ','
32
+ formatted = number_with_precision(num, precision: max_digits, separator: separator, delimiter: delimiter, strip_insignificant_zeros: true)
33
+ nums = formatted.split(separator)
34
+ length_after_separator = (nums[1] || '').length
35
+ if length_after_separator >= min_digits
36
+ formatted
37
+ else
38
+ "#{nums[0]}#{separator}#{(nums[1] || '').ljust(min_digits, '0')}"
39
+ end
40
+ end
41
+
21
42
  module_function :singular_name
22
43
  module_function :extract_value
44
+ module_function :number_format
23
45
  end
24
46
  end
data/lib/forma/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module Forma
3
- VERSION = "0.1.1"
3
+ VERSION = "0.1.2"
4
4
  end
@@ -0,0 +1,15 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'test_helper'
3
+
4
+ class UtilsTest < Test::Unit::TestCase
5
+ include Forma::Utils
6
+
7
+ def test_number_format
8
+ assert_equal '123,456', number_format(123_456)
9
+ assert_equal '123,456.00', number_format(123_456, min_digits: 2)
10
+ assert_equal '123,456.79', number_format(123_456.789)
11
+ assert_equal '123,456.789', number_format(123_456.789, max_digits: 3)
12
+ assert_equal '123,456.789', number_format(123_456.789, max_digits: 4)
13
+ assert_equal '123,456.7890', number_format(123_456.789, max_digits: 4, min_digits: 4)
14
+ end
15
+ end
@@ -69,6 +69,35 @@
69
69
  });
70
70
  };
71
71
 
72
+ var initializeSelectActions = function() {
73
+ $('.ff-select-action').click(function() {
74
+ var valueId = window.name + '_id_value';
75
+ var typeId = window.name + '_type_value';
76
+ var labelId = window.name + '_text';
77
+ var valueElement = window.opener.$('#' + valueId);
78
+ var labelElement = window.opener.$('#' + labelId);
79
+ var typeElement = window.opener.$('#' + typeId);
80
+ valueElement.val($(this).attr('data-value-id'));
81
+ if (typeElement) { typeElement.val($(this).attr('data-value-type')); }
82
+ labelElement.html($(this).attr('data-value-text'));
83
+ labelElement.removeClass('ff-empty');
84
+ window.close();
85
+ return false;
86
+ });
87
+ $('.ff-clear-selection-action').click(function() {
88
+ var valueId = $(this).attr('data-id') + '_id_value';
89
+ var labelId = $(this).attr('data-id') + '_text';
90
+ var typeId = $(this).attr('data-id') + '_type_value';
91
+ var valueElement = $('#' + valueId);
92
+ var labelElement = $('#' + labelId);
93
+ var typeElement = $('#' + typeId);
94
+ valueElement.val(null);
95
+ if (typeElement) { typeElement.val(null); }
96
+ labelElement.text('(empty)');
97
+ labelElement.addClass('ff-empty');
98
+ });
99
+ };
100
+
72
101
  // google map initialization
73
102
 
74
103
  var mapsData = {};
@@ -121,6 +150,24 @@
121
150
  }
122
151
  };
123
152
 
153
+ var initPopovers = function() {
154
+ $('.ff-popover').popover({ trigger: 'click' });
155
+ };
156
+
157
+ var initDatePicker = function() {
158
+ var pickers = $('.ff-date');
159
+ for (var i = 0, l = pickers.length; i < l; i++) {
160
+ var picker = $(pickers[i]);
161
+ picker.datepicker({
162
+ dateFormat: 'dd-M-yy',
163
+ altField: '#' + picker.attr('data-altfield'), altFormat: 'yy-mm-dd',
164
+ onClose: function(dateText, inst) {
165
+ if(dateText == '') { $(inst.settings["altField"]).val(dateText); }
166
+ },
167
+ });
168
+ }
169
+ };
170
+
124
171
  // prepare function
125
172
 
126
173
  var ready = function() {
@@ -129,7 +176,11 @@
129
176
  initializeFormSubmit();
130
177
  initializeTooltips();
131
178
  initGoogleMaps();
179
+ initPopovers();
180
+ initDatePicker();
181
+ // selectors
132
182
  initializeSelectFields();
183
+ initializeSelectActions();
133
184
  };
134
185
 
135
186
  // turbolink initilization!
@@ -1,41 +1,47 @@
1
- .user-select-none{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;}
2
- .ff-form,.ff-table{margin-bottom:16px;}.ff-form .ff-action,.ff-table .ff-action{margin-left:4px;}.ff-form .ff-action:first-child,.ff-table .ff-action:first-child{margin-left:0;}
3
- .ff-form .ff-action img,.ff-table .ff-action img{padding-right:4px;height:16px;width:16px;}
4
- .ff-form .ff-title,.ff-table .ff-title{position:relative;}.ff-form .ff-title .ff-active-title,.ff-table .ff-title .ff-active-title{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:pointer;display:inline-block;padding:4px 8px;font-weight:bold;font-size:14px;}.ff-form .ff-title .ff-active-title .ff-collapse,.ff-table .ff-title .ff-active-title .ff-collapse{display:inline-block;height:14px;width:14px;margin-right:4px;margin-top:2px;}
5
- .ff-form .ff-title .ff-active-title img,.ff-table .ff-title .ff-active-title img{height:16px;width:16px;overflow:hidden;margin-right:4px;}
6
- .ff-form .ff-title .ff-title-actions,.ff-table .ff-title .ff-title-actions{position:absolute;right:0;top:4px;padding:0 8px;}
7
- .ff-form .ff-collapse,.ff-table .ff-collapse{background:url(/assets/glyphicons-halflings.png) -313px -119px;}
8
- .ff-form .ff-collapsed.ff-collapse,.ff-table .ff-collapsed.ff-collapse{background:url(/assets/glyphicons-halflings.png) -456px -72px;}
9
- .ff-form .ff-field-hint,.ff-table .ff-field-hint{background:url(/assets/glyphicons-halflings.png) -456px -72px;}
10
- .ff-form .ff-tabs,.ff-table .ff-tabs{padding:4px 0;}.ff-form .ff-tabs ul.ff-tabs-header,.ff-table .ff-tabs ul.ff-tabs-header{list-style-type:none;margin:0;position:relative;border-bottom:1px solid #b1b1b1;}.ff-form .ff-tabs ul.ff-tabs-header li,.ff-table .ff-tabs ul.ff-tabs-header li{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:pointer;display:inline-block;padding:4px 8px;margin-left:8px;margin-bottom:-1px;border:1px solid transparent;}.ff-form .ff-tabs ul.ff-tabs-header li img,.ff-table .ff-tabs ul.ff-tabs-header li img{margin:0 4px;height:16px;width:16px;}
11
- .ff-form .ff-tabs ul.ff-tabs-header li.ff-selected,.ff-table .ff-tabs ul.ff-tabs-header li.ff-selected{background:#ffffcc;border:1px solid #b1b1b1;border-bottom:1px solid #ffffcc;}
12
- .ff-form .ff-tabs .ff-tab-actions,.ff-table .ff-tabs .ff-tab-actions{padding:4px 8px;background:#ffffcc;border:1px solid #b1b1b1;margin-top:-1px;}
13
- .ff-form .ff-tabs .ff-cols,.ff-table .ff-tabs .ff-cols{margin:4px 0 0 0;position:relative;font-size:13px;}.ff-form .ff-tabs .ff-cols .ff-col-100,.ff-table .ff-tabs .ff-cols .ff-col-100{width:100%;}
14
- .ff-form .ff-tabs .ff-cols .ff-col-50,.ff-table .ff-tabs .ff-cols .ff-col-50{width:50%;float:left;}
15
- .ff-form .ff-tabs .ff-cols .ff-col:first-child .ff-col-inner,.ff-table .ff-tabs .ff-cols .ff-col:first-child .ff-col-inner{padding-right:4px;}
16
- .ff-form .ff-tabs .ff-cols .ff-col:last-child .ff-col-inner,.ff-table .ff-tabs .ff-cols .ff-col:last-child .ff-col-inner{padding-left:4px;}
17
- .ff-form .ff-tabs .ff-cols .ff-col-inner,.ff-table .ff-tabs .ff-cols .ff-col-inner{padding:0;position:relative;}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field{position:relative;border-bottom:1px solid #b1b1b1;}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field:first-child,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field:first-child{border-top:1px solid #b1b1b1;}
18
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label{display:table-cell;vertical-align:top;position:relative;width:150px;padding:4px 4px 4px 8px;background-color:#c8ffb0;border-right:3px solid transparent;}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label.ff-required,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label.ff-required{border-right:3px solid #cf0000;}
19
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label .ff-field-hint,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label .ff-field-hint{display:block;height:16px;width:16px;position:absolute;right:0;top:8px;background-position:-96px -96px;}
20
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value{display:table-cell;padding:4px;vertical-align:top;}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value .ff-field-actions,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value .ff-field-actions{display:inline-block;border-left:1px solid #b1b1b1;padding-left:8px;margin-left:8px;}
21
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-before,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-before{padding-right:8px;}
22
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-after,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-after{padding-left:8px;}
23
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-complex-part,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-complex-part{display:inline-block;padding-right:8px;}
24
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-field,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-field{display:inline-block;border:none;padding-right:8px;}
25
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-link,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-link{margin-left:8px;}
26
- .ff-form .ff-tabs .ff-cols:after,.ff-table .ff-tabs .ff-cols:after{content:".";display:block;clear:both;width:0;height:0;overflow:hidden;}
27
- .ff-form .ff-tabs input,.ff-table .ff-tabs input{padding:4px;margin:0;font-size:13px;}
28
- .ff-form .ff-tabs select,.ff-table .ff-tabs select{padding:4px;margin:0;font-size:13px;width:auto;}
29
- .ff-form .ff-tabs .ff-error input,.ff-table .ff-tabs .ff-error input{border:1px solid red;color:red;background:#fff8f8;}
30
- .ff-form .ff-form-errors,.ff-table .ff-form-errors{margin:8px;padding:8px;border:1px solid red;background:#fee;color:red;}
31
- .ff-form ul.ff-form-errors li,.ff-table ul.ff-form-errors li{margin-left:16px;}
32
- .ff-form .ff-field-errors,.ff-table .ff-field-errors{color:red;padding:4px 0;}
33
- .ff-form .ff-bottom-actions,.ff-table .ff-bottom-actions{padding:8px;border-top:1px solid #b1b1b1;background:#e0e0e0;}
34
- .ff-form table.ff-common-table,.ff-table table.ff-common-table{margin-top:8px;width:100%;border-collapse:collapse;}.ff-form table.ff-common-table thead,.ff-table table.ff-common-table thead{text-align:left;border-top:1px solid #3316ff;border-bottom:1px solid #3316ff;background:#c8ffb0;}.ff-form table.ff-common-table thead th,.ff-table table.ff-common-table thead th{font-weight:normal;padding:4px 8px;}
35
- .ff-form table.ff-common-table thead .ff-field-hint,.ff-table table.ff-common-table thead .ff-field-hint{display:inline-block;height:16px;width:16px;background-position:-96px -96px;}
36
- .ff-form table.ff-common-table tbody td,.ff-table table.ff-common-table tbody td{padding:4px 8px;border-bottom:1px solid #3316ff;}
37
- .ff-empty{color:#a0a0a0;}
38
- .ff-table-empty{padding:32px;text-align:center;background-color:#f6f6f6;border:1px solid #a0a0a0;border-top:none;}
39
- .ff-map{width:100%;height:100%;overflow:hidden;position:absolute;top:0;left:0;right:0;bottom:0;}.ff-map img{max-width:none;}
40
- .ff-form .ff-title,.ff-table .ff-title{background-color:#cfc8ff;border-bottom:2px solid #3316ff;border-top:2px solid #3316ff;}
41
- .tooltip-inner{white-space:pre;max-width:none;}
1
+ .user-select-none{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}
2
+ .ff-form,.ff-table{margin-bottom:16px}.ff-form .ff-action,.ff-table .ff-action{margin-left:4px;font-size:13px}.ff-form .ff-action:first-child,.ff-table .ff-action:first-child{margin-left:0}
3
+ .ff-form .ff-action img,.ff-table .ff-action img{padding-right:4px;height:16px;width:16px}
4
+ .ff-form .ff-title,.ff-table .ff-title{position:relative}.ff-form .ff-title .ff-active-title,.ff-table .ff-title .ff-active-title{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:pointer;display:inline-block;padding:4px 8px;font-weight:bold;font-size:14px}.ff-form .ff-title .ff-active-title .ff-collapse,.ff-table .ff-title .ff-active-title .ff-collapse{display:inline-block;height:14px;width:14px;margin-right:4px;margin-top:2px}
5
+ .ff-form .ff-title .ff-active-title img,.ff-table .ff-title .ff-active-title img{height:16px;width:16px;overflow:hidden;margin-right:4px}
6
+ .ff-form .ff-title .ff-title-actions,.ff-table .ff-title .ff-title-actions{position:absolute;right:0;top:4px;padding:0 8px}
7
+ .ff-form .ff-collapse,.ff-table .ff-collapse{background:url(/glyphicons-halflings.png) -313px -119px}
8
+ .ff-form .ff-collapsed.ff-collapse,.ff-table .ff-collapsed.ff-collapse{background:url(/glyphicons-halflings.png) -456px -72px}
9
+ .ff-form .ff-field-hint,.ff-table .ff-field-hint{background:url(/glyphicons-halflings.png) -456px -72px}
10
+ .ff-form .ff-tabs,.ff-table .ff-tabs{padding:4px 0}.ff-form .ff-tabs ul.ff-tabs-header,.ff-table .ff-tabs ul.ff-tabs-header{font-size:13px;list-style-type:none;margin:0;padding:0;position:relative;border-bottom:1px solid #b1b1b1}.ff-form .ff-tabs ul.ff-tabs-header li,.ff-table .ff-tabs ul.ff-tabs-header li{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:pointer;display:inline-block;padding:4px 8px;margin-left:8px;margin-bottom:-1px;border:1px solid transparent}.ff-form .ff-tabs ul.ff-tabs-header li img,.ff-table .ff-tabs ul.ff-tabs-header li img{margin:0 4px;height:16px;width:16px}
11
+ .ff-form .ff-tabs ul.ff-tabs-header li.ff-selected,.ff-table .ff-tabs ul.ff-tabs-header li.ff-selected{background:#ffc;border:1px solid #b1b1b1;border-bottom:1px solid #ffc}
12
+ .ff-form .ff-tabs .ff-tab-actions,.ff-table .ff-tabs .ff-tab-actions{padding:4px 8px;background:#ffc;border:1px solid #b1b1b1;margin-top:-1px}
13
+ .ff-form .ff-tabs .ff-cols,.ff-table .ff-tabs .ff-cols{margin:4px 0 0 0;position:relative;font-size:13px}.ff-form .ff-tabs .ff-cols .ff-col-100,.ff-table .ff-tabs .ff-cols .ff-col-100{width:100%}
14
+ .ff-form .ff-tabs .ff-cols .ff-col-50,.ff-table .ff-tabs .ff-cols .ff-col-50{width:50%;float:left}
15
+ .ff-form .ff-tabs .ff-cols .ff-col:first-child .ff-col-inner,.ff-table .ff-tabs .ff-cols .ff-col:first-child .ff-col-inner{padding-right:4px}
16
+ .ff-form .ff-tabs .ff-cols .ff-col:last-child .ff-col-inner,.ff-table .ff-tabs .ff-cols .ff-col:last-child .ff-col-inner{padding-left:4px}
17
+ .ff-form .ff-tabs .ff-cols .ff-col-inner,.ff-table .ff-tabs .ff-cols .ff-col-inner{padding:0;position:relative}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field{position:relative;border-bottom:1px solid #b1b1b1}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field:first-child,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field:first-child{border-top:1px solid #b1b1b1}
18
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label{display:table-cell;vertical-align:top;position:relative;width:150px;padding:4px 4px 4px 8px;background-color:#c8ffb0;border-right:3px solid transparent}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label.ff-required,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label.ff-required{border-right:3px solid #cf0000}
19
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label .ff-field-hint,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label .ff-field-hint{display:block;height:16px;width:16px;position:absolute;right:0;top:8px;background-position:-96px -96px}
20
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value{display:table-cell;padding:4px;vertical-align:top}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value .ff-field-actions,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value .ff-field-actions{display:inline-block;border-left:1px solid #b1b1b1;padding-left:8px;margin-left:8px}
21
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value.ff-value-no-label,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value.ff-value-no-label{display:block}
22
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field{display:inline-block}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-link,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-link{margin-left:4px}
23
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-label,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-label{display:inline-block;padding:0 4px}
24
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field{display:inline}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field .ff-array-part,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field .ff-array-part{-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px;display:inline-block;margin:0 4px 4px 0;padding:2px 8px;border:1px solid #ddd;background:#fafafa}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field .ff-array-part .ff-action,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field .ff-array-part .ff-action{margin-left:4px}
25
+ .ff-form .ff-tabs .ff-cols:after,.ff-table .ff-tabs .ff-cols:after{content:".";display:block;clear:both;width:0;height:0;overflow:hidden}
26
+ .ff-form .ff-tabs input,.ff-table .ff-tabs input{padding:4px;margin:0;font-size:13px}
27
+ .ff-form .ff-tabs select,.ff-table .ff-tabs select{padding:4px;margin:0;font-size:13px;width:auto}
28
+ .ff-form .ff-tabs .ff-error input,.ff-table .ff-tabs .ff-error input{border:1px solid red;color:red;background:#fff8f8}
29
+ .ff-form .ff-tabs .ff-inline-hint,.ff-table .ff-tabs .ff-inline-hint{padding:4px 2px;color:#88f;font-size:12px}
30
+ .ff-form .ff-form-errors,.ff-table .ff-form-errors{margin:8px;padding:8px;border:1px solid red;background:#fee;color:red}
31
+ .ff-form ul.ff-form-errors li,.ff-table ul.ff-form-errors li{margin-left:16px}
32
+ .ff-form .ff-field-errors,.ff-table .ff-field-errors{color:red;padding:4px 0}
33
+ .ff-form .ff-bottom-actions,.ff-table .ff-bottom-actions{padding:8px;border-top:1px solid #b1b1b1;background:#e0e0e0}
34
+ .ff-form table.ff-common-table,.ff-table table.ff-common-table{margin-top:8px;width:100%;border-collapse:collapse}.ff-form table.ff-common-table thead,.ff-table table.ff-common-table thead{text-align:left;border-top:1px solid #3316ff;border-bottom:1px solid #3316ff;background:#c8ffb0}.ff-form table.ff-common-table thead th,.ff-table table.ff-common-table thead th{font-size:13px;font-weight:normal;padding:4px 8px}
35
+ .ff-form table.ff-common-table thead .ff-field-hint,.ff-table table.ff-common-table thead .ff-field-hint{display:inline-block;height:16px;width:16px;background-position:-96px -96px}
36
+ .ff-form table.ff-common-table tbody td,.ff-table table.ff-common-table tbody td{font-size:13px;padding:4px 8px;border-bottom:1px solid #3316ff;background:white}
37
+ .ff-form .ff-paginate .ff-totals,.ff-table .ff-paginate .ff-totals{padding:4px}.ff-form .ff-paginate .ff-totals span,.ff-table .ff-paginate .ff-totals span{padding-left:4px}
38
+ .ff-form .ff-field-before,.ff-table .ff-field-before{padding-right:8px}
39
+ .ff-form .ff-field-after,.ff-table .ff-field-after{padding-left:8px}
40
+ .ff-form .ff-complex-field .ff-complex-part,.ff-table .ff-complex-field .ff-complex-part{display:inline-block;padding-right:8px}
41
+ .ff-form .ff-complex-field .ff-field,.ff-table .ff-complex-field .ff-field{display:inline-block;border:none;padding-right:8px}
42
+ .ff-form .ff-select-field,.ff-table .ff-select-field{-webkit-border-radius:5px;-moz-border-radius:5px;-ms-border-radius:5px;-o-border-radius:5px;border-radius:5px;border:1px solid #ddd;padding:3px 3px 2px 3px;background:#ffc}.ff-form .ff-select-field .ff-select-label,.ff-table .ff-select-field .ff-select-label{min-width:100px}
43
+ .ff-empty{color:#a0a0a0}
44
+ .ff-table-empty{padding:32px;text-align:center;background-color:#f6f6f6;border:1px solid #a0a0a0;border-top:none}
45
+ .ff-map{width:100%;height:100%;overflow:hidden;position:absolute;top:0;left:0;right:0;bottom:0}.ff-map img{max-width:none}
46
+ .ff-form .ff-title,.ff-table .ff-title{background-color:#cfc8ff;border-bottom:2px solid #3316ff;border-top:2px solid #3316ff}
47
+ .tooltip-inner{white-space:pre;max-width:none}
@@ -11,6 +11,7 @@
11
11
  .ff-form .ff-action,
12
12
  .ff-table .ff-action {
13
13
  margin-left: 4px;
14
+ font-size: 13px;
14
15
  }
15
16
  .ff-form .ff-action:first-child,
16
17
  .ff-table .ff-action:first-child {
@@ -62,15 +63,15 @@
62
63
  }
63
64
  .ff-form .ff-collapse,
64
65
  .ff-table .ff-collapse {
65
- background: url(/assets/glyphicons-halflings.png) -313px -119px;
66
+ background: url(/glyphicons-halflings.png) -313px -119px;
66
67
  }
67
68
  .ff-form .ff-collapsed.ff-collapse,
68
69
  .ff-table .ff-collapsed.ff-collapse {
69
- background: url(/assets/glyphicons-halflings.png) -456px -72px;
70
+ background: url(/glyphicons-halflings.png) -456px -72px;
70
71
  }
71
72
  .ff-form .ff-field-hint,
72
73
  .ff-table .ff-field-hint {
73
- background: url(/assets/glyphicons-halflings.png) -456px -72px;
74
+ background: url(/glyphicons-halflings.png) -456px -72px;
74
75
  }
75
76
  .ff-form .ff-tabs,
76
77
  .ff-table .ff-tabs {
@@ -78,8 +79,10 @@
78
79
  }
79
80
  .ff-form .ff-tabs ul.ff-tabs-header,
80
81
  .ff-table .ff-tabs ul.ff-tabs-header {
82
+ font-size: 13px;
81
83
  list-style-type: none;
82
84
  margin: 0;
85
+ padding: 0;
83
86
  position: relative;
84
87
  border-bottom: 1px solid #b1b1b1;
85
88
  }
@@ -189,28 +192,43 @@
189
192
  padding-left: 8px;
190
193
  margin-left: 8px;
191
194
  }
192
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-before,
193
- .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-before {
194
- padding-right: 8px;
195
- }
196
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-after,
197
- .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-after {
198
- padding-left: 8px;
199
- }
200
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-complex-part,
201
- .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-complex-part {
202
- display: inline-block;
203
- padding-right: 8px;
195
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value.ff-value-no-label,
196
+ .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value.ff-value-no-label {
197
+ display: block;
204
198
  }
205
- .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-field,
206
- .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-field {
199
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field,
200
+ .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field {
207
201
  display: inline-block;
208
- border: none;
209
- padding-right: 8px;
210
202
  }
211
203
  .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-link,
212
204
  .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-link {
213
- margin-left: 8px;
205
+ margin-left: 4px;
206
+ }
207
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-label,
208
+ .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-label {
209
+ display: inline-block;
210
+ padding: 0 4px;
211
+ }
212
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field,
213
+ .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field {
214
+ display: inline;
215
+ }
216
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field .ff-array-part,
217
+ .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field .ff-array-part {
218
+ -webkit-border-radius: 5px;
219
+ -moz-border-radius: 5px;
220
+ -ms-border-radius: 5px;
221
+ -o-border-radius: 5px;
222
+ border-radius: 5px;
223
+ display: inline-block;
224
+ margin: 0 4px 4px 0;
225
+ padding: 2px 8px;
226
+ border: 1px solid #ddd;
227
+ background: #fafafa;
228
+ }
229
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field .ff-array-part .ff-action,
230
+ .ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-array-field .ff-array-part .ff-action {
231
+ margin-left: 4px;
214
232
  }
215
233
  .ff-form .ff-tabs .ff-cols:after,
216
234
  .ff-table .ff-tabs .ff-cols:after {
@@ -240,6 +258,12 @@
240
258
  color: red;
241
259
  background: #fff8f8;
242
260
  }
261
+ .ff-form .ff-tabs .ff-inline-hint,
262
+ .ff-table .ff-tabs .ff-inline-hint {
263
+ padding: 4px 2px;
264
+ color: #88f;
265
+ font-size: 12px;
266
+ }
243
267
  .ff-form .ff-form-errors,
244
268
  .ff-table .ff-form-errors {
245
269
  margin: 8px;
@@ -278,6 +302,7 @@
278
302
  }
279
303
  .ff-form table.ff-common-table thead th,
280
304
  .ff-table table.ff-common-table thead th {
305
+ font-size: 13px;
281
306
  font-weight: normal;
282
307
  padding: 4px 8px;
283
308
  }
@@ -290,8 +315,52 @@
290
315
  }
291
316
  .ff-form table.ff-common-table tbody td,
292
317
  .ff-table table.ff-common-table tbody td {
318
+ font-size: 13px;
293
319
  padding: 4px 8px;
294
320
  border-bottom: 1px solid #3316ff;
321
+ background: white;
322
+ }
323
+ .ff-form .ff-paginate .ff-totals,
324
+ .ff-table .ff-paginate .ff-totals {
325
+ padding: 4px;
326
+ }
327
+ .ff-form .ff-paginate .ff-totals span,
328
+ .ff-table .ff-paginate .ff-totals span {
329
+ padding-left: 4px;
330
+ }
331
+ .ff-form .ff-field-before,
332
+ .ff-table .ff-field-before {
333
+ padding-right: 8px;
334
+ }
335
+ .ff-form .ff-field-after,
336
+ .ff-table .ff-field-after {
337
+ padding-left: 8px;
338
+ }
339
+ .ff-form .ff-complex-field .ff-complex-part,
340
+ .ff-table .ff-complex-field .ff-complex-part {
341
+ display: inline-block;
342
+ padding-right: 8px;
343
+ }
344
+ .ff-form .ff-complex-field .ff-field,
345
+ .ff-table .ff-complex-field .ff-field {
346
+ display: inline-block;
347
+ border: none;
348
+ padding-right: 8px;
349
+ }
350
+ .ff-form .ff-select-field,
351
+ .ff-table .ff-select-field {
352
+ -webkit-border-radius: 5px;
353
+ -moz-border-radius: 5px;
354
+ -ms-border-radius: 5px;
355
+ -o-border-radius: 5px;
356
+ border-radius: 5px;
357
+ border: 1px solid #ddd;
358
+ padding: 3px 3px 2px 3px;
359
+ background: #ffffcc;
360
+ }
361
+ .ff-form .ff-select-field .ff-select-label,
362
+ .ff-table .ff-select-field .ff-select-label {
363
+ min-width: 100px;
295
364
  }
296
365
  .ff-empty {
297
366
  color: #a0a0a0;
@@ -1,8 +1,8 @@
1
1
  .user-select-none { user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none;}
2
2
  .rounded-corners(@radius: 5px) { -webkit-border-radius: @radius; -moz-border-radius: @radius; -ms-border-radius: @radius; -o-border-radius: @radius; border-radius: @radius; }
3
3
 
4
- @halflings: url(/assets/glyphicons-halflings.png);
5
- @halflings-white: url(/assets/glyphicons-halflings-white.png);
4
+ @halflings: url(/glyphicons-halflings.png);
5
+ @halflings-white: url(/glyphicons-halflings-white.png);
6
6
  @border-color: #b1b1b1;
7
7
  @selected-tab-color: #ffffcc;
8
8
  @base-font-size: 13px;
@@ -15,6 +15,7 @@
15
15
  margin-bottom: 16px;
16
16
  .ff-action {
17
17
  margin-left: 4px;
18
+ font-size: 13px;
18
19
  &:first-child { margin-left: 0; };
19
20
  img { padding-right: 4px; height: 16px; width: 16px; }
20
21
  }
@@ -33,7 +34,8 @@
33
34
  .ff-tabs {
34
35
  padding: 4px 0;
35
36
  ul.ff-tabs-header {
36
- list-style-type: none; margin: 0; position: relative;
37
+ font-size: 13px;
38
+ list-style-type: none; margin: 0; padding: 0; position: relative;
37
39
  border-bottom: 1px solid @border-color;
38
40
  li { .user-select-none; cursor: pointer; display: inline-block;
39
41
  padding: 4px 8px; margin-left: 8px; margin-bottom: -1px;
@@ -74,15 +76,21 @@
74
76
  .ff-value {
75
77
  display: table-cell; padding: 4px; vertical-align: top;
76
78
  .ff-field-actions { display: inline-block; border-left: 1px solid @border-color; padding-left: 8px; margin-left: 8px; }
77
- }
78
- .ff-field-before { padding-right: 8px; }
79
- .ff-field-after { padding-left: 8px; }
80
- .ff-complex-field {
81
- .ff-complex-part { display: inline-block; padding-right: 8px; }
82
- .ff-field { display: inline-block; border: none; padding-right: 8px; }
79
+ &.ff-value-no-label { display: block; }
83
80
  }
84
81
  .ff-select-field {
85
- .ff-select-link { margin-left: 8px; }
82
+ display: inline-block;
83
+ .ff-select-link { margin-left: 4px; }
84
+ .ff-select-label { display: inline-block; padding: 0 4px; }
85
+ }
86
+ .ff-array-field {
87
+ display: inline;
88
+ .ff-array-part {
89
+ .rounded-corners; display: inline-block; margin: 0 4px 4px 0;
90
+ padding: 2px 8px; border: 1px solid #ddd;
91
+ background: #fafafa;
92
+ .ff-action { margin-left: 4px; }
93
+ }
86
94
  }
87
95
  }
88
96
  }
@@ -91,6 +99,7 @@
91
99
  input { padding: 4px; margin: 0; font-size: @base-font-size; }
92
100
  select { padding: 4px; margin: 0; font-size: @base-font-size; width: auto; }
93
101
  .ff-error input { border: 1px solid red; color: red; background: #fff8f8; }
102
+ .ff-inline-hint { padding: 4px 2px; color: #88f; font-size: 12px; }
94
103
  }
95
104
  .ff-form-errors { margin: 8px; padding: 8px; border: 1px solid red; background: #fee; color: red;}
96
105
  ul.ff-form-errors { li { margin-left: 16px; } }
@@ -102,16 +111,40 @@
102
111
  border-top: 1px solid @blue-theme-border;
103
112
  border-bottom: 1px solid @blue-theme-border;
104
113
  background: @label-background;
105
- th { font-weight: normal; padding: 4px 8px; }
114
+ th { font-size: 13px; font-weight: normal; padding: 4px 8px; }
106
115
  .ff-field-hint { display: inline-block; height: 16px; width: 16px; background-position: -96px -96px; }
107
116
  }
108
117
  tbody {
109
- td { padding: 4px 8px; border-bottom: 1px solid @blue-theme-border; }
118
+ td {
119
+ font-size: 13px;
120
+ padding: 4px 8px; border-bottom: 1px solid @blue-theme-border;
121
+ background: white;
122
+ }
110
123
  }
111
124
  }
125
+ .ff-paginate {
126
+ .ff-totals {
127
+ padding: 4px;
128
+ span { padding-left: 4px; }
129
+ }
130
+ }
131
+ .ff-field-before { padding-right: 8px; }
132
+ .ff-field-after { padding-left: 8px; }
133
+ .ff-complex-field {
134
+ .ff-complex-part { display: inline-block; padding-right: 8px; }
135
+ .ff-field { display: inline-block; border: none; padding-right: 8px; }
136
+ }
137
+ .ff-select-field {
138
+ .rounded-corners;
139
+ border: 1px solid #ddd;
140
+ padding: 3px 3px 2px 3px;
141
+ background: @selected-tab-color;
142
+ .ff-select-label { min-width: 100px; }
143
+ }
112
144
  }
113
145
  .ff-empty { color: #a0a0a0; }
114
146
  .ff-table-empty { padding: 32px; text-align: center; background-color: #f6f6f6; border: 1px solid #a0a0a0; border-top: none; }
147
+ //.ff-table-body { background: white; }
115
148
 
116
149
  .ff-map {
117
150
  width:100%; height:100%; overflow:hidden; position: absolute;
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
5
- prerelease:
4
+ version: 0.1.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Dimitri Kurashvili
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-05-23 00:00:00.000000000 Z
11
+ date: 2014-01-04 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,23 +27,20 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rspec
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ~>
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ~>
60
53
  - !ruby/object:Gem::Version
@@ -62,7 +55,6 @@ dependencies:
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: therubyracer
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
59
  - - ~>
68
60
  - !ruby/object:Gem::Version
@@ -70,7 +62,6 @@ dependencies:
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
66
  - - ~>
76
67
  - !ruby/object:Gem::Version
@@ -78,7 +69,6 @@ dependencies:
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: less
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
73
  - - ~>
84
74
  - !ruby/object:Gem::Version
@@ -86,7 +76,6 @@ dependencies:
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
80
  - - ~>
92
81
  - !ruby/object:Gem::Version
@@ -94,33 +83,29 @@ dependencies:
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: railties
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - '>='
100
88
  - !ruby/object:Gem::Version
101
89
  version: '3.1'
102
90
  type: :runtime
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - '>='
108
95
  - !ruby/object:Gem::Version
109
96
  version: '3.1'
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: activesupport
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - '>='
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :runtime
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
108
+ - - '>='
124
109
  - !ruby/object:Gem::Version
125
110
  version: '0'
126
111
  description: rich forms for ruby
@@ -157,6 +142,7 @@ files:
157
142
  - test/field_test.rb
158
143
  - test/form_test.rb
159
144
  - test/test_helper.rb
145
+ - test/utils_test.rb
160
146
  - vendor/assets/images/ff-icons.png
161
147
  - vendor/assets/javascripts/forma.js
162
148
  - vendor/assets/stylesheets/forma-min.css
@@ -165,27 +151,26 @@ files:
165
151
  homepage: http://github.com/dimakura/forma
166
152
  licenses:
167
153
  - MIT
154
+ metadata: {}
168
155
  post_install_message:
169
156
  rdoc_options: []
170
157
  require_paths:
171
158
  - lib
172
159
  required_ruby_version: !ruby/object:Gem::Requirement
173
- none: false
174
160
  requirements:
175
- - - ! '>='
161
+ - - '>='
176
162
  - !ruby/object:Gem::Version
177
163
  version: '0'
178
164
  required_rubygems_version: !ruby/object:Gem::Requirement
179
- none: false
180
165
  requirements:
181
- - - ! '>='
166
+ - - '>='
182
167
  - !ruby/object:Gem::Version
183
168
  version: '0'
184
169
  requirements: []
185
170
  rubyforge_project:
186
- rubygems_version: 1.8.24
171
+ rubygems_version: 2.0.3
187
172
  signing_key:
188
- specification_version: 3
173
+ specification_version: 4
189
174
  summary: rich forms for ruby
190
175
  test_files:
191
176
  - spec/config_spec.rb
@@ -197,3 +182,4 @@ test_files:
197
182
  - test/field_test.rb
198
183
  - test/form_test.rb
199
184
  - test/test_helper.rb
185
+ - test/utils_test.rb