Switch the thread board to the table when the board is wide enough, not the page

This commit is contained in:
Coracle-Bot 2026-09-09 17:17:55 +00:00 committed by hodlbod
parent 057f0bc2bb
commit 3739ea7119
5 changed files with 33 additions and 48 deletions

View file

@ -1611,8 +1611,8 @@ Threads indicator is as actionable on a phone as it is on a desktop.
Acceptance: Acceptance:
- The thread list below the md breakpoint is a list of links rather than a - The thread list in a board too narrow for the table is a list of links rather
table, and alice's thread carries a dot there; bob's own does not. than a table, and alice's thread carries a dot there; bob's own does not.
### US-114 — See which listings are unread ### US-114 — See which listings are unread

View file

@ -626,8 +626,9 @@ test("US-113 see which threads are unread on a phone", async ({seed, as}) => {
const space = scenario.space("space") const space = scenario.space("space")
// ThreadBoard swaps its table for a list of links below tailwind's md breakpoint, and the two // ThreadBoard swaps its table for a list of links when the board is too narrow for the table,
// branches render the thread separately, so a dot on one says nothing about the other. // and the two branches render the thread separately, so a dot on one says nothing about the
// other.
const bob = await as(users.bob, `${spacePath(space.url)}/threads`, { const bob = await as(users.bob, `${spacePath(space.url)}/threads`, {
context: {viewport: {width: 390, height: 844}, hasTouch: true}, context: {viewport: {width: 390, height: 844}, hasTouch: true},
}) })

View file

@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
import Add from "@assets/icons/add.svg?dataurl" import Add from "@assets/icons/add.svg?dataurl"
import {matchMd} from "@lib/theme"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import RoomNameWithImage from "@app/components/RoomNameWithImage.svelte" import RoomNameWithImage from "@app/components/RoomNameWithImage.svelte"
@ -19,10 +18,12 @@
const {url, h, threads, context}: Props = $props() const {url, h, threads, context}: Props = $props()
let width = $state(0)
const createThread = () => pushModal(ThreadCreate, {url, h}) const createThread = () => pushModal(ThreadCreate, {url, h})
</script> </script>
<section class="card card-flat p-0"> <section bind:clientWidth={width} class="card card-flat p-0">
<header <header
class="flex items-center justify-between gap-2 border-b border-solid border-line px-4 py-3"> class="flex items-center justify-between gap-2 border-b border-solid border-line px-4 py-3">
<h2 class="text-lg"> <h2 class="text-lg">
@ -45,9 +46,10 @@
</header> </header>
{#if threads.length === 0} {#if threads.length === 0}
<p class="text-content-muted p-4 text-sm">No topics yet.</p> <p class="text-content-muted p-4 text-sm">No topics yet.</p>
{:else if $matchMd} {:else}
<div class="scroll-container overflow-x-auto pb-4"> <div class="pb-4">
<table class="w-full min-w-[640px] border-collapse"> {#if width >= 640}
<table class="w-full border-collapse">
<thead <thead
class="border-b border-solid border-line bg-surface-less text-xs font-bold uppercase tracking-wide text-content-muted"> class="border-b border-solid border-line bg-surface-less text-xs font-bold uppercase tracking-wide text-content-muted">
<tr> <tr>
@ -63,12 +65,11 @@
{/each} {/each}
</tbody> </tbody>
</table> </table>
</div>
{:else} {:else}
<div class="pb-4">
{#each threads as event (event.id)} {#each threads as event (event.id)}
<ThreadBoardItem mobile {url} {event} {context} /> <ThreadBoardItem stacked {url} {event} {context} />
{/each} {/each}
{/if}
</div> </div>
{/if} {/if}
</section> </section>

View file

@ -15,10 +15,10 @@
url: string url: string
event: TrustedEvent event: TrustedEvent
context: FeedContext context: FeedContext
mobile?: boolean stacked?: boolean
} }
const {url, event, context, mobile = false}: Props = $props() const {url, event, context, stacked = false}: Props = $props()
const related = context.related(event) const related = context.related(event)
const replies = derived(related, $related => filter(spec({kind: COMMENT}), $related)) const replies = derived(related, $related => filter(spec({kind: COMMENT}), $related))
@ -29,7 +29,7 @@
const onClick = () => navigate(path) const onClick = () => navigate(path)
</script> </script>
{#if mobile} {#if stacked}
<Link <Link
href={path} href={path}
class="cv hover:bg-surface-less flex w-full flex-col gap-2 border-b border-solid border-line px-4 py-3 text-left text-sm transition-colors"> class="cv hover:bg-surface-less flex w-full flex-col gap-2 border-b border-solid border-line px-4 py-3 text-left text-sm transition-colors">

View file

@ -1,17 +0,0 @@
import theme from "tailwindcss/defaultTheme"
import {readable} from "svelte/store"
import type {Readable} from "svelte/store"
export const matchWidth = (query: string): Readable<boolean> =>
readable(typeof window !== "undefined" && window.matchMedia(query).matches, set => {
const mq = window.matchMedia(query)
const onChange = () => set(mq.matches)
onChange()
mq.addEventListener("change", onChange)
return () => mq.removeEventListener("change", onChange)
})
export const matchMd = matchWidth(`(min-width: ${theme.screens.md})`)