Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile version #79

Merged
merged 14 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"d3-array": "3.2.4",
"date-fns": "^3.3.1",
"deck.gl": "8.9.19",
"embla-carousel": "^8.3.0",
"embla-carousel-react": "^8.3.0",
"eslint": "8.42.0",
"eslint-config-next": "13.4.5",
"framer-motion": "^10.16.4",
Expand Down
4 changes: 2 additions & 2 deletions client/src/app/(landing)/globe/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ async function prefetchQueries(searchParams: HomePageProps['searchParams']) {
});

// Stories
let categoryId;
let categoryId: string | undefined;

// If there is a category in the search params, we need to get the category id to use as a category filter
if (searchParams.category) {
const categories = queryClient.getQueryData<CategoryListResponse>(categoriesQueryKey);

categoryId = categories?.data?.find((category) => {
return `"${category.attributes?.slug}"` === searchParams.category;
})?.id;
})?.attributes?.slug;
}

const params = getStoriesParams(categoryId ? { category: categoryId } : {});
Expand Down
26 changes: 0 additions & 26 deletions client/src/app/(landing)/stories/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,6 @@ import Story from '@/containers/story';

type StoryPageProps = { params: { id: string } };

// You can't generate static params for dynamic routes if they are using useSearchParams https://nextjs.org/docs/messages/deopted-into-client-rendering
// The solution is to wrap the component with Suspense
// By doing this, we will have errors related to hydration
// As we use it inside RecoilURLSyncNext, we can't generate static params

// export async function generateStaticParams() {
// try {
// const { data: storiesData } = await getStories({
// 'pagination[limit]': 200,
// });

// if (!storiesData) {
// throw new Error('Failed to parse storiesData');
// }

// console.log('storiesData', storiesData);

// return storiesData.map((s) => ({
// id: `${s.id}`,
// }));
// } catch (e) {
// console.error(e);
// return [];
// }
// }

