A pinch of responsiveness
This commit is contained in:
14
packages/frontend/src/hooks/useBreakpoint.ts
Normal file
14
packages/frontend/src/hooks/useBreakpoint.ts
Normal 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);
|
||||
@@ -5,6 +5,7 @@ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogT
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { useBreakpoint } from "@/hooks/useBreakpoint";
|
||||
import { useCache } from "@/hooks/useCache";
|
||||
import { useLoading } from "@/hooks/useLoading";
|
||||
import { authors, created, DEBOUNCE, modified, SAVE_DELAY } from "@/snippets";
|
||||
@@ -25,6 +26,8 @@ export function Pieces() {
|
||||
const [importDialogOpen, setImportDialogOpen] = useState(false);
|
||||
|
||||
const debounce = useRef(Effect.void);
|
||||
const breakpoint = useBreakpoint();
|
||||
const columns = breakpoint ? 4 : 1;
|
||||
|
||||
const { isLoading, error, data: pieceIds, refresh } = useLoading(Effect.gen(function* () {
|
||||
yield* debounce.current;
|
||||
@@ -39,7 +42,7 @@ export function Pieces() {
|
||||
|
||||
return (
|
||||
<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>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline">
|
||||
@@ -57,7 +60,7 @@ export function Pieces() {
|
||||
<ImportPiecesDialogContent setDialogOpen={setImportDialogOpen} refresh={refresh} />
|
||||
</Dialog>
|
||||
<Input
|
||||
className="w-[32ch]"
|
||||
className="w-full md:w-[32ch]"
|
||||
type="text"
|
||||
placeholder="Tytuł"
|
||||
value={name}
|
||||
@@ -67,7 +70,7 @@ export function Pieces() {
|
||||
}}
|
||||
/>
|
||||
<Input
|
||||
className="w-[32ch]"
|
||||
className="w-full md:w-[32ch]"
|
||||
type="text"
|
||||
placeholder="Twórcy"
|
||||
value={author}
|
||||
@@ -78,18 +81,20 @@ export function Pieces() {
|
||||
/>
|
||||
</div>
|
||||
<Table>
|
||||
<TableHeader className="bg-white sticky top-0">
|
||||
<TableRow>
|
||||
<TableHead>Tytuł</TableHead>
|
||||
<TableHead>Twórcy</TableHead>
|
||||
<TableHead className="text-center">Dodano</TableHead>
|
||||
<TableHead className="text-center">Zmodyfikowano</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{breakpoint && (
|
||||
<TableHeader className="bg-white sticky top-0">
|
||||
<TableRow>
|
||||
<TableHead>Tytuł</TableHead>
|
||||
<TableHead>Twórcy</TableHead>
|
||||
<TableHead className="text-center">Dodano</TableHead>
|
||||
<TableHead className="text-center">Zmodyfikowano</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
)}
|
||||
<TableBody>
|
||||
{isLoading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4} >
|
||||
<TableCell colSpan={columns} >
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Loader2 className="animate-spin" />
|
||||
Ładowanie…
|
||||
@@ -98,7 +103,7 @@ export function Pieces() {
|
||||
</TableRow>
|
||||
) : error !== null ? (
|
||||
<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)}`}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -121,10 +126,13 @@ function PieceRow(props: PieceRow.Props) {
|
||||
|
||||
const { isLoading, error, data: piece } = useCache(pieceCache, props.pieceId);
|
||||
|
||||
const breakpoint = useBreakpoint();
|
||||
const columns = breakpoint ? 4 : 1;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4}>Ładowanie…</TableCell>
|
||||
<TableCell colSpan={columns}>Ładowanie…</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
@@ -132,7 +140,7 @@ function PieceRow(props: PieceRow.Props) {
|
||||
if (error !== null) {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4}>
|
||||
<TableCell colSpan={columns}>
|
||||
Wystąpił błąd: {Match.value(error).pipe(
|
||||
Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
|
||||
Match.tag("NotFound", () => "Utwór nie istnieje"),
|
||||
@@ -147,18 +155,25 @@ function PieceRow(props: PieceRow.Props) {
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Link className="underline" to={piece.pieceId}>{piece.name}</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{authors(piece)}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs">
|
||||
{created(piece)}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs">
|
||||
{modified(piece)}
|
||||
</TableCell>
|
||||
{breakpoint ? (<>
|
||||
<TableCell>
|
||||
<Link className="underline" to={piece.pieceId}>{piece.name}</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{authors(piece)}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs">
|
||||
{created(piece)}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs">
|
||||
{modified(piece)}
|
||||
</TableCell>
|
||||
</>) : (
|
||||
<TableCell className="flex flex-col">
|
||||
<Link className="underline" to={piece.pieceId}>{piece.name}</Link>
|
||||
<div className="text-xs">{authors(piece)}</div>
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogT
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { useBreakpoint } from "@/hooks/useBreakpoint";
|
||||
import { useCache } from "@/hooks/useCache";
|
||||
import { useLoading } from "@/hooks/useLoading";
|
||||
import { created, DEBOUNCE, modified } from "@/snippets";
|
||||
@@ -19,6 +20,8 @@ export function Repertoires() {
|
||||
const [name, setName] = useState("");
|
||||
|
||||
const debounce = useRef(Effect.void);
|
||||
const breakpoint = useBreakpoint();
|
||||
const columns = breakpoint ? 4 : 1;
|
||||
|
||||
const { isLoading, error, data: repertoireIds } = useLoading(Effect.gen(function* () {
|
||||
yield* debounce.current;
|
||||
@@ -32,7 +35,7 @@ export function Repertoires() {
|
||||
|
||||
return (
|
||||
<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>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline">
|
||||
@@ -42,7 +45,7 @@ export function Repertoires() {
|
||||
<AddRepertoireDialogContent />
|
||||
</Dialog>
|
||||
<Input
|
||||
className="w-[32ch]"
|
||||
className="w-full md:w-[32ch]"
|
||||
type="text"
|
||||
placeholder="Nazwa"
|
||||
value={name}
|
||||
@@ -53,18 +56,20 @@ export function Repertoires() {
|
||||
/>
|
||||
</div>
|
||||
<Table>
|
||||
<TableHeader className="bg-white sticky top-0">
|
||||
<TableRow>
|
||||
<TableHead>Nazwa</TableHead>
|
||||
<TableHead>Utwory</TableHead>
|
||||
<TableHead className="text-center">Dodano</TableHead>
|
||||
<TableHead className="text-center">Zmodyfikowano</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{breakpoint && (
|
||||
<TableHeader className="bg-white sticky top-0">
|
||||
<TableRow>
|
||||
<TableHead>Nazwa</TableHead>
|
||||
<TableHead>Utwory</TableHead>
|
||||
<TableHead className="text-center">Dodano</TableHead>
|
||||
<TableHead className="text-center">Zmodyfikowano</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
)}
|
||||
<TableBody>
|
||||
{isLoading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4} >
|
||||
<TableCell colSpan={columns} >
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Loader2 className="animate-spin" />
|
||||
Ładowanie…
|
||||
@@ -73,7 +78,7 @@ export function Repertoires() {
|
||||
</TableRow>
|
||||
) : error !== null ? (
|
||||
<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)}`}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -96,10 +101,13 @@ function RepertoireRow(props: RepertoireRow.Props) {
|
||||
|
||||
const { isLoading, error, data: repertoire } = useCache(repertoireCache, props.repertoireId);
|
||||
|
||||
const breakpoint = useBreakpoint();
|
||||
const columns = breakpoint ? 4 : 1;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4}>Ładowanie…</TableCell>
|
||||
<TableCell colSpan={columns}>Ładowanie…</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
@@ -107,7 +115,7 @@ function RepertoireRow(props: RepertoireRow.Props) {
|
||||
if (error !== null) {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4}>
|
||||
<TableCell colSpan={columns}>
|
||||
Wystąpił błąd: {Match.value(error).pipe(
|
||||
Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
|
||||
Match.tag("NotFound", () => "Repertuar nie istnieje"),
|
||||
@@ -136,18 +144,25 @@ function RepertoireRow(props: RepertoireRow.Props) {
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Link className="underline" to={repertoire.repertoireId}>{repertoire.name}</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{...piecesParts}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs">
|
||||
{created(repertoire)}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs">
|
||||
{modified(repertoire)}
|
||||
</TableCell>
|
||||
{breakpoint ? (<>
|
||||
<TableCell>
|
||||
<Link className="underline" to={repertoire.repertoireId}>{repertoire.name}</Link>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{...piecesParts}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs">
|
||||
{created(repertoire)}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs">
|
||||
{modified(repertoire)}
|
||||
</TableCell>
|
||||
</>) : (
|
||||
<TableCell className="flex flex-col">
|
||||
<Link className="underline" to={repertoire.repertoireId}>{repertoire.name}</Link>
|
||||
<div className="text-xs">{...piecesParts}</div>
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user