A music player that connects to your cloud/distributed storage.
0
fork

Configure Feed

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

feat: dropdown + nicer facets/themes lists

+276 -147
+38 -22
src/_components/facets.vto
··· 1 1 <ul> 2 - {{ for item of items }} 2 + {{ for index, item of items }} 3 3 <li data-url="{{item.url}}" data-name="{{item.title}}"> 4 - <span>{{item.title}}</span> 4 + <div style="position: relative;"> 5 + <a href="facets/l/?url={{encodeURIComponent(item.url)}}"> 6 + {{item.title}} 7 + </a> 8 + <button 9 + class="button--fixed button--transparent" 10 + popovertarget="facet-menu-{{id}}-{{index}}" 11 + style="anchor-name: --facet-anchor-{{id}}-{{index}}; position: absolute; right: 0; top: 50%; transform: translateY(-50%);" 12 + > 13 + <i class="ph-fill ph-dots-three-circle"></i> 14 + </button> 15 + </div> 5 16 <div class="list-description"> 6 - <div class="button-row"> 7 - <a href="facets/l/?url={{encodeURIComponent(item.url)}}" class="button button--bg-twist-1"> 8 - <span class="with-icon"> 9 - <i class="ph-fill ph-globe"></i> Open 10 - </span> 11 - </a> 12 - <button rel="save" class="button--bg-twist-1 button--subtle"> 13 - <span class="with-icon"> 14 - <i class="ph-fill ph-download"></i> Save 15 - </span> 16 - </button> 17 - <button rel="fork" class="button--bg-twist-1 button--subtle"> 18 - <span class="with-icon"> 19 - <i class="ph-fill ph-cursor-text"></i> Edit 20 - </span> 21 - </button> 22 - </div> 23 - <div> 24 - {{item.desc |> md(true)}} 25 - </div> 17 + {{item.desc |> md(true)}} 18 + </div> 19 + 20 + <!-- Dropdown Menu --> 21 + <div 22 + id="facet-menu-{{id}}-{{index}}" 23 + class="dropdown" 24 + style="position-anchor: --facet-anchor-{{id}}-{{index}}" 25 + popover 26 + > 27 + <a href="facets/l/?url={{encodeURIComponent(item.url)}}"> 28 + <span class="with-icon"> 29 + <i class="ph-fill ph-globe"></i> Open 30 + </span> 31 + </a> 32 + <a rel="save"> 33 + <span class="with-icon"> 34 + <i class="ph-fill ph-download"></i> Save 35 + </span> 36 + </a> 37 + <a rel="fork"> 38 + <span class="with-icon"> 39 + <i class="ph-fill ph-cursor-text"></i> Edit 40 + </span> 41 + </a> 26 42 </div> 27 43 </li> 28 44 {{ /for }}
+38 -22
src/_components/themes.vto
··· 1 1 <ul> 2 - {{ for item of items }} 2 + {{ for index, item of items }} 3 3 <li data-url="{{item.url}}" data-name="{{item.title}}"> 4 - <span>{{item.title}}</span> 4 + <div style="position: relative;"> 5 + <a href="themes/l/?url={{encodeURIComponent(item.url)}}"> 6 + {{item.title}} 7 + </a> 8 + <button 9 + class="button--fixed button--transparent" 10 + popovertarget="theme-menu-{{id}}-{{index}}" 11 + style="anchor-name: --theme-anchor-{{id}}-{{index}}; position: absolute; right: 0; top: 50%; transform: translateY(-50%);" 12 + > 13 + <i class="ph-fill ph-dots-three-circle"></i> 14 + </button> 15 + </div> 5 16 <div class="list-description"> 6 - <div class="button-row"> 7 - <a href="themes/l/?url={{encodeURIComponent(item.url)}}" class="button button--bg-twist-1"> 8 - <span class="with-icon"> 9 - <i class="ph-fill ph-globe"></i> Open 10 - </span> 11 - </a> 12 - <button rel="save" class="button--bg-twist-1 button--subtle"> 13 - <span class="with-icon"> 14 - <i class="ph-fill ph-download"></i> Save 15 - </span> 16 - </button> 17 - <button rel="fork" class="button--bg-twist-1 button--subtle"> 18 - <span class="with-icon"> 19 - <i class="ph-fill ph-cursor-text"></i> Edit 20 - </span> 21 - </button> 22 - </div> 23 - <div> 24 - {{item.desc |> md(true)}} 25 - </div> 17 + {{item.desc |> md(true)}} 18 + </div> 19 + 20 + <!-- Dropdown Menu --> 21 + <div 22 + id="theme-menu-{{id}}-{{index}}" 23 + class="dropdown" 24 + style="position-anchor: --theme-anchor-{{id}}-{{index}}" 25 + popover 26 + > 27 + <a href="themes/l/?url={{encodeURIComponent(item.url)}}"> 28 + <span class="with-icon"> 29 + <i class="ph-fill ph-globe"></i> Open 30 + </span> 31 + </a> 32 + <a rel="save"> 33 + <span class="with-icon"> 34 + <i class="ph-fill ph-download"></i> Save 35 + </span> 36 + </a> 37 + <a rel="fork"> 38 + <span class="with-icon"> 39 + <i class="ph-fill ph-cursor-text"></i> Edit 40 + </span> 41 + </a> 26 42 </div> 27 43 </li> 28 44 {{ /for }}
+58 -43
src/facets/index.js
··· 67 67 68 68 const output = foundation.orchestrator.output(); 69 69 70 + listEl.innerHTML = ""; 71 + 70 72 effect(() => { 71 73 const col = output.facets.collection().sort((a, b) => { 72 74 return a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()); 73 75 }); 74 76 75 - const h = col.length 77 + const state = output.facets.state(); 78 + 79 + const h = col.length && state === "loaded" 76 80 ? html` 77 81 <ul> 78 82 ${col.map((c) => 79 83 html` 80 - <li style="margin-bottom: var(--space-2xs)"> 81 - <span>${c.name}</span> 84 + <li> 85 + <div style="position: relative;"> 86 + <a href="facets/l/?id=${c.id}"> 87 + ${c.name} 88 + </a> 89 + <button 90 + class="button--fixed button--transparent" 91 + popovertarget="facet-menu-col-${c.id}" 92 + style="anchor-name: --facet-anchor-col-${c 93 + .id}; position: absolute; right: 0; top: 50%; transform: translateY(-50%);" 94 + > 95 + <i class="ph-fill ph-dots-three-circle"></i> 96 + </button> 97 + </div> 82 98 <div class="list-description"> 83 - <div style="margin-bottom: var(--space-2xs)"> 84 - ${c.url && !c.html 85 - ? html` 86 - <span class="with-icon"> 87 - <i class="ph-fill ph-binoculars"></i> 88 - <span>Tracking the original <a href="${c 89 - .url}">URL</a></span> 90 - </span> 91 - ` 92 - : html` 93 - <span class="with-icon"> 94 - <i class="ph-fill ph-code"></i> 95 - <span>Custom code</span> 96 - </span> 97 - `} 98 - </div> 99 - <div class="button-row"> 100 - <a href="facets/l/?id=${c 101 - .id}" class="button button--bg-twist-1"> 99 + ${c.url && !c.html 100 + ? html` 102 101 <span class="with-icon"> 103 - <i class="ph-fill ph-globe"></i> Open 102 + <i class="ph-fill ph-binoculars"></i> 103 + <span>Tracking the original <a href="${c 104 + .url}">URL</a></span> 104 105 </span> 105 - </a> 106 - <button 107 - class="button--bg-twist-1 button--subtle" 108 - @click="${() => editFacet(c)}" 109 - > 106 + ` 107 + : html` 110 108 <span class="with-icon"> 111 - <i class="ph-fill ph-cursor-text"></i> Edit 109 + <i class="ph-fill ph-code"></i> 110 + <span>Custom code</span> 112 111 </span> 113 - </button> 114 - <button 115 - class="button--bg-twist-2 button--subtle" 116 - @click="${deleteFacet({ 117 - id: c.id, 118 - })}" 119 - > 120 - <span class="with-icon"> 121 - <i class="ph-fill ph-eraser"></i> Delete 122 - </span> 123 - </button> 124 - </div> 112 + `} 113 + </div> 114 + 115 + <!-- Dropdown Menu --> 116 + <div 117 + id="facet-menu-col-${c.id}" 118 + class="dropdown" 119 + style="position-anchor: --facet-anchor-col-${c.id}" 120 + popover 121 + > 122 + <a href="facets/l/?id=${c.id}"> 123 + <span class="with-icon"> 124 + <i class="ph-fill ph-globe"></i> Open 125 + </span> 126 + </a> 127 + <a @click="${() => editFacet(c)}"> 128 + <span class="with-icon"> 129 + <i class="ph-fill ph-cursor-text"></i> Edit 130 + </span> 131 + </a> 132 + <a @click="${deleteFacet({ id: c.id })}"> 133 + <span class="with-icon"> 134 + <i class="ph-fill ph-eraser"></i> Delete 135 + </span> 136 + </a> 125 137 </div> 126 138 </li> 127 139 ` 128 140 )} 129 141 </ul> 130 142 ` 131 - : output.facets.state() === "loaded" 143 + : state === "loaded" 132 144 ? emptyFacetsList 133 145 : html` 134 - <i class="ph-bold ph-spinner-gap"></i> 146 + <div class="with-icon" style="font-size: var(--fs-sm);"> 147 + <i class="ph-bold ph-spinner-gap"></i> 148 + Loading items 149 + </div> 135 150 `; 136 151 137 152 render(h, listEl);
+9 -3
src/facets/index.vto
··· 5 5 styles: 6 6 - styles/base.css 7 7 - styles/diffuse/page.css 8 + - styles/vendor/phosphor/bold/style.css 8 9 - styles/vendor/phosphor/fill/style.css 9 10 10 11 scripts: ··· 80 81 <section class="flex"> 81 82 <h2 id="built-in">Built-in</h2> 82 83 83 - {{ await comp.facets({ items: builtIn }) }} 84 + {{ await comp.facets({ id: "builtin", items: builtIn }) }} 84 85 </section> 85 86 86 87 <section class="flex"> ··· 98 99 <div class="columns"> 99 100 <section class="flex"> 100 101 <h2 id="collection">Your collection</h2> 101 - <div id="list"></div> 102 + <div id="list"> 103 + <div class="with-icon" style="font-size: var(--fs-sm);"> 104 + <i class="ph-bold ph-spinner-gap"></i> 105 + Loading items 106 + </div> 107 + </div> 102 108 </section> 103 109 104 110 <section class="flex"> ··· 108 114 Some simple examples to help you understand how to build your own facet. Fork them to load them into the code editor below (or save → edit). 109 115 </p> 110 116 111 - {{ await comp.facets({ items: examples }) }} 117 + {{ await comp.facets({ id: "examples", items: examples }) }} 112 118 </section> 113 119 </div> 114 120
+69 -13
src/styles/diffuse/page.css
··· 202 202 } 203 203 204 204 /** 205 + * Dropdown menu 206 + */ 207 + 208 + .dropdown { 209 + position: fixed; 210 + position-area: bottom span-left; 211 + margin: 0; 212 + margin-top: var(--space-3xs); 213 + 214 + background: var(--bg-color); 215 + border: 0; 216 + border-radius: var(--radius-md); 217 + box-shadow: var(--box-shadow-lg); 218 + font-size: var(--fs-xs); 219 + padding: 0; 220 + text-align: left; 221 + 222 + &::backdrop { 223 + background: transparent; 224 + } 225 + 226 + & > a { 227 + cursor: pointer; 228 + display: block; 229 + padding: var(--space-3xs) var(--space-2xs); 230 + text-decoration: none; 231 + 232 + & > * { 233 + pointer-events: none; 234 + } 235 + } 236 + 237 + & > a:not(:last-child) { 238 + border-bottom: 1px solid oklch(from currentColor l c h / 0.05); 239 + } 240 + 241 + i { 242 + opacity: 0.4; 243 + transition: opacity 250ms; 244 + } 245 + 246 + a:hover i { 247 + opacity: 1; 248 + } 249 + } 250 + 251 + /** 205 252 * Forms 206 253 */ 207 254 ··· 325 372 background-color: oklch(from var(--accent-twist-5) l c h / var(--button-bg-opacity)); 326 373 } 327 374 375 + &.button--transparent { 376 + background-color: transparent; 377 + color: var(--text-color); 378 + } 379 + 380 + &.button--small { 381 + font-size: var(--fs-xs); 382 + line-height: 1; 383 + padding: var(--space-3xs); 384 + border-radius: var(--radius-xs); 385 + } 386 + 328 387 &.button--subtle { 329 388 --button-bg-opacity: 0.3; 330 - /*background-color: transparent;*/ 331 389 } 332 390 333 391 & > span { ··· 342 400 font-size: var(--fs-xs); 343 401 } 344 402 403 + .button-col { 404 + display: inline-flex; 405 + flex-direction: column; 406 + gap: var(--space-2xs); 407 + } 408 + 345 409 .button-row { 346 410 display: inline-flex; 347 411 gap: var(--space-2xs); ··· 378 442 width: 32.5%; 379 443 } 380 444 445 + /** 446 + * Lists 447 + */ 448 + 381 449 .list-description { 382 450 color: oklch(from currentColor l c h / 0.6); 383 451 font-size: var(--fs-xs); ··· 388 456 text-underline-offset: 3px; 389 457 } 390 458 391 - button, 392 - .button { 393 - line-height: 1; 394 - padding: var(--space-3xs); 395 - border-radius: var(--radius-xs); 396 - } 397 - 398 459 code { 399 460 font-size: var(--fs-xs); 400 - } 401 - 402 - .button-row { 403 - gap: var(--space-3xs); 404 - margin: 0 0 var(--space-2xs); 405 461 } 406 462 } 407 463
+58 -43
src/themes/index.js
··· 65 65 const listEl = document.querySelector("#list"); 66 66 if (!listEl) throw new Error("List element not found"); 67 67 68 + listEl.innerHTML = ""; 69 + 68 70 const output = foundation.orchestrator.output(); 69 71 70 72 effect(() => { ··· 72 74 return a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()); 73 75 }); 74 76 75 - const h = col.length 77 + const state = output.themes.state(); 78 + 79 + const h = col.length && state === "loaded" 76 80 ? html` 77 81 <ul> 78 82 ${col.map((c) => 79 83 html` 80 - <li style="margin-bottom: var(--space-2xs)"> 81 - <span>${c.name}</span> 84 + <li> 85 + <div style="position: relative;"> 86 + <a href="themes/l/?id=${c.id}"> 87 + ${c.name} 88 + </a> 89 + <button 90 + class="button--fixed button--transparent" 91 + popovertarget="theme-menu-col-${c.id}" 92 + style="anchor-name: --theme-anchor-col-${c 93 + .id}; position: absolute; right: 0; top: 50%; transform: translateY(-50%);" 94 + > 95 + <i class="ph-fill ph-dots-three-circle"></i> 96 + </button> 97 + </div> 82 98 <div class="list-description"> 83 - <div style="margin-bottom: var(--space-2xs)"> 84 - ${c.url && !c.html 85 - ? html` 86 - <span class="with-icon"> 87 - <i class="ph-fill ph-binoculars"></i> 88 - <span>Tracking the original <a href="${c 89 - .url}">URL</a></span> 90 - </span> 91 - ` 92 - : html` 93 - <span class="with-icon"> 94 - <i class="ph-fill ph-code"></i> 95 - <span>Custom code</span> 96 - </span> 97 - `} 98 - </div> 99 - <div class="button-row"> 100 - <a href="themes/l/?id=${c 101 - .id}" class="button button--bg-twist-1"> 99 + ${c.url && !c.html 100 + ? html` 102 101 <span class="with-icon"> 103 - <i class="ph-fill ph-globe"></i> Open 102 + <i class="ph-fill ph-binoculars"></i> 103 + <span>Tracking the original <a href="${c 104 + .url}">URL</a></span> 104 105 </span> 105 - </a> 106 - <button 107 - class="button--bg-twist-1 button--subtle" 108 - @click="${() => editTheme(c)}" 109 - > 106 + ` 107 + : html` 110 108 <span class="with-icon"> 111 - <i class="ph-fill ph-cursor-text"></i> Edit 109 + <i class="ph-fill ph-code"></i> 110 + <span>Custom code</span> 112 111 </span> 113 - </button> 114 - <button 115 - class="button--bg-twist-2 button--subtle" 116 - @click="${deleteTheme({ 117 - id: c.id, 118 - })}" 119 - > 120 - <span class="with-icon"> 121 - <i class="ph-fill ph-eraser"></i> Delete 122 - </span> 123 - </button> 124 - </div> 112 + `} 113 + </div> 114 + 115 + <!-- Dropdown Menu --> 116 + <div 117 + id="theme-menu-col-${c.id}" 118 + class="dropdown" 119 + style="position-anchor: --theme-anchor-col-${c.id}" 120 + popover 121 + > 122 + <a href="themes/l/?id=${c.id}"> 123 + <span class="with-icon"> 124 + <i class="ph-fill ph-globe"></i> Open 125 + </span> 126 + </a> 127 + <a @click="${() => editTheme(c)}"> 128 + <span class="with-icon"> 129 + <i class="ph-fill ph-cursor-text"></i> Edit 130 + </span> 131 + </a> 132 + <a @click="${deleteTheme({ id: c.id })}"> 133 + <span class="with-icon"> 134 + <i class="ph-fill ph-eraser"></i> Delete 135 + </span> 136 + </a> 125 137 </div> 126 138 </li> 127 139 ` 128 140 )} 129 141 </ul> 130 142 ` 131 - : output.themes.state() === "loaded" 143 + : state === "loaded" 132 144 ? emptyThemesList 133 145 : html` 134 - <i class="ph-bold ph-spinner-gap"></i> 146 + <div class="with-icon" style="font-size: var(--fs-sm);"> 147 + <i class="ph-bold ph-spinner-gap"></i> 148 + Loading items 149 + </div> 135 150 `; 136 151 137 152 render(h, listEl);
+6 -1
src/themes/index.vto
··· 82 82 <div class="columns"> 83 83 <section class="flex"> 84 84 <h2 id="collection">Your collection</h2> 85 - <div id="list"></div> 85 + <div id="list"> 86 + <div class="with-icon" style="font-size: var(--fs-sm);"> 87 + <i class="ph-bold ph-spinner-gap"></i> 88 + Loading items 89 + </div> 90 + </div> 86 91 </section> 87 92 88 93 <section class="flex"></section>