polymer-rails-forms 0.1.15 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 99e7b1677ddc0f20f5fd3b099d441374ef2026e7
4
- data.tar.gz: 70aa1538f6ff43a9f410aee9234ac5f2f39825d8
3
+ metadata.gz: ddb55e7abf7c864da70f1a3c27bef1c123444db2
4
+ data.tar.gz: e28459f4839d74416d98189908cc64e2cb406b9a
5
5
  SHA512:
6
- metadata.gz: 1e31bb104ede0d50094ccf06a6967b2a4f66b320f7556881a984d4f2d0c9224aad51ecbb135626d965a4c996dcac7a5c75dc40a72104ccee02473dd164371376
7
- data.tar.gz: 50e87d98b438f12bb271feab02ad8a95cd1b75a015ce99f0fa1c2c5c5c8596464d14e11e1f8da254bcc08f544e6d89d96a8099035f3624f6fd44690d9f57dcad
6
+ metadata.gz: 476fc7749837a3ad74e61b4468de204f1c9e159068e34e3c26cca011a037290a00fcdea05ff886bf49b907ac216181f8db40bf84184f4a357b22ae43d4d56789
7
+ data.tar.gz: 571d731a8643a554bc9226fcd54af5d88e016edb05e3950651951ffd416b6454eaf43ad49eb14ba79f186a8568a8c9a6cd2d6457d7274556646484f10bd72a15
data/README.md CHANGED
@@ -29,7 +29,7 @@ then in your /app/assets/components/application.html file add
29
29
  This is what a simple login form could look like using this gem
30
30
 
