diff --git a/src/pages/showcase/_components/ShowcaseCard/index.tsx b/src/pages/showcase/_components/ShowcaseCard/index.tsx index a7ea020c..6d56a075 100644 --- a/src/pages/showcase/_components/ShowcaseCard/index.tsx +++ b/src/pages/showcase/_components/ShowcaseCard/index.tsx @@ -1,4 +1,3 @@ - import React from 'react'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; @@ -10,40 +9,46 @@ import { Tag, TagList, Tags, TagType, User } from '@site/src/data/users'; import { sortBy } from '@site/src/utils/jsUtils'; import IdealImage from '@theme/IdealImage'; -const TagComp = React.forwardRef( - ({label, color, description}, ref) => ( -
  • - {label.toLowerCase()} - -
  • - ), -); +const TagComp = React.forwardRef< + HTMLLIElement, + Tag & {label: string} +>(({label, color, description}, ref) => ( +
  • + + {label.toLowerCase()} +
  • +)); function ShowcaseCardTag({tags}: {tags: TagType[]}) { const tagObjects = tags.map((tag) => ({tag, ...Tags[tag]})); - - // Keep same order for all tags const tagObjectsSorted = sortBy(tagObjects, (tagObject) => TagList.indexOf(tagObject.tag), ); return ( - <> + ); } @@ -54,50 +59,59 @@ function ShowcaseCard({user}: {user: User}) { const handleSourceClick = (e: React.MouseEvent) => { e.stopPropagation(); - // The link will handle the navigation }; + console.log(user.preview) + return ( -
  • -
    -
    +
    + {/* Image Section */} +
    + {user.tags.includes('favorite') && ( +
    + +
    + )} + {user.source && ( + + 💻 + source + + )}
    -
    + + {/* Content Section */} +
    -

    - - {user.title} - -

    - {user.tags.includes('favorite') && ( - - )} - {user.source && ( - - source - - )} +

    {user.title}

    -

    {user.description}

    -
    -
      + +

      {user.description}

      + + {/* Tags */} -
    -
  • + + {/* Footer */} +
    + +
    + + + {/* Card Effects */} +
    + ); } diff --git a/src/pages/showcase/_components/ShowcaseCard/styles.module.css b/src/pages/showcase/_components/ShowcaseCard/styles.module.css index 5cb25f0e..bcc5977f 100644 --- a/src/pages/showcase/_components/ShowcaseCard/styles.module.css +++ b/src/pages/showcase/_components/ShowcaseCard/styles.module.css @@ -1,336 +1,356 @@ -.showcaseCardImage { +/* Enhanced Showcase Card Styles */ + +@keyframes cardFloat { + 0%, 100% { + transform: translateY(0px); + } + 50% { + transform: translateY(-4px); + } +} + +@keyframes tagPulse { + 0%, 100% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } +} + +/* Main Card */ +.enhancedShowcaseCard { + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 20px; overflow: hidden; - height: 150px; - border-bottom: 2px solid var(--ifm-color-emphasis-200); - transition: transform var(--ifm-transition-fast) ease; -} - -.showcaseCardHeader { + backdrop-filter: blur(20px); + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); + cursor: pointer; + position: relative; display: flex; - align-items: center; - margin-bottom: 12px; + flex-direction: column; + height: 100%; } -.showcaseCardTitle { - margin-bottom: 0; - flex: 1 1 auto; +.enhancedShowcaseCard::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(102, 126, 234, 0.02) 0%, rgba(240, 147, 251, 0.02) 100%); + opacity: 0; + transition: opacity 0.4s ease; } -.showcaseCardTitle a { - text-decoration: none; - background: linear-gradient( - var(--ifm-color-primary), - var(--ifm-color-primary) - ) - 0% 100% / 0% 1px no-repeat; - transition: all var(--ifm-transition-fast) ease; - position: relative; - z-index: 2; +.enhancedShowcaseCard:hover::before { + opacity: 1; } -.showcaseCardTitle a:not(:focus):hover { - background-size: 100% 1px; - color: var(--ifm-link-hover-color); +.enhancedShowcaseCard:hover { + background: rgba(255, 255, 255, 0.08); + border-color: rgba(102, 126, 234, 0.3); + transform: translateY(-8px) scale(1.02); + box-shadow: + 0 20px 40px rgba(0, 0, 0, 0.2), + 0 10px 20px rgba(102, 126, 234, 0.15); } -.showcaseCardTitle, -.showcaseCardHeader .svgIconFavorite { - margin-right: 0.25rem; +/* Image Section */ +.showcaseCardImage { + position: relative; + height: 200px; + overflow: hidden; + background: rgba(255, 255, 255, 0.05); } -.showcaseCardHeader .svgIconFavorite { - color: var(--site-color-svg-icon-favorite); +.showcaseCardImage img { + width: 100%; + height: 100%; + object-fit: cover; + transition: all 0.3s ease; } -.showcaseCardSrcBtn { - margin-left: 6px; - padding-left: 12px; - padding-right: 12px; - border: none; +.enhancedShowcaseCard:hover .showcaseCardImage img { + transform: scale(1.05); } -.showcaseCardSrcBtn:focus-visible { - background-color: var(--ifm-color-secondary-dark); +.favoriteIcon { + position: absolute; + top: 12px; + left: 12px; + width: 32px; + height: 32px; + background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4px 12px rgba(245, 158, 11, 0.4); + animation: cardFloat 3s ease-in-out infinite; } -[data-theme='dark'] .showcaseCardSrcBtn { - background-color: var(--ifm-color-emphasis-200) !important; - color: inherit; +.svgIconFavorite { + width: 16px; + height: 16px; + color: white; } -[data-theme='dark'] .showcaseCardSrcBtn:hover { - background-color: var(--ifm-color-emphasis-300) !important; +.sourceButton { + position: absolute; + top: 12px; + right: 12px; + display: flex; + align-items: center; + gap: 4px; + padding: 6px 12px; + background: rgba(0, 0, 0, 0.7); + border-radius: 20px; + color: white; + text-decoration: none; + font-size: 12px; + font-weight: 600; + backdrop-filter: blur(10px); + opacity: 0; + transition: all 0.3s ease; } -.showcaseCardBody { - font-size: smaller; - line-height: 1.66; +.enhancedShowcaseCard:hover .sourceButton { + opacity: 1; + transform: translateY(0); } -.cardFooter { - display: flex; - flex-wrap: wrap; - position: relative; - z-index: 2; +.sourceButton:hover { + background: rgba(0, 0, 0, 0.9); + transform: scale(1.05); + color: white; + text-decoration: none; } -.tag { - font-size: 0.675rem; - border: 1px solid var(--ifm-color-secondary-darkest); - cursor: default; - margin-right: 6px; - margin-bottom: 6px !important; - border-radius: 12px; - display: inline-flex; - align-items: center; +.sourceIcon { + font-size: 12px; } -.tag .textLabel { - margin-left: 8px; +.sourceText { + font-size: 11px; } -.tag .colorLabel { - width: 7px; - height: 7px; - border-radius: 50%; - margin-left: 6px; - margin-right: 6px; +/* Content Section */ +.showcaseCardContent { + padding: 20px; + flex: 1; + display: flex; + flex-direction: column; } -/* Card Styles */ -.card { - --card-scale: 1; - --card-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.1); - --card-border: 1px solid var(--ifm-color-emphasis-200); - - position: relative; - overflow: hidden; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - border: var(--card-border); - border-radius: 8px; - height: 100%; +.showcaseCardHeader { display: flex; - flex-direction: column; - cursor: pointer; - background: var(--ifm-card-background-color); - transform: scale(var(--card-scale)) translateZ(0); - will-change: transform, box-shadow, border-color; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 12px; } -.card:hover { - --card-scale: 1.02; - --card-shadow: 0 12px 28px 0 rgba(0, 0, 0, 0.15); - --card-border: 1px solid var(--ifm-color-primary); - transform: translateY(-6px) scale(var(--card-scale)) translateZ(0); - box-shadow: var(--card-shadow); - border-color: var(--ifm-color-primary); +.showcaseCardTitle { + font-size: 18px; + font-weight: 700; + color: #ffffff; + margin: 0; + line-height: 1.3; + flex: 1; + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } -/* Card clickable overlay */ -.cardLink { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 1; +.cardActions { + display: flex; + gap: 8px; opacity: 0; - background: linear-gradient(135deg, rgba(var(--ifm-color-primary-rgb), 0.1) 0%, rgba(var(--ifm-color-primary-rgb), 0) 100%); transition: opacity 0.3s ease; } -.card:hover .cardLink { +.enhancedShowcaseCard:hover .cardActions { opacity: 1; } -/* Card content */ -.card__body { - position: relative; - z-index: 2; - flex-grow: 1; +.actionButton { + width: 28px; + height: 28px; + border: none; + background: rgba(255, 255, 255, 0.1); + border-radius: 6px; + cursor: pointer; + transition: all 0.3s ease; + font-size: 12px; display: flex; - flex-direction: column; - pointer-events: none; - padding: 1.5rem 1.5rem 0.5rem; - transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); + align-items: center; + justify-content: center; } -.card:hover .card__body { - transform: translateY(-4px); +.actionButton:hover { + background: rgba(255, 255, 255, 0.2); + transform: scale(1.1); } -.card__body > * { - pointer-events: auto; +.showcaseCardDescription { + font-size: 14px; + color: #94a3b8; + margin: 0 0 16px 0; + line-height: 1.5; + flex: 1; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; } -.card__footer { - position: relative; - z-index: 2; - padding: 0.5rem 1.5rem 1.5rem; - transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); +/* Enhanced Tags */ +.enhancedTagsList { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin: 0 0 16px 0; + padding: 0; + list-style: none; } -.card:hover .card__footer { - transform: translateY(4px); +.enhancedTag { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 8px; + border-radius: 12px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + border: 1px solid; + transition: all 0.3s ease; + position: relative; + overflow: hidden; } -/* Source button */ -.showcaseCardSrcBtn { - position: relative; - z-index: 3; - pointer-events: auto; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - transform: translateY(2px); - opacity: 0.9; - border-radius: 20px; - font-weight: 500; - letter-spacing: 0.5px; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); - background: var(--ifm-color-primary); - color: white !important; - border: none; +.enhancedTag::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + transition: left 0.6s ease; } -.showcaseCardSrcBtn:hover { - transform: translateY(-1px) scale(1.03); - opacity: 1; - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); - background: var(--ifm-color-primary-dark); +.enhancedTag:hover::before { + left: 100%; } -.showcaseCardSrcBtn:active { - transform: translateY(1px) scale(0.98); +.enhancedTag:hover { + transform: translateY(-2px) scale(1.05); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); } -/* Title styles */ -.showcaseCardTitle { - position: relative; - display: inline-block; - margin: 0; +.tagDot { + width: 6px; + height: 6px; + border-radius: 50%; + flex-shrink: 0; } -.showcaseCardTitle a { - position: relative; - z-index: 2; - pointer-events: auto; - display: inline-block; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - font-weight: 600; - color: var(--ifm-heading-color); - text-decoration: none; - background: linear-gradient( - var(--ifm-color-primary), - var(--ifm-color-primary) - ) - 0% 100% / 0% 2px no-repeat; - padding-bottom: 2px; +/* Card Footer */ +.showcaseCardFooter { + margin-top: auto; + display: flex; + justify-content: center; } -.showcaseCardTitle a:hover { - background-size: 100% 2px; - color: var(--ifm-color-primary); +.viewButton { + display: flex; + align-items: center; + gap: 6px; + padding: 10px 20px; + background: linear-gradient(135deg, #1DB954 0%, #1ed760 100%); + color: white; + border: none; + border-radius: 20px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + opacity: 0; + transform: translateY(10px); + font-size: 12px; } -/* Image hover effect */ -.card__image, -.showcaseCardImage { - position: relative; - z-index: 2; - overflow: hidden; - transform: translateZ(0); +.enhancedShowcaseCard:hover .viewButton { + opacity: 1; + transform: translateY(0); } -.showcaseCardImage img { - transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1); - transform: scale(1); - will-change: transform; +.viewButton:hover { + background: linear-gradient(135deg, #1ed760 0%, #1DB954 100%); + transform: translateY(-2px) scale(1.05); + box-shadow: 0 8px 20px rgba(29, 185, 84, 0.4); } -.card:hover .showcaseCardImage img { - transform: scale(1.05); +.viewIcon { + font-size: 12px; } -.showcaseCardImage::after { - content: ''; +/* Card Glow Effect */ +.cardGlow { position: absolute; top: 0; left: 0; right: 0; bottom: 0; - background: linear-gradient( - to bottom, - rgba(0, 0, 0, 0) 0%, - rgba(0, 0, 0, 0.2) 100% - ); - opacity: 0.5; + border-radius: 20px; + opacity: 0; transition: opacity 0.3s ease; - mix-blend-mode: multiply; -} - -.card:hover .showcaseCardImage::after { - opacity: 0.8; -} - -/* Tag styles */ -.tag { - --tag-scale: 1; - --tag-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - - position: relative; - overflow: hidden; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - transform: scale(var(--tag-scale)) translateZ(0); - will-change: transform, box-shadow; - border-radius: 12px; - background: var(--ifm-color-emphasis-100); - border: 1px solid var(--ifm-color-emphasis-200); - box-shadow: var(--tag-shadow); -} - -.tag:hover { - --tag-scale: 1.05; - --tag-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - transform: translateY(-2px) scale(var(--tag-scale)) translateZ(0); - z-index: 1; -} - -.tag::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient( - 135deg, - rgba(255, 255, 255, 0.2) 0%, - rgba(255, 255, 255, 0) 100% - ); - transform: translateX(-100%) skewX(-20deg); - transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1); pointer-events: none; + background: radial-gradient(circle at 50% 50%, rgba(102, 126, 234, 0.1) 0%, transparent 70%); } -.tag:hover::before { - transform: translateX(200%) skewX(-20deg); -} - -/* Favorite icon animation */ -.svgIconFavorite { - transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); +.enhancedShowcaseCard:hover .cardGlow { + opacity: 1; } -.card:hover .svgIconFavorite { - transform: scale(1.2) rotate(8deg); - filter: drop-shadow(0 2px 4px rgba(255, 0, 0, 0.3)); +/* Responsive Design */ +@media (max-width: 768px) { + .showcaseCardContent { + padding: 16px; + } + + .showcaseCardTitle { + font-size: 16px; + } + + .showcaseCardDescription { + font-size: 13px; + } + + .enhancedTag { + font-size: 10px; + padding: 3px 6px; + } + + .viewButton { + padding: 8px 16px; + font-size: 11px; + } } -/* Card description */ -.showcaseCardBody { - transition: all 0.3s ease; - opacity: 0.9; +/* Force consistent colors */ +[data-theme='dark'] .enhancedShowcaseCard *, +[data-theme='light'] .enhancedShowcaseCard *, +html[data-theme='dark'] .enhancedShowcaseCard *, +html[data-theme='light'] .enhancedShowcaseCard * { + color: inherit !important; } -.card:hover .showcaseCardBody { - opacity: 1; +[data-theme='dark'] .enhancedShowcaseCard .showcaseCardTitle, +[data-theme='light'] .enhancedShowcaseCard .showcaseCardTitle { + color: #ffffff !important; } diff --git a/src/pages/showcase/index.tsx b/src/pages/showcase/index.tsx index d74ba5c5..96f3d48f 100644 --- a/src/pages/showcase/index.tsx +++ b/src/pages/showcase/index.tsx @@ -11,18 +11,17 @@ import ShowcaseFilterToggle, { } from './_components/ShowcaseFilterToggle'; import ShowcaseCard from './_components/ShowcaseCard'; import ShowcaseTooltip from './_components/ShowcaseTooltip'; - import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; import Translate, { translate } from '@docusaurus/Translate'; import { useHistory, useLocation } from '@docusaurus/router'; import { usePluralForm } from '@docusaurus/theme-common'; import { motion } from "framer-motion"; -import styles from './styles.module.css'; +import './styles.css'; import { sortedUsers, - Tags, - TagList, - type User, - type TagType,} from '@site/src/data/users'; + Tags, + TagList, + type User, + type TagType,} from '@site/src/data/users'; import FavoriteIcon from '@site/src/components/svgIcons/FavoriteIcon'; import { useColorMode } from '@docusaurus/theme-common'; @@ -54,7 +53,6 @@ export function prepareUserState(): UserState | undefined { focusedElementId: document.activeElement?.id, }; } - return undefined; } @@ -91,18 +89,16 @@ function filterUsers( } function useFilteredUsers() { - const location = useLocation(); + const location = useLocation(); const [operator, setOperator] = useState('OR'); - // On SSR / first mount (hydration) no tag is selected const [selectedTags, setSelectedTags] = useState([]); const [searchName, setSearchName] = useState(null); - // Sync tags from QS to state (delayed on purpose to avoid SSR/Client - // hydration mismatch) + useEffect(() => { setSelectedTags(readSearchTags(location.search)); setOperator(readOperator(location.search)); setSearchName(readSearchName(location.search)); - restoreUserState(location.state); + // restoreUserState(location?.state); }, [location]); return useMemo( @@ -113,53 +109,49 @@ function useFilteredUsers() { function ShowcaseHeader() { return ( -
    - {TITLE} - {DESCRIPTION} +
    +
    +
    + 🚀 + Community Showcase +
    +

    + Open Source Projects Hub +

    +

    + {DESCRIPTION} +

    + + {/* Stats */} +
    +
    +
    {sortedUsers.length}+
    +
    Projects
    +
    +
    +
    1050+
    +
    Forks
    +
    +
    +
    950+
    +
    Stars
    +
    +
    - - - - 🌟 Join the Hive Community - - - -
    + {/* CTA Button */} + + + ); } @@ -173,7 +165,7 @@ function useSiteCountPlural() { id: 'showcase.filters.resultCount', description: 'Pluralized label for the number of sites found on the showcase. Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)', - message: '1 site|{sitesCount} sites', + message: '1 project|{sitesCount} projects', }, {sitesCount}, ), @@ -183,272 +175,194 @@ function useSiteCountPlural() { function ShowcaseFilters() { const filteredUsers = useFilteredUsers(); const siteCountPlural = useSiteCountPlural(); + return ( -
    -
    -
    - - Filters - - - {siteCountPlural(filteredUsers.length)} - +
    +
    +

    + 🔍 + Explore Projects +

    +
    + {siteCountPlural(filteredUsers.length)}
    -
    - - {TagList.map((tag, i) => { - const {label, description, color} = Tags[tag]; - const id = `showcase_checkbox_id_${tag}`; + +
    +
    + +
    + +
    + {TagList.map((tag, i) => { + const {label, description, color} = Tags[tag]; + const id = `showcase_checkbox_id_${tag}`; - return ( -
  • + return ( - ) : ( - - ) + } /> -
  • - ); - })} - -
    + ); + })} + + + ); } -const favoriteUsers = sortedUsers.filter((user) => - user.tags.includes('favorite'), -); -const otherUsers = sortedUsers.filter( - (user) => !user.tags.includes('favorite'), -); - function SearchBar() { const history = useHistory(); const location = useLocation(); const [value, setValue] = useState(null); + useEffect(() => { setValue(readSearchName(location.search)); }, [location]); + return ( -
    - { - setValue(e.currentTarget.value); - const newSearch = new URLSearchParams(location.search); - newSearch.delete(SearchNameQueryKey); - if (e.currentTarget.value) { - newSearch.set(SearchNameQueryKey, e.currentTarget.value); - } - history.push({ - ...location, - search: newSearch.toString(), - state: prepareUserState(), - }); - setTimeout(() => { - document.getElementById('searchbar')?.focus(); - }, 0); - }} - /> +
    +
    +
    🔍
    + { + setValue(e.currentTarget.value); + const newSearch = new URLSearchParams(location.search); + newSearch.delete(SearchNameQueryKey); + if (e.currentTarget.value) { + newSearch.set(SearchNameQueryKey, e.currentTarget.value); + } + history.push({ + ...location, + search: newSearch.toString(), + state: prepareUserState(), + }); + setTimeout(() => { + document.getElementById('searchbar')?.focus(); + }, 0); + }} + /> +
    ); } +const favoriteUsers = sortedUsers.filter((user) => + user.tags.includes('favorite'), +); +const otherUsers = sortedUsers.filter( + (user) => !user.tags.includes('favorite'), +); + function ShowcaseCards() { const filteredUsers = useFilteredUsers(); if (filteredUsers.length === 0) { return ( -
    -
    -

    - No result -

    - -
    -
    +
    +
    🔍
    +

    No projects found

    +

    Try adjusting your filters or search terms

    +
    ); } return ( -
    +
    {filteredUsers.length === sortedUsers.length ? ( <> -
    -
    -
    - - - Best of the Hive - - - - - - + {favoriteUsers.length > 0 && ( +
    +

    + + Best of the Hive + ({favoriteUsers.length}) +

    +
    + {favoriteUsers.map((user, index) => ( + + + + ))}
    - - - {favoriteUsers.map((user) => ( - +
    + )} + + {otherUsers.length > 0 && ( +
    +

    + 🌟 + All Projects + ({otherUsers.length}) +

    +
    + {otherUsers.map((user, index) => ( + + + ))} - +
    -
    -
    - - All sites - - - {otherUsers.map((user) => ( - - ))} - -
    + )} ) : ( -
    -
    - -
    -
      - {filteredUsers.map((user) => ( - +
      +

      + 🔍 + Search Results + ({filteredUsers.length}) +

      +
      + {filteredUsers.map((user, index) => ( + + + ))} -
    +
    )} -
    - ); -} - -export default function Showcase(): JSX.Element { - - return ( - - - +
    ); } @@ -457,23 +371,23 @@ function ShowcaseContent() { const isDark = colorMode === "dark"; return ( -
    - -