From fd9a2da134554114e805add0d3c635a9bf60da41 Mon Sep 17 00:00:00 2001 From: Evan Sloan Date: Mon, 1 Apr 2024 19:52:48 -0400 Subject: [PATCH] add option to scroll items simultaneously on compare (#97) --- .../collection-log/collection-log-items.tsx | 19 +++-- components/compare/compare-content.tsx | 81 ++++++++++++++----- components/ui/checkbox.tsx | 30 +++++++ package-lock.json | 48 +++++++++++ package.json | 1 + 5 files changed, 152 insertions(+), 27 deletions(-) create mode 100644 components/ui/checkbox.tsx diff --git a/components/collection-log/collection-log-items.tsx b/components/collection-log/collection-log-items.tsx index e9511bb..60e54c0 100644 --- a/components/collection-log/collection-log-items.tsx +++ b/components/collection-log/collection-log-items.tsx @@ -4,17 +4,17 @@ import Item from '@/components/item'; import { OpenView } from '@/lib/hooks'; import { cn } from '@/lib/utils'; -interface CollectionLogItemsProps { +interface CollectionLogItemsProps extends React.InputHTMLAttributes { activeOpenView: OpenView; showQuantity: boolean; - className?: string; } -const CollectionLogItems = ({ +const CollectionLogItems = React.forwardRef(({ activeOpenView, showQuantity, className, -}: CollectionLogItemsProps) => { + onScroll, +}, ref) => { const { page: { name: pageName, @@ -35,7 +35,7 @@ const CollectionLogItems = ({
@@ -52,7 +52,11 @@ const CollectionLogItems = ({

))}
-
+
{items.map((item, i) => (
); -}; +}); +CollectionLogItems.displayName = 'CollectionLogItems'; export default CollectionLogItems; diff --git a/components/compare/compare-content.tsx b/components/compare/compare-content.tsx index 7f6a961..7fabf11 100644 --- a/components/compare/compare-content.tsx +++ b/components/compare/compare-content.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useEffect } from 'react'; +import React, { RefObject, useEffect, useRef, useState } from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; import { @@ -12,6 +12,7 @@ import { import { UserTypeahead } from '@/components/typeahead'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; +import { Checkbox } from '@/components/ui/checkbox'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { CollectionLogFull, @@ -44,6 +45,9 @@ const CompareContent = ({ data1, data2, startPage }: CompareContentProps) => { sortCollectionLog(collectionLog) ); + const container1Ref = useRef(null); + const container2Ref = useRef(null); + const { tabs: tabs1 } = collectionLog1; const { tabs: tabs2 } = collectionLog2; @@ -65,6 +69,8 @@ const CompareContent = ({ data1, data2, startPage }: CompareContentProps) => { updateViewByPage: updateViewByPage2, } = useCollectionLogView(collectionLog2, startPage); + const [syncScroll, setSyncScroll] = useState(true); + const onTabClick = (tabName: string) => { updateViewByTab1(tabName); updateViewByTab2(tabName); @@ -75,6 +81,24 @@ const CompareContent = ({ data1, data2, startPage }: CompareContentProps) => { updateViewByPage2(pageName); }; + const onContainerScroll = ( + containerRef: RefObject, + scrollValue: number + ) => { + if (!containerRef.current) { + return; + } + + if (!syncScroll) { + return; + } + + containerRef.current.scrollTo({ + top: scrollValue, + behavior: 'smooth' + }); + }; + useEffect(() => { replaceUrl(`/compare/${username1}/${username2}/${activePageName}`); }, [activePageName, username1, username2]); @@ -121,25 +145,34 @@ const CompareContent = ({ data1, data2, startPage }: CompareContentProps) => { defaultRankType={settings1.displayRank} /> -
- `/compare/${username}/${username2}`} - usePopover - > - - - `/compare/${username1}/${username}`} - usePopover - > - - +
+
+ `/compare/${username}/${username2}`} + usePopover + > + + + `/compare/${username1}/${username}`} + usePopover + > + + +
+
+ setSyncScroll(!syncScroll)} + /> +

Scroll items simultaneously

+
{ className='col-span-3' activeOpenView={openView1} showQuantity={settings1.showQuantity} + onScroll={(e) => + onContainerScroll(container2Ref, e.currentTarget.scrollTop) + } + ref={container1Ref} /> { className='col-span-3' activeOpenView={openView2} showQuantity={settings2.showQuantity} + onScroll={(e) => + onContainerScroll(container1Ref, e.currentTarget.scrollTop) + } + ref={container2Ref} /> ))} diff --git a/components/ui/checkbox.tsx b/components/ui/checkbox.tsx new file mode 100644 index 0000000..63f4ef3 --- /dev/null +++ b/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +'use client'; + +import * as React from 'react'; +import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; +import { Check } from 'lucide-react'; + +import { cn } from '@/lib/utils'; + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)); +Checkbox.displayName = CheckboxPrimitive.Root.displayName; + +export { Checkbox }; diff --git a/package-lock.json b/package-lock.json index 9f84645..20cb032 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@next/third-parties": "^14.1.1", "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", @@ -2010,6 +2011,36 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz", + "integrity": "sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collapsible": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz", @@ -2685,6 +2716,23 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-rect": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", diff --git a/package.json b/package.json index 8dfe56d..c701bbe 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dependencies": { "@next/third-parties": "^14.1.1", "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2",