31
31
  ```html
32
- <rails-form id='sign_in_form' action="/users/sign_in" methos="POST" scope="user" submitText="Sign In"></rails-form>
32
+ <rails-form id='sign_in_form' action="/users/sign_in" method="POST" scope="user" submitText="Sign In"></rails-form>
33
33
  <script>
34
34
  document.getElementById("sign_in_form").setAttribute("structure", JSON.stringify({
35
35
  email: { type: 'string', label: "Email Address" },
@@ -85,8 +85,8 @@ again, but this time with nested location attributes
85
85
 
86
86
  ready: function(){
87
87
  this.structure = {
88
- email: { type: 'string', label: "Email Address" },
89
- password: { type: 'password' },
88
+ email: { type: 'string', label: "Email Address", required: true },
89
+ password: { type: 'password', required: true },
90
90
  location: { type: 'nest', allowAdd: false, multiple: false, structure: {
91
91
  address: { type: "string" },
92
92
  city: { type: "string" },
@@ -106,6 +106,11 @@ to set ```multiple: true``` the name would become ```user[location_attributes][0
106
106
  you were to set ```allowAdd: true``` the inputs would be in a list with the option to create more.
107
107
 
108
108
 
109
+ ###Validations
110
+
111
+ Also not that I've included ```required: true``` on the email and password fields. This means this triggers
112
+ them to be validated onSubmit and onChange. For a custome validation just use ```validates: 'method_name'```
113
+
109
114
  ####Note:
110
115
 
111
116
  If you're going to use the domReady function in your custom form element, be sure to call
@@ -118,6 +123,12 @@ functionality though, just make sure you run the ```this.appendInputs()``` so th
118
123
  To make the form ajaxy just include the form element's ```xhr='true'``` param
119
124
  and override the ```handleXhrCallback``` function.
120
125
 
126
+
127
+ ###Selects
128
+
129
+ selects are the same as any other input except that they also require the values property which is and array of
130
+ arrays, so ```[[value1, text2], [value2, text2]]```
131
+
121
132
  ##What's supported what's not
122
133
 
123
134
  ###So far the field types that are supported are:
@@ -138,7 +149,6 @@ and override the ```handleXhrCallback``` function.
138
149
  ###What's not supported
139
150
 
140
151
  * radio buttons
141
- * selects
142
152
  * ranges
143
153
  * everything else
144
154
 
@@ -0,0 +1,101 @@
1
+ <polymer-element name="form-spinner">
2
+ <template>
3
+ <style>
4
+ .spinner {
5
+ width: {{ width }}{{ unit }};
6
+ height: {{ height }}{{ unit }};
7
+ position: relative;
8
+ display: block;
9
+ }
10
+
11
+ .circle {
12
+ width: 25%;
13
+ height: 25%;
14
+ border-radius: 50%;
15
+ background-color: white;
16
+ position: absolute;
17
+ opacity: 0;
18
+
19
+ transition: opacity 300ms ease-out;
20
+ }
21
+
22
+ .fade.circle {
23
+ opacity: 1;
24
+ }
25
+
26
+ .circle_row_1 {
27
+ top: 0;
28
+ }
29
+
30
+ .circle_row_2 {
31
+ top: 50%;
32
+ margin-top: -12.5%;
33
+ }
34
+
35
+ .circle_row_3 {
36
+ bottom: 0;
37
+ }
38
+
39
+ .circle_column_1 {
40
+ left: 0;
41
+ }
42
+
43
+ .circle_column_2 {
44
+ left: 33.3%;
45
+ margin-left: -12.5%;
46
+ }
47
+
48
+ .circle_column_3 {
49
+ left: 66.6%;
50
+ margin-left: -12.5%;
51
+ }
52
+
53
+ .circle_column_4 {
54
+ right: 0;
55
+ }
56
+ </style>
57
+ <div class="spinner">
58
+ <div class="circle circle_row_1 circle_column_2 circle6"></div>
59
+ <div class="circle circle_row_1 circle_column_3 circle1"></div>
60
+ <div class="circle circle_row_2 circle_column_1 circle5"></div>
61
+ <div class="circle circle_row_2 circle_column_4 circle2"></div>
62
+ <div class="circle circle_row_3 circle_column_2 circle4"></div>
63
+ <div class="circle circle_row_3 circle_column_3 circle3"></div>
64
+ </div>
65
+ </template>
66
+
67
+ <script>
68
+ Polymer({
69
+ publish: {
70
+ width: 2,
71
+ height: 2,
72
+ unit: 'em'
73
+ },
74
+
75
+ start: function(){
76
+ console.log("starting")
77
+ var index = 1,
78
+ alt = 4,
79
+ spinner = this.shadowRoot.querySelector('.spinner');
80
+
81
+ spinner.classList.add('active');
82
+ var t = setInterval(function(){
83
+ if (spinner.classList.contains("active") === -1){
84
+ clearInterval(t);
85
+ return;
86
+ }
87
+ spinner.querySelector(".circle" + index).classList.add("fade");
88
+ spinner.querySelector(".circle" + alt).classList.remove("fade");
89
+
90
+ index = index + 1 > 6 ? 1 : index + 1;
91
+ alt = alt + 1 > 6 ? 1 : alt + 1;
92
+ }.bind(this), 100);
93
+ },
94
+
95
+ stop: function(){
96
+ console.log("stop")
97
+ this.shadowRoot.querySelector('.spinner').classList.remove('active');
98
+ }
99
+ });
100
+ </script>
101
+ </polymer-element>
@@ -41,7 +41,15 @@
41
41
 
42
42
  hasClass: function(el, cls){
43
43
  var reg = new RegExp("\\s+" + cls, "g")
44
- return reg.test(el.className);
44
+ return reg.test(el.className) || el.className === cls;
45
+ },
46
+
47
+ toggleClass: function(el, cls){
48
+ if (this.hasClass(el, cls)){
49
+ this.removeClass(el, cls);
50
+ } else {
51
+ this.addClass(el, cls);
52
+ }
45
53
  },
46
54
 
47
55
  indexStore: {},
@@ -54,6 +62,12 @@
54
62
  return key;
55
63
  },
56
64
 
