Inline editing
This commit is contained in:
parent
df01fb891b
commit
633b5942f5
@ -1,19 +1,19 @@
|
|||||||
component InputText(id string, value string, label string, placeholder string)
|
component InputText(id string, value string, label string, placeholder string)
|
||||||
.widget-input
|
.widget-input
|
||||||
label(for=id)= label + ":"
|
label(for=id)= label + ":"
|
||||||
input.widget-element.action(id=id, type="text", value=value, placeholder=placeholder, title=placeholder, data-action="save", data-trigger="change")
|
input.widget-element.action(id=id, data-field=id, type="text", value=value, placeholder=placeholder, title=placeholder, data-action="save", data-trigger="change")
|
||||||
|
|
||||||
component InputTextArea(id string, value string, label string, placeholder string)
|
component InputTextArea(id string, value string, label string, placeholder string)
|
||||||
.widget-input
|
.widget-input
|
||||||
label(for=id)= label + ":"
|
label(for=id)= label + ":"
|
||||||
textarea.widget-element.action(id=id, placeholder=placeholder, title=placeholder, data-action="save", data-trigger="change")= value
|
textarea.widget-element.action(id=id, data-field=id, placeholder=placeholder, title=placeholder, data-action="save", data-trigger="change")= value
|
||||||
|
|
||||||
component InputNumber(id string, value float64, label string, placeholder string, min string, max string, step string)
|
component InputNumber(id string, value float64, label string, placeholder string, min string, max string, step string)
|
||||||
.widget-input
|
.widget-input
|
||||||
label(for=id)= label + ":"
|
label(for=id)= label + ":"
|
||||||
input.widget-element.action(id=id, type="number", value=value, min=min, max=max, step=step, placeholder=placeholder, title=placeholder, data-action="save", data-trigger="change")
|
input.widget-element.action(id=id, data-field=id, type="number", value=value, min=min, max=max, step=step, placeholder=placeholder, title=placeholder, data-action="save", data-trigger="change")
|
||||||
|
|
||||||
component InputSelection(id string, value string, label string, placeholder string, values []string)
|
component InputSelection(id string, value string, label string, placeholder string, values []string)
|
||||||
.widget-input
|
.widget-input
|
||||||
label(for=id)= label + ":"
|
label(for=id)= label + ":"
|
||||||
select.widget-element.action(id=id, value=value, title=placeholder, data-action="save", data-trigger="change")
|
select.widget-element.action(id=id, data-field=id, value=value, title=placeholder, data-action="save", data-trigger="change")
|
||||||
|
@ -4,58 +4,69 @@ component AnimeLists(animeLists map[string]*arn.AnimeList, viewUser *arn.User, u
|
|||||||
if len(animeLists[arn.AnimeListStatusWatching].Items) > 0
|
if len(animeLists[arn.AnimeListStatusWatching].Items) > 0
|
||||||
.anime-list-container
|
.anime-list-container
|
||||||
h3.status-name Watching
|
h3.status-name Watching
|
||||||
AnimeList(animeLists[arn.AnimeListStatusWatching], user)
|
AnimeList(animeLists[arn.AnimeListStatusWatching], viewUser, user)
|
||||||
|
|
||||||
if len(animeLists[arn.AnimeListStatusCompleted].Items) > 0
|
if len(animeLists[arn.AnimeListStatusCompleted].Items) > 0
|
||||||
.anime-list-container
|
.anime-list-container
|
||||||
h3.status-name Completed
|
h3.status-name Completed
|
||||||
AnimeList(animeLists[arn.AnimeListStatusCompleted], user)
|
AnimeList(animeLists[arn.AnimeListStatusCompleted], viewUser, user)
|
||||||
|
|
||||||
if len(animeLists[arn.AnimeListStatusPlanned].Items) > 0
|
if len(animeLists[arn.AnimeListStatusPlanned].Items) > 0
|
||||||
.anime-list-container
|
.anime-list-container
|
||||||
h3.status-name Planned
|
h3.status-name Planned
|
||||||
AnimeList(animeLists[arn.AnimeListStatusPlanned], user)
|
AnimeList(animeLists[arn.AnimeListStatusPlanned], viewUser, user)
|
||||||
|
|
||||||
if len(animeLists[arn.AnimeListStatusHold].Items) > 0
|
if len(animeLists[arn.AnimeListStatusHold].Items) > 0
|
||||||
.anime-list-container
|
.anime-list-container
|
||||||
h3.status-name On hold
|
h3.status-name On hold
|
||||||
AnimeList(animeLists[arn.AnimeListStatusHold], user)
|
AnimeList(animeLists[arn.AnimeListStatusHold], viewUser, user)
|
||||||
|
|
||||||
if len(animeLists[arn.AnimeListStatusDropped].Items) > 0
|
if len(animeLists[arn.AnimeListStatusDropped].Items) > 0
|
||||||
.anime-list-container
|
.anime-list-container
|
||||||
h3.status-name Dropped
|
h3.status-name Dropped
|
||||||
AnimeList(animeLists[arn.AnimeListStatusDropped], user)
|
AnimeList(animeLists[arn.AnimeListStatusDropped], viewUser, user)
|
||||||
|
|
||||||
//- for status, animeList := range animeLists
|
//- for status, animeList := range animeLists
|
||||||
//- h3= status
|
//- h3= status
|
||||||
//- AnimeList(animeList, user)
|
//- AnimeList(animeList, user)
|
||||||
|
|
||||||
component AnimeList(animeList *arn.AnimeList, user *arn.User)
|
component AnimeList(animeList *arn.AnimeList, viewUser *arn.User, user *arn.User)
|
||||||
table.anime-list
|
table.anime-list
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
th.anime-list-item-name Anime
|
th.anime-list-item-name Anime
|
||||||
th.anime-list-item-airing-date Airing
|
th.anime-list-item-airing-date Airing
|
||||||
th.anime-list-item-episodes Episodes
|
th.anime-list-item-episodes Episodes
|
||||||
th.anime-list-item-rating Rating
|
th.anime-list-item-rating Overall
|
||||||
|
//- th.anime-list-item-rating Story
|
||||||
|
//- th.anime-list-item-rating Visuals
|
||||||
|
//- th.anime-list-item-rating Soundtrack
|
||||||
if user != nil
|
if user != nil
|
||||||
th.anime-list-item-actions Actions
|
th.anime-list-item-actions Actions
|
||||||
tbody
|
tbody
|
||||||
each item in animeList.Items
|
each item in animeList.Items
|
||||||
tr.anime-list-item.mountable(title=item.Notes)
|
tr.anime-list-item.mountable(title=item.Notes, data-api="/api/animelist/" + animeList.UserID + "/update/" + item.AnimeID)
|
||||||
td.anime-list-item-name
|
td.anime-list-item-name
|
||||||
a.ajax(href=item.Link(animeList.User().Nick))= item.Anime().Title.Canonical
|
a.ajax(href=item.Link(animeList.User().Nick))= item.Anime().Title.Canonical
|
||||||
td.anime-list-item-airing-date
|
td.anime-list-item-airing-date
|
||||||
if item.Anime().UpcomingEpisode() != nil
|
if item.Anime().UpcomingEpisode() != nil
|
||||||
span.utc-date(data-start-date=item.Anime().UpcomingEpisode().Episode.AiringDate.Start, data-end-date=item.Anime().UpcomingEpisode().Episode.AiringDate.End, data-episode-number=item.Anime().UpcomingEpisode().Episode.Number)
|
span.utc-date(data-start-date=item.Anime().UpcomingEpisode().Episode.AiringDate.Start, data-end-date=item.Anime().UpcomingEpisode().Episode.AiringDate.End, data-episode-number=item.Anime().UpcomingEpisode().Episode.Number)
|
||||||
td.anime-list-item-episodes
|
td.anime-list-item-episodes
|
||||||
.anime-list-item-episodes-watched= item.Episodes
|
.anime-list-item-episodes-watched
|
||||||
|
.action(contenteditable=utils.SameUser(user, viewUser), data-field="Episodes", data-type="number", data-trigger="focusout", data-action="save")= item.Episodes
|
||||||
.anime-list-item-episodes-separator /
|
.anime-list-item-episodes-separator /
|
||||||
.anime-list-item-episodes-max= item.Anime().EpisodeCountString()
|
.anime-list-item-episodes-max= item.Anime().EpisodeCountString()
|
||||||
//- .anime-list-item-episodes-edit
|
//- .anime-list-item-episodes-edit
|
||||||
//- a.ajax(href=, title="Edit anime")
|
//- a.ajax(href=, title="Edit anime")
|
||||||
//- RawIcon("pencil")
|
//- RawIcon("pencil")
|
||||||
td.anime-list-item-rating= item.FinalRating()
|
td.anime-list-item-rating(title="Overall rating")
|
||||||
|
.action(contenteditable=utils.SameUser(user, viewUser), data-field="Rating.Overall", data-type="number", data-trigger="focusout", data-action="save")= fmt.Sprintf("%.1f", item.Rating.Overall)
|
||||||
|
//- td.anime-list-item-rating(title="Story rating")
|
||||||
|
//- .action(contenteditable=utils.SameUser(user, viewUser), data-field="Rating.Story", data-type="number", data-trigger="focusout", data-action="save")= fmt.Sprintf("%.1f", item.Rating.Story)
|
||||||
|
//- td.anime-list-item-rating(title="Visuals rating")
|
||||||
|
//- .action(contenteditable=utils.SameUser(user, viewUser), data-field="Rating.Visuals", data-type="number", data-trigger="focusout", data-action="save")= fmt.Sprintf("%.1f", item.Rating.Visuals)
|
||||||
|
//- td.anime-list-item-rating(title="Soundtrack rating")
|
||||||
|
//- .action(contenteditable=utils.SameUser(user, viewUser), data-field="Rating.Soundtrack", data-type="number", data-trigger="focusout", data-action="save")= fmt.Sprintf("%.1f", item.Rating.Soundtrack)
|
||||||
if user != nil
|
if user != nil
|
||||||
td.anime-list-item-actions
|
td.anime-list-item-actions
|
||||||
a(href=arn.Nyaa.GetLink(item.Anime()), title="Search on Nyaa", target="_blank", rel="noopener")
|
a(href=arn.Nyaa.GetLink(item.Anime()), title="Search on Nyaa", target="_blank", rel="noopener")
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
// margin-bottom -2px
|
// margin-bottom -2px
|
||||||
|
|
||||||
.anime-list-item-rating
|
.anime-list-item-rating
|
||||||
flex-basis 100px
|
flex-basis 50px
|
||||||
text-align center
|
text-align center
|
||||||
|
|
||||||
.anime-list-item-actions
|
.anime-list-item-actions
|
||||||
|
@ -14,7 +14,7 @@ component AnimeListItem(viewUser *arn.User, item *arn.AnimeListItem, anime *arn.
|
|||||||
option(value=arn.AnimeListStatusDropped) Dropped
|
option(value=arn.AnimeListStatusDropped) Dropped
|
||||||
|
|
||||||
.anime-list-item-rating-edit
|
.anime-list-item-rating-edit
|
||||||
InputNumber("Rating.Overall", item.Rating.Overall, "Overall", "Overall rating on a scale of 0 to 10", "0", "10", "0.1")
|
InputNumber("Rating.Overall", item.Rating.Overall, item.OverallRatingName(), "Overall rating on a scale of 0 to 10", "0", "10", "0.1")
|
||||||
InputNumber("Rating.Story", item.Rating.Story, "Story", "Story rating on a scale of 0 to 10", "0", "10", "0.1")
|
InputNumber("Rating.Story", item.Rating.Story, "Story", "Story rating on a scale of 0 to 10", "0", "10", "0.1")
|
||||||
InputNumber("Rating.Visuals", item.Rating.Visuals, "Visuals", "Visuals rating on a scale of 0 to 10", "0", "10", "0.1")
|
InputNumber("Rating.Visuals", item.Rating.Visuals, "Visuals", "Visuals rating on a scale of 0 to 10", "0", "10", "0.1")
|
||||||
InputNumber("Rating.Soundtrack", item.Rating.Soundtrack, "Soundtrack", "Soundtrack rating on a scale of 0 to 10", "0", "10", "0.1")
|
InputNumber("Rating.Soundtrack", item.Rating.Soundtrack, "Soundtrack", "Soundtrack rating on a scale of 0 to 10", "0", "10", "0.1")
|
||||||
|
@ -26,5 +26,5 @@ func Get(ctx *aero.Context) string {
|
|||||||
animeList.Sort()
|
animeList.Sort()
|
||||||
watchingList := animeList.SplitByStatus()[arn.AnimeListStatusWatching]
|
watchingList := animeList.SplitByStatus()[arn.AnimeListStatusWatching]
|
||||||
|
|
||||||
return utils.AllowEmbed(ctx, ctx.HTML(components.AnimeList(watchingList, user)))
|
return utils.AllowEmbed(ctx, ctx.HTML(components.AnimeList(watchingList, animeList.User(), user)))
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,18 @@ import { Diff } from "./Diff"
|
|||||||
export function save(arn: AnimeNotifier, input: HTMLInputElement | HTMLTextAreaElement) {
|
export function save(arn: AnimeNotifier, input: HTMLInputElement | HTMLTextAreaElement) {
|
||||||
arn.loading(true)
|
arn.loading(true)
|
||||||
|
|
||||||
|
let isContentEditable = input.isContentEditable
|
||||||
let obj = {}
|
let obj = {}
|
||||||
let value = input.value
|
let value = isContentEditable ? input.innerText : input.value
|
||||||
|
|
||||||
if(input.type === "number") {
|
if(input.type === "number" || input.dataset.type === "number") {
|
||||||
if(input.getAttribute("step") === "1") {
|
if(input.getAttribute("step") === "1" || input.dataset.step === "1") {
|
||||||
obj[input.id] = parseInt(value)
|
obj[input.dataset.field] = parseInt(value)
|
||||||
} else {
|
} else {
|
||||||
obj[input.id] = parseFloat(value)
|
obj[input.dataset.field] = parseFloat(value)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
obj[input.id] = value
|
obj[input.dataset.field] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(input.type, input.dataset.api, obj, JSON.stringify(obj))
|
// console.log(input.type, input.dataset.api, obj, JSON.stringify(obj))
|
||||||
@ -35,7 +36,11 @@ export function save(arn: AnimeNotifier, input: HTMLInputElement | HTMLTextAreaE
|
|||||||
throw "API object not found"
|
throw "API object not found"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isContentEditable) {
|
||||||
|
input.contentEditable = "false"
|
||||||
|
} else {
|
||||||
input.disabled = true
|
input.disabled = true
|
||||||
|
}
|
||||||
|
|
||||||
fetch(apiObject.dataset.api, {
|
fetch(apiObject.dataset.api, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -51,7 +56,12 @@ export function save(arn: AnimeNotifier, input: HTMLInputElement | HTMLTextAreaE
|
|||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
arn.loading(false)
|
arn.loading(false)
|
||||||
|
|
||||||
|
if(isContentEditable) {
|
||||||
|
input.contentEditable = "true"
|
||||||
|
} else {
|
||||||
input.disabled = false
|
input.disabled = false
|
||||||
|
}
|
||||||
|
|
||||||
return arn.reloadContent()
|
return arn.reloadContent()
|
||||||
})
|
})
|
||||||
|
@ -265,13 +265,26 @@ export class AnimeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown(e: KeyboardEvent) {
|
onKeyDown(e: KeyboardEvent) {
|
||||||
|
let activeElement = document.activeElement
|
||||||
|
|
||||||
// Ignore hotkeys on input elements
|
// Ignore hotkeys on input elements
|
||||||
switch(document.activeElement.tagName) {
|
switch(activeElement.tagName) {
|
||||||
case "INPUT":
|
case "INPUT":
|
||||||
case "TEXTAREA":
|
case "TEXTAREA":
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disallow Enter key in contenteditables
|
||||||
|
if(activeElement.getAttribute("contenteditable") === "true" && e.keyCode == 13) {
|
||||||
|
if("blur" in activeElement) {
|
||||||
|
activeElement["blur"]()
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// F = Search
|
// F = Search
|
||||||
if(e.keyCode == 70) {
|
if(e.keyCode == 70) {
|
||||||
let search = this.app.find("search") as HTMLInputElement
|
let search = this.app.find("search") as HTMLInputElement
|
||||||
@ -281,6 +294,7 @@ export class AnimeNotifier {
|
|||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,3 +9,20 @@ import (
|
|||||||
func GetUser(ctx *aero.Context) *arn.User {
|
func GetUser(ctx *aero.Context) *arn.User {
|
||||||
return arn.GetUserFromContext(ctx)
|
return arn.GetUserFromContext(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SameUser returns "true" or "false" depending on if the users are the same.
|
||||||
|
func SameUser(a *arn.User, b *arn.User) string {
|
||||||
|
if a == nil {
|
||||||
|
return "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
if b == nil {
|
||||||
|
return "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.ID == b.ID {
|
||||||
|
return "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "false"
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user