export async function generateMetadata({ params }: StoryPageProps): Promise<Metadata> {
try {
// read route params
Expand Down
1 change: 0 additions & 1 deletion client/src/app/layout-providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { PropsWithChildren, useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import { TooltipProvider } from '@/components/ui/tooltip';

import { notesESA, openSans } from '@/styles/fonts';

export default function Providers({ children }: PropsWithChildren) {
Expand Down
23 changes: 19 additions & 4 deletions client/src/components/map/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import type { ViewState } from 'react-map-gl';
import { CustomMapProps } from './types';

export const DEFAULT_VIEW_STATE: Partial<ViewState> = {
export const DEFAULT_VIEW_STATE = {
zoom: 2,
latitude: 0,
longitude: 0,
pitch: 0,
bearing: 0,
padding: {
top: 0,
bottom: 0,
left: 0,
right: 0,
},
};

export const DEFAULT_MOBILE_ZOOM = 0.75;

export const DEFAULT_PROPS: CustomMapProps = {
id: 'default',
initialViewState: DEFAULT_VIEW_STATE,
minZoom: DEFAULT_MOBILE_ZOOM,
maxZoom: 14,
};
2 changes: 1 addition & 1 deletion client/src/components/map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export const MapMapbox: FC<CustomMapProps> = ({
}, [bounds, isFlying]);

return (
<div className={cn('relative z-0 h-full w-full', className)}>
<div className={cn('relative z-0 h-full w-screen', className)}>
<ReactMapGL
id={id}
initialViewState={initialViewState}
Expand Down
113 changes: 70 additions & 43 deletions client/src/components/map/layers/marker/index.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,99 @@
'use client';

import { Marker as RMarker } from 'react-map-gl';

import { XIcon } from 'lucide-react';

import { cn } from '@/lib/classnames';

import { useIsMobile } from '@/hooks/screen-size';

import { Button } from '@/components/ui/button';
import CategoryIcon from '@/components/ui/category-icon';

type MarkerProps = {
markers?: (GeoJSON.Feature<GeoJSON.Point> | null)[];
handleClick: (id: string | number) => void;
handleClose?: () => void;
};

const Marker = ({ markers, handleClick }: MarkerProps) => {
const Marker = ({ markers, handleClick, handleClose }: MarkerProps) => {
const { coordinates } = markers?.[0]?.geometry || {};

const isMobile = useIsMobile();

if (!coordinates?.length) return null;
return (
<RMarker anchor="left" latitude={coordinates[1]} longitude={coordinates[0]}>
<div className="flex items-center">

const MARKER = () => (
<div className="pointer-events-auto relative flex w-full items-center">
<div
className={cn({
'relative z-50 hidden h-6 w-6 -translate-x-1/2 cursor-pointer items-center justify-center sm:flex':
true,
})}
>
<div
className={cn({
'relative z-50 flex h-6 w-6 -translate-x-1/2 cursor-pointer items-center justify-center':
'absolute left-1/2 top-1/2 flex h-3 w-3 -translate-x-1/2 -translate-y-1/2 rotate-45 items-center justify-center border-[1.5px] border-[#FFE094] transition-all':
true,
'bg-background scale-[1.25] border-gray-200': true,
})}
>
<div
className={cn({
'absolute left-1/2 top-1/2 flex h-3 w-3 -translate-x-1/2 -translate-y-1/2 rotate-45 items-center justify-center border-[1.5px] border-[#FFE094] transition-all':
true,
'bg-background scale-[1.25] border-gray-200': true,
})}
>
<div className="h-[5px] w-[5px] bg-gray-200"></div>
</div>
<div className="h-[5px] w-[5px] bg-gray-200"></div>
</div>
</div>

<div className="max-w-[230px] -translate-x-6 rounded border border-gray-700 bg-[rgba(51,94,111,0.50)] px-0 text-white backdrop-blur-lg">
{markers?.map((marker) => {
if (!marker || !marker?.id) return null;
return (
<div
className="border-b border-b-gray-700 p-4 last-of-type:border-b-0"
key={marker.id}
onMouseMove={(e) => e.stopPropagation()}
>
<div className="mb-2 flex items-center space-x-4">
<CategoryIcon
slug={marker?.properties?.category}
className="h-10 w-10 fill-transparent stroke-teal-300 opacity-80"
/>
<p className="font-open-sans text-xs">{marker?.properties?.categoryName}</p>
</div>
<p className="font-notes text-sm">{marker?.properties?.title}</p>
<p className="font-open-sans mb-4 mt-2 text-xs italic text-gray-300">
{marker?.properties?.location}
</p>

<div className="mx-4 w-full max-w-full rounded border border-gray-700 bg-[rgba(51,94,111,0.50)] px-0 text-white backdrop-blur-lg sm:mx-0 sm:max-w-[230px] sm:-translate-x-6">
{markers?.map((marker) => {
if (!marker || !marker?.id) return null;
return (
<div
className="border-b border-b-gray-700 p-6 last-of-type:border-b-0 sm:py-4"
key={marker.id}
onMouseMove={(e) => e.stopPropagation()}
>
<div className="mb-2 flex items-center space-x-4">
<CategoryIcon
slug={marker?.properties?.category}
className="h-10 w-10 shrink-0 fill-transparent stroke-teal-300 opacity-80"
/>
<p className="font-open-sans text-xs">{marker?.properties?.categoryName}</p>
<Button
variant="secondary"
className="h-8 w-full rounded-3xl bg-teal-500 py-2 text-xs text-white hover:bg-teal-500/50"
onClick={() => !!marker.id && handleClick(marker.id)}
disabled={!marker?.properties?.active}
className="absolute right-0 top-0 sm:hidden"
size="icon"
variant="icon"
onClick={handleClose}
>
Discover story
<XIcon className="h-5 w-5" />
</Button>
</div>
);
})}
</div>
<p className="font-notes text-sm">{marker?.properties?.title}</p>
<p className="font-open-sans mb-4 mt-2 text-xs italic text-gray-300">
{marker?.properties?.location}
</p>

<Button
variant="secondary"
className="h-8 w-full rounded-3xl bg-teal-500 py-2 text-xs text-white hover:bg-teal-500/50"
onClick={() => !!marker.id && handleClick(marker.id)}
disabled={!marker?.properties?.active}
>
Discover story
</Button>
</div>
);
})}
</div>
</div>
);

return isMobile ? (
<div className="pointer-events-none absolute left-0 top-0 flex h-screen w-screen items-center justify-center">
<MARKER />
</div>
) : (
<RMarker anchor="left" latitude={coordinates[1]} longitude={coordinates[0]}>
<MARKER />
</RMarker>
);
};
Expand Down
41 changes: 17 additions & 24 deletions client/src/components/map/legend/index.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,44 @@
'use client';

import React, { useMemo, Children, isValidElement } from 'react';

import { ChevronDown } from 'lucide-react';

import { cn } from '@/lib/classnames';

import { useIsMobile } from '@/hooks/screen-size';

import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';

import SortableList from './sortable/list';
import { LegendProps } from './types';

export const Legend: React.FC<LegendProps> = ({
children,
className = '',
sortable,
onChangeOrder,
}: LegendProps) => {
export const Legend: React.FC<LegendProps> = ({ children, className = '' }: LegendProps) => {
const isChildren = useMemo(() => {
return !!Children.count(Children.toArray(children).filter((c) => isValidElement(c)));
}, [children]);

const isMobile = useIsMobile();

return (
isChildren && (
<div
className={cn({
'bg-card-map relative flex-col space-y-2 rounded-lg p-2 px-3 backdrop-blur-sm': true,
'bg-card-map relative flex-col space-y-2 rounded-lg p-4 backdrop-blur sm:backdrop-blur-sm':
true,
hidden: !isChildren,
[className]: !!className,
})}
>
{isChildren && (
<div className="flex flex-col gap-4 overflow-x-hidden">
{!!sortable?.enabled && !!onChangeOrder ? (
<SortableList sortable={sortable} onChangeOrder={onChangeOrder}>
<div className="flex flex-col overflow-x-hidden">
<Collapsible defaultOpen={!isMobile}>
<CollapsibleTrigger className="font-open-sans group flex w-full items-center justify-between gap-2 text-sm font-semibold text-white">
Legend <ChevronDown className="w-5 group-data-[state=closed]:rotate-180" />
</CollapsibleTrigger>
<CollapsibleContent className="flex flex-col overflow-x-hidden">
{children}
</SortableList>
) : Array.isArray(children) && children.length > 1 ? (
<Collapsible defaultOpen className="space-y-2">
<CollapsibleTrigger className="font-open-sans group flex w-full items-center justify-between gap-2 text-sm font-semibold text-white">
Legends <ChevronDown className="w-5 group-data-[state=closed]:rotate-180" />
</CollapsibleTrigger>
<CollapsibleContent className="flex flex-col gap-2 overflow-x-hidden">
{children}
</CollapsibleContent>
</Collapsible>
) : (
children
)}
</CollapsibleContent>
</Collapsible>
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const LegendTypeBasic: React.FC<LegendTypeProps> = ({
}) => {
return (
<div
className={cn({
className={cn('mt-3', {
[className]: !!className,
})}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const LegendTypeChoropleth: React.FC<LegendTypeProps> = ({
}) => {
return (
<div
className={cn('font-open', {
className={cn('font-open mt-3', {
[className]: !!className,
})}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const LegendTypeGradient: React.FC<LegendTypeProps> = ({
}) => {
return (
<div
className={cn({
className={cn('mt-3', {
[className || '']: !!className,
})}
>
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/map/legend/item-types/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type LegendHeaderProps = {

const LegendHeader = ({ title, info }: LegendHeaderProps) => {
return (
<div className="font-open-sans mb-4 flex items-center justify-between gap-2 text-white">
<div className="font-open-sans mb-2 flex items-center justify-between gap-2 text-white">
{!!title && <div className="text-sm font-semibold text-white">{title}</div>}
{!!info && (
<Popover>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const LegendTypeMatrix: React.FC<LegendTypeProps & LegendMatrixIntersecti
intersections = [],
}) => {
return (
<div className="flex items-center space-x-14">
<div className="mt-3 flex items-center space-x-14">
<div className="relative ml-10 w-16 flex-shrink-0 py-12">
<p className="font-heading absolute left-1/2 top-1 -translate-x-1/2 transform text-xs font-medium text-white">
Always
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const LegendTypeSwitch = ({
);

return (
<div style={props.style} className="flex items-center justify-between gap-2">
<div style={props.style} className="mt-3 flex items-center justify-between gap-2">
<div className="flex">
<label
className="font-open-sans cursor-pointer text-sm text-white"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export const LegendTypeTimeline: React.FC<LegendTypeTimelineProps> = ({
}

return (
<div style={props?.style} className="z-30 mt-4">
<div style={props?.style} className="z-30 mt-3">
<LegendHeader title={title} info={info} />
<div className="flex items-center gap-8">
<Button
Expand Down
1 change: 0 additions & 1 deletion client/src/components/map/legend/item/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/component
import LegendItemToolbar from './toolbar';

export const LegendItem: React.FC<PropsWithChildren & LegendItemProps> = ({
id,
children,
name,
className = '',
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const buttonVariants = cva(
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
secondary: 'bg-secondary text-gray-200 hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
icon: '',
Expand Down
Loading
Loading