diff options
-rw-r--r-- | cmd/ponzu/options.go | 23 | ||||
-rw-r--r-- | content/post.go | 24 | ||||
-rw-r--r-- | management/editor/elements.go | 88 | ||||
-rw-r--r-- | system/admin/static/dashboard/css/admin.css | 4 |
4 files changed, 111 insertions, 28 deletions
diff --git a/cmd/ponzu/options.go b/cmd/ponzu/options.go index cabe91a..6f42f0c 100644 --- a/cmd/ponzu/options.go +++ b/cmd/ponzu/options.go @@ -72,12 +72,12 @@ type {{ .name }} struct { editor editor.Editor // required: all maintained {{ .name }} fields must have json tags! - Title string ` + "`json:" + `"title"` + "`" + ` - Content string ` + "`json:" + `"content"` + "`" + ` - Author string ` + "`json:" + `"author"` + "`" + ` - Picture string ` + "`json:" + `"picture"` + "`" + ` - Category []string ` + "`json:" + `"category"` + "`" + ` - ThemeStyle string ` + "`json:" + `"theme"` + "`" + ` + Title string ` + "`json:" + `"title"` + "`" + ` + Content string ` + "`json:" + `"content"` + "`" + ` + Author string ` + "`json:" + `"author"` + "`" + ` + Photo string ` + "`json:" + `"picture"` + "`" + ` + Category []string ` + "`json:" + `"category"` + "`" + ` + Theme string ` + "`json:" + `"theme"` + "`" + ` } func init() { @@ -101,7 +101,6 @@ func ({{ .initial }} *{{ .name }}) Editor() *editor.Editor { return &{{ .initial // MarshalEditor writes a buffer of html to edit a {{ .name }} and partially implements editor.Editable func ({{ .initial }} *{{ .name }}) MarshalEditor() ([]byte, error) { -/* EXAMPLE CODE (from post.go, the default content type) */ view, err := editor.Form({{ .initial }}, editor.Field{ // Take careful note that the first argument to these Input-like methods @@ -127,22 +126,18 @@ func ({{ .initial }} *{{ .name }}) MarshalEditor() ([]byte, error) { }), }, editor.Field{ - View: editor.File("Picture", {{ .initial }}, map[string]string{ + View: editor.File("Photo", {{ .initial }}, map[string]string{ "label": "Author Photo", "placeholder": "Upload a profile picture for the author", }), }, editor.Field{ - View: editor.Checkbox("Category", {{ .initial }}, map[string]string{ + View: editor.Tags("Category", {{ .initial }}, map[string]string{ "label": "{{ .name }} Category", - }, map[string]string{ - "important": "Important", - "active": "Active", - "unplanned": "Unplanned", }), }, editor.Field{ - View: editor.Select("ThemeStyle", {{ .initial }}, map[string]string{ + View: editor.Select("Theme", {{ .initial }}, map[string]string{ "label": "Theme Style", }, map[string]string{ "dark": "Dark", diff --git a/content/post.go b/content/post.go index 7dc99c1..d121ed7 100644 --- a/content/post.go +++ b/content/post.go @@ -11,12 +11,12 @@ type Post struct { Item editor editor.Editor - Title string `json:"title"` - Content string `json:"content"` - Photo string `json:"photo"` - Author string `json:"author"` - Category []string `json:"category"` - ThemeStyle string `json:"theme"` + Title string `json:"title"` + Content string `json:"content"` + Photo string `json:"photo"` + Author string `json:"author"` + Category []string `json:"category"` + Theme string `json:"theme"` } func init() { @@ -55,7 +55,7 @@ func (p *Post) MarshalEditor() ([]byte, error) { }), }, editor.Field{ - View: editor.File("Picture", p, map[string]string{ + View: editor.File("Photo", p, map[string]string{ "label": "Author Photo", "placeholder": "Upload a profile picture for the author", }), @@ -68,16 +68,12 @@ func (p *Post) MarshalEditor() ([]byte, error) { }), }, editor.Field{ - View: editor.Checkbox("Category", p, map[string]string{ - "label": "Post Category", - }, map[string]string{ - "important": "Important", - "active": "Active", - "unplanned": "Unplanned", + View: editor.Tags("Category", p, map[string]string{ + "label": "Post Categories", }), }, editor.Field{ - View: editor.Select("ThemeStyle", p, map[string]string{ + View: editor.Select("Theme", p, map[string]string{ "label": "Theme Style", }, map[string]string{ "dark": "Dark", diff --git a/management/editor/elements.go b/management/editor/elements.go index 4d829ad..2326358 100644 --- a/management/editor/elements.go +++ b/management/editor/elements.go @@ -332,6 +332,94 @@ func Checkbox(fieldName string, p interface{}, attrs, options map[string]string) return domElementWithChildrenCheckbox(div, opts) } +// Tags returns the []byte of a tag input (in the style of Materialze 'Chips') with a label. +// 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 Tags(fieldName string, p interface{}, attrs map[string]string) []byte { + name := tagNameFromStructField(fieldName, p) + + // get the saved tags if this is already an existing post + values := valueFromStructField(fieldName, p) // returns refelct.Value + tags := values.Slice(0, values.Len()).Interface().([]string) // casts reflect.Value to []string + + html := ` + <div class="col s12 tags ` + name + `"> + <label class="active">` + attrs["label"] + ` (Type and press "Enter")</label> + <div class="chips ` + name + `"></div> + ` + + var initial []string + i := 0 + for _, tag := range tags { + tagName := tagNameFromStructFieldMulti(fieldName, i, p) + html += `<input type="hidden" class="tag ` + tag + `" name=` + tagName + ` value="` + tag + `"/>` + initial = append(initial, `{tag: '`+tag+`'}`) + i++ + } + + script := ` + <script> + $(function() { + var tags = $('.tags.` + name + `'); + $('.chips.` + name + `').material_chip({ + data: [` + strings.Join(initial, ",") + `], + secondaryPlaceholder: '+` + name + `' + }); + + // handle events specific to tags + var chips = tags.find('.chips'); + + chips.on('chip.add', function(e, chip) { + chips.parent().find('.empty-tag').remove(); + + var input = $('<input>'); + input.attr({ + class: 'tag '+chip.tag, + name: '` + name + `.'+String(tags.find('input[type=hidden]').length), + value: chip.tag, + type: 'hidden' + }); + + tags.append(input); + }); + + chips.on('chip.delete', function(e, chip) { + // convert tag string to class-like selector "some tag" -> ".some.tag" + var sel = '.tag.'+chip.tag.split(' ').join('.'); + console.log(sel); + console.log(chips.parent().find(sel)); + chips.parent().find(sel).remove(); + + // iterate through all hidden tag inputs to re-name them with the correct ` + name + `.index + var hidden = chips.parent().find('input[type=hidden]'); + + // if there are no tags, set a blank + if (hidden.length === 0) { + var input = $('<input>'); + input.attr({ + class: 'empty-tag', + name: '` + name + `', + type: 'hidden' + }); + + tags.append(input); + return; + } + + for (var i = 0; i < hidden.length; i++) { + $(hidden[i]).attr('name', '` + name + `.'+String(i)); + } + }); + }); + </script> + ` + + html += `</div>` + + return []byte(html + script) +} + // domElementSelfClose is a special DOM element which is parsed as a // self-closing tag and thus needs to be created differently func domElementSelfClose(e *element) []byte { diff --git a/system/admin/static/dashboard/css/admin.css b/system/admin/static/dashboard/css/admin.css index 7acffab..1de966d 100644 --- a/system/admin/static/dashboard/css/admin.css +++ b/system/admin/static/dashboard/css/admin.css @@ -203,3 +203,7 @@ li:hover .quick-delete-post, li:hover .delete-user { -o-transform: translateY(-140%); transform: translateY(-140%); } + +.chips { + margin-top: 10px; +}
\ No newline at end of file |