atmosphere explorer pdsls.dev
atproto tool typescript

collection filter styling #4

closed opened by futur.blue targeting main from futur.blue/pdsls: filter-styling
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:uu5axsmbm2or2dngy4gwchec/sh.tangled.repo.pull/3lyecvsfzoe22
+78 -7
Diff #0
+75 -6
src/views/collection.tsx
··· 3 3 import { $type, ActorIdentifier, InferXRPCBodyOutput } from "@atcute/lexicons"; 4 4 import * as TID from "@atcute/tid"; 5 5 import { A, useParams } from "@solidjs/router"; 6 - import { createEffect, createResource, createSignal, For, Show, untrack } from "solid-js"; 6 + import { createEffect, createResource, createSignal, For, Show, untrack, onMount, onCleanup } from "solid-js"; 7 7 import { createStore } from "solid-js/store"; 8 - import { Button } from "../components/button.jsx"; 8 + import { Button, type ButtonProps } from "../components/button.jsx"; 9 9 import { JSONType, JSONValue } from "../components/json.jsx"; 10 - 11 - 10 + import { agent } from "../components/login.jsx"; 11 + import { TextInput } from "../components/text-input.jsx"; 12 12 13 13 14 14 ··· 72 72 const [batchDelete, setBatchDelete] = createSignal(false); 73 73 const [lastSelected, setLastSelected] = createSignal<number>(); 74 74 const [reverse, setReverse] = createSignal(false); 75 + const [filterStuck, setFilterStuck] = createSignal(false); 75 76 const did = params.repo; 76 77 let pds: string; 77 78 let rpc: Client; 79 + let sticky!: HTMLDivElement; 78 80 79 81 const fetchRecords = async () => { 80 82 if (!pds) pds = await resolvePDS(did); ··· 157 159 true, 158 160 ); 159 161 162 + const FilterButton = (props: ButtonProps) => { 163 + return <Button 164 + class="flex items-center gap-1 rounded-lg bg-white px-2 py-1.5 text-xs font-semibold border-[0.5px] border-neutral-300 dark:border-neutral-700 shadow-md" 165 + classList={{ 166 + "dark:bg-dark-300 dark:hover:bg-dark-100 dark:active:bg-dark-100 bg-white hover:bg-neutral-50 active:bg-neutral-50": !filterStuck(), 167 + "dark:bg-dark-100 dark:hover:bg-dark-50 dark:active:bg-dark-50 bg-neutral-50 hover:bg-neutral-200 active:bg-neutral-200": filterStuck() 168 + }} 169 + {...props} 170 + /> 171 + } 172 + 173 + onMount(() => { 174 + let ticking = false; 175 + const tick = () => { 176 + const topPx = parseFloat(getComputedStyle(sticky).top); 177 + const { top } = sticky.getBoundingClientRect(); 178 + setFilterStuck(top <= topPx + 0.5); 179 + ticking = false; 180 + }; 181 + 182 + const onScroll = () => { 183 + if (!ticking) { 184 + ticking = true; 185 + requestAnimationFrame(tick); 186 + } 187 + }; 188 + 189 + window.addEventListener("scroll", onScroll, { passive: true }); 190 + 191 + tick(); 192 + 193 + onCleanup(() => { 194 + window.removeEventListener("scroll", onScroll); 195 + }); 196 + }); 197 + 160 198 return ( 161 199 <Show when={records.length || response()}> 162 200 <div class="flex w-full flex-col items-center"> 163 - <div class="dark:bg-dark-500 sticky top-0 z-5 flex w-screen flex-col items-center justify-center gap-2 bg-neutral-100 pt-1 pb-3"> 201 + <div 202 + ref={(el) => (sticky = el)} 203 + class="sticky top-2 z-10 flex flex-col items-center justify-center gap-2 rounded-lg p-3 transition-colors" 204 + classList={{ 205 + "bg-neutral-50 dark:bg-dark-300 border-[0.5px] border-neutral-300 dark:border-neutral-700 shadow-md": filterStuck(), 206 + "bg-transparent border-transparent shadow-none -mt-2": !filterStuck(), 207 + }} 208 + > 164 209 <div class="flex w-[22rem] items-center gap-2 sm:w-[24rem]"> 165 210 <Show when={agent() && agent()?.sub === did}> 166 211 <div class="flex items-center gap-x-2"> ··· 221 266 </div> 222 267 <Show when={records.length > 1}> 223 268 <div class="flex w-[22rem] items-center justify-between gap-x-2 sm:w-[24rem]"> 224 - <Button 269 + <FilterButton 225 270 onClick={() => { 226 271 setReverse(!reverse()); 272 + setRecords([]); 273 + 274 + 275 + 276 + 277 + 278 + class={`iconify ${reverse() ? "lucide--rotate-ccw" : "lucide--rotate-cw"} text-sm`} 279 + ></span> 280 + Reverse 281 + </FilterButton> 282 + <div> 283 + <Show when={batchDelete()}> 284 + <span>{records.filter((rec) => rec.toDelete).length}</span> 285 + 286 + 287 + 288 + 289 + <div class="flex w-[5rem] items-center justify-end"> 290 + <Show when={cursor()}> 291 + <Show when={!response.loading}> 292 + <FilterButton onClick={() => refetch()}>Load More</FilterButton> 293 + </Show> 294 + <Show when={response.loading}> 295 + <div class="iconify lucide--loader-circle w-[5rem] animate-spin text-xl" />
+3 -1
src/components/button.tsx
··· 2 2 3 3 export interface ButtonProps { 4 4 class?: string; 5 + classList?: Record<string, boolean | undefined>; 5 6 onClick?: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent>; 6 7 children?: JSX.Element; 7 8 } ··· 12 13 type="button" 13 14 class={ 14 15 props.class ?? 15 - "dark:hover:bg-dark-100 dark:bg-dark-300 dark:shadow-dark-800 dark:active:bg-dark-100 flex items-center gap-1 rounded-lg bg-white px-2 py-1.5 text-xs font-semibold shadow-sm hover:bg-neutral-50 active:bg-neutral-50" 16 + "dark:hover:bg-dark-100 dark:bg-dark-300 dark:shadow-dark-800 dark:active:bg-dark-100 flex items-center gap-1 rounded-lg bg-white px-2 py-1.5 text-xs font-semibold border-[0.5px] border-neutral-300 dark:border-neutral-700 shadow-md hover:bg-neutral-50 active:bg-neutral-50" 16 17 } 18 + classList={props.classList} 17 19 onClick={props.onClick} 18 20 > 19 21 {props.children}

Submissions

sign up or login to add to the discussion
futur.blue submitted #1
1 commit
expand
prettier
futur.blue

gonna try again with a patch I guess?

closed without merging
futur.blue submitted #0
3 commits
expand
collection filter background on scroll
buttons styling
naming
futur.blue

huh weird diff