A pinch of responsiveness

This commit is contained in:
2025-10-10 18:45:16 +02:00
parent 9148c1f320
commit e26fa39e88
3 changed files with 97 additions and 53 deletions

View File

@@ -0,0 +1,14 @@
import { useSyncExternalStore } from "react";
const query = window.matchMedia("@media (width >= 48rem)");
const subscribe = (callback: () => void) => {
query.addEventListener("change", callback);
return () => {
query.removeEventListener("change", callback);
};
}
const getSnapshot = () => query.matches;
export const useBreakpoint = () => useSyncExternalStore(subscribe, getSnapshot);

View File

@@ -5,6 +5,7 @@ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogT
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { useBreakpoint } from "@/hooks/useBreakpoint";
import { useCache } from "@/hooks/useCache"; import { useCache } from "@/hooks/useCache";
import { useLoading } from "@/hooks/useLoading"; import { useLoading } from "@/hooks/useLoading";
import { authors, created, DEBOUNCE, modified, SAVE_DELAY } from "@/snippets"; import { authors, created, DEBOUNCE, modified, SAVE_DELAY } from "@/snippets";
@@ -25,6 +26,8 @@ export function Pieces() {
const [importDialogOpen, setImportDialogOpen] = useState(false); const [importDialogOpen, setImportDialogOpen] = useState(false);
const debounce = useRef(Effect.void); const debounce = useRef(Effect.void);
const breakpoint = useBreakpoint();
const columns = breakpoint ? 4 : 1;
const { isLoading, error, data: pieceIds, refresh } = useLoading(Effect.gen(function* () { const { isLoading, error, data: pieceIds, refresh } = useLoading(Effect.gen(function* () {
yield* debounce.current; yield* debounce.current;
@@ -39,7 +42,7 @@ export function Pieces() {
return ( return (
<div className="p-4 overflow-y-auto flex flex-col items-start gap-4"> <div className="p-4 overflow-y-auto flex flex-col items-start gap-4">
<div className="flex flex-row gap-4"> <div className="flex flex-row flex-wrap gap-4">
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="outline"> <Button variant="outline">
@@ -57,7 +60,7 @@ export function Pieces() {
<ImportPiecesDialogContent setDialogOpen={setImportDialogOpen} refresh={refresh} /> <ImportPiecesDialogContent setDialogOpen={setImportDialogOpen} refresh={refresh} />
</Dialog> </Dialog>
<Input <Input
className="w-[32ch]" className="w-full md:w-[32ch]"
type="text" type="text"
placeholder="Tytuł" placeholder="Tytuł"
value={name} value={name}
@@ -67,7 +70,7 @@ export function Pieces() {
}} }}
/> />
<Input <Input
className="w-[32ch]" className="w-full md:w-[32ch]"
type="text" type="text"
placeholder="Twórcy" placeholder="Twórcy"
value={author} value={author}
@@ -78,6 +81,7 @@ export function Pieces() {
/> />
</div> </div>
<Table> <Table>
{breakpoint && (
<TableHeader className="bg-white sticky top-0"> <TableHeader className="bg-white sticky top-0">
<TableRow> <TableRow>
<TableHead>Tytuł</TableHead> <TableHead>Tytuł</TableHead>
@@ -86,10 +90,11 @@ export function Pieces() {
<TableHead className="text-center">Zmodyfikowano</TableHead> <TableHead className="text-center">Zmodyfikowano</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
)}
<TableBody> <TableBody>
{isLoading ? ( {isLoading ? (
<TableRow> <TableRow>
<TableCell colSpan={4} > <TableCell colSpan={columns} >
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
<Loader2 className="animate-spin" /> <Loader2 className="animate-spin" />
Ładowanie Ładowanie
@@ -98,7 +103,7 @@ export function Pieces() {
</TableRow> </TableRow>
) : error !== null ? ( ) : error !== null ? (
<TableRow> <TableRow>
<TableCell colSpan={4} className="text-center"> <TableCell colSpan={columns} className="text-center">
{Cause.isUnknownException(error) ? "Wystąpił nieznany błąd" : `Wystąpił błąd: ${JSON.stringify(error)}`} {Cause.isUnknownException(error) ? "Wystąpił nieznany błąd" : `Wystąpił błąd: ${JSON.stringify(error)}`}
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -121,10 +126,13 @@ function PieceRow(props: PieceRow.Props) {
const { isLoading, error, data: piece } = useCache(pieceCache, props.pieceId); const { isLoading, error, data: piece } = useCache(pieceCache, props.pieceId);
const breakpoint = useBreakpoint();
const columns = breakpoint ? 4 : 1;
if (isLoading) { if (isLoading) {
return ( return (
<TableRow> <TableRow>
<TableCell colSpan={4}>Ładowanie</TableCell> <TableCell colSpan={columns}>Ładowanie</TableCell>
</TableRow> </TableRow>
); );
} }
@@ -132,7 +140,7 @@ function PieceRow(props: PieceRow.Props) {
if (error !== null) { if (error !== null) {
return ( return (
<TableRow> <TableRow>
<TableCell colSpan={4}> <TableCell colSpan={columns}>
Wystąpił błąd: {Match.value(error).pipe( Wystąpił błąd: {Match.value(error).pipe(
Match.tag("FetchError", () => "Nie można połączyć się z serwerem"), Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
Match.tag("NotFound", () => "Utwór nie istnieje"), Match.tag("NotFound", () => "Utwór nie istnieje"),
@@ -147,6 +155,7 @@ function PieceRow(props: PieceRow.Props) {
return ( return (
<TableRow> <TableRow>
{breakpoint ? (<>
<TableCell> <TableCell>
<Link className="underline" to={piece.pieceId}>{piece.name}</Link> <Link className="underline" to={piece.pieceId}>{piece.name}</Link>
</TableCell> </TableCell>
@@ -159,6 +168,12 @@ function PieceRow(props: PieceRow.Props) {
<TableCell className="text-center text-xs"> <TableCell className="text-center text-xs">
{modified(piece)} {modified(piece)}
</TableCell> </TableCell>
</>) : (
<TableCell className="flex flex-col">
<Link className="underline" to={piece.pieceId}>{piece.name}</Link>
<div className="text-xs">{authors(piece)}</div>
</TableCell>
)}
</TableRow> </TableRow>
); );
} }

View File

@@ -5,6 +5,7 @@ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogT
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { useBreakpoint } from "@/hooks/useBreakpoint";
import { useCache } from "@/hooks/useCache"; import { useCache } from "@/hooks/useCache";
import { useLoading } from "@/hooks/useLoading"; import { useLoading } from "@/hooks/useLoading";
import { created, DEBOUNCE, modified } from "@/snippets"; import { created, DEBOUNCE, modified } from "@/snippets";
@@ -19,6 +20,8 @@ export function Repertoires() {
const [name, setName] = useState(""); const [name, setName] = useState("");
const debounce = useRef(Effect.void); const debounce = useRef(Effect.void);
const breakpoint = useBreakpoint();
const columns = breakpoint ? 4 : 1;
const { isLoading, error, data: repertoireIds } = useLoading(Effect.gen(function* () { const { isLoading, error, data: repertoireIds } = useLoading(Effect.gen(function* () {
yield* debounce.current; yield* debounce.current;
@@ -32,7 +35,7 @@ export function Repertoires() {
return ( return (
<div className="p-4 overflow-y-auto flex flex-col items-start gap-4"> <div className="p-4 overflow-y-auto flex flex-col items-start gap-4">
<div className="flex flex-row gap-4"> <div className="flex flex-row flex-wrap gap-4">
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="outline"> <Button variant="outline">
@@ -42,7 +45,7 @@ export function Repertoires() {
<AddRepertoireDialogContent /> <AddRepertoireDialogContent />
</Dialog> </Dialog>
<Input <Input
className="w-[32ch]" className="w-full md:w-[32ch]"
type="text" type="text"
placeholder="Nazwa" placeholder="Nazwa"
value={name} value={name}
@@ -53,6 +56,7 @@ export function Repertoires() {
/> />
</div> </div>
<Table> <Table>
{breakpoint && (
<TableHeader className="bg-white sticky top-0"> <TableHeader className="bg-white sticky top-0">
<TableRow> <TableRow>
<TableHead>Nazwa</TableHead> <TableHead>Nazwa</TableHead>
@@ -61,10 +65,11 @@ export function Repertoires() {
<TableHead className="text-center">Zmodyfikowano</TableHead> <TableHead className="text-center">Zmodyfikowano</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
)}
<TableBody> <TableBody>
{isLoading ? ( {isLoading ? (
<TableRow> <TableRow>
<TableCell colSpan={4} > <TableCell colSpan={columns} >
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
<Loader2 className="animate-spin" /> <Loader2 className="animate-spin" />
Ładowanie Ładowanie
@@ -73,7 +78,7 @@ export function Repertoires() {
</TableRow> </TableRow>
) : error !== null ? ( ) : error !== null ? (
<TableRow> <TableRow>
<TableCell colSpan={4} className="text-center"> <TableCell colSpan={columns} className="text-center">
{Cause.isUnknownException(error) ? "Wystąpił nieznany błąd" : `Wystąpił błąd: ${JSON.stringify(error)}`} {Cause.isUnknownException(error) ? "Wystąpił nieznany błąd" : `Wystąpił błąd: ${JSON.stringify(error)}`}
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -96,10 +101,13 @@ function RepertoireRow(props: RepertoireRow.Props) {
const { isLoading, error, data: repertoire } = useCache(repertoireCache, props.repertoireId); const { isLoading, error, data: repertoire } = useCache(repertoireCache, props.repertoireId);
const breakpoint = useBreakpoint();
const columns = breakpoint ? 4 : 1;
if (isLoading) { if (isLoading) {
return ( return (
<TableRow> <TableRow>
<TableCell colSpan={4}>Ładowanie</TableCell> <TableCell colSpan={columns}>Ładowanie</TableCell>
</TableRow> </TableRow>
); );
} }
@@ -107,7 +115,7 @@ function RepertoireRow(props: RepertoireRow.Props) {
if (error !== null) { if (error !== null) {
return ( return (
<TableRow> <TableRow>
<TableCell colSpan={4}> <TableCell colSpan={columns}>
Wystąpił błąd: {Match.value(error).pipe( Wystąpił błąd: {Match.value(error).pipe(
Match.tag("FetchError", () => "Nie można połączyć się z serwerem"), Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
Match.tag("NotFound", () => "Repertuar nie istnieje"), Match.tag("NotFound", () => "Repertuar nie istnieje"),
@@ -136,6 +144,7 @@ function RepertoireRow(props: RepertoireRow.Props) {
return ( return (
<TableRow> <TableRow>
{breakpoint ? (<>
<TableCell> <TableCell>
<Link className="underline" to={repertoire.repertoireId}>{repertoire.name}</Link> <Link className="underline" to={repertoire.repertoireId}>{repertoire.name}</Link>
</TableCell> </TableCell>
@@ -148,6 +157,12 @@ function RepertoireRow(props: RepertoireRow.Props) {
<TableCell className="text-center text-xs"> <TableCell className="text-center text-xs">
{modified(repertoire)} {modified(repertoire)}
</TableCell> </TableCell>
</>) : (
<TableCell className="flex flex-col">
<Link className="underline" to={repertoire.repertoireId}>{repertoire.name}</Link>
<div className="text-xs">{...piecesParts}</div>
</TableCell>
)}
</TableRow> </TableRow>
); );
} }