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

feat(map): adds preview component, updates UI #39

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
28 changes: 19 additions & 9 deletions src/app/siteMapPage/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,33 @@ const SiteMap = dynamic(
},
);



/**
* @returns Page for the interactive map
*/
function MapPage() {

// move tour logic here: need to share state between filter
const handleFilter = () => {};

const renderFilterContainer = () => (
<div className="container mb-6 mt-9">
<p className="text-[#808080]">FILTER BY</p>
<div className="inline-flex flex-row items-center gap-x-2.5 w-full overflow-x-scroll no-scrollbar">
{filterButtonContent &&
filterButtonContent.map(text => (
<FilterButton key={text} content={text} onClick={handleFilter} />
))}
</div>
</div>
);

return (
<>
<NavBar />
<div className="flex flex-col items-start pl-6">
<div className="container mb-6 mt-9">
<h1 style={{ fontSize: '2rem', fontWeight: 700 }}>Site Map</h1>
<div className="inline-flex flex-row items-center gap-x-2.5 w-full overflow-x-scroll no-scrollbar">
{filterButtonContent &&
filterButtonContent.map(text => (
<FilterButton key={text} content={text} />
))}
</div>
</div>
{renderFilterContainer()}
<div className=" w-full pr-6 flex h-2/3 mb-8">
<SiteMap />
</div>
Expand Down
21 changes: 15 additions & 6 deletions src/components/userComponents/FilterButton/FilterButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@ interface FilterButtonProps {
onClick?: () => void;
}

function FilterButton({ content, ...children }: FilterButtonProps) {
/**
*
* @param onClick.content
* @param onClick function to handle the functionality we want to tie to our filter buttons
* @param content is the content to be rendered in the button
* @param root0.onClick
* @param onClick.onClick
*/
function FilterButton({ content, onClick, ...children }: FilterButtonProps) {
const [isSelected, setIsSelected] = useState<boolean>(false);

const defaultStyle = 'bg-[#4b711d]/[0.13] text-[#4B711D] border-[#547829]';
const selectedStyle = 'bg-[#547829] text-white border-[#547829]';
const selectedStyle = 'bg-[#7ca24e]/[0.3]';

const buttonClass = `py-2 px-4 rounded-2xl whitespace-nowrap border-[0.8px] border-[#386131] border-solid flex-nowrap text-[#386131] ${
isSelected ? selectedStyle : ''
}`;

const handleClick = () => {
setIsSelected(!isSelected);
Expand All @@ -18,9 +29,7 @@ function FilterButton({ content, ...children }: FilterButtonProps) {
return (
<button
type="button"
className={`${
isSelected ? selectedStyle : defaultStyle
} py-2 px-4 rounded-full whitespace-nowrap border border-solid flex-nowrap`}
className={buttonClass}
onClick={handleClick}
{...children}
>
Expand Down
85 changes: 85 additions & 0 deletions src/components/userComponents/SiteMap/Control.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* eslint-disable jsdoc/require-returns */
/* eslint-disable jsdoc/require-jsdoc */
import L from 'leaflet';
import React, { useState, createRef, useEffect } from 'react';
import { useMap } from 'react-leaflet';

interface ControlProps {
position: L.ControlPosition;
children?: React.ReactNode;
container?: React.HTMLAttributes<HTMLDivElement>;
prepend?: boolean;
}

const POSITION_CLASSES = {
bottomleft: 'leaflet-bottom leaflet-left',
bottomright: 'leaflet-bottom leaflet-right',
topleft: 'leaflet-top leaflet-left',
topright: 'leaflet-top leaflet-right',
};

function Control({
position,
children,
container,
prepend,
}: ControlProps): JSX.Element {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [portalRoot, setPortalRoot] = useState<any>(
document.createElement('div'),
);
const positionClass =
(position && POSITION_CLASSES[position]) || POSITION_CLASSES.topright;
const controlContainerRef = createRef<HTMLDivElement>();
const map = useMap();

/**
* Whenever the control container ref is created,
* Ensure the click / scroll propagation is removed
* This way click/scroll events do not bubble down to the map
*/
useEffect(() => {
if (controlContainerRef.current !== null) {
L.DomEvent.disableClickPropagation(controlContainerRef.current);
L.DomEvent.disableScrollPropagation(controlContainerRef.current);
}
}, [controlContainerRef]);

/**
* Whenever the position is changed, go ahead and get the container of the map and the first
* instance of the position class in that map container
*/
useEffect(() => {
const mapContainer = map.getContainer();
const targetDiv = mapContainer.getElementsByClassName(positionClass);
setPortalRoot(targetDiv[0]);
}, [positionClass, map]);

/**
* Whenever the portal root is complete,
* append or prepend the control container to the portal root
*/
useEffect(() => {
if (portalRoot !== null) {
if (prepend !== undefined && prepend === true) {
portalRoot.prepend(controlContainerRef.current);
} else {
portalRoot.append(controlContainerRef.current);
}
}
}, [portalRoot, prepend, controlContainerRef]);

/**
* Concatenate the props.container className to the class of the control div, per leaflet's built in styles.
* Will need to change styling of component itself based on screen breakpoints
*/
const className = `${container?.className?.concat(' ') || ''}leaflet-control`;

return (
<div {...container} ref={controlContainerRef} className={className}>
{children}
</div>
);
}

export default Control;
102 changes: 102 additions & 0 deletions src/components/userComponents/SiteMap/DisplayPreviewCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React, { useEffect, useState } from 'react';
import { LatLngExpression } from 'leaflet';
import { useMapEvents } from 'react-leaflet';
import Link from 'next/link';
import Image from 'next/image';
import { TourRow } from '../../../types/types';
import { fetchImagesForTour } from '../../../supabase/media/queries';

interface DisplayCardProps {
tour: TourRow;
handleClose: () => void;
handleClick?: () => void;
}

/**
* @param DisplayCardProps.display display to preview
* @param DisplayCardProps.handleClick function to handle actions when clicked
* @param DisplayCardProps.handleClose function to handle closing of preview card
* @param DisplayCardProps.display.display
* @param DisplayCardProps.display.handleClick
* @param DisplayCardProps.display.handleClose
* @param DisplayCardProps.display.tour
* @returns preview card component to display within leaflet map container
*/
function DisplayPreviewCard({
tour,
handleClick,
handleClose,
}: DisplayCardProps) {
const [loading, setLoading] = useState<boolean>(false);
const [previewImage, setPreviewImage] = useState<string>('');
const { id, name, description, coordinates } = tour;

// Map Context
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const map = useMapEvents({
click: e => {
if (!e.latlng.equals(coordinates as LatLngExpression)) {
handleClose();
}
},
});

// fetch image to use for preview
useEffect(() => {
const fetchImages = async () => {
setLoading(true);
const images = await fetchImagesForTour(id);
if (images) {
setPreviewImage(images[0].url);
}
setLoading(false);
};

fetchImages();
}, [tour]);

/** route this to spotlights */

return (
<Link href={`/spotlightPage/${id}`}>
<div className="flex flex-col h-fit left-1/2 translate-x-5">
<div
className="flex flex-row align-center rounded-md shadow-[0_4px_21px_0_rgba(0, 0, 0, 0.25)] overflow-hidden w-80 max-h-28 absolute bottom-0"
onClick={handleClick}
aria-hidden="true"
>
{!loading &&
<Image
className="shrink"
src={previewImage}
alt="Placeholder for this input"
/>
}
<div
className="bg-white rounded-r-md align-center justify-items-center align-middle pt-6 pb-4 px-3.5 w-10/12 overflow-hidden"
onClick={handleClick}
onKeyDown={e => {
if (handleClick && e.key === 'Enter') {
handleClick();
}
}}
role="button"
tabIndex={0}
>
<div className="w-full flex-col block gap-x-5 overflow-hidden text-base">
<h3
className="truncate text-black"
style={{ fontWeight: 'bolder' }}
>
{name}
</h3>
<h4 className="max-w-full line-clamp-2">{description}</h4>
</div>
</div>
</div>
</div>
</Link>
);
}

export default DisplayPreviewCard;
Loading