65
+ getIndex: function(obj){
66
+ for (var x in this.indexStore){
67
+ if (this.indexStore[x].obj === obj) return this.indexStore[x].index;
68
+ }
69
+ },
70
+
57
71
  addToIndexStore: function(obj, index){
58
72
  var key = this.genIndexStoreKey();
59
73
  this.indexStore[key] = {obj: obj, index: index}
@@ -73,19 +87,21 @@
73
87
 
74
88
  enumerate: function(obj){
75
89
  //TODO: keygen should check for uniquness
76
- for (var i=0; i<obj.length; i++){
77
- this.addToIndexStore(obj[i], i)
90
+ var used = [];
91
+ for (var i=0; i<obj.length; i++){
92
+ var current_index = this.getIndex(obj[i]);
93
+ if (current_index) used.push();
94
+ }
95
+
96
+ for (var i=0; i<obj.length; i++){
97
+ var index = i;
98
+ while (used.indexOf(index) !== -1){ index++ }
99
+ this.addToIndexStore(obj[i], index);
78
100
  }
79
101
 
80
102
  return obj
81
103
  },
82
104
 
83
- getIndex: function(obj){
84
- for (var x in this.indexStore){
85
- if (this.indexStore[x].obj === obj) return this.indexStore[x].index;
86
- }
87
- },
88
-
89
105
  /* Type Checks */
90
106
 
91
107
  getKeys: function(o){
@@ -135,7 +151,6 @@
135
151
  }
136
152
  }
137
153
  } else {
138
- console.log(!(o[x] === null || o[x].length === 0))
139
154
  if (!(o[x] === null || o[x].length === 0)){
140
155
  blank = false;
141
156
  break;
@@ -206,7 +221,12 @@
206
221
  var data = this.classToData(t.className),
207
222
  newData = {};
208
223
 
209
- this.addToIndexStore(newData, data.length);
224
+ var index = data.length,
225
+ used = [];
226
+ for (var i=0; i<data.length; i++){ used.push(this.getIndex(data[i])) }
227
+ while (used.indexOf(index) !== -1){ index++ }
228
+
229
+ this.addToIndexStore(newData, index);
210
230
  data.push(newData)
211
231
  } else {
212
232
  invalid.focus();
@@ -214,18 +234,25 @@
214
234
  },
215
235
 
216
236
  updateScope: function(key){
217
- var scope = this.scope + "[" + this.key + "_attributes]";
237
+ var scope = this.structure[this.key].unscoped ? this.key : this.scope + "[" + this.key + "_attributes]";
238
+
218
239
  this.scope = scope;
219
240
  return this.scope;
220
241
  },
221
242
 
222
243
  scopeFieldData: function(data, key){
223
- if (data[key] === void(0)) data[key] = null;
224
- return data;
244
+ data = this.structure[this.key].unscoped ? this.unscopedData : data;
245
+ //console.log("field", key, data[key]);
246
+ if (this.structure[this.key].type !== 'html'){
247
+ if (data[key] === void(0)) data[key] = this.structure[this.key].value || null;
248
+ return data;
249
+ }
225
250
  },
226
251
 
227
252
  scopeNestData: function(data, key){
228
- if (this.structure[key].multiple){
253
+ data = this.structure[this.key].unscoped ? this.unscopedData : data;
254
+ //console.log("nest", key, data[key]);
255
+ if (this.structure[key].multiple){
229
256
  if (data[key] === void(0)){
230
257
  if (this.structure[key].allowAdd) {
231
258
  data[key] = [];
@@ -325,9 +352,8 @@
325
352
  createListItem: function(obj){
326
353
  var className = this.listItemClassName(obj),
327
354
  isEmpty = Object.keys(obj).length === 0,
328
- container = document.createElement("div");
329
- this.addClass(container, "list-item-display")
330
-
355
+ container = this.createWithAttributes("div", {'class': "list-item-display"});
356
+
331
357
  if (this.structure[this.key].display_fields){
332
358
  for (var i=0; i<this.structure[this.key].display_fields.length; i++){
333
359
  var field = this.structure[this.key].display_fields[i],
@@ -336,7 +362,8 @@
336
362
 
337
363
  var el = document.createElement("div"),
338
364
  label = this.createWithAttributes("span", { text: this.capitalize(nameField.replace(/_/g, " ")) + ": " });
339
-
365
+
366
+
340
367
  var value = document.createElement("span"),
341
368
  val = document.createTextNode("");
342
369
 
@@ -357,17 +384,39 @@
357
384
  if (!obj[baseField]) obj[baseField] = "";
358
385
  val.bind("textContent", new PathObserver(obj, field));
359
386
  }
360
-
361
- el.appendChild(label)
362
- el.appendChild(value)
363
- value.appendChild(val)
387
+
388
+ el.appendChild(label);
389
+ el.appendChild(value);
390
+ value.appendChild(val);
364
391
 
365
392
  container.appendChild(el);
366
393
  }
367
394
  } else {
368
395
  container.appendChild(this.createWithAttributes("div", {text: "item " + (this.getIndex(obj) + 1)}))
369
396
  }
370
-
397
+
398
+ var remove_button = this.createWithAttributes("core-icon", {icon: 'clear'});
399
+ remove_button.addEventListener("click", function(e){
400
+ e.stopPropagation();
401
+ var clone = this.parentData.slice(0);
402
+ clone.splice(clone.indexOf(obj), 1);
403
+
404
+ while (this.parentData.length){
405
+ this.removeFromIndexStore(this.parentData[0]);
406
+ this.parentData.shift();
407
+ }
408
+
409
+ this.async(function(){
410
+ for (var i=0; i<clone.length; i++){
411
+ this.addToIndexStore(clone[i], i);
412
+ this.parentData.push(clone[i]);
413
+ }
414
+ });
415
+
416
+ }.bind(this), false)
417
+
418
+ container.appendChild(remove_button);
419
+
371
420
  var self = this;
372
421
  this.inputList.push({ element: container, container: ".list-item." + className, events: {} })
373
422
 
@@ -432,14 +481,26 @@
432
481
  },
433
482
 
434
483
  scopeToClass: function(key){
435
- var className = this.scope.replace(/\[/g, "_").replace(/\]/g, "") + "_" + key;
436
- return className;
484
+ if (this.structure[this.key].unscoped){
485
+ var className = key
486
+ } else {
487
+ var className = this.scope.replace(/\[/g, "_").replace(/\]/g, "") + "_" + key;
488
+ }
489
+ return className;
437
490
  },
438
491
 
439
- classToData: function(className){
440
- var cn = className.split("_").slice(1).join("_"),
441
- data = this.data;
492
+ classToData: function(cn){
493
+ var self = this,
494
+ data = this.unscopedData;
442
495
 
496
+ for (var x in self.unscopedData){
497
+ if (cn.substr(0, x.length) === x){
498
+ cn = cn.replace(new RegExp(x + "_?"), "");
499
+ data = data[x];
500
+ break;
501
+ }
502
+ }
503
+
443
504
  function nest(str){
444
505
  var p = str.split("_attributes_");
445
506
  if (p.length > 1){
@@ -463,7 +524,7 @@
463
524
  }
464
525
 
465
526
  while (nest(cn) || arr(cn)){ }
466
- return data[cn];
527
+ return cn.length > 0 ? data[cn] : data;
467
528
  },
468
529
 
469
530
  checkForBlank: function(group){
@@ -551,6 +612,96 @@
551
612
  return wrapper;
552
613
  }.bind(this),
553
614
 
615
+ select: function(){
616
+ var structure = this.structure[this.key],
617
+ values = structure.values;
618
+
619
+ var wrapper = this.createWithAttributes("div", {"class": "select-wrapper"}),
620
+ decorator = this.createWithAttributes("gc-input-decorator", { label: this.capitalize(structure.label || this.key), floatingLabel: structure.options.floatingLabel}),
621
+ secretInput = this.createWithAttributes('input', {type: 'hidden', name: this.updateFieldScope()}),
622
+ input = this.createWithAttributes("input", { type: "text", name: "", "class": "pseudo-select-input", disabled: true }),
623
+ select = this.createWithAttributes("core-selector", {valueattr: "val", "class": "pseudo-select", multi: (!!structure.multi) });
624
+
625
+ secretInput.bind('value', new PathObserver(this.data, this.key));
626
+ select.bind('selected', new PathObserver(this.data, this.key));
627
+
628
+ if (!structure.blank){
629
+ this.data[this.key] = values.length > 0 ? values[0][0] : "";
630
+ input.value = values.length > 0 ? values[0][1] : "";
631
+ }
632
+ var self = this;
633
+
634
+ select.addEventListener("click", function(e){
635
+ if (!!self.structure[self.key].multi) e.stopPropagation();
636
+ }, false);
637
+
638
+ decorator.addEventListener('click', function(e){
639
+ if (self.hasClass(wrapper, 'opened')) return;
640
+
641
+ e.stopPropagation();
642
+ decorator.updateLabelVisibility("down");
643
+ self.addClass(wrapper, 'opened');
644
+
645
+ window.addEventListener('click', function(){
646
+ self.removeClass(wrapper, 'opened');
647
+ window.removeEventListener('click', arguments.callee);
648
+ }, false);
649
+ }, false);
650
+
651
+ select.addEventListener('core-select', function(){
652
+ if (!self.structure[self.key].multi) self.removeClass(wrapper, 'opened');
653
+ decorator.updateLabelVisibility(select.selected ? select.selected : "");
654
+
655
+ self.async(function(){
656
+ if (!!structure.multi){
657
+ input.value = select === null ? "" : select.selectedItem.map(function(s){ return s.textContent }).join(', ');
658
+ secretInput.value = JSON.stringify( select.selectedItem.map(function(s){ return s.getAttribute('val') }) );
659
+ } else {
660
+ input.value = select === null ? "" : select.selectedItem.textContent;
661
+ secretInput.value = select.selectedItem.getAttribute('val');
662
+ }
663
+ });
664
+ }, false);
665
+
666
+ var build_options = function(){
667
+ while (select.firstChild) { select.removeChild(select.firstChild) };
668
+ if (structure.blank) select.appendChild(this.createWithAttributes("div", {val: "", text: "" }))
669
+ for (var i=0; i<structure.values.length; i++){
670
+ var div = self.createWithAttributes("div", {val: structure.values[i][0], text: structure.values[i][1] });
671
+
672
+ if (!!structure.multi){
673
+ var checkbox = this.createWithAttributes("paper-checkbox", {"class": "select-check"});
674
+ div.appendChild(checkbox);
675
+ div.addEventListener("click", function(el, box){
676
+ this.async(function(){
677
+ box.checked = this.hasClass(el, 'core-selected');
678
+ }.bind(this));
679
+ }.bind(this, div, checkbox), false)
680
+ }
681
+ select.appendChild(div);
682
+ }
683
+
684
+ var nil = structure.values.length === 0 ? self.addClass(wrapper, 'is-blank') : self.removeClass(wrapper, 'is-blank');
685
+ nil = structure.values.length === 1 ? self.addClass(wrapper, 'is-single') : self.removeClass(wrapper, 'is-single');
686
+ }.bind(this);
687
+
688
+ build_options();
689
+ new PathObserver(structure, 'values').open(function(){
690
+ build_options();
691
+ })
692
+
693
+ new PathObserver(structure, 'label').open(function(){
694
+ decorator.label = this.capitalize(this.structure[this.key].label || this.key)
695
+ }.bind(this));
696
+
697
+ wrapper.appendChild(decorator);
698
+ decorator.appendChild(this.createWithAttributes("core-icon", {icon: "expand-more", "class": 'select-icon'}));
699
+ decorator.appendChild(input);
700
+ wrapper.appendChild(select);
701
+ wrapper.appendChild(secretInput);
702
+ return wrapper;
703
+ }.bind(this),
704
+
554
705
  location: function(){
555
706
  var wrapper = document.createElement("gc-input-decorator"),
556
707
  input = document.createElement( "input" );
@@ -576,8 +727,8 @@
576
727
  self.data[self.key].lat = place.geometry.location.lat();
577
728
  self.data[self.key].lng = place.geometry.location.lng();
578
729
 
579
- if (self.structure[self.key].options.events.select){
580
- self.structure[self.key].options.events.select(self.data[self.key])
730
+ if (!!self.structure[self.key].events.select){
731
+ self.structure[self.key].events.select(self.data[self.key])
581
732
  }
582
733
  })
583
734
 
@@ -610,9 +761,13 @@
610
761
  wrapper.parentNode.style.backgroundImage = "url(" + oFREvent.target.result + ")";
611
762
  self.addClass(wrapper.parentNode, "has-image");
612
763
  };
764
+
765
+ self.data[self.key] = this.files[0];
613
766
  } else {
614
767
  self.removeClass(wrapper.parentNode, 'has-image');
615
768
  wrapper.parentNode.style.backgroundImage = "";
769
+
770
+ self.data[self.key] = null;
616
771
  }
617
772
  }, true)
618
773
 
@@ -664,6 +819,7 @@
664
819
 
665
820
  input.type = type;
666
821
  input.name = name || this.updateFieldScope();
822
+ input.value = this.structure[this.key].value || '';
667
823
  input.id = this.scopeToClass(this.key)
668
824
  if (this.structure[this.key].required) input.required = true;
669
825
  input.bind('value', new PathObserver(this.data, this.key))
@@ -683,21 +839,20 @@
683
839
  createJSONField: function(fields){
684
840
  //TODO: only basic JSON support, if you want crazy complicated stuff, just use a textarea or custom input
685
841
  var wrapper = this.createWithAttributes('div', { "class": "json-wrapper" }),
686
- input = this.createWithAttributes("textarea", { name: this.updateFieldScope(), "class": "hidden" }),
842
+ input = this.createWithAttributes("textarea", { name: this.updateFieldScope(), "class": "hidden", text: JSON.stringify(this.data[this.key]) }),
687
843
  isList = this.parentStructure !== null && this.parentStructure[this.parentContext].allowAdd;
688
844
 
689
845
  if (!this.data[this.key]) this.data[this.key] = {};
690
-
691
- var observer = new ObjectObserver(this.data[this.key])
692
- observer.open(function(){
846
+
847
+ new ObjectObserver(this.data[this.key]).open(function(){
693
848
  input.value = JSON.stringify(this.data[this.key])
694
849
  }.bind(this))
695
850
 
696
- input.bind('value', new PathObserver(this.data, this.key))
851
+ // input.bind('value', new PathObserver(this.data, this.key))
697
852
 
698
853
  wrapper.appendChild(input);
699
854
  for (var i=0; i<fields.length; i++){
700
- this.data[this.key][fields[i]] = "";
855
+ this.data[this.key][fields[i]] = this.data[this.key][fields[i]] || "";
701
856
  var input_wrapper = this.createWithAttributes("div", { "class": (isList ? "input-wrapper" : "input") + " json-input-wrapper"})
702
857
  decorator = document.createElement("gc-input-decorator"),
703
858
  el = this.createWithAttributes('input');
@@ -709,8 +864,6 @@
709
864
 
710
865
  el.bind("value", new PathObserver(this.data[this.key], fields[i]))
711
866
 
712
- //TODO: create a parentStruture thing. check the parent structure for isNull or allowAdd
713
- //change the container selector appropriately
714
867
  if (isList){
715
868
  var className = "." + this.parentContext + "-" + this.getIndex(this.data) + " .input-wrapper." + this.key;
716
869
  var o = { element: input_wrapper, container: className, events: {}, position: "before" }
@@ -719,7 +872,6 @@
719
872
  }
720
873
 
721
874
  this.inputList.push(o)
722
- //wrapper.appendChild(decorator);
723
875
  }
724
876
 
725
877
  return wrapper;
@@ -1,18 +1,22 @@
1
1
  <script>
2
2
  var Validation = {
3
3
  errors: {},
4
+ validatedInputs: {},
4
5
 
5
6
  addValidation: function(type, input){
6
7
  //TODO should validate everything
7
8
  if (this.structure[this.key].required){
8
- input.addEventListener("change", function(){
9
- this.validate(this.structure[this.key].type, input);
10
- }.bind(this), false)
9
+ var fn = function(){ this.validate(this.structure[this.key].type, input); }.bind(this)
11
10
  } else if (!!this.structure[this.key].validates){
12
- input.addEventListener("change", function(){
13
- this[this.structure[this.key].validates](type, input)
14
- }.bind(this), false)
11
+ var fn = function(){ this[this.structure[this.key].validates](type, input) }.bind(this)
12
+ } else {
13
+ var fn = function(){ return false; }
15
14
  }
15
+
16
+ var key = this.genIndexStoreKey();
17
+ this.validatedInputs[key] = {input: input, fn: fn};
18
+ input.addEventListener('change', fn, false);
19
+ input.addEventListener('DOMNodeRemoved', function(){ delete this.validatedInputs[key] }.bind(this), false);
16
20
  },
17
21
 
18
22
  invalidate: function(type, input, isInvalid){
@@ -65,8 +69,7 @@
65
69
  }.bind(this),
66
70
 
67
71
  integer: function(){
68
- console.log("checking integer", isNaN(input.value))
69
- return !(required && !isNaN(input.value))
72
+ return !(required && input.value.length > 0 && !isNaN(input.value))
70
73
  }.bind(this),
71
74
 
72
75
  url: function(){
@@ -91,7 +94,11 @@
91
94
 
92
95
  image: function(){
93
96
  return !(required && input.value.length > 0)
94
- }.bind(this)
97
+ }.bind(this),
98
+
99
+ select: function(){
100
+ return false;
101
+ }
95
102
  }
96
103
 
97
104
  var isInvalid = (!!validators[type] && validators[type]())
@@ -100,6 +107,17 @@
100
107
  this.invalidate(type, input, isInvalid);
101
108
  }
102
109
 
110
+ return isInvalid;
111
+ },
112
+
113
+ isFormInvalid: function(){
114
+ var isInvalid = false;
115
+ for (var x in this.validatedInputs){
116
+ if (this.validatedInputs[x].fn()){
117
+ isInvalid = true;
118
+ }
119
+ }
120
+
103
121
  return isInvalid;
104
122
  }
105
123
  }
@@ -1,6 +1,7 @@
1
1
  <link rel="import" href="rails-form-helpers.html" >
2
2
  <link rel="import" href="rails-form-validators.html" >
3
3
  <link rel="import" href="gc-paper-decorator.html" >
4
+ <link rel="import" href="form-spinner.html" >
4
5
  <link rel="import" href="../paper-button/paper-button.html" >
5
6
  <link rel="import" href="../core-icons/image-icons.html" >
6
7
  <link rel="import" href="../core-ajax/core-xhr.html" >
@@ -11,6 +12,9 @@
11
12
  <template>
12
13
  <link rel='stylesheet' href='pikaday.css'>
13
14
  <style>
15
+ :host {
16
+ display: block;
17
+ }
14
18
  paper-button {
15
19
  font-size: 14px;
16
20
  }
@@ -22,11 +26,26 @@
22
26
 
23
27
  #submit-button {
24
28
  margin-top: 15px;
29
+ margin-bottom: 5px;
25
30
  padding-left: 30px;
26
31
  padding-right: 30px;
27
32
  background-color: #5bc0de;
28
33
  border-color: #46b8da;
29
34
  color: white;
35
+ clear: both;
36
+ margin-right: 15px;
37
+ }
38
+
39
+ #submit-button.in-transit span{
40
+ display: none;
41
+ }
42
+
43
+ #submit-button.in-transit form-spinner {
44
+ display: block;
45
+ }
46
+
47
+ #submit-button form-spinner {
48
+ display: none;
30
49
  }
