@abcagency/hc-ui-components 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/globals.css +3 -0
  2. package/dist/index.js +4644 -0
  3. package/dist/output.css +784 -0
  4. package/dist/services/globals.css +3 -0
  5. package/dist/services/listingService.js +606 -0
  6. package/package.json +38 -0
  7. package/postcss.config.js +15 -0
  8. package/rollup.config.js +67 -0
  9. package/src/apis/hcApi.js +68 -0
  10. package/src/clientToken.js +9 -0
  11. package/src/components/layout/footer.js +34 -0
  12. package/src/components/layout/header.js +23 -0
  13. package/src/components/layout/layout.js +36 -0
  14. package/src/components/modules/accordions/MapAccordionItem.js +69 -0
  15. package/src/components/modules/accordions/default.js +173 -0
  16. package/src/components/modules/accordions/filterItem.js +53 -0
  17. package/src/components/modules/accordions/filters.js +44 -0
  18. package/src/components/modules/animations/slidein.js +41 -0
  19. package/src/components/modules/buttons/button-group-apply.js +75 -0
  20. package/src/components/modules/buttons/commute-pill.js +21 -0
  21. package/src/components/modules/buttons/default.js +196 -0
  22. package/src/components/modules/buttons/items-pill.js +31 -0
  23. package/src/components/modules/buttons/pill-wrapper.js +26 -0
  24. package/src/components/modules/buttons/show-all-button.js +20 -0
  25. package/src/components/modules/cards/default.js +168 -0
  26. package/src/components/modules/cards/filter.js +55 -0
  27. package/src/components/modules/dialogs/apply-dialog.js +47 -0
  28. package/src/components/modules/filter/commute.js +149 -0
  29. package/src/components/modules/filter/index.js +86 -0
  30. package/src/components/modules/filter/item.js +77 -0
  31. package/src/components/modules/filter/location.js +69 -0
  32. package/src/components/modules/filter/points-of-interest.js +43 -0
  33. package/src/components/modules/filter/radio-item.js +51 -0
  34. package/src/components/modules/filter/search.js +89 -0
  35. package/src/components/modules/filter/search.js.rej +9 -0
  36. package/src/components/modules/filter/sort.js +83 -0
  37. package/src/components/modules/form.js +362 -0
  38. package/src/components/modules/grid.js +75 -0
  39. package/src/components/modules/icon.js +33 -0
  40. package/src/components/modules/jobListing/listing-details.js +87 -0
  41. package/src/components/modules/jumbotron.js +81 -0
  42. package/src/components/modules/maps/info-window-card.js +17 -0
  43. package/src/components/modules/maps/info-window-content.js +60 -0
  44. package/src/components/modules/maps/list/field-mapper.js +113 -0
  45. package/src/components/modules/maps/list/header-item.js +90 -0
  46. package/src/components/modules/maps/list/header.js +46 -0
  47. package/src/components/modules/maps/list/index.js +104 -0
  48. package/src/components/modules/maps/list/item-expand-card/index.js +21 -0
  49. package/src/components/modules/maps/list/item-expand-card/recruiter-contact-nav.js +48 -0
  50. package/src/components/modules/maps/list/item-expand-card/recruiter-details.js +67 -0
  51. package/src/components/modules/maps/list/item-expand-card/recruiter-headshot.js +22 -0
  52. package/src/components/modules/maps/list/list-item/index.js +133 -0
  53. package/src/components/modules/maps/map-list.js +73 -0
  54. package/src/components/modules/maps/map-marker.js +84 -0
  55. package/src/components/modules/maps/map.js +218 -0
  56. package/src/components/modules/maps/place-marker.js +41 -0
  57. package/src/components/modules/maps/tabs.js +79 -0
  58. package/src/components/modules/navigation/nav-link.js +65 -0
  59. package/src/components/modules/navigation/navbar.js +109 -0
  60. package/src/components/modules/navigation/skip-link.js +21 -0
  61. package/src/components/modules/navigation/social.js +29 -0
  62. package/src/components/modules/sections/default.js +59 -0
  63. package/src/components/modules/sections/sectionContext.js +4 -0
  64. package/src/components/modules/video-player.js +126 -0
  65. package/src/constants/placeTypes.js +8 -0
  66. package/src/contexts/mapContext.js +116 -0
  67. package/src/contexts/mapListContext.js +212 -0
  68. package/src/contexts/placesContext.js +98 -0
  69. package/src/hooks/useClickOutside.js +16 -0
  70. package/src/hooks/useEventListener.js +25 -0
  71. package/src/hooks/useEventTracker.js +19 -0
  72. package/src/hooks/useList.js +102 -0
  73. package/src/hooks/useRefScrollProgress.js +24 -0
  74. package/src/hooks/useScript.js +63 -0
  75. package/src/hooks/useScrollDirection.js +39 -0
  76. package/src/hooks/useSectionTracker.js +95 -0
  77. package/src/hooks/useUserAgent.js +43 -0
  78. package/src/hooks/useWindowSize.js +28 -0
  79. package/src/index.css +25 -0
  80. package/src/index.js +116 -0
  81. package/src/services/configService.js +16 -0
  82. package/src/services/googlePlacesNearbyService.js +33 -0
  83. package/src/services/listingAggregatorService.js +42 -0
  84. package/src/services/listingEntityService.js +14 -0
  85. package/src/services/listingService.js +28 -0
  86. package/src/services/recruiterService.js +17 -0
  87. package/src/styles/fonts.js +0 -0
  88. package/src/styles/globals.css +25 -0
  89. package/src/tailwind/preset.default.js +15 -0
  90. package/src/tailwind/tailwind.config.js +126 -0
  91. package/src/util/arrayUtil.js +3 -0
  92. package/src/util/fieldMapper.js +19 -0
  93. package/src/util/filterUtil.js +195 -0
  94. package/src/util/loading.js +17 -0
  95. package/src/util/localStorageUtil.js +27 -0
  96. package/src/util/mapIconUtil.js +179 -0
  97. package/src/util/mapUtil.js +91 -0
  98. package/src/util/page-head.js +62 -0
  99. package/src/util/provider.js +12 -0
  100. package/src/util/sortUtil.js +33 -0
  101. package/src/util/stringUtils.js +6 -0
  102. package/src/util/urlFilterUtil.js +91 -0
