this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

✨ Add a search bar

+111 -7
+74 -2
components/gallery.templ
··· 2 2 3 3 import "github.com/ortfo/db" 4 4 5 + css galleryMaxWidth() { 6 + max-width: 1400px; 7 + margin: 0 auto; 8 + overflow: hidden; 9 + } 10 + 5 11 css galleryPageMain() { 6 12 display: flex; 7 13 flex-direction: column; 14 + justify-content: center; 15 + padding: 1rem; 16 + } 17 + 18 + css gallerySearch() { 19 + display: flex; 20 + flex-direction: row; 8 21 align-items: center; 9 - justify-content: center; 10 22 padding: 1rem; 11 23 } 12 24 25 + script onInput(inputClass string, galleryClass string) { 26 + var query = document.querySelector("." + inputClass).value.toLowerCase(); 27 + 28 + document.search(query) 29 + } 30 + 31 + script setupSearcher(galleryClass string) { 32 + document.addEventListener("DOMContentLoaded", () => { 33 + works = Array.from(document.querySelectorAll("." + galleryClass + " [data-work]")); 34 + 35 + console.log(works.map(w => Object.assign({}, w.dataset))) 36 + 37 + document.searcher = new Fuse(works.map(w => Object.assign({}, w.dataset)), { 38 + keys: ['work', 'title', 'tags'], 39 + threshold: 0.4, 40 + }) 41 + 42 + console.log(`Indexed ${works.length} works`) 43 + }) 44 + 45 + function debounce(delay, fn) { 46 + var timer = null; 47 + return function () { 48 + var context = this, args = arguments; 49 + clearTimeout(timer); 50 + timer = setTimeout(function () { 51 + fn.apply(context, args); 52 + }, delay); 53 + }; 54 + } 55 + 56 + document.search = debounce(500, (query) => { 57 + var gallery = document.querySelector("." + galleryClass); 58 + var works = document.querySelectorAll("." + galleryClass + " [data-work]"); 59 + var matchingIDs = document.searcher.search(query).map(r => r.item.work); 60 + 61 + works.forEach(work => { 62 + if (!query) { 63 + work.style.display = "block" 64 + } else if (!matchingIDs.includes(work.dataset.work)) { 65 + work.style.display = "none"; 66 + } else { 67 + work.style.display = "block"; 68 + } 69 + }) 70 + 71 + // Fix the width to prevent resizing when searching 72 + var realWidth = document.querySelector('.works').getBoundingClientRect().width; 73 + document.mason.pack() 74 + document.mason.resize(true) 75 + document.querySelector('.works').style.width = realWidth + "px"; 76 + }) 77 + } 78 + 13 79 templ GalleryPage(works []ortfodb.AnalyzedWork, language string) { 14 - <section class={ galleryPageMain() }> 80 + <form method="get" action="/" class={ galleryMaxWidth(), gallerySearch() }> 81 + @setupSearcher("works") 82 + @SearchBar(language, func (inputClass string) templ.ComponentScript { 83 + return onInput(inputClass, "works") 84 + }) 85 + </form> 86 + <section class={ galleryMaxWidth(), galleryPageMain() }> 15 87 { children... } 16 88 @WorksGrid(works, language, "works") 17 89 </section>
+25
components/ui.templ
··· 31 31 </span> 32 32 </a> 33 33 } 34 + 35 + css searchBar() { 36 + display: flex; 37 + align-items: center; 38 + flex-wrap: wrap; 39 + column-gap: 0.5em; 40 + padding: 0.25em 0.5em; 41 + border: 2px solid var(--primary, black); 42 + } 43 + 44 + css searchBarInput() { 45 + border: none; 46 + background-color: transparent; 47 + font-family: Manrope; 48 + } 49 + 50 + templ SearchBar(placeholder string, onInput func(string) templ.ComponentScript) { 51 + <label class={ searchBar() }> 52 + @shared.OnHover(searchBar(), shared.Declarations{ 53 + "background-color": "lightgray", 54 + }) 55 + <img src={ string(shared.Asset("icons/magnifying_glass.svg")) }/> 56 + <input name="q" oninput={ onInput(searchBarInput().ClassName()) } type="text" class={ searchBarInput() } i18n:placeholder="Search"/> 57 + </label> 58 + }
+2
components/work_card.templ
··· 99 99 </style> 100 100 <article 101 101 data-work={ work.ID } 102 + data-title={ work.Content[language].Title.String() } 103 + data-tags={ strings.Join(work.Metadata.Tags, " ") } 102 104 class={ "work", card() } 103 105 data-no-thumb?={ ThumbPath(work, language) == "http://localhost:8080/" } 104 106 data-primary={ shared.Color(work.Colors(language).Primary) }
+8 -4
components/works_grid.templ
··· 24 24 } 25 25 26 26 waitForElm(selector).then(() => { 27 - const mason = Bricks({ 27 + document.mason = Bricks({ 28 28 container: '.works', 29 29 packed: 'data-packed', 30 30 sizes: [ ··· 35 35 { mq: '1500px', columns: 4, gutter: 30 }, 36 36 ], 37 37 }) 38 - mason.pack() 39 - mason.resize(true) 38 + document.mason.pack() 39 + document.mason.resize(true) 40 40 }) 41 41 } 42 42 43 + css gridSection() { 44 + margin: 0 auto; 45 + } 46 + 43 47 templ WorksGrid(works []ortfodb.AnalyzedWork, language string, sectionName string) { 44 48 @masonry("." + sectionName) 45 - <section class={ sectionName }> 49 + <section class={ sectionName, gridSection() }> 46 50 for _, work := range ortfodb.SortWorksByDate(works) { 47 51 @WorkCard(work, language) 48 52 }
+2 -1
pages/layout.templ
··· 150 150 <link rel="stylesheet" href="https://assets.ewen.works/fonts/import.css"/> 151 151 <link rel="stylesheet" href="https://assets.ewen.works/global.css"/> 152 152 <script src={ string(shared.Asset("bricks.js")) }> </script> 153 + <script defer src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0"></script> 153 154 </head> 154 155 <body> 155 156 <header> ··· 179 180 case "fr": 180 181 en français 181 182 case "en": 182 - en anglais 183 + in english 183 184 } 184 185 </a> 185 186 </li>