31
50
 
32
51
  gc-input-decorator {
@@ -99,6 +118,16 @@
99
118
  min-height: 55px;
100
119
  }
101
120
 
121
+ .list-wrapper paper-button {
122
+ max-width: 28%;
123
+ margin: 0px;
124
+ }
125
+
126
+ .list-wrapper paper-button /deep/ .button-content{
127
+ -webkit-justify-content: flex-start !important;
128
+ justify-content: flex-start !important;
129
+ }
130
+
102
131
  .list-wrapper .list {
103
132
  overflow: auto;
104
133
  position: relative;
@@ -126,6 +155,16 @@
126
155
  box-sizing: border-box;
127
156
  }
128
157
 
158
+ .list-group .list-item core-icon {
159
+ height: 0px;
160
+ border: solid 1px #A1A1A1;
161
+ padding: 7px;
162
+ width: 0px;
163
+ border-radius: 8px;
164
+ color: red;
165
+ margin-right: 10px;
166
+ }
167
+
129
168
  .list-group .list-form {
130
169
  display: none;
131
170
  }
@@ -136,12 +175,65 @@
136
175
 
137
176
  .list-group.focused .list-form, .list-group.is-invalid .list-form, .list-group.is-blank .list-form {
138
177
  display: block;
178
+ overflow: hidden;
139
179
  }
