mirror of
https://github.com/ClipFusion-org/clipfusion.git
synced 2025-08-03 19:15:08 +00:00
settings page responsive design fixes
This commit is contained in:
parent
fb5cf87b43
commit
2304b7223f
@ -2,6 +2,7 @@
|
|||||||
@import "tw-animate-css";
|
@import "tw-animate-css";
|
||||||
@import "tailwindcss-safe-area";
|
@import "tailwindcss-safe-area";
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
@custom-variant adjust (&:where([data-adjust="true"] *));
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
|
@ -34,6 +34,7 @@ import StaticSidebarTrigger from "@/components/static-sidebar-trigger";
|
|||||||
import SidebarTriggerAdjustable from "@/components/sidebar-trigger-adjustable";
|
import SidebarTriggerAdjustable from "@/components/sidebar-trigger-adjustable";
|
||||||
import ScrollFadingTitle from "@/components/scroll-fading-title";
|
import ScrollFadingTitle from "@/components/scroll-fading-title";
|
||||||
import { fa } from "zod/v4/locales";
|
import { fa } from "zod/v4/locales";
|
||||||
|
import AscendingCard from "@/components/ascending-card";
|
||||||
|
|
||||||
type SortingType = "byCreationDate"
|
type SortingType = "byCreationDate"
|
||||||
| "byEditDate"
|
| "byEditDate"
|
||||||
@ -412,7 +413,7 @@ const ProjectContainer = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AspectRatio data-selectable="true" ratio={16 / 9} onClick={handleCheck}>
|
<AspectRatio data-selectable="true" ratio={16 / 9} onClick={handleCheck}>
|
||||||
<Card className="relative rounded-lg shadow-md w-full h-full overflow-hidden hover:scale-[101%] hover:drop-shadow-xl duration-100" data-selectable="true">
|
<AscendingCard className="relative rounded-lg w-full h-full overflow-hidden" data-selectable="true">
|
||||||
<div className="absolute bottom-0 left-0 w-full h-full bg-gradient-to-t from-white dark:from-black to-transparent opacity-50" data-selectable="true" />
|
<div className="absolute bottom-0 left-0 w-full h-full bg-gradient-to-t from-white dark:from-black to-transparent opacity-50" data-selectable="true" />
|
||||||
<div className="absolute bottom-0 left-0 p-2 w-full flex flex-row justify-between items-center" data-selectable="true">
|
<div className="absolute bottom-0 left-0 p-2 w-full flex flex-row justify-between items-center" data-selectable="true">
|
||||||
<div data-selectable="true">
|
<div data-selectable="true">
|
||||||
@ -430,7 +431,7 @@ const ProjectContainer = ({
|
|||||||
<Checkbox checked={selectedProjects.includes(project.uuid)} onCheckedChange={(_) => handleCheck} />
|
<Checkbox checked={selectedProjects.includes(project.uuid)} onCheckedChange={(_) => handleCheck} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</AscendingCard>
|
||||||
</AspectRatio>
|
</AspectRatio>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { SidebarTrigger } from "@/components/ui/sidebar";
|
import { SidebarTrigger } from "@/components/ui/sidebar";
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
import { ChartPieIcon, InfoIcon } from "lucide-react";
|
import { ChartPieIcon, ChevronRightIcon, InfoIcon } from "lucide-react";
|
||||||
import { ReactNode, useEffect, useState } from "react";
|
import { ReactNode, useEffect, useState } from "react";
|
||||||
import { usePersistenceContext } from "../persistence-provider";
|
import { usePersistenceContext } from "../persistence-provider";
|
||||||
import StaticSidebarTrigger from "@/components/static-sidebar-trigger";
|
import StaticSidebarTrigger from "@/components/static-sidebar-trigger";
|
||||||
@ -14,12 +14,12 @@ import { useIsMobile } from "@/hooks/use-mobile";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import AscendingCard from "@/components/ascending-card";
|
import AscendingCard from "@/components/ascending-card";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import WideAscendingCard from "@/components/wide-container";
|
||||||
|
import WideContainer from "@/components/wide-container";
|
||||||
|
|
||||||
export default function Settings(): ReactNode {
|
export default function Settings(): ReactNode {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
const cardWidth = isMobile ? "w-full" : "w-2xl";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-5 w-full h-full">
|
<div className="p-5 w-full h-full">
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
@ -29,22 +29,29 @@ export default function Settings(): ReactNode {
|
|||||||
</ScrollFadingTitle>
|
</ScrollFadingTitle>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col sticky top-safe bg-background gap-2 mt-2 pb-2 pt-2 p-5 w-[100% + 5 * var(--spacing)] z-10 -mx-5">
|
<div className="flex flex-col sticky top-safe bg-background gap-2 mt-2 pb-2 pt-2 p-5 w-[100% + 5 * var(--spacing)] z-10 -mx-5">
|
||||||
<SidebarTriggerAdjustable className={cn("flex justify-center",)}>
|
<SidebarTriggerAdjustable adjustWidth={!isMobile ? 0 : 12} className="flex items-center justify-center">
|
||||||
<Search placeholder="Search Settings" className={cardWidth} />
|
<WideContainer>
|
||||||
|
<Search />
|
||||||
|
</WideContainer>
|
||||||
</SidebarTriggerAdjustable>
|
</SidebarTriggerAdjustable>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-center items-center w-full gap-1 md:lg:gap-2 mt-2">
|
<div className="flex flex-col items-center gap-1 md:lg:gap-2 mt-2 h-screen">
|
||||||
<Link href="/settings/storage" className="w-full flex items-center justify-center">
|
<WideContainer>
|
||||||
<AscendingCard className={`flex flex-row gap-3 p-3 ${cardWidth}`}>
|
<Link href="/settings/storage">
|
||||||
<div className="flex items-center justify-center">
|
<AscendingCard className="flex flex-row justify-between items-center gap-2 p-4">
|
||||||
<ChartPieIcon />
|
<div className="flex flex-row justify-between items-center gap-3">
|
||||||
</div>
|
<div className="flex items-center justify-center">
|
||||||
<div className="flex flex-col items-start">
|
<ChartPieIcon />
|
||||||
<h3 className="font-semibold break-keep text-lg sm:text-xl">Storage</h3>
|
</div>
|
||||||
<p className="text-sm text-secondary-foreground">Memory usage, media, cache etc.</p>
|
<div className="flex flex-col items-start">
|
||||||
</div>
|
<h3 className="font-semibold break-keep text-lg sm:text-xl">Storage</h3>
|
||||||
</AscendingCard>
|
<p className="text-sm text-secondary-foreground">Memory usage, media, cache etc.</p>
|
||||||
</Link>
|
</div>
|
||||||
|
</div>
|
||||||
|
<ChevronRightIcon />
|
||||||
|
</AscendingCard>
|
||||||
|
</Link>
|
||||||
|
</WideContainer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { usePersistenceContext } from "@/app/persistence-provider";
|
import { usePersistenceContext } from "@/app/persistence-provider";
|
||||||
|
import AscendingCard from "@/components/ascending-card";
|
||||||
import ScrollFadingTitle from "@/components/scroll-fading-title";
|
import ScrollFadingTitle from "@/components/scroll-fading-title";
|
||||||
import Search from "@/components/search";
|
import Search from "@/components/search";
|
||||||
import SidebarTriggerAdjustable from "@/components/sidebar-trigger-adjustable";
|
import SidebarTriggerAdjustable from "@/components/sidebar-trigger-adjustable";
|
||||||
import StaticSidebarTrigger from "@/components/static-sidebar-trigger";
|
import StaticSidebarTrigger from "@/components/static-sidebar-trigger";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
|
import WideContainer from "@/components/wide-container";
|
||||||
import { useIsMobile } from "@/hooks/use-mobile";
|
import { useIsMobile } from "@/hooks/use-mobile";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { ArrowLeftIcon } from "lucide-react";
|
import { ArrowLeftIcon } from "lucide-react";
|
||||||
@ -33,8 +35,6 @@ export default function Storage() {
|
|||||||
setStatus(localStorage.getItem("persistence-status"));
|
setStatus(localStorage.getItem("persistence-status"));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const cardWidth = isMobile ? "w-full" : "w-2xl";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-5 w-full">
|
<div className="p-5 w-full">
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
@ -49,15 +49,21 @@ export default function Storage() {
|
|||||||
</ScrollFadingTitle>
|
</ScrollFadingTitle>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col sticky top-safe bg-background gap-2 mt-2 pb-2 pt-2 p-5 w-[100% + 5 * var(--spacing)] z-10 -mx-5">
|
<div className="flex flex-col sticky top-safe bg-background gap-2 mt-2 pb-2 pt-2 p-5 w-[100% + 5 * var(--spacing)] z-10 -mx-5">
|
||||||
<SidebarTriggerAdjustable adjustWidth="20" className={cn("flex justify-center", cardWidth)}>
|
<SidebarTriggerAdjustable adjustWidth={!isMobile ? 0 : 12} className="flex items-center justify-center">
|
||||||
<Search placeholder="Search Settings" className={cardWidth} />
|
<WideContainer>
|
||||||
|
<Search />
|
||||||
|
</WideContainer>
|
||||||
</SidebarTriggerAdjustable>
|
</SidebarTriggerAdjustable>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row justify-between items-center w-full max-w-96">
|
<div className="flex flex-col items-center justify-start gap-1 md:lg:gap-2 mt-2 h-screen">
|
||||||
<div className="flex flex-row gap-2 items-center">
|
<WideContainer>
|
||||||
<Label>Persistent Storage</Label>
|
<AscendingCard className="flex flex-row justify-between items-center p-4">
|
||||||
</div>
|
<div className="flex flex-row gap-2 ">
|
||||||
<PersistentStorageControl status={status} />
|
<Label>Persistent Storage</Label>
|
||||||
|
</div>
|
||||||
|
<PersistentStorageControl status={status} />
|
||||||
|
</AscendingCard>
|
||||||
|
</WideContainer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -7,6 +7,7 @@ import { cn } from "@/lib/utils";
|
|||||||
export const Search = (props: ComponentProps<typeof Input>): ReactNode => (
|
export const Search = (props: ComponentProps<typeof Input>): ReactNode => (
|
||||||
<div className={cn("relative", props.className)}>
|
<div className={cn("relative", props.className)}>
|
||||||
<Input
|
<Input
|
||||||
|
placeholder="Search"
|
||||||
{...props}
|
{...props}
|
||||||
type="text"
|
type="text"
|
||||||
className="peer block w-full rounded-md border py-[9px] pl-10"
|
className="peer block w-full rounded-md border py-[9px] pl-10"
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { useIsMobile } from "@/hooks/use-mobile";
|
import { useIsMobile } from "@/hooks/use-mobile";
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { sign } from "crypto";
|
|
||||||
import { ComponentProps, useEffect, useState } from "react";
|
import { ComponentProps, useEffect, useState } from "react";
|
||||||
|
|
||||||
const easeSlide = (x: number) => (
|
const easeSlide = (x: number) => (
|
||||||
@ -11,41 +9,32 @@ const easeSlide = (x: number) => (
|
|||||||
const lerp = (a: number, b: number, t: number) => (
|
const lerp = (a: number, b: number, t: number) => (
|
||||||
a * t + b * (1 - t)
|
a * t + b * (1 - t)
|
||||||
);
|
);
|
||||||
const cssLerp = (a: string, b: string, t: string) => (
|
|
||||||
`${a} * ${t} + ${b} * (1 - ${t})`
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
export const SidebarTriggerAdjustable = (props: ComponentProps<"div"> & {
|
export const SidebarTriggerAdjustable = (props: ComponentProps<"div"> & {
|
||||||
adjustWidth?: number | `${number}`;
|
adjustWidth?: number | `${number}`
|
||||||
}) => {
|
}) => {
|
||||||
const adjustWidth = props.adjustWidth ? +props.adjustWidth : 12;
|
const adjustWidth = props.adjustWidth === undefined ? 12 : +props.adjustWidth;
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
const [slideAmount, setSlideAmount] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const triggerElement = document.querySelector('div[data-sidebar-trigger="true"]');
|
const handleScroll = () =>
|
||||||
|
setSlideAmount(easeSlide(
|
||||||
const handleScroll = () => {
|
|
||||||
if (!triggerElement) {
|
|
||||||
console.log("triggerElement is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const triggerDiv = triggerElement as HTMLDivElement;
|
|
||||||
const slideAmount = easeSlide(
|
|
||||||
Math.max(0, Math.min(1, window.scrollY / (window.innerHeight / 20)))
|
Math.max(0, Math.min(1, window.scrollY / (window.innerHeight / 20)))
|
||||||
);
|
));
|
||||||
triggerDiv.style.transform = `translateX(calc(var(--spacing) * ${adjustWidth * slideAmount}))`;
|
|
||||||
triggerDiv.style.paddingTop = `calc(var(--spacing) * ${(isMobile ? 1 : 3) * slideAmount})`;
|
|
||||||
triggerDiv.style.width = `calc(100% - var(--spacing) * ${lerp(0, adjustWidth, 1 - slideAmount)})`;
|
|
||||||
};
|
|
||||||
|
|
||||||
handleScroll();
|
handleScroll();
|
||||||
window.addEventListener('scroll', handleScroll, {passive: true});
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('scroll', handleScroll);
|
window.removeEventListener('scroll', handleScroll);
|
||||||
};
|
};
|
||||||
}, [isMobile]);
|
}, [isMobile]);
|
||||||
|
|
||||||
return <div data-sidebar-trigger="true" className={props.className}>{props.children}</div>;
|
return <div style={{
|
||||||
|
transform: `translateX(calc(var(--spacing) * ${adjustWidth * slideAmount}))`,
|
||||||
|
marginTop: `calc(var(--spacing) * ${(isMobile ? 1 : 3) * slideAmount})`,
|
||||||
|
width: `calc(100% - var(--spacing) * ${lerp(0, adjustWidth, 1 - slideAmount)})`
|
||||||
|
}} className={props.className}>{props.children}</div>;
|
||||||
}
|
}
|
12
src/components/wide-container/WideContainer.tsx
Normal file
12
src/components/wide-container/WideContainer.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { ComponentProps } from "react";
|
||||||
|
import { useIsMobile } from "@/hooks/use-mobile";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
export const WideContainer = (props: ComponentProps<"div">) => {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
const cardWidth = "max-w-2xl w-full md:w-85 lg:w-130 xl:w-2xl";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div {...props} className={cn(`${isMobile ? "w-full" : cardWidth}`, props.className)}/>
|
||||||
|
);
|
||||||
|
};
|
3
src/components/wide-container/index.ts
Normal file
3
src/components/wide-container/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { WideContainer } from "./WideContainer";
|
||||||
|
|
||||||
|
export default WideContainer;
|
Loading…
Reference in New Issue
Block a user