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)
|
||||
.widget-input
|
||||
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)
|
||||
.widget-input
|
||||
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)
|
||||
.widget-input
|
||||
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)
|
||||
.widget-input
|
||||
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
|
||||
.anime-list-container
|
||||
h3.status-name Watching
|
||||
AnimeList(animeLists[arn.AnimeListStatusWatching], user)
|
||||
AnimeList(animeLists[arn.AnimeListStatusWatching], viewUser, user)
|
||||
|
||||
if len(animeLists[arn.AnimeListStatusCompleted].Items) > 0
|
||||
.anime-list-container
|
||||
h3.status-name Completed
|
||||
AnimeList(animeLists[arn.AnimeListStatusCompleted], user)
|
||||
AnimeList(animeLists[arn.AnimeListStatusCompleted], viewUser, user)
|
||||
|
||||
if len(animeLists[arn.AnimeListStatusPlanned].Items) > 0
|
||||
.anime-list-container
|
||||
h3.status-name Planned
|
||||
AnimeList(animeLists[arn.AnimeListStatusPlanned], user)
|
||||
AnimeList(animeLists[arn.AnimeListStatusPlanned], viewUser, user)
|
||||
|
||||
if len(animeLists[arn.AnimeListStatusHold].Items) > 0
|
||||
.anime-list-container
|
||||
h3.status-name On hold
|
||||
AnimeList(animeLists[arn.AnimeListStatusHold], user)
|
||||
AnimeList(animeLists[arn.AnimeListStatusHold], viewUser, user)
|
||||
|
||||
if len(animeLists[arn.AnimeListStatusDropped].Items) > 0
|
||||
.anime-list-container
|
||||
h3.status-name Dropped
|
||||
AnimeList(animeLists[arn.AnimeListStatusDropped], user)
|
||||
AnimeList(animeLists[arn.AnimeListStatusDropped], viewUser, user)
|
||||
|
||||
//- for status, animeList := range animeLists
|
||||
//- h3= status
|
||||
//- 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
|
||||
thead
|
||||
tr
|
||||
th.anime-list-item-name Anime
|
||||
th.anime-list-item-airing-date Airing
|
||||
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
|
||||
th.anime-list-item-actions Actions
|
||||
tbody
|
||||
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
|
||||
a.ajax(href=item.Link(animeList.User().Nick))= item.Anime().Title.Canonical
|
||||
td.anime-list-item-airing-date
|
||||
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)
|
||||
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-max= item.Anime().EpisodeCountString()
|
||||
//- .anime-list-item-episodes-edit
|
||||
//- a.ajax(href=, title="Edit anime")
|
||||
//- 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
|
||||
td.anime-list-item-actions
|
||||
a(href=arn.Nyaa.GetLink(item.Anime()), title="Search on Nyaa", target="_blank", rel="noopener")
|
||||
|
@ -46,7 +46,7 @@
|
||||
// margin-bottom -2px
|
||||
|
||||
.anime-list-item-rating
|
||||
flex-basis 100px
|
||||
flex-basis 50px
|
||||
text-align center
|
||||
|
||||
.anime-list-item-actions
|
||||
|
@ -14,7 +14,7 @@ component AnimeListItem(viewUser *arn.User, item *arn.AnimeListItem, anime *arn.
|
||||
option(value=arn.AnimeListStatusDropped) Dropped
|
||||
|
||||
.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.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")
|
||||
|
@ -26,5 +26,5 @@ func Get(ctx *aero.Context) string {
|
||||
animeList.Sort()
|
||||
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) {
|
||||
arn.loading(true)
|
||||
|
||||
let isContentEditable = input.isContentEditable
|
||||
let obj = {}
|
||||
let value = input.value
|
||||
let value = isContentEditable ? input.innerText : input.value
|
||||
|
||||
if(input.type === "number") {
|
||||
if(input.getAttribute("step") === "1") {
|
||||
obj[input.id] = parseInt(value)
|
||||
if(input.type === "number" || input.dataset.type === "number") {
|
||||
if(input.getAttribute("step") === "1" || input.dataset.step === "1") {
|
||||
obj[input.dataset.field] = parseInt(value)
|
||||
} else {
|
||||
obj[input.id] = parseFloat(value)
|
||||
obj[input.dataset.field] = parseFloat(value)
|
||||
}
|
||||
} else {
|
||||
obj[input.id] = value
|
||||
obj[input.dataset.field] = value
|
||||
}
|
||||
|
||||
// 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"
|
||||
}
|
||||
|
||||
if(isContentEditable) {
|
||||
input.contentEditable = "false"
|
||||
} else {
|
||||
input.disabled = true
|
||||
}
|
||||
|
||||
fetch(apiObject.dataset.api, {
|
||||
method: "POST",
|
||||
@ -51,7 +56,12 @@ export function save(arn: AnimeNotifier, input: HTMLInputElement | HTMLTextAreaE
|
||||
.catch(console.error)
|
||||
.then(() => {
|
||||
arn.loading(false)
|
||||
|
||||
if(isContentEditable) {
|
||||
input.contentEditable = "true"
|
||||
} else {
|
||||
input.disabled = false
|
||||
}
|
||||
|
||||
return arn.reloadContent()
|
||||
})
|
||||
|
@ -265,13 +265,26 @@ export class AnimeNotifier {
|
||||
}
|
||||
|
||||
onKeyDown(e: KeyboardEvent) {
|
||||
let activeElement = document.activeElement
|
||||
|
||||
// Ignore hotkeys on input elements
|
||||
switch(document.activeElement.tagName) {
|
||||
switch(activeElement.tagName) {
|
||||
case "INPUT":
|
||||
case "TEXTAREA":
|
||||
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
|
||||
if(e.keyCode == 70) {
|
||||
let search = this.app.find("search") as HTMLInputElement
|
||||
@ -281,6 +294,7 @@ export class AnimeNotifier {
|
||||
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
@ -9,3 +9,20 @@ import (
|
||||
func GetUser(ctx *aero.Context) *arn.User {
|
||||
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