@@ -0,0 +1,133 @@
1
+ import React, { forwardRef } from 'react';
2
+ import Grid, { GridItem } from '~/components/modules/grid';
3
+ import Icon from '~/components/modules/icon';
4
+ import FieldMapper from '~/components/modules/maps/list/field-mapper';
5
+ import { useMapList } from '~/contexts/mapListContext';
6
+
7
+
8
+ const ListItem = forwardRef(
9
+ (
10
+ {
11
+ isActive,
12
+ bodyClassName,
13
+ className,
14
+ item,
15
+ fieldsShown,
16
+ specialFeatures,
17
+ onItemSelected,
18
+ showMap,
19
+ setMobileTab,
20
+ favorites,
21
+ setFavorites,
22
+ ...props
23
+ },
24
+ ref,
25
+ ) => {
26
+ const {siteConfig } = useMapList();
27
+ const mapPinColor = siteConfig.colors.primary.replace("#", "");
28
+
29
+ const handleClick = () => {
30
+ if (onItemSelected) {
31
+ onItemSelected(item);
32
+ }
33
+ };
34
+
35
+ let isFavorite = favorites.includes(item.id);
36
+
37
+ const handleFavouriteClick = event => {
38
+ event.stopPropagation();
39
+ let updatedFavorites;
40
+ if (isFavorite) {
41
+ updatedFavorites = favorites.filter(fav => fav !== item.id);
42
+ } else {
43
+ updatedFavorites = [...favorites, item.id];
44
+ }
45
+ isFavorite = !isFavorite;
46
+ setFavorites(updatedFavorites);
47
+
48
+ };
49
+
50
+ return (
51
+ <button
52
+ ref={ref}
53
+ onClick={handleClick}
54
+ className={`
55
+ group relative flex md:flex-col w-full md:pl-4 text-left bg-clip-border border border-transparent break-words overflow-hidden cursor-pointer transition-colors hover:bg-uiAccent/5 focus:hover:bg-uiAccent/5
56
+ ${isActive ? "bg-uiAccent/5 border-secondary border" : "text-uiText bg-white"}
57
+ ${className ?? ""}
58
+ `}
59
+ {...props}
60
+ >
61
+ <Grid
62
+ columns="grid-flow-col auto-cols-fr"
63
+ gap="gap-2"
64
+ isAnimated={false}
65
+ className={`
66
+ block md:grid flex-auto p-2 ps-4 w-full grow leading-tight text-sm md:text-xs lg:text-sm
67
+ ${bodyClassName ?? ""}
68
+ `}
69
+ >
70
+ <Grid.Item className="hidden md:block md:absolute md:left-1.5 top-1.5">
71
+ <span className="sr-only">Expand row</span>
72
+ <Icon
73
+ icon="fluent-emoji-high-contrast:plus"
74
+ size="w-2.5 h-2.5"
75
+ className={`
76
+ opacity-0 text-uiText/60 transition group-hover:opacity-100 group-active:opacity-100
77
+ ${isActive ? "opacity-100 rotate-45" : ""}
78
+ `}
79
+ />
80
+
81
+ </Grid.Item>
82
+
83
+ <FieldMapper
84
+ item={item}
85
+ fieldsShown={fieldsShown}
86
+ specialFeatures={specialFeatures}
87
+ isFavorite={isFavorite}
88
+ handleFavouriteClick={handleFavouriteClick}
89
+ />
90
+ <Grid.Item
91
+ key={"favorites"}
92
+ className="hidden md:block"
93
+ >
94
+
95
+ <Icon
96
+ icon={isFavorite ? "mdi:heart" : "mdi:heart-outline"}
97
+ size="w-3.5 h-3.5"
98
+ iconClasses={isFavorite ? "text-primary" : ""}
99
+ title={!isFavorite ? 'Add job to favorites': 'Remove job from favorites'}
100
+ className={`
101
+ pr-2 transition-opacity duration-300 cursor-pointer opacity-100"
102
+ `}
103
+ onClick={e => handleFavouriteClick(e)}
104
+ />
105
+ </Grid.Item>
106
+ </Grid>
107
+
108
+ {showMap && (
109
+ <div onClick={() => {setMobileTab("mapTab"); handleClick();}} className="md:hidden w-2/5 sm:w-1/3 p-1.5 my-1 bg-uiAccent/5 border border-uiAccent/10 rounded-sm">
110
+ <img
111
+ src={`https://maps.googleapis.com/maps/api/staticmap?scale=2&center=${item.mapDetails?.latitude},${item.mapDetails?.longitude}&zoom=10&size=240x180&maptype=roadmap&markers=color:0x${mapPinColor}%7Clabel:•%7C${item.mapDetails?.latitude},${item.mapDetails?.longitude}&key=ToDo`}
112
+ alt={`Map of location for ${item.fields.position}`}
113
+ className="w-full h-full object-cover"
114
+ />
115
+ </div>
116
+ )}
117
+ </button>
118
+ );
119
+ },
120
+ );
121
+
122
+ ListItem.displayName = "ListItem";
123
+
124
+ export default React.memo(ListItem, (prevProps, nextProps) => {
125
+ return (
126
+ (prevProps.isActive === nextProps.isActive ||
127
+ prevProps.isActive !== nextProps.isActive) &&
128
+ prevProps.bodyClassName === nextProps.bodyClassName &&
129
+ prevProps.className === nextProps.className &&
130
+ prevProps.fieldsShown === nextProps.fieldsShown &&
131
+ prevProps.item.id === nextProps.item.id
132
+ );
133
+ });
@@ -0,0 +1,73 @@
1
+ import List from "~/components/modules/maps/list";
2
+ import Map from "~/components/modules/maps/map";
3
+ import Tabs from "~/components/modules/maps/tabs";
4
+ import Filter from "~/components/modules/filter";
5
+ import React from 'react'
6
+
7
+ const MapList = ({
8
+ loading = false,
9
+ mapDetails,
10
+ markerConfigs,
11
+ itemExpandedContent,
12
+ fieldsShown,
13
+ specialFeatures,
14
+ fieldNames,
15
+ showMap,
16
+ placeMappings
17
+ }) => {
18
+
19
+ const listProps = {
20
+ fieldsShown: fieldsShown,
21
+ fieldNames: fieldNames,
22
+ itemExpandedContent: itemExpandedContent,
23
+ loading: loading,
24
+ showMap: showMap,
25
+ specialFeatures: specialFeatures
26
+ };
27
+
28
+ const mapProps = {
29
+ mapDetails: mapDetails,
30
+ markerConfigs: markerConfigs,
31
+ placeMappings: placeMappings,
32
+ clusterGridSize: 60,
33
+ showMap: showMap
34
+ };
35
+
36
+ // useEffect(() => {
37
+ // if (selectedItem !== null) {
38
+ // trackEvent("Map", "View Location", selectedItem[titlePropName]);
39
+ // }
40
+ // // eslint-disable-next-line react-hooks/exhaustive-deps
41
+ // }, []);
42
+ const map = <Map {...mapProps} />;
43
+ return (
44
+ <>
45
+ <div
46
+ className={`
47
+ ${showMap == false ? "md:grid-rows-[100vh]" : "md:grid-rows-[50vh_50vh]"}
48
+ md:grid md:pt-4 overflow-hidden relative
49
+ `}
50
+ >
51
+ <div className="">
52
+ <List {...listProps} />
53
+ </div>
54
+ <div>{showMap && map}</div>
55
+ </div>
56
+ <div className="md:hidden">
57
+ <Tabs
58
+ showMap={showMap}
59
+ list={<List {...listProps} />}
60
+ map={!loading && showMap && map}
61
+ filter={
62
+ <Filter
63
+ showMap={showMap}
64
+ className="md:hidden"
65
+ />
66
+ }
67
+ />
68
+ </div>
69
+ </>
70
+ );
71
+ };
72
+
73
+ export default MapList;
@@ -0,0 +1,84 @@
1
+ import React, { useEffect, useRef } from "react";
2
+ import { MarkerF, InfoWindow } from "@react-google-maps/api";
3
+ import InfoWindowCard from "~/components/modules/maps/info-window-card";
4
+ import { useMap } from "~/contexts/mapContext";
5
+ import { useMapList } from "~/contexts/mapListContext";
6
+ import InfoWindowContent from "./info-window-content";
7
+
8
+ const MapMarker = ({
9
+ item,
10
+ markerRefs,
11
+ infoWindowClasses,
12
+ markerClickHandler,
13
+ clusterer,
14
+ markerIconSelected,
15
+ markerIcon,
16
+ selectedLocation,
17
+ setMapInteracted
18
+ }) => {
19
+ const { setLocation, commuteLocation } = useMap();
20
+ const isSelected = item.id === selectedLocation?.id;
21
+ const markerRef = useRef(null);
22
+
23
+ const {
24
+ handleFilterListingsByLocation
25
+ } = useMapList();
26
+
27
+ useEffect(() => {
28
+ markerRefs.current[item.id] = markerRef.current;
29
+ }, []);
30
+
31
+ const onMarkerClick = () => {
32
+ setMapInteracted(true);
33
+ markerClickHandler(item);
34
+ };
35
+
36
+ const getInfoWindow = () => {
37
+ if (!isSelected || !item || !item.id || !selectedLocation) {
38
+ return;
39
+ }
40
+ //Direct dom removal of empty info windows
41
+ document.querySelectorAll("div.gm-style-iw.gm-style-iw-c").forEach(x => { if (!x.innerHTML.includes(item.id)) x.parentElement.remove(); });
42
+ return (
43
+ <InfoWindow
44
+ position={{ lat: item.latitude, lng: item.longitude }}
45
+ anchor={markerRefs.current[item.id]}
46
+ onCloseClick={() => {
47
+ setMapInteracted(true);
48
+ setLocation(null);
49
+ }}
50
+ options={{ maxWidth: 400 }}
51
+ >
52
+ <InfoWindowCard
53
+ className={infoWindowClasses}
54
+ id={item.id}
55
+ content={<InfoWindowContent
56
+ item={item}
57
+ commuteLocation={commuteLocation}
58
+ filterListingsByLocation={() =>
59
+ handleFilterListingsByLocation(selectedLocation)
60
+ }
61
+ />}
62
+ />
63
+ </InfoWindow>
64
+ );
65
+ };
66
+ return (
67
+ <MarkerF
68
+ key={item.id}
69
+ position={{ lat: item.latitude, lng: item.longitude }}
70
+ title={item.name}
71
+ options={{
72
+ icon: isSelected ? markerIconSelected : markerIcon
73
+ }}
74
+ zIndex={isSelected ? 9999 : 1}
75
+ onLoad={marker => (markerRef.current = marker)}
76
+ onClick={onMarkerClick}
77
+ clusterer={clusterer}
78
+ >
79
+ {isSelected && getInfoWindow()}
80
+ </MarkerF>
81
+ );
82
+ };
83
+
84
+ export default MapMarker;
@@ -0,0 +1,218 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+ import { GoogleMap, MarkerF, MarkerClustererF, InfoWindowF } from "@react-google-maps/api";
3
+ import MapMarker from "./map-marker";
4
+ import { usePlaces } from "~/contexts/placesContext";
5
+ import { useMap } from "~/contexts/mapContext";
6
+ import { markerIconSelected, markerIcon, pinIcon } from "~/util/mapIconUtil";
7
+ import { clusterOptions } from "~/util/mapUtil";
8
+ import { useMapList } from "~/contexts/mapListContext";
9
+ import InfoWindowContent from "./info-window-content";
10
+ import PlaceMarker from "./place-marker";
11
+ import ShowAllButton from "../buttons/show-all-button";
12
+
13
+ const Map = ({
14
+ markerConfigs,
15
+ infoWindowClasses,
16
+ clusterGridSize = 60
17
+ }) => {
18
+ const {
19
+ location,
20
+ zoom,
21
+ center,
22
+ commuteLocation,
23
+ selectLocationEntity,
24
+ setLocation,
25
+ mapInteracted,
26
+ setMapInteracted
27
+ } = useMap();
28
+
29
+ const {
30
+ mapItems, handleFilterListingsByLocation, filteredListings, setSelectedFilters,
31
+ setQuery
32
+ } = useMapList();
33
+
34
+ const mapRef = useRef();
35
+ const markerRefs = useRef({});
36
+ const mapContainerRef = useRef(null);
37
+ const { poiMarkers, setCurrentCenter, currentCenter, setCurrentZoom, currentZoom, selectedPlaceMarker, setSelectedPlaceMarker, placesWindow, setPlacesWindow } =
38
+ usePlaces();
39
+
40
+ const onIdle = () => {
41
+ if(!currentCenter || !mapRef.current) return;
42
+ const newCenter = mapRef.current.getCenter().toJSON();
43
+ const newZoom = mapRef.current.zoom;
44
+
45
+ setCurrentCenter(newCenter);
46
+
47
+ if (newZoom != currentZoom) {
48
+ setCurrentZoom(newZoom);
49
+ }
50
+ };
51
+
52
+ useEffect(() => {
53
+ if (mapContainerRef.current) {
54
+ const handleScroll = () => {
55
+ setMapInteracted(true);
56
+ };
57
+ var mapContainerRefCurrent = mapContainerRef.current;
58
+ mapContainerRef.current.addEventListener('wheel', handleScroll);
59
+ return () => mapContainerRefCurrent.removeEventListener('wheel', handleScroll);
60
+ }},[mapContainerRef.current]);
61
+
62
+ useEffect(() => {
63
+ if(mapRef.current){
64
+ const mapInstance = mapRef.current;
65
+ const dragStartListener = mapInstance.addListener('dragstart', () => setMapInteracted(true));
66
+ const mouseDownListener = mapInstance.addListener('mousedown', () => setMapInteracted(true));
67
+ return () => {
68
+ google.maps.event.removeListener(dragStartListener);
69
+ google.maps.event.removeListener(mouseDownListener);
70
+ };
71
+ }
72
+ }, [mapRef.current, mapContainerRef.current]);
73
+
74
+ useEffect(() => {
75
+ if ((mapItems && mapItems.length > 0 || poiMarkers.length > 0) && mapRef.current) {
76
+ fitBounds(mapRef.current);
77
+ }
78
+ }, [mapItems, mapRef.current, location]);
79
+
80
+ useEffect(() => {
81
+ if (mapItems && mapItems.length > 0 || poiMarkers.length > 0 && mapRef.current && mapInteracted) {
82
+ fitBounds(mapRef.current, true);
83
+ }
84
+ if(mapRef.current){
85
+ let currZoom = mapRef.current.zoom;
86
+ let setZoomVal = currZoom < 13 ? currZoom : 12;
87
+ mapRef.current.setZoom(setZoomVal);
88
+ }
89
+ }, [filteredListings]);
90
+
91
+ useEffect(() => {
92
+ if (!mapRef.current || !location || mapInteracted) return;
93
+ mapRef.current.panTo(
94
+ new google.maps.LatLng(location.latitude, location.longitude),
95
+ );
96
+ if(mapRef.current){
97
+ let setZoomVal = zoom < 13 ? zoom : 12;
98
+ mapRef.current.setZoom(setZoomVal);
99
+ }
100
+ }, [location, zoom, mapRef.current]);
101
+
102
+ const fitBounds = (map, overload = false) => {
103
+ if((mapInteracted === false || overload) && mapItems != null){
104
+ const bounds = new window.google.maps.LatLngBounds();
105
+ mapItems.forEach(item => {
106
+ bounds.extend(new google.maps.LatLng(item.latitude, item.longitude));
107
+ });
108
+ if(!map) return;
109
+ map.fitBounds(bounds);
110
+ }
111
+ if(mapRef.current.zoom > 17){
112
+ mapRef.current.setZoom(16);
113
+ }
114
+ };
115
+
116
+ const markerClickHandler = mapLocation => {
117
+ setMapInteracted(true);
118
+ selectLocationEntity(mapLocation);
119
+ };
120
+
121
+ const onLoad = map => {
122
+ if (!location || location === null) {
123
+ mapRef.current = map;
124
+ fitBounds(map, true);
125
+ return;
126
+ }
127
+ mapRef.current = map;
128
+
129
+ if(mapInteracted === false) {
130
+ mapRef.current.panTo(
131
+ new google.maps.LatLng(location.latitude, location.longitude),
132
+ );
133
+
134
+ mapRef.current.setZoom(zoom);
135
+ }
136
+ };
137
+
138
+ const pinIconUrl = pinIcon({
139
+ fillColor: markerConfigs.fillColor,
140
+ strokeColor: markerConfigs.strokeColor
141
+ });
142
+
143
+ return (
144
+ <>{zoom && center && <div ref={mapContainerRef} className="h-full relative">
145
+ <GoogleMap
146
+ zoom={zoom}
147
+ onLoad={onLoad}
148
+ onIdle={onIdle}
149
+ center={center}
150
+ mapContainerStyle={{
151
+ width: "100%",
152
+ height: "100%"
153
+ }}
154
+ options={{
155
+ styles: [
156
+ {
157
+ featureType: "poi",
158
+ elementType: "labels",
159
+ stylers: [{ visibility: "off" }]
160
+ }
161
+ ]
162
+ }}
163
+ >
164
+ <MarkerClustererF options={clusterOptions(clusterGridSize, markerConfigs.fillColor)}>
165
+ {clusterer => (
166
+ <>
167
+ {mapItems.map(item => (
168
+ <MapMarker
169
+ key={item.id}
170
+ item={item}
171
+ selectedLocation={location}
172
+ markerRefs={markerRefs}
173
+ infoWindowClasses={infoWindowClasses}
174
+ setSelectedLocation={setLocation}
175
+ markerClickHandler={markerClickHandler}
176
+ clusterer={clusterer}
177
+ markerIcon={markerIcon(markerConfigs)}
178
+ markerIconSelected={markerIconSelected(markerConfigs)}
179
+ setMapInteracted={setMapInteracted}
180
+ />
181
+ ))}
182
+ {poiMarkers && poiMarkers.markers.map((marker, index) => {
183
+ return (
184
+ <PlaceMarker
185
+ key={`marker-${marker.title}-${index}`}
186
+ marker={marker}
187
+ index={index}
188
+ selectedPlaceMarker={selectedPlaceMarker}
189
+ placesWindow={placesWindow}
190
+ setPlacesWindow={setPlacesWindow}
191
+ setSelectedPlaceMarker={setSelectedPlaceMarker}
192
+ />
193
+ );
194
+ })}
195
+ </>
196
+ )}
197
+ </MarkerClustererF>
198
+ </GoogleMap>
199
+ {mapInteracted && markerConfigs && (
200
+ <ShowAllButton
201
+ mapInteracted={mapInteracted}
202
+ markerConfigs={markerConfigs}
203
+ setMapInteracted={setMapInteracted}
204
+ fitBounds={fitBounds}
205
+ mapRef={mapRef}
206
+ pinIconUrl={pinIconUrl}
207
+ setQuery={setQuery}
208
+ listingCount={filteredListings.length}
209
+ setSelectedFilters={setSelectedFilters}
210
+ />
211
+ )}
212
+ </div>
213
+ }
214
+ </>
215
+ );
216
+ };
217
+
218
+ export default Map;
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ import { Marker, InfoWindowF } from '@react-google-maps/api';
3
+
4
+ const PlaceMarker = ({
5
+ marker,
6
+ index,
7
+ selectedPlaceMarker,
8
+ setSelectedPlaceMarker,
9
+ placesWindow,
10
+ setPlacesWindow
11
+ }) => {
12
+ const markerKey = `marker-${marker.title}-${index}`;
13
+
14
+ const placeMarkerClickHandler = () => {
15
+ setSelectedPlaceMarker(markerKey);
16
+ setPlacesWindow(true);
17
+ };
18
+
19
+ return (
20
+ <Marker
21
+ key={markerKey}
22
+ position={marker.position}
23
+ title={marker.title}
24
+ icon={marker.icon}
25
+ options={{ optimized: false }}
26
+ onClick={placeMarkerClickHandler}
27
+ >
28
+ {selectedPlaceMarker === markerKey && placesWindow && marker &&(
29
+ <InfoWindowF
30
+ position={marker.position}
31
+ onCloseClick={() => setPlacesWindow(false)}
32
+ options={{ maxWidth: 400 }}
33
+ >
34
+ <div>{marker.title}</div>
35
+ </InfoWindowF>
36
+ )}
37
+ </Marker>
38
+ );
39
+ };
40
+
41
+ export default PlaceMarker;
@@ -0,0 +1,79 @@
1
+ import * as Tabs from "@radix-ui/react-tabs";
2
+ import Button from "~/components/modules/buttons/default";
3
+ import { useMapList } from "~/contexts/mapListContext";
4
+ import React from 'react'
5
+
6
+ const MapTabs = ({
7
+ map,
8
+ list,
9
+ filter,
10
+ className,
11
+ showMap
12
+ }) => {
13
+ const tabButtonClasses =
14
+ "flex items-center gap-2 rounded-none border-x-0 data-[state=active]:bg-primary data-[state=active]:text-white";
15
+ const { mobileTab, setMobileTab } = useMapList();
16
+ return (
17
+ <div
18
+ className={`
19
+ relative overflow-hidden
20
+ ${className ?? ""}
21
+ `}
22
+ >
23
+ <Tabs.Root
24
+ className="flex flex-col h-screen min-h-screen"
25
+ //defaultValue="listTab"
26
+ value={mobileTab}
27
+ >
28
+ <Tabs.List
29
+ className="w-full shrink-0 flex divide-x divide-primary"
30
+ aria-label="Review positions"
31
+ >
32
+ <Tabs.Trigger value="listTab" asChild onClick={() => setMobileTab("listTab")}>
33
+ <Button.Btn variant="outline" isBlock className={tabButtonClasses}>
34
+ <Button.Body className="justify-center">
35
+ <Button.Icon icon="mdi:view-list" size="w-5 h-5" />
36
+ List
37
+ </Button.Body>
38
+ </Button.Btn>
39
+ </Tabs.Trigger>
40
+ {showMap && (
41
+ <Tabs.Trigger value="mapTab" asChild onClick={() => setMobileTab("mapTab")}>
42
+ <Button.Btn
43
+ variant="outline"
44
+ isBlock
45
+ className={tabButtonClasses}
46
+ >
47
+ <Button.Body className="justify-center">
48
+ <Button.Icon icon="mdi:map" size="w-5 h-5" />
49
+ Map
50
+ </Button.Body>
51
+ </Button.Btn>
52
+ </Tabs.Trigger>
53
+ )}
54
+ <Tabs.Trigger value="filterTab" asChild onClick={() => setMobileTab("filterTab")}>
55
+ <Button.Btn variant="outline" isBlock className={tabButtonClasses}>
56
+ <Button.Body className="justify-center">
57
+ <Button.Icon icon="fluent:search-12-filled" size="w-5 h-5" />
58
+ Filter
59
+ </Button.Body>
60
+ </Button.Btn>
61
+ </Tabs.Trigger>
62
+ </Tabs.List>
63
+ <Tabs.Content className="grow bg-white outline-none" value="listTab">
64
+ {list}
65
+ </Tabs.Content>
66
+ {showMap && (
67
+ <Tabs.Content className="grow bg-white outline-none" value="mapTab">
68
+ {map}
69
+ </Tabs.Content>
70
+ )}
71
+ <Tabs.Content className="grow bg-white outline-none p-2" value="filterTab">
72
+ {filter}
73
+ </Tabs.Content>
74
+ </Tabs.Root>
75
+ </div>
76
+ );
77
+ };
78
+
79
+ export default MapTabs;
@@ -0,0 +1,65 @@
1
+ import { forwardRef } from 'react';
2
+ import { Link, useLocation } from 'react-router-dom';
3
+ import React from 'react'
4
+
5
+ const NavLink = ({ children }) => {
6
+ return (
7
+ <>{children}</>
8
+ );
9
+ };
10
+
11
+ export const Anchor = ({
12
+ to,
13
+ children,
14
+ activeClassName,
15
+ className,
16
+ partiallyActive = false,
17
+ ...rest
18
+ }) => {
19
+ const location = useLocation();
20
+ const isActive = (activeClassName && location.pathname === to) || (location.pathname.startsWith(`${to}/`) && partiallyActive);
21
+
22
+ return (
23
+ <Link
24
+ to={to}
25
+ className={`
26
+ group
27
+ ${className ?? ''}
28
+ ${isActive ? `is-active ${activeClassName}` : ''}
29
+ `}
30
+ {...rest}
31
+ >
32
+ {children}
33
+ </Link>
34
+ );
35
+ };
36
+
37
+ export const ScrollAnchor = forwardRef((props, ref) => {
38
+ const {
39
+ children,
40
+ to,
41
+ className,
42
+ size,
43
+ variant,
44
+ isBlock,
45
+ hasUnderline,
46
+ ...rest
47
+ } = props;
48
+
49
+ return (
50
+ <Link
51
+ ref={ref}
52
+ to={`#${to}`}
53
+ className={buttonClasses(variant, size, isBlock, hasUnderline, className)}
54
+ {...rest}
55
+ >
56
+ {children}
57
+ </Link>
58
+ );
59
+ });
60
+
61
+ NavLink.Anchor = Anchor;
62
+ NavLink.ScrollAnchor = ScrollAnchor;
63
+
64
+ ScrollAnchor.displayName = 'NavLink:ScrollAnchor';
65
+ export default NavLink;