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

rollback the broken dependancies #224

Merged
merged 4 commits into from
Aug 14, 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
138 changes: 95 additions & 43 deletions components/MapMarker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { FC, useEffect, useState, useRef } from 'react'
import { FC, useEffect, useState, useRef, useContext } from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { useSelector } from 'react-redux'
import map from 'lodash/map'
import findIndex from 'lodash/findIndex'
import filter from 'lodash/filter'
import sortedUniq from 'lodash/sortedUniq'
import { highlightSelector } from '../selectors/view'
import Category, { CategoryToNameMapper } from '../dtos/Categories'
import { DivIcon, divIcon, Icon, IconOptions, Marker, Point } from 'leaflet'
Expand All @@ -20,7 +24,8 @@ import { HighlightIDOrNull } from '../slices/viewSlice'
import { VIEW } from '../consts/view'
import MapMarkerBalloon from './MapMarkerBalloon'
import useTagMarkerColor from '../hooks/useTagMarkerColor'

import MapMarkerIcon from './MapMarkerIcon'
import MapMarkerTagsIconsContext from '../contexts'

const balloonIcons: Record<Category, Icon<IconOptions> | DivIcon | null> = {
[Category.EVENT]: null,
Expand All @@ -29,11 +34,29 @@ const balloonIcons: Record<Category, Icon<IconOptions> | DivIcon | null> = {
[Category.UNKNOWN]: null,
}

const getDefaultBalloonIcon = (typeId: Category): Icon<IconOptions> | DivIcon | null=> {
const getDefaultBalloonIcon = (
typeId: Category,
iconName: string | null,
): Icon<IconOptions> | DivIcon | null => {
const typeName = CategoryToNameMapper[typeId]

if (iconName !== null) {
return divIcon({
iconSize: new Point(50, 50),
html: renderToStaticMarkup(
<svg height="40" width="40">
<image href={`/projects/main/pins/balloon_${typeName}.svg`} height={50} width={50} />
<foreignObject x={20} y={13} width="12" height="12">
<MapMarkerIcon icon={iconName} style={{ color: 'white' }} />
</foreignObject>
</svg>,
),
})
}

const icon = balloonIcons[typeId]

if (!icon) {
const typeName = CategoryToNameMapper[typeId]
balloonIcons[typeId] = new Icon({
iconUrl: `/projects/main/pins/balloon_${typeName}.svg`,
iconSize: new Point(50, 50),
Expand All @@ -45,56 +68,77 @@ const getDefaultBalloonIcon = (typeId: Category): Icon<IconOptions> | DivIcon |
return icon
}

const getCustomColoredBalloonIcon = (color: string): DivIcon => (
const getCustomColoredBalloonIcon = (color: string, iconName: string | null): DivIcon =>
divIcon({
iconSize: new Point(50, 50),
html: renderToStaticMarkup(
<MapMarkerBalloon color={color} />
)
html: renderToStaticMarkup(<MapMarkerBalloon color={color} icon={iconName} />),
})
)

// memoize icons to prevent object creations
export const getBalloonIcon = (typeId: Category, color=''): Icon<IconOptions> | DivIcon | null => {
export const getBalloonIcon = (
typeId: Category,
color = '',
iconName: string | null
): Icon<IconOptions> | DivIcon | null => {
if (color) {
return getCustomColoredBalloonIcon(color)
return getCustomColoredBalloonIcon(color, iconName)
}

return getDefaultBalloonIcon(typeId)
return getDefaultBalloonIcon(typeId, iconName)
}


// this function is inspired by the original implementation of KVM
// however a better idea would be to use CircleMarker from leaflet
// https://leafletjs.com/reference-1.7.1.html#circlemarker
const getCircleIcon = (typeId: Category, color: string): Icon<IconOptions> | DivIcon => {
const getCircleIcon = (
typeId: Category,
color: string,
iconName: string | null,
): Icon<IconOptions> | DivIcon => {
const categoryName = CategoryToNameMapper[typeId]

const icon = divIcon({
return divIcon({
iconSize: new Point(20, 20),
html: renderToStaticMarkup(
<svg height='20' width='20'>
<circle
className={`${categoryName}-circle-marker`}
cx='10'
cy='10'
r='9'
stroke='white'
strokeWidth='0.7'
style={{
fill: color,
}}
opacity={1}
/>
</svg>
<svg height="20" width="20">
<circle
className={`${categoryName}-circle-marker`}
cx="10"
cy="10"
r="9"
stroke="white"
strokeWidth="0.7"
style={{
fill: color,
}}
opacity={1}
/>

{iconName !== null && (
<foreignObject width="20" height="20">
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
width: '100%',
}}
>
<MapMarkerIcon icon={iconName} style={{ color: 'white' }} />
</div>
</foreignObject>
)}
</svg>,
),
})

return icon
}


const getIcon = (entity: SearchResult, possibleColor: string): Icon<IconOptions> | DivIcon | null => {
const getIcon = (
entity: SearchResult,
possibleColor: string,
iconName: string | null,
): Icon<IconOptions> | DivIcon | null => {
const { categories: types } = entity

// the reason we define types as array is because backend sends us an array of categories
Expand All @@ -105,14 +149,13 @@ const getIcon = (entity: SearchResult, possibleColor: string): Icon<IconOptions>
if (hasRatings) {
const rating: number = (entity as SearchEntry).ratings.total
if (rating <= 0) {
return getCircleIcon(typeId, possibleColor)
return getCircleIcon(typeId, possibleColor, iconName)
}
}

return getBalloonIcon(typeId, possibleColor)
return getBalloonIcon(typeId, possibleColor, iconName)
}


const onClickOnPin = (router: NextRouter, searchResult: SearchResult) => () => {
const { query } = router
const rootSlugAction = getRootSlugActionFromQuery(query)
Expand Down Expand Up @@ -162,9 +205,8 @@ const onClickOnPin = (router: NextRouter, searchResult: SearchResult) => () => {
)
}


export interface MapMarkerProps {
searchResult: SearchResult,
searchResult: SearchResult
}

const MapMarker: FC<MapMarkerProps> = (props) => {
Expand All @@ -173,9 +215,21 @@ const MapMarker: FC<MapMarkerProps> = (props) => {
const { id: searchResultId } = searchResult

const router = useRouter()
const highlightId: HighlightIDOrNull = useSelector(
(state: RootState) => (highlightSelector(state)),

const tagsIcons = useContext(MapMarkerTagsIconsContext)

let iconName: string | null = null
const prioritySortedIndices = sortedUniq(
filter(
map(tags, (tag) => findIndex(tagsIcons, (tagIcon) => tagIcon.tag === tag)),
(idx) => idx !== -1,
),
)
if (prioritySortedIndices.length !== 0) {
iconName = tagsIcons[prioritySortedIndices[0]].icon
}

const highlightId: HighlightIDOrNull = useSelector((state: RootState) => highlightSelector(state))
const isSelected = searchResultId === highlightId

const [opacity, setOpacity] = useState<number>(VIEW.highlight.dark)
Expand Down Expand Up @@ -203,14 +257,13 @@ const MapMarker: FC<MapMarkerProps> = (props) => {
}
}, [highlightId, isSelected])


const possibleEvent = searchResult as CompactEvent

return (
<LeafletMarker
ref={ref}
position={[searchResult.lat, searchResult.lng]}
icon={getIcon(searchResult, possibleColor) || undefined}
icon={getIcon(searchResult, possibleColor, iconName) || undefined}
eventHandlers={{
click: onClickOnPin(router, searchResult),
}}
Expand All @@ -229,5 +282,4 @@ const MapMarker: FC<MapMarkerProps> = (props) => {
)
}


export default MapMarker
77 changes: 48 additions & 29 deletions components/MapMarkerBalloon.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FC } from 'react'
import { parseToHsla, toHex, hsla } from 'color2k'
import MapMarkerIcon from './MapMarkerIcon'


interface GradientProp {
Expand Down Expand Up @@ -41,43 +42,61 @@ const gradientProps: GradientProp[] = [

interface MapMarkerBalloonProps {
color: string
icon: string | null
}

const MapMarkerBalloon: FC<MapMarkerBalloonProps> = (props) => {
const { color } = props
const { color, icon } = props


const [h, s, _l, a] = parseToHsla(color)
const mapMarkerBalloonId = `map-marker-balloon-${color}`

return (
<>
<svg
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
viewBox='0 0 141.7 141.7'
>
<linearGradient id={mapMarkerBalloonId} gradientUnits="userSpaceOnUse" x1="75.26" y1="27.88" x2="75.26" y2="112.21">
{
gradientProps.map(({ lightness: l, offset }) => {
const stopColor = toHex(hsla(h, s, l, a))
return (
<stop key={`stop-color-${color}-${l}`} stopColor={stopColor} offset={offset} />
)
})
}
</linearGradient>
<path
d='M103.6,55.6c0-15.3-12.7-27.7-28.3-27.7S46.9,40.3,46.9,55.6c0,4.4,1,8.5,2.9,12.2
c-0.1,1.3,25.8,44.5,25.8,44.5S96.1,76.5,100.1,69C102.3,65,103.6,60.4,103.6,55.6z'
style={{
fill: `url(#${mapMarkerBalloonId})`,
}}
/>
</svg>
)
</>
<svg
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
viewBox='0 0 141.7 141.7'
width={50}
height={50}
>
<linearGradient id={mapMarkerBalloonId} gradientUnits="userSpaceOnUse" x1="75.26" y1="27.88" x2="75.26" y2="112.21">
{
gradientProps.map(({ lightness: l, offset }) => {
const stopColor = toHex(hsla(h, s, l, a))
return (
<stop key={`stop-color-${color}-${l}`} stopColor={stopColor} offset={offset} />
)
})
}
</linearGradient>
<path
d='M103.6,55.6c0-15.3-12.7-27.7-28.3-27.7S46.9,40.3,46.9,55.6c0,4.4,1,8.5,2.9,12.2
c-0.1,1.3,25.8,44.5,25.8,44.5S96.1,76.5,100.1,69C102.3,65,103.6,60.4,103.6,55.6z'
style={{
fill: `url(#${mapMarkerBalloonId})`,
}}
/>

{ icon !== null && (
<foreignObject x={60} y={40} width="30" height="30">
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
width: '100%',
}}
>
<MapMarkerIcon icon={icon} style={{ color: 'white', width: '100%', height: '100%' }} />
</div>
</foreignObject>
)
}
</svg>
)
}

Expand Down
36 changes: 36 additions & 0 deletions components/MapMarkerIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { FC } from 'react'

import { FaFutbol } from '@react-icons/all-files/fa/FaFutbol'
import { FaLock } from '@react-icons/all-files/fa/FaLock'
import { IconBaseProps, IconType } from 'react-icons'


interface MapMarkerIconProps extends IconBaseProps {
icon: string
}

const MapMarkerIcon: FC<MapMarkerIconProps> = (props) => {
const { icon, ...iconProps } = props

let Icon = null
switch (icon) {
case 'FaFutbol':
Icon = FaFutbol
break
case 'FaLock':
Icon = FaLock
break
default:
break
}

if (Icon === null) {
return null
}

return (
<Icon {...iconProps} />
)
}

export default MapMarkerIcon
Loading
Loading