|
1 | 1 | <script lang="ts"> |
2 | 2 | import type { Package } from '$lib/server/content'; |
3 | | - import { fix_position } from '../../../../../packages/site-kit/src/lib/actions/utils'; |
4 | 3 | import PackageCard from './PackageCard.svelte'; |
5 | 4 |
|
6 | 5 | interface Props { |
|
11 | 10 |
|
12 | 11 | let { title, description, packages }: Props = $props(); |
13 | 12 |
|
14 | | - let header: HTMLElement; |
15 | | -
|
16 | 13 | const INITIAL_ITEMS = 3; |
17 | 14 | let showAll = $state(false); |
18 | | - let visiblePackages = $derived(showAll ? packages : packages.slice(0, INITIAL_ITEMS)); |
19 | 15 | </script> |
20 | 16 |
|
21 | 17 | <section class="category"> |
22 | 18 | <header> |
23 | | - <h2 bind:this={header}> |
24 | | - {title} |
25 | | - </h2> |
| 19 | + <h2>{title}</h2> |
26 | 20 |
|
27 | 21 | {#if description} |
28 | 22 | <p>{@html description}</p> |
29 | 23 | {/if} |
30 | 24 | </header> |
31 | 25 |
|
32 | | - <div class="content"> |
33 | | - {#each visiblePackages as pkg} |
| 26 | + <div class="grid"> |
| 27 | + {#each packages.slice(0, INITIAL_ITEMS) as pkg} |
34 | 28 | <div class="item"> |
35 | 29 | <PackageCard {pkg} /> |
36 | 30 | </div> |
37 | 31 | {/each} |
38 | 32 | </div> |
39 | 33 |
|
40 | 34 | {#if packages.length > INITIAL_ITEMS} |
41 | | - <div class="show-more-container"> |
42 | | - <label> |
43 | | - <button |
44 | | - class="raised" |
45 | | - aria-label="Show more" |
46 | | - aria-pressed={showAll} |
47 | | - onclick={(e) => { |
48 | | - const { bottom } = header.getBoundingClientRect(); |
49 | | - |
50 | | - // if the current section is wholly visible, don't muck about with the scroll position |
51 | | - if (!showAll || bottom > 0) { |
52 | | - showAll = !showAll; |
53 | | - return; |
54 | | - } |
55 | | - |
56 | | - // otherwise, keep the button in the same position |
57 | | - fix_position(e.currentTarget, () => { |
58 | | - showAll = !showAll; |
59 | | - }); |
60 | | - }}><span class="icon"></span></button |
61 | | - > |
62 | | - |
63 | | - {showAll ? 'show less' : `show all (${packages.length})`} |
64 | | - </label> |
65 | | - </div> |
| 35 | + <details> |
| 36 | + <summary> |
| 37 | + <span class="raised button" aria-label="Toggle"> |
| 38 | + <span class="icon"></span> |
| 39 | + </span> |
| 40 | + |
| 41 | + <span> |
| 42 | + {showAll ? 'show less' : `show all (${packages.length})`} |
| 43 | + </span> |
| 44 | + </summary> |
| 45 | + |
| 46 | + <div class="grid"> |
| 47 | + {#each packages.slice(INITIAL_ITEMS) as pkg} |
| 48 | + <div class="item"> |
| 49 | + <PackageCard {pkg} /> |
| 50 | + </div> |
| 51 | + {/each} |
| 52 | + </div> |
| 53 | + </details> |
66 | 54 | {/if} |
67 | 55 | </section> |
68 | 56 |
|
|
83 | 71 | } |
84 | 72 | } |
85 | 73 |
|
86 | | - .content { |
| 74 | + .grid { |
87 | 75 | display: grid; |
88 | 76 | grid-template-columns: 1fr; |
89 | 77 | gap: 2rem; |
90 | | - margin-top: 1rem; |
91 | 78 |
|
92 | 79 | @media (min-width: 1024px) { |
93 | 80 | grid-template-columns: repeat(3, 1fr); |
94 | 81 | } |
95 | 82 | } |
96 | 83 |
|
| 84 | + details { |
| 85 | + position: relative; |
| 86 | + margin-bottom: 9rem; |
| 87 | +
|
| 88 | + .grid { |
| 89 | + margin-top: 2rem; |
| 90 | + } |
| 91 | + } |
| 92 | +
|
| 93 | + summary { |
| 94 | + position: absolute; |
| 95 | + bottom: -6rem; |
| 96 | + font: var(--sk-font-ui-small); |
| 97 | + display: flex; |
| 98 | + align-items: center; |
| 99 | + gap: 1rem; |
| 100 | +
|
| 101 | + .icon { |
| 102 | + mask-size: 2rem; |
| 103 | + mask-image: url(icons/plus); |
| 104 | +
|
| 105 | + [open] & { |
| 106 | + mask-image: url(icons/minus); |
| 107 | + } |
| 108 | + } |
| 109 | + } |
| 110 | +
|
97 | 111 | .item { |
98 | 112 | height: 16rem; |
99 | 113 | min-width: 0; /* Prevents grid items from overflowing */ |
|
0 commit comments