From 923e28282da5cac02744d7e22f7adab1db97c4bc Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Thu, 3 Feb 2022 18:54:38 +0200 Subject: [PATCH] fix: fixed carousels ux - made categories badges a carousel - added custom arrows to apps carousel on non-tocuh devices - fixed invalid items offset on mobile sometimes --- package-lock.json | 148 ++++++++++++++++++ package.json | 2 + src/Components/Badge/Badge.tsx | 10 +- src/Components/Slider/Slider.stories.tsx | 35 +++++ src/Components/Slider/Slider.tsx | 45 ++++++ src/index.css | 6 + .../ExplorePage/Categories/Categories.tsx | 10 +- .../ExplorePage/ProjectsRow/ProjectsRow.tsx | 61 ++++++-- src/pages/ExplorePage/ProjectsRow/style.css | 9 ++ src/pages/ProjectPage/Tip/TipCard.tsx | 9 +- 10 files changed, 310 insertions(+), 25 deletions(-) create mode 100644 src/Components/Slider/Slider.stories.tsx create mode 100644 src/Components/Slider/Slider.tsx create mode 100644 src/pages/ExplorePage/ProjectsRow/style.css diff --git a/package-lock.json b/package-lock.json index 00bb10f..dedc4c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@apollo/client": "^3.5.5", "@prisma/client": "3.5.0", + "@react-spring/web": "^9.4.2", "@reduxjs/toolkit": "^1.6.2", "@testing-library/jest-dom": "^5.15.0", "@testing-library/react": "^11.2.7", @@ -18,6 +19,7 @@ "@types/node": "^12.20.36", "@types/react": "^17.0.33", "@types/react-dom": "^17.0.10", + "@use-gesture/react": "^10.2.5", "apollo-server": "^3.5.0", "apollo-server-lambda": "^3.5.0", "axios": "^0.24.0", @@ -4029,6 +4031,73 @@ "react-dom": "15.x || 16.x || 16.4.0-alpha.0911da3" } }, + "node_modules/@react-spring/animated": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.4.2.tgz", + "integrity": "sha512-Dzum5Ho8e+LIAegAqRyoQFakD2IVH3ZQ2nsFXJorAFq3Xjv6IVPz/+TNxb/wSvnsMludfoF+ZIf319FSFmgD5w==", + "dependencies": { + "@react-spring/shared": "~9.4.0", + "@react-spring/types": "~9.4.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.4.2.tgz", + "integrity": "sha512-Ej/ULwdx8rQtMAWEpLgwbKcQEx6vPfjyG3cxLP05zAInpCoWkYpl+sXOp9tn3r99mTNQPTTt7BgQsSnmQA8+rQ==", + "dependencies": { + "@react-spring/animated": "~9.4.0", + "@react-spring/rafz": "~9.4.0", + "@react-spring/shared": "~9.4.0", + "@react-spring/types": "~9.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.4.2.tgz", + "integrity": "sha512-rSm+G8E/XEEpnCGtT/xYN6o8VvEXlU8wN/hyKp4Q44XAZzGSMHLIFP7pY94/MmWsxCxjkw1AxUWhiFYxWrnI5Q==" + }, + "node_modules/@react-spring/shared": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.4.2.tgz", + "integrity": "sha512-mZtbQLpMm6Vy5+O1MSlY9KuAcMO8rdUQvtdnC7Or7y7xiZlnzj8oAILyO6Y2rD2ZC1PmgVS0gMev/8T+MykW+Q==", + "dependencies": { + "@react-spring/rafz": "~9.4.0", + "@react-spring/types": "~9.4.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.4.2.tgz", + "integrity": "sha512-GGiIscTM+CEUNV52anj3g5FqAZKL2+eRKtvBOAlC99qGBbvJ3qTLImrUR/I3lXY7PRuLgzI6kh34quA1oUxWYQ==" + }, + "node_modules/@react-spring/web": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.4.2.tgz", + "integrity": "sha512-sWfA9NkVuvVOpjSlMkD2zcF6X3i8NSHTeH/uHCGKsN3mYqgkhvAF+E8GASO/H4KKGNhbRvgCZiwJXOtOGyUg6A==", + "dependencies": { + "@react-spring/animated": "~9.4.0", + "@react-spring/core": "~9.4.0", + "@react-spring/shared": "~9.4.0", + "@react-spring/types": "~9.4.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "1.6.2", "integrity": "sha512-HbfI/hOVrAcMGAYsMWxw3UJyIoAS9JTdwddsjlr5w3S50tXhWb+EMyhIw+IAvCVCLETkzdjgH91RjDSYZekVBA==", @@ -8268,6 +8337,22 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@use-gesture/core": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.2.5.tgz", + "integrity": "sha512-Pggq5qLipJNYjMq6R7p7Y5h1juUlTkrhi9KqFJH6PcRFCNn+evjbOvpX7JEFehrx4Ik9UZcQpkmR+E0s7a5Lbg==" + }, + "node_modules/@use-gesture/react": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.2.5.tgz", + "integrity": "sha512-AEVi2BBKOms7UNoRzthxwe4t3UVe/S1Vm4K2Vitbup3TkbhF3x70/ZbgE/TxhucYFcOPUcrQ89squEs/liQezg==", + "dependencies": { + "@use-gesture/core": "10.2.5" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, "node_modules/@vendia/serverless-express": { "version": "4.5.2", "integrity": "sha512-mekBOPnBxfhIvBYKVwfvjp9NtS+bOs3F08Vudxa3Fb7zkxtdjRn0UMLRT6zwWf6i4V5rjk2aEhzbIDSCV1GQ0w==", @@ -56662,6 +56747,56 @@ "react-lifecycles-compat": "^3.0.4" } }, + "@react-spring/animated": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.4.2.tgz", + "integrity": "sha512-Dzum5Ho8e+LIAegAqRyoQFakD2IVH3ZQ2nsFXJorAFq3Xjv6IVPz/+TNxb/wSvnsMludfoF+ZIf319FSFmgD5w==", + "requires": { + "@react-spring/shared": "~9.4.0", + "@react-spring/types": "~9.4.0" + } + }, + "@react-spring/core": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.4.2.tgz", + "integrity": "sha512-Ej/ULwdx8rQtMAWEpLgwbKcQEx6vPfjyG3cxLP05zAInpCoWkYpl+sXOp9tn3r99mTNQPTTt7BgQsSnmQA8+rQ==", + "requires": { + "@react-spring/animated": "~9.4.0", + "@react-spring/rafz": "~9.4.0", + "@react-spring/shared": "~9.4.0", + "@react-spring/types": "~9.4.0" + } + }, + "@react-spring/rafz": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.4.2.tgz", + "integrity": "sha512-rSm+G8E/XEEpnCGtT/xYN6o8VvEXlU8wN/hyKp4Q44XAZzGSMHLIFP7pY94/MmWsxCxjkw1AxUWhiFYxWrnI5Q==" + }, + "@react-spring/shared": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.4.2.tgz", + "integrity": "sha512-mZtbQLpMm6Vy5+O1MSlY9KuAcMO8rdUQvtdnC7Or7y7xiZlnzj8oAILyO6Y2rD2ZC1PmgVS0gMev/8T+MykW+Q==", + "requires": { + "@react-spring/rafz": "~9.4.0", + "@react-spring/types": "~9.4.0" + } + }, + "@react-spring/types": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.4.2.tgz", + "integrity": "sha512-GGiIscTM+CEUNV52anj3g5FqAZKL2+eRKtvBOAlC99qGBbvJ3qTLImrUR/I3lXY7PRuLgzI6kh34quA1oUxWYQ==" + }, + "@react-spring/web": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.4.2.tgz", + "integrity": "sha512-sWfA9NkVuvVOpjSlMkD2zcF6X3i8NSHTeH/uHCGKsN3mYqgkhvAF+E8GASO/H4KKGNhbRvgCZiwJXOtOGyUg6A==", + "requires": { + "@react-spring/animated": "~9.4.0", + "@react-spring/core": "~9.4.0", + "@react-spring/shared": "~9.4.0", + "@react-spring/types": "~9.4.0" + } + }, "@reduxjs/toolkit": { "version": "1.6.2", "integrity": "sha512-HbfI/hOVrAcMGAYsMWxw3UJyIoAS9JTdwddsjlr5w3S50tXhWb+EMyhIw+IAvCVCLETkzdjgH91RjDSYZekVBA==", @@ -59624,6 +59759,19 @@ "eslint-visitor-keys": "^2.0.0" } }, + "@use-gesture/core": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.2.5.tgz", + "integrity": "sha512-Pggq5qLipJNYjMq6R7p7Y5h1juUlTkrhi9KqFJH6PcRFCNn+evjbOvpX7JEFehrx4Ik9UZcQpkmR+E0s7a5Lbg==" + }, + "@use-gesture/react": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.2.5.tgz", + "integrity": "sha512-AEVi2BBKOms7UNoRzthxwe4t3UVe/S1Vm4K2Vitbup3TkbhF3x70/ZbgE/TxhucYFcOPUcrQ89squEs/liQezg==", + "requires": { + "@use-gesture/core": "10.2.5" + } + }, "@vendia/serverless-express": { "version": "4.5.2", "integrity": "sha512-mekBOPnBxfhIvBYKVwfvjp9NtS+bOs3F08Vudxa3Fb7zkxtdjRn0UMLRT6zwWf6i4V5rjk2aEhzbIDSCV1GQ0w==" diff --git a/package.json b/package.json index 90d2841..ee59a6d 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "@apollo/client": "^3.5.5", "@prisma/client": "3.5.0", + "@react-spring/web": "^9.4.2", "@reduxjs/toolkit": "^1.6.2", "@testing-library/jest-dom": "^5.15.0", "@testing-library/react": "^11.2.7", @@ -14,6 +15,7 @@ "@types/node": "^12.20.36", "@types/react": "^17.0.33", "@types/react-dom": "^17.0.10", + "@use-gesture/react": "^10.2.5", "apollo-server": "^3.5.0", "apollo-server-lambda": "^3.5.0", "axios": "^0.24.0", diff --git a/src/Components/Badge/Badge.tsx b/src/Components/Badge/Badge.tsx index 5d4c9c9..9366519 100644 --- a/src/Components/Badge/Badge.tsx +++ b/src/Components/Badge/Badge.tsx @@ -29,8 +29,8 @@ const badgeSize: UnionToObjectKeys = { } const loadingBadgeSize: UnionToObjectKeys = { - sm: "w-48 h-24 text-body6", - md: "w-64 h-32 text-body4", + sm: "w-48 h-[28px] text-body6", + md: "w-64 h-[38px] text-body4", lg: "w-64 h-42 text-body3" } @@ -50,7 +50,8 @@ export default function Badge( : PropsWithChildren) { const classes = ` - rounded-48 shadow-${shadow} border inline-block relative align-middle + rounded-48 border inline-block relative align-middle + shadow-${shadow} ${badgrColor[color]} ${badgeSize[size]} ${className} @@ -58,7 +59,8 @@ export default function Badge( ` if (isLoading) - return + return + return ( diff --git a/src/Components/Slider/Slider.stories.tsx b/src/Components/Slider/Slider.stories.tsx new file mode 100644 index 0000000..d189e67 --- /dev/null +++ b/src/Components/Slider/Slider.stories.tsx @@ -0,0 +1,35 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react'; + +import Slider from './Slider'; + +export default { + title: 'Shared/Slider', + component: Slider, + +} as ComponentMeta; + +const Template: ComponentStory = (args) =>
+
{ alert(1) }}> + 1 +
+
{ alert(2) }}> + 2 +
+
{ alert(3) }}> + 3 +
+
{ alert(4) }}> + 4 +
+
{ alert(5) }}> + 5 +
+
{ alert(6) }}> + 6 +
+
; + + +export const Default = Template.bind({}); +Default.args = { +} \ No newline at end of file diff --git a/src/Components/Slider/Slider.tsx b/src/Components/Slider/Slider.tsx new file mode 100644 index 0000000..eacde14 --- /dev/null +++ b/src/Components/Slider/Slider.tsx @@ -0,0 +1,45 @@ +import React, { useRef, useState, useEffect, useCallback, PropsWithChildren } from 'react'; +import { useDrag } from '@use-gesture/react' +import { useSpring, animated } from '@react-spring/web' +import { useResizeListener } from 'src/utils/hooks'; + +interface Props { + gap?: number +} + +export default function Slider({ + gap = 12, + children +}: PropsWithChildren) { + const [{ x, y }, api] = useSpring(() => ({ x: 0, y: 0 })) + const containerRef = useRef(null!); + const sliderRef = useRef(null!); + const [sliderWidth, setSliderWidth] = useState(-1); + + const bind = useDrag(({ down, offset: [ox, oy] }) => api.start({ x: ox, y: oy, immediate: down }), { + bounds: () => ({ left: -sliderWidth, right: 0, top: 0, bottom: 0 }), + rubberband: [.15, 0], + filterTaps: true + + }) + + const resizeListener = useCallback(() => { + setSliderWidth(Math.max(sliderRef.current?.scrollWidth - containerRef.current?.clientWidth, 0)); + }, [setSliderWidth]); + + + useResizeListener(resizeListener) + + useEffect(() => { + setSliderWidth(Math.max(sliderRef.current?.scrollWidth - containerRef.current?.clientWidth, 0)); + }, [setSliderWidth]); + + + return <> +
+ + {children} + +
+ ; +} diff --git a/src/index.css b/src/index.css index a91f6ff..89dcaaf 100644 --- a/src/index.css +++ b/src/index.css @@ -94,3 +94,9 @@ svg { .no-scrollbar ::-webkit-scrollbar { display: none; /* Safari and Chrome */ } + +@media (pointer: coarse) { + .touch-device\:hidden { + display: none; + } +} diff --git a/src/pages/ExplorePage/Categories/Categories.tsx b/src/pages/ExplorePage/Categories/Categories.tsx index 59acbca..c169b1c 100644 --- a/src/pages/ExplorePage/Categories/Categories.tsx +++ b/src/pages/ExplorePage/Categories/Categories.tsx @@ -1,6 +1,8 @@ import { useQuery } from '@apollo/client'; import { ALL_CATEGORIES_QUERY, ALL_CATEGORIES_QUERY_RES } from './query'; import Badge from 'src/Components/Badge/Badge' +import Slider from 'src/Components/Slider/Slider' + export default function Categories() { @@ -11,17 +13,19 @@ export default function Categories() { } if (loading || !data) - return
+ return
{Array(5).fill(0).map((_, idx) => )}
return ( -
+ //
+ {data?.allCategories.map(category => handleClick(category.id)}>{category.title} )} -
+ + //
) } diff --git a/src/pages/ExplorePage/ProjectsRow/ProjectsRow.tsx b/src/pages/ExplorePage/ProjectsRow/ProjectsRow.tsx index 2adafcc..6230548 100644 --- a/src/pages/ExplorePage/ProjectsRow/ProjectsRow.tsx +++ b/src/pages/ExplorePage/ProjectsRow/ProjectsRow.tsx @@ -1,21 +1,30 @@ -import { ReactElement, useCallback, useRef, useState } from "react"; +import { ReactElement, useRef, useState } from "react"; import { ProjectCard } from "../../../utils/interfaces"; import Carousel from 'react-multi-carousel'; -import { MdDoubleArrow, } from 'react-icons/md'; +import { MdArrowRight, MdDoubleArrow, } from 'react-icons/md'; import { useAppDispatch } from "../../../utils/hooks"; import { openModal } from "../../../redux/features/modals.slice"; import ProjectCardMini from "../ProjectCardMini/ProjectCardMini"; import { useResizeListener } from 'src/utils/hooks' +import { IoIosArrowBack, IoIosArrowForward } from "react-icons/io"; +import './style.css'; + + const responsive = { all: { breakpoint: { max: 5000, min: 0 }, - items: (((window.innerWidth - 64) / (296 + 48))), + items: calcNumItems() } } -const calcNumItems = () => { - const items = (((window.innerWidth - 32 - 296) / (296 + 20))); +// const calcNumItems = () => { +// const items = (((window.innerWidth - 32 - 296) / (296 + 20))); +// return items; +// } + +function calcNumItems() { + const items = (((window.innerWidth - 2 * 32) / (296 + 20))); return items; } @@ -39,9 +48,14 @@ export default function ProjectsRow({ title, categoryId, projects }: Props) { } useResizeListener(() => { + console.log(calcNumItems()); + setCarouselItmsCnt(calcNumItems()); }, [setCarouselItmsCnt]) + if (projects.length === 0) + return <> + return (
@@ -52,18 +66,31 @@ export default function ProjectsRow({ title, categoryId, projects }: Props) { }} /> - - {projects.map((project, idx) => - - )} - +
+ + + + } + customRightArrow={ + + } + > + {projects.map((project, idx) => + + )} + +
+
) diff --git a/src/pages/ExplorePage/ProjectsRow/style.css b/src/pages/ExplorePage/ProjectsRow/style.css new file mode 100644 index 0000000..035fa15 --- /dev/null +++ b/src/pages/ExplorePage/ProjectsRow/style.css @@ -0,0 +1,9 @@ +@media (pointer: coarse) { + .carousel-btns { + display: none; + } + + .react-multi-carousel-list { + overflow: visible; + } +} diff --git a/src/pages/ProjectPage/Tip/TipCard.tsx b/src/pages/ProjectPage/Tip/TipCard.tsx index 6334569..d0f987c 100644 --- a/src/pages/ProjectPage/Tip/TipCard.tsx +++ b/src/pages/ProjectPage/Tip/TipCard.tsx @@ -72,8 +72,15 @@ export default function TipCard({ onClose, direction, tipValue, ...props }: Prop setPaymentStatus(PaymentStatus.AWAITING_PAYMENT); const webln = await Wallet_Service.getWebln() const paymentResponse = await webln.sendPayment(votingData.vote.payment_request); + console.log(paymentResponse); + setPaymentStatus(PaymentStatus.PAID); - confirmVote({ variables: { paymentRequest: votingData.vote.payment_request, preimage: paymentResponse.preimage } }) + confirmVote({ + variables: { + paymentRequest: votingData.vote.payment_request, + preimage: paymentResponse.preimage + } + }) .catch() // ONLY TEMPROARY !!! SHOULD BE FIXED FROM BACKEND .finally(() => { setTimeout(() => {