diff options
| author | 2025-10-17 19:28:37 +0700 | |
|---|---|---|
| committer | 2025-10-17 19:28:37 +0700 | |
| commit | fa2d3009ed93c28e05758d52367b25fd6a04a2b7 (patch) | |
| tree | c4a05a93b5b5a1c586f0ed8a9eea2ef2f90dd3c6 /internal/glightbox/html_renderer.go | |
| parent | 2e93141f1f53410d6c38122b918972581ec4aa8d (diff) | |
| parent | 152bbebe8d2d0a07ab6958a74b5f53159c79e45f (diff) | |
| download | web-0.9.0.tar.gz web-0.9.0.zip | |
v0.9.0 (#10)v0.9.0
- [feat: implement new prototype
gallery](https://github.com/SayaAndy/saya-today-web/commit/44fe2478478225450becb77ad5d273462c9b400a)
(masonry + glightbox instead of lightgallery)
- A simpler column gallery, expandable between 2 and 5 columns;
- Allows highlighting images via tags;
- A simpler lightbox layout with wider image, especially on mobile
devices;
- Displays tooltips;
- [feat: adapted layouts for mobile/tablet
devices](https://github.com/SayaAndy/saya-today-web/commit/d3ffefbd3d9de2378b564e4c6f965c03a5a06113)
- [feat: switched most length units to absolute in
layouts](https://github.com/SayaAndy/saya-today-web/commit/454e2f3cd84b6c0b02a0c2936a574d3d9c35c5a1)
- [feat: use dvw/dvh instead of
vw/vh](https://github.com/SayaAndy/saya-today-web/commit/e231d8a3074853fbdbae9b368976bd902a1428f7)
- [feat: add onclick links in blog post
links](https://github.com/SayaAndy/saya-today-web/commit/db174993127a8b2faf863a670fd6ce8ca8d7c1af)
- feat: add href for any onclick links/icons
- feat: use page-first layout for page titles (like "{page-header} //
SAYA TODAY", not vice versa)
- chore: rm any remains of old lightgallery
- fix: map redefining
Diffstat (limited to 'internal/glightbox/html_renderer.go')
| -rw-r--r-- | internal/glightbox/html_renderer.go | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/internal/glightbox/html_renderer.go b/internal/glightbox/html_renderer.go new file mode 100644 index 0000000..44fbd4c --- /dev/null +++ b/internal/glightbox/html_renderer.go @@ -0,0 +1,188 @@ +package glightbox + +import ( + "bytes" + "fmt" + "math/rand" + "regexp" + "strings" + "time" + + "github.com/SayaAndy/saya-today-web/internal/tailwind" + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/renderer" + "github.com/yuin/goldmark/renderer/html" + "github.com/yuin/goldmark/util" +) + +type GLightboxHTMLRenderer struct { + html.Config + md goldmark.Markdown + anchorMatchRe *regexp.Regexp +} + +func NewGLightboxHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { + r := &GLightboxHTMLRenderer{ + Config: html.NewConfig(), + md: goldmark.New( + goldmark.WithParserOptions( + parser.WithAutoHeadingID(), + parser.WithAttribute(), + ), + goldmark.WithRenderer( + renderer.NewRenderer( + renderer.WithNodeRenderers( + util.Prioritized(tailwind.NewCustomLinkRenderer( + html.WithUnsafe(), html.WithHardWraps(), html.WithXHTML(), + ), 50), + util.Prioritized(html.NewRenderer( + html.WithUnsafe(), html.WithHardWraps(), html.WithXHTML(), + ), 100), + ), + ), + ), + ), + } + for _, opt := range opts { + opt.SetHTMLOption(&r.Config) + } + r.anchorMatchRe = regexp.MustCompile(`(?s)<\s*a(\s+[^<]*)(href\s*=\s*["'].*?["'])\s*([^<]*)>(.*?)<\s*\/\s*a\s*>`) + return r +} + +func (r *GLightboxHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { + reg.Register(KindGLightboxBlock, r.renderGLightbox) +} + +func (r *GLightboxHTMLRenderer) renderGLightbox(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { + if entering { + gallery := n.(*GLightboxBlock) + + if len(gallery.Images) == 0 { + return ast.WalkContinue, nil + } + + galleryID := generateDivId(8) + + var elements []string + for i, img := range gallery.Images { + imageUrlSegments := strings.Split(img.URL, ".") + imageUrlWithoutExt := strings.Join(imageUrlSegments[:len(imageUrlSegments)-1], ".") + imageUrlParts := strings.Split(imageUrlWithoutExt, "/") + imageNameParts := strings.Split(imageUrlParts[len(imageUrlParts)-1], "-") + + var captionBuf bytes.Buffer + captionHTML := img.Caption + if err := r.md.Convert(img.Caption, &captionBuf); err == nil { + captionHTML = captionBuf.Bytes() + captionHTML = bytes.TrimPrefix(captionHTML, []byte("<p>")) + captionHTML = bytes.TrimSuffix(captionHTML, []byte("</p>\n")) + captionHTML = bytes.TrimSuffix(captionHTML, []byte("</p>")) + } + + dayDate, _ := time.Parse("20060102 150405", imageNameParts[len(imageNameParts)-2]+" "+imageNameParts[len(imageNameParts)-1]) + dayDate = dayDate.In(gallery.Location) + + glightboxDescId := "" + if len(captionHTML) != 0 { + glightboxDescId = fmt.Sprintf("glightbox-desc-%s-%d", galleryID, i) + } + + dataDescriptionAttribute := "" + if glightboxDescId != "" { + dataDescriptionAttribute = fmt.Sprintf("data-description=\".%s\"", glightboxDescId) + } + + tagClassList := make([]string, 0, len(img.Tags)) + for _, tag := range img.Tags { + switch tag { + case "2x": + tagClassList = append(tagClassList, "grid-item-2x") + } + } + + if len(img.Caption) > 0 { + tagClassList = append(tagClassList, "grid-tooltip") + } + + anchorlessCaptionHTML := r.anchorMatchRe.ReplaceAll(captionHTML, []byte("<span class=\"linklike\" $1 $3>$4</span>")) + + elements = append(elements, fmt.Sprintf(` + <a href="https://f003.backblazeb2.com/file/sayana-photos/full/%s" class="glightbox-%s grid-item %s grid-item-%s p-1" + data-gallery="gallery-%s" data-title="%s" %s> + <picture> + <source media="(width < 640px)" srcset="https://f003.backblazeb2.com/file/sayana-photos/webp-320p/%s.webp" /> + <source media="(width < 1120px)" srcset="https://f003.backblazeb2.com/file/sayana-photos/webp-560p/%s.webp" /> + <source media="(width < 1600px)" srcset="https://f003.backblazeb2.com/file/sayana-photos/webp-800p/%s.webp" /> + <source media="(width < 2400px)" srcset="https://f003.backblazeb2.com/file/sayana-photos/webp-1200p/%s.webp" /> + <source media="(width >= 2400px)" srcset="https://f003.backblazeb2.com/file/sayana-photos/webp-1600p/%s.webp" /> + <img src="https://f003.backblazeb2.com/file/sayana-photos/webp-800p/%s.webp" /> + </picture> + <span class="grid-tooltip-text"><p>%s</p></span> + <span class="grid-item-index">%d</span> + </a>`, img.URL, galleryID, strings.Join(tagClassList, " "), galleryID, galleryID, dayDate.Format("2006-01-02 15:04:05 -07:00"), dataDescriptionAttribute, imageUrlWithoutExt, imageUrlWithoutExt, imageUrlWithoutExt, imageUrlWithoutExt, imageUrlWithoutExt, imageUrlWithoutExt, anchorlessCaptionHTML, i+1)) + + if glightboxDescId != "" { + elements = append(elements, fmt.Sprintf(` + <div class="glightbox-desc rounded-4 %s"> + <p>%s</p> + </div>`, glightboxDescId, captionHTML)) + } + } + + w.WriteString(fmt.Sprintf(` +<div class="justify-content-center display-block m-1"> + <hr class="border-t-3 border-dotted border-main-dark mt-1 mb-2 w-[80%%] ml-auto mr-auto"> + <div class="grid masonry-grid-%s mx-auto"> + <div class="grid-sizer grid-sizer-%s"></div> + %s + </div> + <hr class="border-t-3 border-dotted border-main-dark mt-1 mb-2 w-[80%%] ml-auto mr-auto"> +</div>`, galleryID, galleryID, strings.Join(elements, "\n"))) + + w.WriteString(fmt.Sprintf(` +<script> + var lightbox_%s = GLightbox({ + selector: '.glightbox-%s', + moreLength: 0 + }); + + var msnry_%s = new Masonry('.masonry-grid-%s', { + itemSelector: '.grid-item-%s', + columnWidth: '.grid-sizer-%s', + percentPosition: true, + horizontalOrder: true + }); + + var imgLoad_%s_timer; + var imgLoad_%s = imagesLoaded('.masonry-grid-%s'); + + function initMasonryLayout_%s() { + clearTimeout(imgLoad_%s_timer); + imgLoad_%s_timer = setTimeout(() => msnry_%s.layout(), 500); + } + + imgLoad_%s.on('progress', initMasonryLayout_%s); + + document.addEventListener('popout', (e) => { + imgLoad_%s.off('progress', initMasonryLayout_%s); + initMasonryLayout_%s = null; + imgLoad_%s = null; + }); +</script>`, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID, galleryID)) + } + + return ast.WalkContinue, nil +} + +func generateDivId(length int) string { + const charset = "abcdefghijklmnopqrstuvwxyz" + seededRand := rand.New(rand.NewSource(time.Now().UnixNano())) // Seed with current time + b := make([]byte, length) + for i := range b { + b[i] = charset[seededRand.Intn(len(charset))] + } + return string(b) +} |