diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fa755b1ee..1390c9560 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,6 +26,7 @@ "lodash": "^4.17.21", "lucide-react": "^0.469.0", "markdown-it": "^14.1.0", + "millify": "^6.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-icons": "^5.3.0", @@ -4509,7 +4510,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -4524,14 +4524,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/cliui/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4541,7 +4539,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -4556,7 +4553,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -5394,7 +5390,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6339,7 +6334,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -9173,6 +9167,17 @@ "node": ">=8.6" } }, + "node_modules/millify": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/millify/-/millify-6.1.0.tgz", + "integrity": "sha512-H/E3J6t+DQs/F2YgfDhxUVZz/dF8JXPPKTLHL/yHCcLZLtCXJDUaqvhJXQwqOVBvbyNn4T0WjLpIHd7PAw7fBA==", + "dependencies": { + "yargs": "^17.0.1" + }, + "bin": { + "millify": "bin/millify" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -10460,7 +10465,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -11228,7 +11232,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12469,7 +12472,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -12499,7 +12501,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -12518,7 +12519,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -12528,14 +12528,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12545,7 +12543,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", diff --git a/frontend/package.json b/frontend/package.json index 18ee104e9..a67028026 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -36,6 +36,7 @@ "lodash": "^4.17.21", "lucide-react": "^0.469.0", "markdown-it": "^14.1.0", + "millify": "^6.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-icons": "^5.3.0", diff --git a/frontend/src/components/Card.tsx b/frontend/src/components/Card.tsx index 4dbe8bfac..41acfc927 100644 --- a/frontend/src/components/Card.tsx +++ b/frontend/src/components/Card.tsx @@ -1,18 +1,19 @@ import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome' -import { useState } from 'react' +import { useState, useEffect } from 'react' import { Tooltip } from 'react-tooltip' - import { CardProps, tooltipStyle } from 'lib/constants' import FontAwesomeIconWrapper from 'lib/FontAwesomeIconWrapper' import { cn } from 'lib/utils' import ActionButton from 'components/ActionButton' import ContributorAvatar from 'components/ContributorAvatar' - import { Icons } from 'components/data' import DisplayIcon from 'components/DisplayIcon' import Markdown from 'components/MarkdownWrapper' import TopicBadge from 'components/TopicBadge' +// Initial check for mobile screen size +const isMobileInitial = typeof window !== 'undefined' && window.innerWidth < 768 + const Card = ({ title, url, @@ -29,28 +30,39 @@ const Card = ({ social, tooltipLabel, }: CardProps) => { - const [visibleLanguages, setVisibleLanguages] = useState(18) - const [visibleTopics, setVisibleTopics] = useState(18) + const [visibleLanguages, setVisibleLanguages] = useState(isMobileInitial ? 4 : 18) + const [visibleTopics, setVisibleTopics] = useState(isMobileInitial ? 4 : 18) + const [toggleLanguages, setToggleLanguages] = useState(false) + const [toggleTopics, setToggleTopics] = useState(false) + const [isMobile, setIsMobile] = useState(isMobileInitial) - const [toggleLanguages, setToggleLanguages] = useState(true) - const [toggleTopics, setToggleTopics] = useState(true) + // Resize listener to adjust display based on screen width + useEffect(() => { + const checkMobile = () => { + const mobile = window.innerWidth < 768 + setIsMobile(mobile) + setVisibleLanguages(mobile ? 4 : 18) + setVisibleTopics(mobile ? 4 : 18) + } + window.addEventListener('resize', checkMobile) + return () => window.removeEventListener('resize', checkMobile) + }, []) const loadMoreLanguages = () => { - if (toggleLanguages) setVisibleLanguages(languages?.length as number) - else setVisibleLanguages(18) + setVisibleLanguages(toggleLanguages ? (isMobile ? 4 : 18) : languages?.length || 0) setToggleLanguages(!toggleLanguages) } const loadMoreTopics = () => { - if (toggleTopics) setVisibleTopics(topics?.length as number) - else setVisibleTopics(18) + setVisibleTopics(toggleTopics ? (isMobile ? 4 : 18) : topics?.length || 0) setToggleTopics(!toggleTopics) } return ( -