Gisle Aune
6 years ago
14 changed files with 278 additions and 13 deletions
-
28marko/components/modal/style.less
-
12marko/global/colors.less
-
40marko/page/story-content/components/edit-story-tags-modal/component.js
-
7marko/page/story-content/components/edit-story-tags-modal/index.marko
-
7marko/page/story-content/components/page/component.js
-
4marko/page/story-content/components/page/index.marko
-
62marko/page/story-content/components/story-tag-options/component.js
-
21marko/page/story-content/components/story-tag-options/index.marko
-
46marko/page/story-content/components/story-tag-options/style.less
-
5marko/page/story-content/components/story-tags/component.js
-
1marko/page/story-content/components/story-tags/index.marko
-
14marko/page/story-content/components/story-tags/style.less
-
42rpdata/api/Story.js
-
2rpdata/api/Tag.js
@ -0,0 +1,40 @@ |
|||||
|
const moment = require("moment") |
||||
|
|
||||
|
const {storyApi} = require("../../../../../rpdata/api/Story") |
||||
|
|
||||
|
module.exports = class { |
||||
|
onCreate(input) { |
||||
|
this.state = { |
||||
|
error: null, |
||||
|
loading: false, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
change(key, ev) { |
||||
|
this.state.values[key] = ev.target.value |
||||
|
} |
||||
|
|
||||
|
close() { |
||||
|
this.emit("close") |
||||
|
} |
||||
|
|
||||
|
addTag(tag) { |
||||
|
this.state.error = null |
||||
|
|
||||
|
storyApi.addTag({id: this.input.story.id, tag}).then(({tags}) => { |
||||
|
this.emit("tags", tags) |
||||
|
}).catch(errs => { |
||||
|
this.state.error = errs.message || errs[0].message || "Add tag failed" |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
removeTag(tag) { |
||||
|
this.state.error = null |
||||
|
|
||||
|
storyApi.removeTag({id: this.input.story.id, tag}).then(({tags}) => { |
||||
|
this.emit("tags", tags) |
||||
|
}).catch(errs => { |
||||
|
this.state.error = errs.message || errs[0].message || "Remove tag failed" |
||||
|
}) |
||||
|
} |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
<modal class="modal color-text nolabel" key="modal2" enabled=(input.enabled) closable on-close("close") > |
||||
|
<h1>Edit Tags</h1> |
||||
|
|
||||
|
<p key="error" class="color-error">${state.error}</p> |
||||
|
|
||||
|
<story-tag-options tags=input.story.tags loading=state.loading on-remove("removeTag") on-add("addTag") /> |
||||
|
</modal> |
@ -0,0 +1,62 @@ |
|||||
|
const {tagApi} = require("../../../../../rpdata/api/Tag") |
||||
|
|
||||
|
module.exports = class { |
||||
|
onCreate() { |
||||
|
this.state = { |
||||
|
values: { |
||||
|
name: "", |
||||
|
kind: "Event", |
||||
|
}, |
||||
|
tags: [], |
||||
|
suggestions: [], |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMount() { |
||||
|
tagApi.list().then(list => { |
||||
|
this.state.tags = list |
||||
|
}).catch(err => {}) |
||||
|
|
||||
|
this.getEl("name").addEventListener("keydown", ev => { |
||||
|
setTimeout(() => { |
||||
|
const name = this.getEl("name").value |
||||
|
const kind = this.getEl("kind").value |
||||
|
const search = name.toLowerCase() |
||||
|
|
||||
|
this.state.values = {name, kind} |
||||
|
this.state.suggestions = [] |
||||
|
|
||||
|
const suggestions = this.state.tags.filter(t => t.name.toLowerCase().includes(search)) |
||||
|
if (suggestions.length > 0 && suggestions.length < 10) { |
||||
|
if (suggestions.length > 1 || suggestions[0].name !== name) { |
||||
|
this.state.suggestions = suggestions |
||||
|
} |
||||
|
} |
||||
|
}, 100) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
add(tag) { |
||||
|
this.state.values = { |
||||
|
kind: "Event", |
||||
|
name: "", |
||||
|
} |
||||
|
|
||||
|
this.emit("add", tag) |
||||
|
} |
||||
|
|
||||
|
change(key, ev) { |
||||
|
const value = ev.target.value |
||||
|
|
||||
|
this.state.values[key] = value |
||||
|
} |
||||
|
|
||||
|
applySuggestion(tag) { |
||||
|
this.state.values = { |
||||
|
name: tag.name, |
||||
|
kind: tag.kind, |
||||
|
} |
||||
|
|
||||
|
this.state.suggestions = [] |
||||
|
} |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
<div class="tag-options"> |
||||
|
<input key="name" placeholder="Name" class="big" on-change("change", "name") value=state.values.name /> |
||||
|
<select key="kind" placeholder="Kind" class="big" on-change("change", "kind") value=state.values.kind> |
||||
|
<option value="Character" selected=(state.values.kind === "Character")>Character</option> |
||||
|
<option value="Event" selected=(state.values.kind === "Event")>Event</option> |
||||
|
<option value="Location" selected=(state.values.kind === "Location")>Location</option> |
||||
|
<option value="Organization" selected=(state.values.kind === "Organization")>Organization</option> |
||||
|
<option value="Series" selected=(state.values.kind === "Series")>Series</option> |
||||
|
</select> |
||||
|
<div class="suggestion-list" if(state.suggestions.length > 0)> |
||||
|
<div class="header color-menu">Suggestions:</div> |
||||
|
<a for(tag in state.suggestions) on-click("applySuggestion", tag) class=["tag", "color-tag-" + tag.kind.toLowerCase()]>${tag.name}</a> |
||||
|
</div> |
||||
|
|
||||
|
<button disabled=(input.loading) on-click("add", state.values)>Add</button> |
||||
|
|
||||
|
<div for(tag in (input.tags || [])) class="tag" key=(tag.kind+":"+tag.name)> |
||||
|
<div class=("content color-tag-"+tag.kind.toLowerCase())>${tag.name}</div> |
||||
|
<button on-click("emit", "remove", tag)>Remove</button> |
||||
|
</div> |
||||
|
</div> |
@ -0,0 +1,46 @@ |
|||||
|
div.tag-options { |
||||
|
div.tag { |
||||
|
padding: 0.25em; |
||||
|
border-bottom: 1px solid rgba(0, 220, 255, 0.125); |
||||
|
|
||||
|
div.content { |
||||
|
width: 80%; |
||||
|
display: inline-block; |
||||
|
} |
||||
|
|
||||
|
button { |
||||
|
width: 20%; |
||||
|
display: inline-block; |
||||
|
margin: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
div.suggestion-list { |
||||
|
margin-top: 1em; |
||||
|
|
||||
|
a.tag { |
||||
|
display: inline-block; |
||||
|
margin: 0.5em 0.5ch; |
||||
|
padding: 0.0625em 0.5ch; |
||||
|
|
||||
|
outline: 1px solid; |
||||
|
opacity: 0.75; |
||||
|
|
||||
|
user-select: none; |
||||
|
} |
||||
|
a.tag:hover { |
||||
|
opacity: 1; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
div.header { |
||||
|
display: inline-block; |
||||
|
margin: 0.5em 0.5ch; |
||||
|
padding: 0.0625em 0.5ch; |
||||
|
|
||||
|
opacity: 0.5; |
||||
|
|
||||
|
user-select: none; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
module.exports = class { |
||||
|
select(kind) { |
||||
|
this.emit("select", kind) |
||||
|
} |
||||
|
} |
@ -1,3 +1,4 @@ |
|||||
<div class="story-tags"> |
<div class="story-tags"> |
||||
<a for(tag in input.tags) href=("/story/by-tag/" + tag.kind + "/" + encodeURIComponent(tag.name)) class=["tag", "color-tag-" + tag.kind.toLowerCase()]>${tag.name}</a> |
<a for(tag in input.tags) href=("/story/by-tag/" + tag.kind + "/" + encodeURIComponent(tag.name)) class=["tag", "color-tag-" + tag.kind.toLowerCase()]>${tag.name}</a> |
||||
|
<a on-click("select", "edit") class="option color-menu">Edit</a> |
||||
</div> |
</div> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue