Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

style: edit library page responsive design #45

Merged
merged 1 commit into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .env.example

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function DatePickerWithRange({
id="date"
variant={"outline"}
className={cn(
"w-[300px] justify-start text-left font-normal",
"justify-start text-left font-normal mt-1",
!date && "text-muted-foreground"
)}
>
Expand Down
117 changes: 79 additions & 38 deletions apps/next/src/components/Published/Filter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { DatePickerWithRange } from "./DatePickerWithRange";
import { Levels } from "./Levels.enum";
import { useGetPublicDictionariesMutation } from "@/store/publicDictionaries/api";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
Button,
Input,
Label,
Expand All @@ -14,9 +18,9 @@ import {
import { useTranslation } from "next-i18next";
import { useEffect, useState } from "react";
import { type DateRange } from "react-day-picker";
import { useDebounce } from "usehooks-ts";
import { useDebounce, useWindowSize } from "usehooks-ts";

const PublishedFilter = () => {
const PublishedFilter = ({ isAccordionShow, setIsAccordionShow }: any) => {
const [level, setLevel] = useState<string>();
const { t } = useTranslation();

Expand Down Expand Up @@ -50,52 +54,89 @@ const PublishedFilter = () => {
setSearchValue(undefined);
};

const { width } = useWindowSize();

const handleAccordion = () => {
setIsAccordionShow(!isAccordionShow);
};

return (
<main className="flex items-end justify-between mt-10 max-lg:flex-col max-lg:spacse-y-5 max-lg:justify-center max-lg:items-center">
<section>
<main className="relative mx-auto sm:mx-0 w-full max-w-sm sm:max-w-none flex md:flex-col lg:flex-row items-end lg:justify-between gap-4">
<section className="w-full lg:max-w-[250px]">
<Label htmlFor="search">{t("public_dictionaries.search")}</Label>
<Input
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
placeholder={t("public_dictionaries.search")}
className="w-80 mt-1.5 max-md:w-48"
className="mt-2"
id="search"
/>
</section>
<section className="flex items-center space-x-2 max-md:flex-col max-md:space-y-5">
<div className="flex flex-col">
<Label htmlFor="category" className="mb-2">
{t("public_dictionaries.date")}
</Label>
<DatePickerWithRange date={date} setDate={setDate} />
</div>
<div className="ml-3 grid">
<Label htmlFor="level">{t("public_dictionaries.level")}</Label>
<Select value={level} onValueChange={(value) => setLevel(value)}>
<SelectTrigger
id="level"
className="w-48 px-3 py-2 text-muted-foreground h-10 mt-1.5"

<section className="md:w-full">
<Accordion
type="single"
collapsible
value={isAccordionShow || width >= 768 ? "item-1" : ""}
className="mt-2 md:mt-0"
>
<AccordionItem value="item-1" className="border-none">
<AccordionTrigger
onClick={handleAccordion}
className="w-10 sm:w-auto sm:px-3.5 h-10 border rounded-md md:hidden flex items-center justify-center gap-3"
>
<SelectValue placeholder="Level" />
</SelectTrigger>
<SelectContent>
{computedLevels.map(({ label, value }) => (
<SelectItem key={value} className="text-left" value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<Label htmlFor="clearFilters">
<Button
onClick={handleClearFilters}
variant="outline"
className="md:mt-[1.2rem]"
>
{t("public_dictionaries.clear_filters")}
</Button>
</Label>
<div className="hidden sm:block mb-0.5">Filter</div>
</AccordionTrigger>
<AccordionContent>
<section className="absolute md:static left-0 z-10 bg-background mt-4 md:mt-0 w-full flex flex-col sm:flex-row sm:items-end lg:justify-end gap-4">
<div className="w-full lg:max-w-[250px] flex flex-col gap-2">
<Label htmlFor="category">
{t("public_dictionaries.date")}
</Label>
<DatePickerWithRange date={date} setDate={setDate} />
</div>

<div className="w-full lg:max-w-[250px] grid gap-2">
<Label htmlFor="level">
{t("public_dictionaries.level")}
</Label>
<Select
value={level}
onValueChange={(value) => setLevel(value)}
>
<SelectTrigger
id="level"
className="px-3 py-2 text-muted-foreground h-10 mt-1"
>
<SelectValue placeholder="Level" />
</SelectTrigger>
<SelectContent>
{computedLevels.map(({ label, value }) => (
<SelectItem
key={value}
className="text-left"
value={value}
>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>

<Label htmlFor="clearFilters">
<Button
onClick={handleClearFilters}
variant="outline"
className="w-full sm:w-max"
>
{t("public_dictionaries.clear_filters")}
</Button>
</Label>
</section>
</AccordionContent>
</AccordionItem>
</Accordion>
</section>
</main>
);
Expand Down
2 changes: 1 addition & 1 deletion apps/next/src/components/Published/Published.loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const PublishedLoader = () => {
return (
<Fragment>
{new Array(6).fill(0).map((_, index) => (
<div className="max-w-80" key={index}>
<div className="w-full sm:w-[calc(50%-12px)] md:w-[calc(50%-16px)] lg:w-[calc(33%-18.5px)] max-w-sm sm:max-w-none" key={index}>
<Skeleton className="w-full h-60" />
<div className="mt-4 flex flex-col gap-y-2">
<Skeleton className="w-full h-7 rounded-sm" />
Expand Down
46 changes: 22 additions & 24 deletions apps/next/src/components/Published/PublishedItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,32 @@ const PublishedItem = (item: IDictionary) => {
}}
as={`/library/${item.slug}`}
key={item.id}
className="max-md:min-w-[325px] max-md:max-w-[405px] max-w-[405px]"
className="w-full sm:w-[calc(50%-12px)] md:w-[calc(50%-16px)] lg:w-[calc(33%-18.5px)] max-w-sm sm:max-w-none"
>
<div className="flex w-full flex-col justify-between h-full">
<div>
<div className="w-full h-60 relative">
<Image
alt=""
src={item.image || "/images/dictionary_banner.jpg"}
fill
className="rounded-2xl"
/>
</div>
<span className="py-1 inline-flex items-center pl-1 pr-2.5 rounded-[0.625rem] text-xs mt-6 font-medium border">
<span className="px-1.5 inline-flex items-center py-0.5 rounded-md border mr-2">
<Star fill="currentColor" className="h-3 w-3 mr-1" />
{item.rate}
</span>
{item.numberOfWords} {t("general.words")}
<div className="w-full h-full flex flex-col gap-4 sm:gap-5 md:gap-6 justify-between rounded-2xl">
<div className="relative h-60">
<Image
alt=""
src={item.image || "/images/dictionary_banner.jpg"}
fill
className="rounded-2xl object-cover"
/>
</div>

<div className="py-1 w-fit flex items-center pl-1 pr-2.5 rounded-[0.625rem] text-xs font-medium border">
<span className="px-1.5 inline-flex items-center py-0.5 rounded-md border mr-2">
<Star fill="currentColor" className="h-3 w-3 mr-1" />
{item.rate}
</span>
<h3 className="text-2xl font-semibold mt-4">{item.title}</h3>
<h4 className="text-base text-muted-foreground mt-2">
{item.description}
</h4>
{item.numberOfWords} {t("general.words")}
</div>
<span className="flex items-center mt-6">
<Avatar className="relative h-10 w-10 mr-3 md:h-12 md:w-12">

<h3 className="text-2xl font-semibold">{item.title}</h3>

<span className="flex items-center">
<Avatar className="relative mr-3 h-10 md:h-12 w-10 md:w-12">
<AvatarImage
className="w-10 h-10 md:h-12 md:w-12"
className="w-10 md:w-12 h-10 md:h-12"
src={item.author.avatar_url}
alt={"@" + item.author.name}
/>
Expand Down
35 changes: 25 additions & 10 deletions apps/next/src/components/Published/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import PublishedItem from "./PublishedItem";
import NoDataAnimation from "@/animations/no_data.json";
import { useGetPublicDictionariesMutation } from "@/store/publicDictionaries/api";
import { useAppSelector } from "@/utils/hooks";
import { cn } from "@wordigo/ui/lib/utils";
import Lottie from "lottie-react";
import { useTranslation } from "next-i18next";
import { Fragment, useEffect } from "react";
import { Fragment, useEffect, useState } from "react";

const Published = () => {
const { t } = useTranslation();
Expand All @@ -20,19 +21,33 @@ const Published = () => {
void handleGetPublicDictionaries({ page: 1, size: 10 });
}, []);

const [isAccordionShow, setIsAccordionShow] = useState(false);

return (
<section className="flex flex-col w-full py-24 max-xl:py-16 px-20 max-md:px-4">
<h1 className="text-5xl font-semibold text-center max-xl:text-4xl max-md:text-2xl">
{t("public_dictionaries.title")}
</h1>
<p className="text-xl mt-6 text-muted-foreground text-center max-xl:text-lg max-xl:mt-4 max-md:text-base">
{t("public_dictionaries.description")}
</p>
<PublishedFilter />
<section className="py-16 sm:py-20 md:py-24 px-20 max-md:px-4 w-full">
<div className="mb-14 sm:mb-16 md:mb-20 lg:mb-24 text-center max-w-sm sm:max-w-none mx-auto sm:mx-0">
<h1 className="text-5xl font-semibold max-xl:text-4xl max-md:text-2xl">
{t("public_dictionaries.title")}
</h1>

<p className="text-xl mt-6 text-muted-foreground max-xl:text-lg max-xl:mt-4 max-md:text-base">
{t("public_dictionaries.description")}
</p>
</div>

<PublishedFilter
isAccordionShow={isAccordionShow}
setIsAccordionShow={setIsAccordionShow}
/>
{dictionaries?.data?.length === 0 ? (
<Published.NotFound />
) : (
<main className="grid grid-cols-3 gap-x-8 gap-y-16 mt-14 max-xl:mt-10 w-full max-xl:justify-center max-xl:flex max-xl:flex-wrap max-xl:px-2 max-xl:grid-cols-2">
<main
className={cn(
"mt-6 sm:mt-8 flex flex-wrap justify-center sm:justify-start gap-x-4 sm:gap-x-6 md:gap-x-8 gap-y-12 sm:gap-y-14 md:gap-y-16 transition-transform duration-300",
isAccordionShow ? "translate-y-[221px] sm:translate-y-[82px]" : "sm:translate-y-0"
)}
>
{dictionariesLoading ? (
<PublishedLoader />
) : (
Expand Down
43 changes: 10 additions & 33 deletions packages/ui/components/ui/accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,30 @@
"use client";

import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { cn } from "@wordigo/ui/lib/utils";
import { ChevronDown } from "lucide-react";
import * as React from "react";
import { FaFilter } from "react-icons/fa";

const Accordion = AccordionPrimitive.Root;

const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => <AccordionPrimitive.Item ref={ref} className={cn("border-b", className)} {...props} />);
const AccordionItem = React.forwardRef<React.ElementRef<typeof AccordionPrimitive.Item>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>>(({ className, ...props }, ref) => <AccordionPrimitive.Item ref={ref} className={cn("border-b", className)} {...props} />);
AccordionItem.displayName = "AccordionItem";

const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
const AccordionTrigger = React.forwardRef<React.ElementRef<typeof AccordionPrimitive.Trigger>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
className,
)}
{...props}
>
<AccordionPrimitive.Trigger ref={ref} className={className} {...props}>
{children}
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
<FaFilter className="text-sm" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;

const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className={cn(
"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
className,
)}
{...props}
>
<div className="pb-4 pt-0">{children}</div>
const AccordionContent = React.forwardRef<React.ElementRef<typeof AccordionPrimitive.Content>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content ref={ref} className={cn("overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down", className)} {...props}>
<div>{children}</div>
</AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
1 change: 1 addition & 0 deletions packages/ui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from "./components/ui/calendar";
export * from "./components/ui/badge";
export * from "./components/ui/button";
export * from "./components/ui/checkbox";
export * from "./components/ui/accordion";
export * from "./components/ui/card";
export * from "./components/ui/command";
export * from "./components/ui/dialog";
Expand Down
Loading