140
180
 
141
181
  paper-checkbox input[type=checkbox] {
142
182
  display: none;
143
183
  }
144
184
 
185
+ .select-wrapper {
186
+ position: relative;
187
+ }
188
+
189
+ .select-wrapper .pseudo-select {
190
+ opacity: 0;
191
+ display: none;
192
+ transition: opacity 250ms ease-out;
193
+ position: absolute;
194
+ top: 100%;
195
+ width: 106%;
196
+ background-color: white;
197
+ box-shadow: -2px 1px 10px #888;
198
+ margin-top: -11px;
199
+ z-index: 1;
200
+ margin-left: -3%;
201
+ }
202
+
203
+ .select-wrapper.opened .pseudo-select {
204
+ opacity: 1;
205
+ display: block;
206
+ }
207
+
208
+ .select-wrapper input.pseudo-select-input {
209
+ padding-right: 20px;
210
+ box-sizing: border-box;
211
+ }
212
+
213
+ .select-wrapper .select-icon {
214
+ position: absolute;
215
+ right: -6px;
216
+ width: 35px;
217
+ bottom: -6px;
218
+ height: 35px;
219
+ }
220
+
221
+ .select-wrapper .pseudo-select > div {
222
+ min-height: 2em;
223
+ border-bottom: solid 1px;
224
+ padding: 5px 3%;
225
+ position: relative;
226
+ }
227
+
228
+ .select-wrapper .pseudo-select > div:hover {
229
+ background-color: #D5DAFF;
230
+ }
231
+
232
+ .select-wrapper .pseudo-select > div paper-checkbox {
233
+ float: left;
234
+ margin-right: 15px;
235
+ }
236
+
145
237
  </style>
