feat: added grid view 4532bb43
Steve · 2026-02-25 20:17 1 file(s) · +70 −12
src/routes/+page.svelte +70 −12
1 1
<script lang="ts">
2 +
  import { browser } from "$app/environment";
2 3
  import type { PageData } from "./$types";
3 4
  import ProgressiveImage from "$lib/components/ProgressiveImage.svelte";
4 5
  type ImageItem = PageData["photos"][number];
5 6
  let { data }: { data: PageData } = $props();
6 7
8 +
  let viewMode = $state<'feed' | 'grid'>('feed');
7 9
  let photos = $state<ImageItem[]>([]);
8 10
  let loading = $state(false);
9 11
  let hasMore = $derived(photos.length < data.total);
12 +
13 +
  if (browser) {
14 +
    const saved = localStorage.getItem('viewMode');
15 +
    if (saved === 'feed' || saved === 'grid') {
16 +
      viewMode = saved;
17 +
    }
18 +
  }
19 +
20 +
  function toggleViewMode() {
21 +
    viewMode = viewMode === 'feed' ? 'grid' : 'feed';
22 +
    if (browser) {
23 +
      localStorage.setItem('viewMode', viewMode);
24 +
    }
25 +
  }
10 26
11 27
  $effect(() => {
12 28
    photos = data.photos;
49 65
</script>
50 66
51 67
<div class="bg-[#121113] min-h-screen text-white">
52 -
  <div class="fixed bg-[#121113] w-full py-4 sm:px-8 px-4">
68 +
  <div class="fixed bg-[#121113] w-full py-4 sm:px-8 px-4 flex items-center justify-between z-10">
53 69
    <h1 class="text-sm">steve.photo</h1>
70 +
    <button onclick={toggleViewMode} class="text-neutral-400 hover:text-white transition-colors" aria-label="Toggle view mode">
71 +
      {#if viewMode === 'feed'}
72 +
        <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
73 +
          <rect x="3" y="3" width="7" height="7"></rect>
74 +
          <rect x="14" y="3" width="7" height="7"></rect>
75 +
          <rect x="3" y="14" width="7" height="7"></rect>
76 +
          <rect x="14" y="14" width="7" height="7"></rect>
77 +
        </svg>
78 +
      {:else}
79 +
        <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
80 +
          <line x1="3" y1="6" x2="21" y2="6"></line>
81 +
          <line x1="3" y1="12" x2="21" y2="12"></line>
82 +
          <line x1="3" y1="18" x2="21" y2="18"></line>
83 +
        </svg>
84 +
      {/if}
85 +
    </button>
54 86
  </div>
55 87
56 88
  {#snippet figure(image: ImageItem)}
78 110
    </div>
79 111
  {/snippet}
80 112
81 -
  <div class="flex flex-col gap-2 pt-12">
82 -
    {#each photos as image}
83 -
      {@render figure(image)}
84 -
    {/each}
113 +
  {#if viewMode === 'feed'}
114 +
    <div class="flex flex-col gap-2 pt-12">
115 +
      {#each photos as image}
116 +
        {@render figure(image)}
117 +
      {/each}
118 +
    </div>
119 +
  {:else}
120 +
    <div class="grid grid-cols-2 sm:grid-cols-3 gap-1 pt-12 sm:px-8 px-4">
121 +
      {#each photos as image}
122 +
        <a href="/photo/{image.slug}" class="grid-item block overflow-hidden aspect-[3/2]">
123 +
          <ProgressiveImage
124 +
            class="w-full h-full block"
125 +
            src={image.image}
126 +
            thumb={image.thumb}
127 +
            alt={image.title}
128 +
          />
129 +
        </a>
130 +
      {/each}
131 +
    </div>
132 +
  {/if}
85 133
86 -
    <div bind:this={sentinel} class="h-4"></div>
134 +
  <div bind:this={sentinel} class="h-4"></div>
87 135
88 -
    {#if loading}
89 -
      <div class="flex justify-center py-8">
90 -
        <div class="text-neutral-400 text-sm">Loading...</div>
91 -
      </div>
92 -
    {/if}
93 -
  </div>
136 +
  {#if loading}
137 +
    <div class="flex justify-center py-8">
138 +
      <div class="text-neutral-400 text-sm">Loading...</div>
139 +
    </div>
140 +
  {/if}
94 141
</div>
142 +
143 +
<style>
144 +
  .grid-item :global(.progressive-container) {
145 +
    aspect-ratio: unset !important;
146 +
    height: 100%;
147 +
  }
148 +
149 +
  .grid-item :global(.progressive-image) {
150 +
    object-fit: cover;
151 +
  }
152 +
</style>