From 633b5942f53ab75bddf1a0d7a48af1087ab8eb29 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Fri, 30 Jun 2017 23:52:42 +0200 Subject: [PATCH] Inline editing --- mixins/Input.pixy | 8 +++---- pages/animelist/animelist.pixy | 31 +++++++++++++++++--------- pages/animelist/animelist.scarlet | 2 +- pages/animelistitem/animelistitem.pixy | 2 +- pages/embed/embed.go | 2 +- scripts/Actions.ts | 26 ++++++++++++++------- scripts/AnimeNotifier.ts | 16 ++++++++++++- utils/user.go | 17 ++++++++++++++ 8 files changed, 78 insertions(+), 26 deletions(-) diff --git a/mixins/Input.pixy b/mixins/Input.pixy index 01553a40..766b67af 100644 --- a/mixins/Input.pixy +++ b/mixins/Input.pixy @@ -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") diff --git a/pages/animelist/animelist.pixy b/pages/animelist/animelist.pixy index 281cc8a0..cf8eb30c 100644 --- a/pages/animelist/animelist.pixy +++ b/pages/animelist/animelist.pixy @@ -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") diff --git a/pages/animelist/animelist.scarlet b/pages/animelist/animelist.scarlet index fa271dd9..40d76e0e 100644 --- a/pages/animelist/animelist.scarlet +++ b/pages/animelist/animelist.scarlet @@ -46,7 +46,7 @@ // margin-bottom -2px .anime-list-item-rating - flex-basis 100px + flex-basis 50px text-align center .anime-list-item-actions diff --git a/pages/animelistitem/animelistitem.pixy b/pages/animelistitem/animelistitem.pixy index 7194f5f6..305bb9f6 100644 --- a/pages/animelistitem/animelistitem.pixy +++ b/pages/animelistitem/animelistitem.pixy @@ -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") diff --git a/pages/embed/embed.go b/pages/embed/embed.go index 53ba9158..483c3430 100644 --- a/pages/embed/embed.go +++ b/pages/embed/embed.go @@ -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))) } diff --git a/scripts/Actions.ts b/scripts/Actions.ts index 28c7d0b6..a2431b15 100644 --- a/scripts/Actions.ts +++ b/scripts/Actions.ts @@ -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" } - input.disabled = true + 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) - input.disabled = false + + if(isContentEditable) { + input.contentEditable = "true" + } else { + input.disabled = false + } return arn.reloadContent() }) diff --git a/scripts/AnimeNotifier.ts b/scripts/AnimeNotifier.ts index 70e0df4d..666a87a5 100644 --- a/scripts/AnimeNotifier.ts +++ b/scripts/AnimeNotifier.ts @@ -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 } } } \ No newline at end of file diff --git a/utils/user.go b/utils/user.go index 3e665f7e..74d39297 100644 --- a/utils/user.go +++ b/utils/user.go @@ -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" +}