diff --git a/package.json b/package.json index 76780618..244e1c6d 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "react-grid-system": "^8.1.9", "react-i18next": "^12.2.0", "react-icons": "^4.8.0", + "react-loading-skeleton": "^3.4.0", "react-markdown": "^8.0.7", "react-pdf": "^5.7.2", "react-photoswipe-gallery": "^2.2.7", diff --git a/src/components/atoms/BreadCrumbs/BreadCrumbs.tsx b/src/components/atoms/BreadCrumbs/BreadCrumbs.tsx index 6e654ecb..580b07f1 100644 --- a/src/components/atoms/BreadCrumbs/BreadCrumbs.tsx +++ b/src/components/atoms/BreadCrumbs/BreadCrumbs.tsx @@ -49,7 +49,7 @@ const StyledNav = styled("nav")` justify-content: flex-start; align-items: center; align-content: center; - gap: 13px; + gap: 2px; li { &, & > a, diff --git a/src/components/atoms/Button/Button.tsx b/src/components/atoms/Button/Button.tsx index 0e4c2661..76535aa7 100644 --- a/src/components/atoms/Button/Button.tsx +++ b/src/components/atoms/Button/Button.tsx @@ -102,6 +102,7 @@ const StyledButton = styled("button")` ) { return "4px 13px"; } + return "9px 16px"; }}; border-radius: ${(props) => props.theme?.buttonRadius || 2}px; diff --git a/src/components/atoms/Typography/Text.tsx b/src/components/atoms/Typography/Text.tsx index 4a58c459..495eb060 100644 --- a/src/components/atoms/Typography/Text.tsx +++ b/src/components/atoms/Typography/Text.tsx @@ -11,7 +11,7 @@ export interface TextProps ExtendableStyledComponent { noMargin?: boolean; bold?: boolean; - size?: "16" | "14" | "13" | "12" | "11"; + size?: "18" | "16" | "14" | "13" | "12" | "11"; type?: "primary" | "secondary" | "warning" | "danger"; className?: string; } diff --git a/src/components/atoms/Typography/Title.tsx b/src/components/atoms/Typography/Title.tsx index b855e37f..edf79ff7 100644 --- a/src/components/atoms/Typography/Title.tsx +++ b/src/components/atoms/Typography/Title.tsx @@ -1,10 +1,10 @@ import * as React from "react"; import styled, { css, withTheme } from "styled-components"; -import { getStylesBasedOnTheme } from "../../../utils/utils"; import { ExtendableStyledComponent } from "types/component"; import { getFontFromTheme } from "../../../theme/provider"; import { HeaderLevelInt, HeaderLevelStr } from "../../../types/titleTypes"; import { setFontSizeByHeaderLevel } from "../../../utils/components/primitives/titleUtils"; +import { getStylesBasedOnTheme } from "utils/utils"; interface StyledHeader { level?: HeaderLevelInt; @@ -30,7 +30,7 @@ const SharedHeaderStyles = css` &, & > * { color: ${({ theme }) => - getStylesBasedOnTheme(theme.mode, theme.white, theme.gray1)}; + getStylesBasedOnTheme(theme.mode, theme.white, theme.textColor)}; } `; diff --git a/src/components/molecules/CategoryCard/CategoryCard.tsx b/src/components/molecules/CategoryCard/CategoryCard.tsx index 828cd810..22006280 100644 --- a/src/components/molecules/CategoryCard/CategoryCard.tsx +++ b/src/components/molecules/CategoryCard/CategoryCard.tsx @@ -3,7 +3,6 @@ import styled, { withTheme } from "styled-components"; import { ReactNode } from "react"; import { Title } from "../../atoms/Typography/Title"; import { Button } from "../../atoms/Button/Button"; -import chroma from "chroma-js"; import { getStylesBasedOnTheme } from "../../../utils/utils"; import { ExtendableStyledComponent } from "types/component"; @@ -12,6 +11,65 @@ interface StyledCategoryCardProps { variant: "solid" | "gradient"; } +const StyledCategoryCard = styled.div` + text-align: center; + padding: ${({ variant, mobile }) => + variant === "solid" + ? "78px 8px 72px" + : mobile + ? "75px 8px 97px" + : "15px 28px 34px 38px"}; + border-radius: ${({ theme }) => theme.buttonRadius}px; + background: ${({ theme, variant }) => + variant === "gradient" + ? "#F7F7F7" + : getStylesBasedOnTheme( + theme.mode, + theme.dm__cardBackgroundColor, + theme.cardBackgroundColor + )}; + min-height: 229px; + + display: flex; + flex-direction: column; + justify-content: space-between; + + .category-card-icon { + margin-top: 15px; + svg path { + fill: ${({ theme }) => + getStylesBasedOnTheme(theme.mode, theme.white, theme.gray2)}; + } + } + .category-content { + h4 { + font-size: 20px; + margin-bottom: 22px; + } + } + button { + display: flex; + align-items: center; + margin: 0 auto; + * { + margin: 0px; + color: ${({ theme }) => + getStylesBasedOnTheme(theme.mode, theme.white, theme.white)}; + font-weight: 400; + font-size: 16px; + } + + svg { + width: 11px; + height: 11px; + path { + fill: ${({ theme }) => + getStylesBasedOnTheme(theme.mode, theme.white, theme.white)}; + } + } + } +`; + export interface CategoryCardProps extends StyledCategoryCardProps, ExtendableStyledComponent { @@ -23,57 +81,10 @@ export interface CategoryCardProps } export const CategoryCard: React.FC = (props) => { - const StyledCategoryCard = styled("div")` - text-align: center; - padding: ${({ variant, mobile }) => - variant === "solid" - ? "78px 8px 72px" - : mobile - ? "75px 8px 97px" - : "75px 8px 208px"}; - border-radius: ${({ theme }) => theme.cardRadius}px; - background: ${({ theme, variant }) => - variant === "gradient" - ? `linear-gradient(180deg, ${getStylesBasedOnTheme( - theme.mode, - chroma(theme.dm__background).brighten(1).hex(), - chroma(theme.background).darken(0.2).hex() - )} 0%, transparent 100%)` - : getStylesBasedOnTheme( - theme.mode, - theme.dm__cardBackgroundColor, - theme.cardBackgroundColor - )}; - - .category-card-title { - margin-top: 34px; - margin-bottom: 26px; - } - - .category-card-icon { - svg path { - fill: ${({ theme }) => - getStylesBasedOnTheme(theme.mode, theme.white, theme.gray1)}; - } - } - - .category-card-children { - display: flex; - justify-content: center; - - svg { - fill: none; - path { - stroke: currentColor; - } - } - } - `; const { icon, title, subtitle, - buttonText, onButtonClick, variant, mobile = false, @@ -86,19 +97,20 @@ export const CategoryCard: React.FC = (props) => { variant={variant} >
{icon}
- - {title} - -
{subtitle}
- +
+ + {title} + + +
); }; diff --git a/src/components/molecules/CourseCard/CourseCard.tsx b/src/components/molecules/CourseCard/CourseCard.tsx index 5e9ce8f9..bc779297 100644 --- a/src/components/molecules/CourseCard/CourseCard.tsx +++ b/src/components/molecules/CourseCard/CourseCard.tsx @@ -221,7 +221,7 @@ const ImgWrapper = styled.div` transition: 0.3s transform ease; border-radius: ${({ theme }) => theme.cardRadius}px; &:hover { - transform: scale(1.03); + /* transform: scale(1.03); */ } } `; diff --git a/src/components/molecules/DropdownMenu/DropdownMenu.tsx b/src/components/molecules/DropdownMenu/DropdownMenu.tsx index 3185639a..67852be9 100644 --- a/src/components/molecules/DropdownMenu/DropdownMenu.tsx +++ b/src/components/molecules/DropdownMenu/DropdownMenu.tsx @@ -87,7 +87,7 @@ const DropdownMenu: FC = ({ onClick, onChange, }) => { - const [currID, setCurrID] = useState(0); + // const [currID, setCurrID] = useState(0); const dropdownMenuRef = useRef(null); const [isOpen, setIsOpen] = useState(isInitiallyOpen); const closeMenu = () => setIsOpen(false); @@ -95,13 +95,12 @@ const DropdownMenu: FC = ({ const onListItemClick = useCallback( (ind: number) => { - setCurrID(ind); + // setCurrID(ind); onChange?.(menuItems[ind]); closeMenu(); }, [menuItems, onChange] ); - console.log({ currID }); return ( diff --git a/src/components/molecules/Navigation/Navigation.tsx b/src/components/molecules/Navigation/Navigation.tsx index 76e5dd4d..a7c2fe4f 100644 --- a/src/components/molecules/Navigation/Navigation.tsx +++ b/src/components/molecules/Navigation/Navigation.tsx @@ -6,7 +6,6 @@ import Drawer from "rc-drawer"; import "rc-drawer/assets/index.css"; import { Col, Row } from "react-grid-system"; import { Text } from "../../../"; -import chroma from "chroma-js"; import { getStylesBasedOnTheme } from "../../../utils/utils"; import { t } from "i18next"; import { ExtendableStyledComponent } from "types/component"; @@ -98,10 +97,17 @@ export interface NavigationProps extends ExtendableStyledComponent { logo: LogoProps; menuItems: MenuItem[]; search?: ReactNode; + notification?: ReactNode; + cart?: ReactNode; + profile?: ReactNode; } const GlobalStyle = createGlobalStyle` - +.rc-drawer-content-wrapper { + @media (max-width: 530px) { + width: 90% !important; + } +} svg { transition: opacity 0.2s ease-in-out; @@ -113,12 +119,6 @@ const GlobalStyle = createGlobalStyle` .drawer-search { padding: 24px 16px; - border-bottom: ${({ theme }) => - `1px solid ${getStylesBasedOnTheme( - theme.mode, - chroma(theme.white).alpha(0.15).css(), - chroma(theme.background).darken(0.3).css() - )}`}; } .drawer, @@ -138,7 +138,7 @@ const GlobalStyle = createGlobalStyle` justify-content: space-between; padding: 15px 16px; box-sizing: border-box; - box-shadow: 0px -2px 15px rgba(0, 0, 0, 0.1); + /* box-shadow: 0px -2px 15px rgba(0, 0, 0, 0.1); */ } .drawer-content { @@ -154,6 +154,9 @@ const GlobalStyle = createGlobalStyle` margin: 0; list-style-type: none; padding: 0; + li { + padding: 10px 0px; + } } .drawer-menu-item { @@ -164,12 +167,7 @@ const GlobalStyle = createGlobalStyle` align-items: center; justify-content: space-between; width: 100%; - border-bottom: ${({ theme }) => - `1px solid ${getStylesBasedOnTheme( - theme.mode, - chroma(theme.white).alpha(0.15).css(), - chroma(theme.background).darken(0.3).css() - )}`}; + a { text-decoration: none; @@ -224,13 +222,25 @@ const StyledNavigation = styled("div")` .menu-bar { width: 19px; height: 2px; - margin: 2px 0; + margin: 2.4px 0; + border-radius: 3px; background: ${({ theme }) => - getStylesBasedOnTheme(theme.mode, theme.white, theme.gray2)}; + getStylesBasedOnTheme(theme.mode, theme.white, theme.textColor)}; cursor: pointer; } `; +const IconsHeaderMobileWrapper = styled.div` + display: flex; + align-items: center; + gap: 10px; + svg, + button { + width: 28px; + height: 28px; + } +`; + export const Navigation: React.FC = (props) => { const { mobile, logo, menuItems, search, className = "" } = props; const [mobileMenuOpen, setMobileMenuOpen] = useState(false); @@ -333,18 +343,26 @@ export const Navigation: React.FC = (props) => {
- setMobileMenuOpen(true)} - onKeyUp={() => setMobileMenuOpen(true)} - role="button" - aria-label={t("Navigation.ShowHideMenu")} - tabIndex={0} - > - - - - + + + {props.cart} + {props.notification} + {props.profile} + + + setMobileMenuOpen(true)} + onKeyUp={() => setMobileMenuOpen(true)} + role="button" + aria-label={t("Navigation.ShowHideMenu")} + tabIndex={0} + > + + + + +
void; + categoryElements: Category[]; +} + +interface StyledCourseCardProps { + mobile?: boolean; + hideImage?: boolean; +} + +// type guard +function isCategories( + categories: Categories | React.ReactChild | string +): categories is Categories { + return !React.isValidElement(categories); +} + +export interface CourseCardProps + extends StyledCourseCardProps, + ExtendableStyledComponent { + id: number; + image?: Image; + title?: ReactNode; + categories?: Categories | ReactChild; + onImageClick?: () => void; + progress?: ProgressBarProps; + price?: string; +} + +const StyledCourseCard = styled("div")` + display: flex; + flex-direction: column; + width: 100%; + border-radius: 14px; + border: 1px solid transparent; + transition: transform 0.3s ease-in-out, border 0.3s ease-in-out, + box-shadow 0.3s ease-in-out; + padding: 6px 6px 16px 6px; + overflow: hidden; + + .image-section { + position: relative; + z-index: 0; + border-radius: ${({ theme }) => theme.cardRadius}px; + overflow: hidden; + transform: rotate(0); + a > div { + height: 100%; + } + + img { + transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out; + } + } + + .course-section { + margin-top: ${(props) => (props.mobile ? "15px" : "28px")}; + flex: 1; + display: flex; + flex-direction: column; + } + .course-card__content { + padding: 0px 15px; + + @media (max-width: 768px) { + min-height: 100px; + display: flex; + flex-direction: column; + justify-content: space-between; + } + } + .course-title { + margin-bottom: 46px; + margin-bottom: ${(props) => (props.mobile ? "0px" : `45px`)}; + } + .title { + margin-top: 12px; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + margin-bottom: ${(props) => (props.mobile ? "10px" : `45px`)}; + } + + .categories { + color: ${({ theme }) => theme.textColor}; + font-family: ${({ theme }) => theme.font}; + margin-bottom: 7px; + margin-top: 10px; + width: fit-content; + text-transform: uppercase; + display: flex; + flex-wrap: wrap; + gap: 5px; + + * { + font-size: 18px; + line-height: 17px; + color: ${({ theme, hideImage }) => + getStylesBasedOnTheme( + theme.mode, + theme.dm__breadcrumbsColor, + theme.breadcrumbsColor, + hideImage ? theme.gray2 : theme.gray3 + )}; + } + span { + padding: 2px 4px; + background-color: ${({ theme }) => theme.secondaryColor}; + border-radius: 3px; + font-size: 11px; + } + } + + .escolalms-image { + height: 100%; + overflow: hidden; + } + .course-price { + transition: transform 0.3s ease-in-out; + transform: ${(props) => + props.mobile ? "translateY(0px)" : "translateY(+120px)"}; + p { + color: ${({ theme }) => theme.primaryColor}; + } + } + &:hover { + border: ${(props) => + props.mobile ? "none" : `1px solid ${props.theme.gray3}`}; + box-shadow: ${(props) => + props.mobile ? "none" : `0px 5px 15px #00000012`}; + transform: ${(props) => (props.mobile ? "none" : "translateY(-7px)")}; + + .image-section { + img { + transform: scale(1.1) !important; + } + } + + .course-price { + transform: translateY(0); + } + } +`; + +const StyledCategory = styled.span` + transition: 0.3s color ease-in-out; + &:hover { + color: ${({ theme }) => + getStylesBasedOnTheme( + theme.mode, + theme.dm__primaryColor, + theme.primaryColor, + theme.primaryColor + )}; + } +`; + +export const NewCourseCard: React.FC = (props) => { + const { + id, + mobile, + title, + image, + categories, + onImageClick, + hideImage, + className = "", + price = "19.22", + } = props; + + const imageSrc = useMemo(() => { + if (image && ((image as ImageObject).path || (image as ImageObject).url)) { + const { path, url } = image as ImageObject; + return path || url; + } + }, [image]); + + const imageSectionProps: React.HTMLAttributes = + useMemo(() => { + if (onImageClick) { + return { + onClick: onImageClick, + onKeyUp: onImageClick, + tabIndex: 0, + }; + } + return {}; + }, [onImageClick]); + + return ( + + {!hideImage && ( +
+ + {React.isValidElement(image) ? ( + image + ) : ( +
+ {image +
+ )} +
+
+ )} +
+ {React.isValidElement(categories) ? ( +
{categories}
+ ) : ( + categories && + isCategories(categories) && ( + + {categories.categoryElements.map((category, index) => { + return ( + + categories.onCategoryClick(category.id)} + onKeyDown={() => categories.onCategoryClick(category.id)} + role="button" + tabIndex={0} + > + {category.name} + + {categories.categoryElements.length !== index + 1 && ( + / + )} + + ); + })} + + ) + )} +
+ + + {title} + + +
+
+ + {price} + +
+
+
+ ); +}; diff --git a/src/components/organisms/LoginForm/LoginForm.tsx b/src/components/organisms/LoginForm/LoginForm.tsx index a5d8537a..f8a135f9 100644 --- a/src/components/organisms/LoginForm/LoginForm.tsx +++ b/src/components/organisms/LoginForm/LoginForm.tsx @@ -41,8 +41,9 @@ const StyledDiv = styled.div<{ mobile: boolean }>` h2, h3, h4 { - font-size: ${(props) => (props.mobile ? "18px" : "28px")}; + font-size: ${(props) => (props.mobile ? "20px" : "28px")}; } + form { width: 100%; max-width: 440px; diff --git a/src/components/organisms/ResponsiveImage/ResponsiveImage.tsx b/src/components/organisms/ResponsiveImage/ResponsiveImage.tsx index f838d684..f12bac83 100644 --- a/src/components/organisms/ResponsiveImage/ResponsiveImage.tsx +++ b/src/components/organisms/ResponsiveImage/ResponsiveImage.tsx @@ -17,6 +17,9 @@ const StyledDiv = styled("div")` & > .escolalms-image img { width: 100%; max-width: 100%; + &:hover { + transform: none !important; + } } `; diff --git a/src/components/organisms/SearchCourses/SearchCourses.tsx b/src/components/organisms/SearchCourses/SearchCourses.tsx index b8ecd370..b24b5a3a 100644 --- a/src/components/organisms/SearchCourses/SearchCourses.tsx +++ b/src/components/organisms/SearchCourses/SearchCourses.tsx @@ -8,6 +8,7 @@ import styled from "styled-components"; const SearchWrapper = styled.div` min-width: 300px; + input { border-radius: 21px !important; background-color: ${({ theme }) => theme.gray4} !important; @@ -29,6 +30,7 @@ const ItemButton = styled(Button)` export const SearchCourses: React.FC<{ onItemSelected: (item: API.Course) => void; onInputSubmitted: (phrase: string) => void; + mobile?: boolean; }> = ({ onItemSelected, onInputSubmitted }) => { const abortController = useRef(); const [fetching, setFetching] = useState(false); diff --git a/src/components/skeletons/Banner/index.tsx b/src/components/skeletons/Banner/index.tsx new file mode 100644 index 00000000..f947ba24 --- /dev/null +++ b/src/components/skeletons/Banner/index.tsx @@ -0,0 +1,33 @@ +import Skeleton from "react-loading-skeleton"; +import "react-loading-skeleton/dist/skeleton.css"; +import styled from "styled-components"; + +const SkeletonWrapper = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + .banner-skeleton__content { + width: 40%; + } + .banner-skeleton__image { + width: 50%; + } +`; + +export const BannerSkeleton = () => { + return ( + +
+ + +
+ +
+ ); +}; diff --git a/src/components/skeletons/CourseCard/index.tsx b/src/components/skeletons/CourseCard/index.tsx new file mode 100644 index 00000000..1961831c --- /dev/null +++ b/src/components/skeletons/CourseCard/index.tsx @@ -0,0 +1,59 @@ +import Skeleton from "react-loading-skeleton"; +import styled from "styled-components"; +import "react-loading-skeleton/dist/skeleton.css"; +import { useId } from "react"; +import { Col, ScreenClass } from "react-grid-system"; +import { isMobile, isTablet } from "react-device-detect"; + +const CardSkeleton = styled.div<{ $isMobile: boolean; $isTablet: boolean }>` + max-width: 278px; + min-height: 414px; +`; + +type ColProps = React.ComponentProps; + +type Props = { + count?: number; + colProps?: Partial>; +}; + +export const CourseCardSkeleton: React.FC = ({ + count = 1, + colProps, +}) => { + return ( + <> + {Array.from({ length: count }).map(() => + colProps ? ( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + + + + + + + + ) : ( + + + + + + ) + )} + + ); +}; diff --git a/src/index.ts b/src/index.ts index b62b1143..a2173f0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -56,6 +56,7 @@ export { Tutor } from "./components/molecules/Tutor/Tutor"; export { Search } from "./components/molecules/Search/Search"; export { default as List } from "./components/molecules/List/List"; export { default as DropdownMenu } from "./components/molecules/DropdownMenu/DropdownMenu"; +export { NewCourseCard } from "./components/molecules/NewCourseCard/index"; //ORGANISMS export { default as CourseAgenda } from "./components/organisms/CourseAgenda/CourseAgenda"; @@ -69,3 +70,7 @@ export { default as BookmarkNotes } from "./components/organisms/BookmarkNotes"; export { default as ModalNote } from "./components/organisms/ModalNote"; //ADVANCED + +//SKELETONS +export { CourseCardSkeleton } from "./components/skeletons/CourseCard/index"; +export { BannerSkeleton } from "./components/skeletons/Banner/index";