summaryrefslogtreecommitdiff
path: root/management/editor
diff options
context:
space:
mode:
authorSteve Manuel <nilslice@gmail.com>2016-12-22 13:11:14 -0800
committerSteve Manuel <nilslice@gmail.com>2016-12-22 13:11:14 -0800
commit0ca645c0b75752e38d4ddde3c10849a25bb8b53b (patch)
tree81e5e276a6dbd1442b1008f287d14c2534b14ca0 /management/editor
parentd4e33e1dc950664b653ae3a388b237112b1bfb3a (diff)
adding fix for js in repeaters (mainly FileRepeater) and cleanup of form value transforms
Diffstat (limited to 'management/editor')
-rw-r--r--management/editor/elements.go2
-rw-r--r--management/editor/repeaters.go164
2 files changed, 138 insertions, 28 deletions
diff --git a/management/editor/elements.go b/management/editor/elements.go
index 75c37e2..873e81c 100644
--- a/management/editor/elements.go
+++ b/management/editor/elements.go
@@ -15,7 +15,7 @@ import (
// }
//
// func (p *Person) MarshalEditor() ([]byte, error) {
-// view, err := Form(p,
+// view, err := editor.Form(p,
// editor.Field{
// View: editor.Input("Name", p, map[string]string{
// "label": "Name",
diff --git a/management/editor/repeaters.go b/management/editor/repeaters.go
index f84c5ad..81497a5 100644
--- a/management/editor/repeaters.go
+++ b/management/editor/repeaters.go
@@ -2,10 +2,64 @@ package editor
import (
"bytes"
+ "fmt"
"strings"
)
+// InputRepeater returns the []byte of an <input> HTML element with a label.
+// It also includes repeat controllers (+ / -) so the element can be
+// dynamically multiplied or reduced.
+// IMPORTANT:
+// The `fieldName` argument will cause a panic if it is not exactly the string
+// form of the struct field that this editor input is representing
+// type Person struct {
+// Name string `json:"name"`
+// }
+//
+// func (p *Person) MarshalEditor() ([]byte, error) {
+// view, err := editor.Form(p,
+// editor.Field{
+// View: editor.InputRepeater("Name", p, map[string]string{
+// "label": "Name",
+// "type": "text",
+// "placeholder": "Enter the Name here",
+// }),
+// }
+// )
+// }
+func InputRepeater(fieldName string, p interface{}, attrs map[string]string) []byte {
+ // find the field values in p to determine pre-filled inputs
+ fieldVals := valueFromStructField(fieldName, p)
+ vals := strings.Split(fieldVals, "__ponzu")
+
+ scope := tagNameFromStructField(fieldName, p)
+ html := bytes.Buffer{}
+
+ html.WriteString(`<span class="__ponzu-repeat ` + scope + `">`)
+ for i, val := range vals {
+ el := &element{
+ TagName: "input",
+ Attrs: attrs,
+ Name: tagNameFromStructFieldMulti(fieldName, i, p),
+ data: val,
+ viewBuf: &bytes.Buffer{},
+ }
+
+ // only add the label to the first input in repeated list
+ if i == 0 {
+ el.label = attrs["label"]
+ }
+
+ html.Write(domElementSelfClose(el))
+ }
+ html.WriteString(`</span>`)
+
+ return append(html.Bytes(), RepeatController(fieldName, p, "input", ".input-field")...)
+}
+
// SelectRepeater returns the []byte of a <select> HTML element plus internal <options> with a label.
+// It also includes repeat controllers (+ / -) so the element can be
+// dynamically multiplied or reduced.
// IMPORTANT:
// The `fieldName` argument will cause a panic if it is not exactly the string
// form of the struct field that this editor input is representing
@@ -13,7 +67,7 @@ func SelectRepeater(fieldName string, p interface{}, attrs, options map[string]s
// options are the value attr and the display value, i.e.
// <option value="{map key}">{map value}</option>
scope := tagNameFromStructField(fieldName, p)
- html := &bytes.Buffer{}
+ html := bytes.Buffer{}
html.WriteString(`<span class="__ponzu-repeat ` + scope + `">`)
// find the field values in p to determine if an option is pre-selected
@@ -82,31 +136,44 @@ func SelectRepeater(fieldName string, p interface{}, attrs, options map[string]s
}
// FileRepeater returns the []byte of a <input type="file"> HTML element with a label.
+// It also includes repeat controllers (+ / -) so the element can be
+// dynamically multiplied or reduced.
// IMPORTANT:
// The `fieldName` argument will cause a panic if it is not exactly the string
// form of the struct field that this editor input is representing
func FileRepeater(fieldName string, p interface{}, attrs map[string]string) []byte {
- name := tagNameFromStructField(fieldName, p)
+ // find the field values in p to determine if an option is pre-selected
+ fieldVals := valueFromStructField(fieldName, p)
+ vals := strings.Split(fieldVals, "__ponzu")
+
+ addLabelFirst := func(i int, label string) string {
+ if i == 0 {
+ return `<label class="active">` + label + `</label>`
+ }
+
+ return ""
+ }
+
tmpl :=
- `<div class="file-input ` + name + ` input-field col s12">
- <label class="active">` + attrs["label"] + `</label>
+ `<div class="file-input %[5]s %[4]s input-field col s12">
+ %[2]s
<div class="file-field input-field">
<div class="btn">
<span>Upload</span>
- <input class="upload" type="file">
+ <input class="upload %[4]s" type="file" />
</div>
<div class="file-path-wrapper">
- <input class="file-path validate" placeholder="` + attrs["label"] + `" type="text">
+ <input class="file-path validate" placeholder="Add %[5]s" type="text" />
</div>
</div>
<div class="preview"><div class="img-clip"></div></div>
- <input class="store ` + name + `" type="hidden" name="` + name + `" value="` + valueFromStructField(fieldName, p) + `" />
+ <input class="store %[4]s" type="hidden" name="%[1]s" value="%[3]s" />
</div>`
-
+ // 1=nameidx, 2=addLabelFirst, 3=val, 4=className, 5=fieldName
script :=
`<script>
$(function() {
- var $file = $('.file-input.` + name + `'),
+ var $file = $('.file-input.%[2]s'),
upload = $file.find('input.upload'),
store = $file.find('input.store'),
preview = $file.find('.preview'),
@@ -116,9 +183,9 @@ func FileRepeater(fieldName string, p interface{}, attrs map[string]string) []by
uploadSrc = store.val();
preview.hide();
- // when ` + name + ` input changes (file is selected), remove
+ // when %[2]s input changes (file is selected), remove
// the 'name' and 'value' attrs from the hidden store input.
- // add the 'name' attr to ` + name + ` input
+ // add the 'name' attr to %[2]s input
upload.on('change', function(e) {
resetImage();
});
@@ -128,10 +195,11 @@ func FileRepeater(fieldName string, p interface{}, attrs map[string]string) []by
clip.append(img);
preview.show();
- $(reset).addClass('reset ` + name + ` btn waves-effect waves-light grey');
+ $(reset).addClass('reset %[2]s btn waves-effect waves-light grey');
$(reset).html('<i class="material-icons tiny">clear<i>');
$(reset).on('click', function(e) {
e.preventDefault();
+ var preview = $(this).parent().closest('.preview');
preview.animate({"opacity": 0.1}, 200, function() {
preview.slideUp(250, function() {
resetImage();
@@ -145,14 +213,26 @@ func FileRepeater(fieldName string, p interface{}, attrs map[string]string) []by
function resetImage() {
store.val('');
store.attr('name', '');
- upload.attr('name', '` + name + `');
+ upload.attr('name', '%[1]s');
clip.empty();
}
});
</script>`
+ // 1=nameidx, 2=className
+
+ name := tagNameFromStructField(fieldName, p)
+
+ html := bytes.Buffer{}
+ html.WriteString(`<span class="__ponzu-repeat ` + name + `">`)
+ for i, val := range vals {
+ className := fmt.Sprintf("%s-%d", name, i)
+ nameidx := tagNameFromStructFieldMulti(fieldName, i, p)
+ html.WriteString(fmt.Sprintf(tmpl, nameidx, addLabelFirst(i, attrs["label"]), val, className, fieldName))
+ html.WriteString(fmt.Sprintf(script, nameidx, className))
+ }
+ html.WriteString(`</span>`)
- html := `<span class="__ponzu-repeat ` + name + `">` + tmpl + `</span>`
- return append([]byte(html+script), RepeatController(fieldName, p, "input.store."+name, "div.file-input."+name)...)
+ return append(html.Bytes(), RepeatController(fieldName, p, "input.upload", "div.file-input."+fieldName)...)
}
// RepeatController generates the javascript to control any repeatable form
@@ -166,19 +246,45 @@ func RepeatController(fieldName string, p interface{}, inputSelector, cloneSelec
var scope = $('.__ponzu-repeat.` + scope + `');
var getChildren = function() {
- return scope.find('` + inputSelector + `')
+ return scope.find('` + cloneSelector + `')
}
var resetFieldNames = function() {
// loop through children, set its name to the fieldName.i where
// i is the current index number of children array
var children = getChildren();
+
for (var i = 0; i < children.length; i++) {
- var el = children[i];
- $(el).attr('name', '` + scope + `.'+String(i));
-
+ var preset = false;
+ var $el = children.eq(i);
+ var name = '` + scope + `.'+String(i);
+
+ $el.find('` + inputSelector + `').attr('name', name);
+
+ // ensure no other input-like elements besides ` + inputSelector + `
+ // get the new name by setting it to an empty string
+ $el.find('input, select, textarea').each(function(i, elem) {
+ var $elem = $(elem);
+
+ // if the elem is not ` + inputSelector + ` and has no value
+ // set the name to an empty string
+ if (!$elem.is('` + inputSelector + `')) {
+ if ($elem.val() === '') {
+ $elem.attr('name', '');
+ } else {
+ preset = true;
+ }
+ }
+ });
+
+ // if there is a preset value, remove the name attr from the
+ // ` + inputSelector + ` element so it doesn't overwrite db
+ if (preset) {
+ $el.find('` + inputSelector + `').attr('name', '');
+ }
+
// reset controllers
- $(el).find('.controls').remove();
+ $el.find('.controls').remove();
}
applyRepeatControllers();
@@ -190,18 +296,22 @@ func RepeatController(fieldName string, p interface{}, inputSelector, cloneSelec
var add = e.target;
// find and clone the repeatable input-like element
- var subject = $(add).parent().closest('` + cloneSelector + `');
- var clone = subject.clone();
+ var source = $(add).parent().closest('` + cloneSelector + `');
+ var clone = source.clone();
- // if repeat has label, remove it
+ // if clone has label, remove it
clone.find('label').remove();
// remove the pre-filled value from clone
- clone.find('` + inputSelector + `').val("");
+ clone.find('` + inputSelector + `').val('');
+ clone.find('input').val('');
- // remove controls if already present
+ // remove controls from clone if already present
clone.find('.controls').remove();
+ // remove input preview on clone if copied from source
+ clone.find('.preview').remove();
+
// add clone to scope and reset field name attributes
scope.append(clone);
@@ -260,10 +370,10 @@ func RepeatController(fieldName string, p interface{}, inputSelector, cloneSelec
for (var i = 0; i < children.length; i++) {
var el = children[i];
- $(el).parent().find('.controls').remove();
+ $(el).find('` + inputSelector + `').parent().find('.controls').remove();
var controls = createControls();
- $(el).parent().append(controls);
+ $(el).append(controls);
}
}