polymer-rails-forms 0.1.15 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +14 -4
- data/assets/rails-forms/form-spinner.html +101 -0
- data/assets/rails-forms/rails-form-helpers.html +193 -41
- data/assets/rails-forms/rails-form-validators.html +27 -9
- data/assets/rails-forms/rails-form.html +144 -10
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddb55e7abf7c864da70f1a3c27bef1c123444db2
|
4
|
+
data.tar.gz: e28459f4839d74416d98189908cc64e2cb406b9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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"
|
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
|
-
|
77
|
-
|
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
|
-
|
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
|
-
|
224
|
-
|
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
|
-
|
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 =
|
329
|
-
|
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
|
-
|
436
|
-
|
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(
|
440
|
-
var
|
441
|
-
data = this.
|
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].
|
580
|
-
self.structure[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
|
-
|
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
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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>
|
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
|
-
|
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:
|
271
|
-
callback:
|
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.
|
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
|