Skip to content

Commit

Permalink
feat: next.js widget (#19)
Browse files Browse the repository at this point in the history
* remove htmx pagination & add inlince css & add widget query

* add ids

* change error message

* add tailwind prefix

* add cache control header

* create next widget

* create theme option

* remove hbs

* rename var

* remove unused page args

* improve changelog props type

* update README

* remove unnecessary package

* fix next peerDependencies version

* publish version 0.0.1

* fix peerDependencies versions

* add changelog

* add sample app

* add keywords

* change IDs to demo changelog
  • Loading branch information
JonasHiltl authored Oct 26, 2024
1 parent 4187f07 commit 017bcbb
Show file tree
Hide file tree
Showing 64 changed files with 11,266 additions and 314 deletions.
2 changes: 1 addition & 1 deletion .air.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ tmp_dir = "tmp"
bin = "./tmp/openchangelog"
cmd = "go build -ldflags -w -o ./tmp/openchangelog cmd/server.go"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", ".testdata", "internal/handler/web/node_modules", ".cache"]
exclude_dir = ["assets", "tmp", "vendor", ".testdata", "internal/handler/web/node_modules", ".cache", "widget"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
Expand Down
8 changes: 5 additions & 3 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import (
"github.com/jonashiltl/openchangelog/internal/handler/rss"
"github.com/jonashiltl/openchangelog/internal/handler/web"
"github.com/jonashiltl/openchangelog/internal/store"
"github.com/jonashiltl/openchangelog/render"
"github.com/naveensrinivasan/httpcache"
"github.com/naveensrinivasan/httpcache/diskcache"
"github.com/peterbourgon/diskv"
"github.com/rs/cors"
"github.com/sourcegraph/s3cache"
)

Expand All @@ -41,13 +41,15 @@ func main() {
}

loader := changelog.NewLoader(cfg, st, cache)
renderer := web.NewRenderer(cfg)

rest.RegisterRestHandler(mux, rest.NewEnv(st, loader))
web.RegisterWebHandler(mux, web.NewEnv(cfg, loader, render.New(cfg)))
web.RegisterWebHandler(mux, web.NewEnv(cfg, loader, renderer))
rss.RegisterRSSHandler(mux, rss.NewEnv(cfg, loader))
handler := cors.Default().Handler(mux)

fmt.Printf("Starting server at http://%s\n", cfg.Addr)
log.Fatal(http.ListenAndServe(cfg.Addr, mux))
log.Fatal(http.ListenAndServe(cfg.Addr, handler))
}

func parseConfig() (config.Config, error) {
Expand Down
36 changes: 9 additions & 27 deletions components/article.templ
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,13 @@ import (
)

type ArticleListArgs struct {
Articles []ArticleArgs
NextPageURL string
Articles []ArticleArgs
}

templ ArticleList(a ArticleListArgs) {
for _, item := range a.Articles {
@Article(item)
}
if a.NextPageURL != "" {
<div
hx-trigger="revealed"
hx-target="this"
hx-get={ a.NextPageURL }
hx-swap="afterend"
hx-indicator="#skeleton"
>
<div id="skeleton" class="[&:not(.htmx-request)]:opacity-0 [&:not(.htmx-request)]:h-0 [&:not(.htmx-request)]:overflow-hidden">
<div class="animate-pulse w-full space-y-2 mt-12">
<div class="w-3/4 h-8 rounded bg-gray-100"></div>
<div class="w-full h-5 rounded bg-gray-100"></div>
<div class="w-full h-24 rounded bg-gray-100"></div>
</div>
</div>
</div>
}
}

type ArticleArgs struct {
Expand All @@ -44,22 +26,22 @@ type ArticleArgs struct {
}

templ Article(a ArticleArgs) {
<article class="relative">
<h2 id={ a.ID } class="group">
<article class="o-relative">
<h2 id={ a.ID } class="o-group">
{ a.Title }
<a
class="opacity-0 group-hover:opacity-100 transition-opacity"
class="o-opacity-0 group-hover:o-opacity-100 o-transition-opacity"
href={ templ.URL(fmt.Sprintf("#%s", a.ID)) }
>
#
</a>
</h2>
<p class="text-caption">{ a.Description }</p>
<div class="lg:absolute lg:-left-40 lg:max-w-40 lg:top-0 lg:mt-1 lg:mr-2 flex flex-row gap-2 lg:gap-0 items-center lg:items-start lg:flex-col">
<p class="o-text-caption">{ a.Description }</p>
<div class="lg:o-absolute lg:o--left-40 lg:o-max-w-40 lg:o-top-0 lg:o-mt-1 lg:o-mr-2 o-flex o-flex-row o-gap-2 lg:o-gap-0 o-items-center lg:o-items-start lg:o-flex-col">
if !a.PublishedAt.IsZero() {
<p class="text-caption text-nowrap">{ a.PublishedAt.Format("02 Jan 2006") }</p>
<p class="o-text-caption o-text-nowrap">{ a.PublishedAt.Format("02 Jan 2006") }</p>
}
<div class="flex flex-wrap gap-2">
<div class="o-flex o-flex-wrap o-gap-2">
<style>
#tag:where([color-scheme=dark] *) {
color: var(--tag-text-light);
Expand All @@ -75,7 +57,7 @@ templ Article(a ArticleArgs) {
}

templ Tag(name string) {
<div id="tag" class={ "p-1 rounded border text-xs text-nowrap leading-3", tagStyle(name) }>{ name }</div>
<div id="tag" class={ "o-p-1 o-rounded o-border o-text-xs o-text-nowrap o-leading-3", tagStyle(name) }>{ name }</div>
}

css tagStyle(tag string) {
Expand Down
102 changes: 41 additions & 61 deletions components/article_templ.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 69 additions & 2 deletions components/changelog.templ
Original file line number Diff line number Diff line change
@@ -1,7 +1,74 @@
package components

templ ChangelogContainer() {
<main class="mx-4 mt-8 sm:mx-0 sm:mt-10 md:mt-20">
import "html/template"

type ChangelogContainerArgs struct {
CurrentURL string
HasMoreArticle bool
}

// Contains the article list and footer
templ ChangelogContainer(args ChangelogContainerArgs) {
<main id="changelog-container" class="o-mx-4 sm:o-mx-0">
{ children... }
<div id="skeleton" class="o-hidden">
<div class="o-animate-pulse o-w-full o-space-y-4 o-mt-12">
<div class="o-w-3/4 o-h-9 o-rounded o-bg-black/10 dark:o-bg-white/10"></div>
<div class="o-w-full o-h-5 o-rounded o-bg-black/10 dark:o-bg-white/10"></div>
<div class="o-w-full o-h-32 o-rounded o-bg-black/10 dark:o-bg-white/10"></div>
</div>
</div>
</main>
if args.HasMoreArticle {
@templ.FromGoHTML(infiniteScrollTemplate, args.CurrentURL)
}
}

var infiniteScrollTemplate = template.Must(template.New("infiniteScrollTemplate").Parse(`
<script>
const skeleton = document.getElementById("skeleton")
const container = document.getElementById("changelog-container")
const currentPageURL = new URL("{{ . }}")
const params = currentPageURL.searchParams;
let isLoadingNextPage = false
let hasMore = true
function loadStarted() {
isLoadingNextPage = true
skeleton.style.display = "block"
}
function loadEnded() {
isLoadingNextPage = false
skeleton.style.display = "none"
}
async function loadNextPage(url) {
try {
loadStarted()
const currentPage = parseInt(params.get('page')) || 1;
params.set('page', currentPage + 1);
// don't load full html page, only articles list
params.set('articles', "true");
const res = await fetch(currentPageURL)
if (!res.ok || res.status === 204) {
hasMore = false
}
const newArticles = await res.text()
skeleton.insertAdjacentHTML('beforebegin', newArticles)
} finally {
loadEnded()
}
}
window.addEventListener("scroll", () => {
const bufferPx = 100 // start loading before reaching end of container
const endReached = window.scrollY + window.innerHeight + bufferPx >= container.scrollHeight
if(endReached && !isLoadingNextPage && hasMore){
loadNextPage()
}
})
</script>
`,
))
Loading

0 comments on commit 017bcbb

Please sign in to comment.