146
238
  <!-- TODO: structures should be arrays to preserve order -->
147
239
  <form id="rails_form" _action="{{ action }}" method="{{ method }}" enctype="multipart/form-data">
@@ -197,7 +289,11 @@
197
289
  </template>
198
290
  </template>
199
291
  <div class='sumbit-button-wrapper'>
200
- <paper-button id='submit-button' raised>{{ submitText }}</paper-button>
292
+ <paper-button id='submit-button' raised>
293
+ <span class='submit-text'>{{ submitText }}</span>
294
+ <form-spinner height="1.5" width="1.5"></form-spinner>
295
+ </paper-button>
296
+ <!-- <paper-button id='test-button' raised>Test</paper-button> -->
201
297
  <template if="{{ xhr }}">
202
298
  <core-xhr id='xhr'></core-xhr>
203
299
  </template>
@@ -229,12 +325,14 @@
229
325
  data: null,
230
326
  submitText: "Submit",
231
327
  xhr: false,
232
- onSubmit: null
328
+ unscopedData: null
233
329
  },
234
330
 
235
331
  parentContext: null,
236
332
  parentStructure: null,
237
333
  parentData: null,
334
+
335
+ inTransit: false,
238
336
 
239
337
  toArray: function(str){
240
338
  return [str];
@@ -242,21 +340,45 @@
242
340
 
243
341
  created: function(){
244
342
  this.structure = {};
245
- this.data = {};
246
343
  this.inputList = [];
344
+ this.indexStore = {};
345
+
346
+ var data = this.data === null ? {} : this.data;
347
+ if (!!this.scope && this.scope.replace(/\s/g, "").length > 0){
348
+ if (data[this.scope] === void(0)) data[this.scope] = {};
349
+ this.data = data[this.scope];
350
+ } else {
351
+ this.data = data;
352
+ }
353
+
354
+ this.unscopedData = data;
355
+ this.unscopedScope = this.scope;
247
356
  },
248
357
 
249
358
  handleXhrCallback: function(){
250
-
359
+
251
360
  },
252
361
 
253
362
  domReady: function(){
254
363
  var form = this.shadowRoot.querySelector("form"),
255
- button = this.shadowRoot.querySelector("#submit-button");
256
-
364
+ button = this.shadowRoot.querySelector("#submit-button"),
365
+ test = this.shadowRoot.querySelector("#test-button");
366
+
367
+ var callback = function(resp){
368
+ this.inTransit = false;
369
+ this.handleXhrCallback(resp);
370
+ }.bind(this)
371
+
257
372
  button.addEventListener("click", function(e){
373
+ // console.log(this.action, this.unscopedData);
374
+ // return;
375
+ if (this.inTransit) return;
376
+ if (this.isFormInvalid()) return;
377
+
258
378
  if (this.action !== null){
259
379
  if (this.xhr){
380
+ this.inTransit = true;
381
+
260
382
  if (!!this.scope && this.scope.length > 0){
261
383
  var data = {};
262
384
  data[this.scope] = this.data;
@@ -267,18 +389,19 @@
267
389
  this.$.xhr.request({
268
390
  method: this.method,
269
391
  url: this.action,
270
- body: JSON.stringify(data),
271
- callback: this.handleXhrCallback,
272
- headers: { "Content-type": "application/json", "Accept": "application/json" }
392
+ body: new FormData(form),
393
+ callback: callback
394
+ //headers: { "Content-type": "application/json", "Accept": "application/json" }
273
395
  })
396
+
274
397
  } else {
398
+ this.inTransit = true;
275
399
  form.submit();
276
400
  }
277
401
  }
278
402
  }.bind(this), false)
279
403
 
280
404
  this.appendInputs();
281
-
282
405
  var inputObserver = new ArrayObserver(this.inputList);
283
406
  inputObserver.open(function(splices){
284
407
  this.appendInputs();
@@ -291,6 +414,17 @@
291
414
  }
292
415
  }.bind(this), true)
293
416
 
417
+ var spinner = button.querySelector('form-spinner');
418
+ new PathObserver(this, 'inTransit').open(function(){
419
+ if (this.inTransit){
420
+ spinner.start();
421
+ this.addClass(button, 'in-transit');
422
+ } else {
423
+ spinner.stop();
424
+ this.removeClass(button, 'in-transit');
425
+ }
426
+ }.bind(this));
427
+
294
428
  },
295
429
 
296
430
  get getData() {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polymer-rails-forms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.15
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Weber
@@ -33,6 +33,7 @@ extra_rdoc_files: []
33
33
  files:
34
34
  - LICENSE
35
35
  - README.md
36
+ - assets/rails-forms/form-spinner.html
36
37
  - assets/rails-forms/gc-paper-decorator.html
37
38
  - assets/rails-forms/pikaday.css
38
39
  - assets/rails-forms/pikaday.js