@opentripplanner/vehicle-rental-overlay 1.4.2 → 2.0.0-alpha.1

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.
@@ -0,0 +1,124 @@
1
+ import BaseMap from "@opentripplanner/base-map";
2
+ import React from "react";
3
+ import { action } from "@storybook/addon-actions";
4
+ import { boolean } from "@storybook/addon-knobs";
5
+
6
+ import { Company, Station } from "@opentripplanner/types";
7
+ import VehicleRentalOverlay from ".";
8
+ import bikeRentalStations from "../__mocks__/bike-rental-stations.json";
9
+ import carRentalStations from "../__mocks__/car-rental-stations.json";
10
+ import eScooterStations from "../__mocks__/e-scooter-rental-stations.json";
11
+
12
+ const center: [number, number] = [45.518092, -122.671202];
13
+ const configCompanies = [
14
+ {
15
+ id: "BIKETOWN",
16
+ label: "Biketown",
17
+ modes: "BICYCLE_RENT"
18
+ },
19
+ {
20
+ id: "CAR2GO",
21
+ label: "car2go",
22
+ modes: "CAR_RENT"
23
+ },
24
+ {
25
+ id: "RAZOR",
26
+ label: "Razor",
27
+ modes: "MICROMOBILITY_RENT"
28
+ },
29
+ {
30
+ id: "SHARED",
31
+ label: "Shared",
32
+ modes: "MICROMOBILITY_RENT"
33
+ }
34
+ ];
35
+ const setLocation = action("setLocation");
36
+
37
+ const INITIAL_ZOOM = 13;
38
+
39
+ type StoryProps = {
40
+ companies: string[];
41
+ getStationName?: (configCompanies: Company[], station: Station) => string;
42
+ refreshVehicles: () => void;
43
+ stations: Station[];
44
+ visible?: boolean;
45
+ };
46
+
47
+ const ZoomControlledMapWithVehicleRentalOverlay = ({
48
+ companies,
49
+ getStationName,
50
+ refreshVehicles,
51
+ stations,
52
+ visible
53
+ }: StoryProps) => (
54
+ // Caution, <BaseMap> must be a direct parent of <VehicleRentalOverlay>.
55
+ // Therefore, do not place <BaseMap> in a decorator at this time.
56
+ <BaseMap center={center} forceMaxHeight zoom={INITIAL_ZOOM}>
57
+ <VehicleRentalOverlay
58
+ configCompanies={configCompanies}
59
+ companies={companies}
60
+ getStationName={getStationName}
61
+ setLocation={setLocation}
62
+ refreshVehicles={refreshVehicles}
63
+ stations={stations}
64
+ visible={visible}
65
+ />
66
+ </BaseMap>
67
+ );
68
+
69
+ function customStationName(_, station) {
70
+ return `🛴 (ID: ${station.id})`;
71
+ }
72
+
73
+ export default {
74
+ title: "VehicleRentalOverlay",
75
+ component: VehicleRentalOverlay
76
+ };
77
+
78
+ export const RentalBicycles = () => (
79
+ <ZoomControlledMapWithVehicleRentalOverlay
80
+ companies={["BIKETOWN"]}
81
+ refreshVehicles={action("refresh bicycles")}
82
+ stations={bikeRentalStations}
83
+ />
84
+ );
85
+
86
+ export const RentalBicyclesVisibilityControlledByKnob = () => {
87
+ const isOverlayVisible = boolean(
88
+ "Toggle visibility of vehicle rental overlay",
89
+ false
90
+ );
91
+ return (
92
+ <ZoomControlledMapWithVehicleRentalOverlay
93
+ companies={["BIKETOWN"]}
94
+ refreshVehicles={action("refresh bicycles")}
95
+ stations={bikeRentalStations}
96
+ visible={isOverlayVisible}
97
+ />
98
+ );
99
+ };
100
+
101
+ export const RentalCars = () => (
102
+ <ZoomControlledMapWithVehicleRentalOverlay
103
+ companies={["CAR2GO"]}
104
+ refreshVehicles={action("refresh cars")}
105
+ stations={carRentalStations}
106
+ />
107
+ );
108
+
109
+ export const RentalEScooters = () => (
110
+ <ZoomControlledMapWithVehicleRentalOverlay
111
+ companies={["SHARED"]}
112
+ refreshVehicles={action("refresh E-scooters")}
113
+ stations={eScooterStations}
114
+ />
115
+ );
116
+
117
+ export const RentalEScootersWithCustomNaming = () => (
118
+ <ZoomControlledMapWithVehicleRentalOverlay
119
+ companies={["SHARED"]}
120
+ getStationName={customStationName}
121
+ refreshVehicles={action("refresh E-scooters")}
122
+ stations={eScooterStations}
123
+ />
124
+ );
package/src/index.tsx ADDED
@@ -0,0 +1,255 @@
1
+ import { Source, Layer, useMap, Popup } from "react-map-gl";
2
+ import { EventData } from "mapbox-gl";
3
+ import React, { useEffect, useState } from "react";
4
+ import {
5
+ Company,
6
+ Station,
7
+ MapLocationActionArg,
8
+ ConfiguredCompany
9
+ } from "@opentripplanner/types";
10
+ import { MarkerWithPopup } from "@opentripplanner/base-map";
11
+ import StationPopup from "./StationPopup";
12
+ import { BaseBikeRentalIcon, StationMarker } from "./styled";
13
+
14
+ // TODO: Make configurable?
15
+ const DETAILED_MARKER_CUTOFF = 16;
16
+
17
+ const getColorForStation = (v: Station) => {
18
+ if (v.isFloatingCar) return "#009cde";
19
+ if (v.isFloatingVehicle) return "#f5a729";
20
+ // TODO: nicer color to match transitive
21
+ if (v.bikesAvailable !== undefined || v.isFloatingBike) return "#f00";
22
+ return "gray";
23
+ };
24
+
25
+ const checkIfPositionInViewport = (
26
+ bounds: mapboxgl.LngLatBounds,
27
+ lat: number,
28
+ lng: number
29
+ ): boolean => {
30
+ const PADDING = 0.001;
31
+ // @ts-expect-error types appear to be wrong? version issue?
32
+ // eslint-disable-next-line no-underscore-dangle
33
+ const [sw, ne] = [bounds._sw, bounds._ne];
34
+ if (!sw || !ne) return false;
35
+
36
+ return (
37
+ lat >= sw.lat - PADDING &&
38
+ lat <= ne.lat + PADDING &&
39
+ lng >= sw.lng - PADDING &&
40
+ lng <= ne.lng + PADDING
41
+ );
42
+ };
43
+
44
+ type Props = {
45
+ /**
46
+ * The entire companies config array.
47
+ */
48
+ configCompanies: ConfiguredCompany[];
49
+ /**
50
+ * A list of companies that are applicable to just this instance of the
51
+ * overlay.
52
+ */
53
+ companies?: string[];
54
+ /**
55
+ * An id, used to make this layer uniquely identifiable
56
+ */
57
+ id: string;
58
+ /**
59
+ * An optional custom function to create a string name of a particular vehicle
60
+ * rental station. This function takes two arguments of the configCompanies
61
+ * prop and a vehicle rental station. The function must return a string.
62
+ */
63
+ getStationName?: (configCompanies: Company[], station: Station) => string;
64
+ /**
65
+ * If specified, a function that will be triggered every 30 seconds whenever this layer is
66
+ * visible.
67
+ */
68
+ refreshVehicles?: () => void;
69
+ /**
70
+ * A callback for when a user clicks on setting this stop as either the from
71
+ * or to location of a new search.
72
+ *
73
+ * This will be dispatched with the following argument:
74
+ *
75
+ * ```js
76
+ * {
77
+ * location: {
78
+ * lat: number,
79
+ * lon: number,
80
+ * name: string
81
+ * },
82
+ * locationType: "from" or "to"
83
+ * }
84
+ * ```
85
+ */
86
+ setLocation?: (arg: MapLocationActionArg) => void;
87
+ /**
88
+ * A list of the vehicle rental stations specific to this overlay instance.
89
+ */
90
+ stations: Station[];
91
+ /**
92
+ * Whether the overlay is currently visible.
93
+ */
94
+ visible?: boolean;
95
+ /**
96
+ * TODO: Add props for overriding symbols?
97
+ */
98
+ };
99
+
100
+ /**
101
+ * This vehicle rental overlay can be used to render vehicle rentals of various
102
+ * types. This layer can be configured to show different styles of markers at
103
+ * different zoom levels.
104
+ */
105
+ const VehicleRentalOverlay = (props: Props): JSX.Element => {
106
+ const { mainMap } = useMap();
107
+ const zoom = mainMap?.getZoom();
108
+ const bounds = mainMap?.getBounds();
109
+
110
+ const {
111
+ configCompanies,
112
+ companies,
113
+ getStationName,
114
+ id,
115
+ refreshVehicles,
116
+ setLocation,
117
+ stations,
118
+ visible
119
+ } = props;
120
+ const layerId = `rental-vehicles-${id}`;
121
+ const [clickedVehicle, setClickedVehicle] = useState(null);
122
+
123
+ useEffect(() => {
124
+ // TODO: Make 30s configurable?
125
+ if (!refreshVehicles || typeof refreshVehicles !== "function") {
126
+ return;
127
+ }
128
+
129
+ refreshVehicles();
130
+ setInterval(refreshVehicles, 30_000);
131
+ }, [refreshVehicles]);
132
+
133
+ useEffect(() => {
134
+ const VEHICLE_LAYERS = [layerId];
135
+ VEHICLE_LAYERS.forEach(stopLayer => {
136
+ mainMap?.on("mouseenter", stopLayer, () => {
137
+ mainMap.getCanvas().style.cursor = "pointer";
138
+ });
139
+ mainMap?.on("mouseleave", stopLayer, () => {
140
+ mainMap.getCanvas().style.cursor = "";
141
+ });
142
+ mainMap?.on("click", stopLayer, (event: EventData) => {
143
+ setClickedVehicle(event.features?.[0].properties);
144
+ });
145
+ });
146
+ }, [mainMap]);
147
+
148
+ // Don't render if no map or no stops are defined.
149
+ if (visible === false || !stations || stations.length === 0) {
150
+ return <></>;
151
+ }
152
+
153
+ const vehiclesGeoJSON: GeoJSON.FeatureCollection = {
154
+ type: "FeatureCollection",
155
+ features: stations
156
+ .filter(
157
+ vehicle =>
158
+ // Include specified companies only if companies is specified and network info is available
159
+ !companies ||
160
+ !vehicle.networks ||
161
+ companies.includes(vehicle.networks[0])
162
+ )
163
+ .map(vehicle => ({
164
+ type: "Feature",
165
+ properties: {
166
+ ...vehicle,
167
+ networks: JSON.stringify(vehicle.networks),
168
+ "stroke-width":
169
+ vehicle.isFloatingBike || vehicle.isFloatingVehicle ? 1 : 2,
170
+ color: getColorForStation(vehicle)
171
+ },
172
+ geometry: { type: "Point", coordinates: [vehicle.x, vehicle.y] }
173
+ }))
174
+ };
175
+
176
+ return (
177
+ <>
178
+ {zoom < DETAILED_MARKER_CUTOFF && (
179
+ <Source type="geojson" data={vehiclesGeoJSON}>
180
+ <Layer
181
+ id={layerId}
182
+ type="circle"
183
+ paint={{
184
+ "circle-color": ["get", "color"],
185
+ "circle-opacity": 0.9,
186
+ "circle-stroke-width": ["get", "stroke-width"],
187
+ "circle-stroke-color": "#333"
188
+ }}
189
+ />
190
+ {/* this is where we add the symbols layer. add a second layer that gets swapped in and out dynamically */}
191
+ </Source>
192
+ )}
193
+ {zoom >= DETAILED_MARKER_CUTOFF &&
194
+ stations
195
+ .filter(station =>
196
+ checkIfPositionInViewport(bounds, station.y, station.x)
197
+ )
198
+ .map(station => (
199
+ <MarkerWithPopup
200
+ key={station.id}
201
+ position={[station.y, station.x]}
202
+ popupContents={
203
+ <StationPopup
204
+ configCompanies={configCompanies}
205
+ setLocation={location => {
206
+ setClickedVehicle(null);
207
+ setLocation(location);
208
+ }}
209
+ getStationName={getStationName}
210
+ station={station}
211
+ />
212
+ }
213
+ >
214
+ {station.bikesAvailable !== undefined &&
215
+ !station.isFloatingBike &&
216
+ !station.isFloatingVehicle &&
217
+ station.spacesAvailable !== undefined ? (
218
+ <BaseBikeRentalIcon
219
+ percent={
220
+ station?.bikesAvailable /
221
+ (station?.bikesAvailable + station?.spacesAvailable)
222
+ }
223
+ />
224
+ ) : (
225
+ <StationMarker width={12} color={getColorForStation(station)} />
226
+ )}
227
+ </MarkerWithPopup>
228
+ ))}
229
+ {clickedVehicle && (
230
+ <Popup
231
+ onClose={() => {
232
+ setClickedVehicle(null);
233
+ }}
234
+ longitude={clickedVehicle.x}
235
+ latitude={clickedVehicle.y}
236
+ maxWidth="100%"
237
+ >
238
+ <StationPopup
239
+ configCompanies={configCompanies}
240
+ setLocation={location => {
241
+ setClickedVehicle(null);
242
+ setLocation(location);
243
+ }}
244
+ getStationName={getStationName}
245
+ station={{
246
+ ...clickedVehicle,
247
+ networks: JSON.parse(clickedVehicle.networks)
248
+ }}
249
+ />
250
+ </Popup>
251
+ )}
252
+ </>
253
+ );
254
+ };
255
+ export default VehicleRentalOverlay;
package/src/styled.ts ADDED
@@ -0,0 +1,52 @@
1
+ import styled from "styled-components";
2
+ import { MapMarkerAlt } from "@styled-icons/fa-solid/MapMarkerAlt";
3
+
4
+ const getPctIcon = (percent: number) => {
5
+ switch (Math.floor(percent * 10)) {
6
+ case 0:
7
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItMDwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNODMuNCw0M0E0MS4yNSw0MS4yNSwwLDEsMCwyOS41LDgyLjNMNDMuNCw5Ni41LDU1LjMsODIuMUE0MS4yOSw0MS4yOSwwLDAsMCw4My40LDQzWk02NS43LDcwLjZMNDYuNSw0Ny43YTYsNiwwLDAsMCwxLjgtMy4ybDI5LjQsNS4yQTM2LjMzLDM2LjMzLDAsMCwxLDY1LjcsNzAuNlpNNi41LDQ5LjdsMjkuNC01LjJhNi44Myw2LjgzLDAsMCwwLDEuOCwzLjJMMTguNSw3MC42QTM2LjMzLDM2LjMzLDAsMCwxLDYuNSw0OS43Wk01LjksNDNhMzUuODIsMzUuODIsMCwwLDEsNC43LTE3LjhMMzYuNCw0MC4xYTUuOTIsNS45MiwwLDAsMC0uNywyLjl2MC43TDYuMyw0OUEzOS40NSwzOS40NSwwLDAsMSw1LjksNDNaTTI5LjQsOS4xbDEwLjIsMjhhNiw2LDAsMCwwLTIuOCwyLjRMMTAuOSwyNC42QTM2LjYyLDM2LjYyLDAsMCwxLDI5LjQsOS4xWk00Mi4xLDYuOGEzNCwzNCwwLDAsMSwxMiwyLjFsLTEwLjIsMjhhNS42Niw1LjY2LDAsMCwwLTEuOC0uMyw1LjIzLDUuMjMsMCwwLDAtMS44LjNMMzAuMSw4LjlBMzQsMzQsMCwwLDEsNDIuMSw2LjhaTTczLjMsMjQuNkw0Ny41LDM5LjVhNy4yLDcuMiwwLDAsMC0yLjgtMi40bDEwLjItMjhBMzYuOCwzNi44LDAsMCwxLDczLjMsMjQuNlpNMTkuMSw3MUwzOC4zLDQ4LjFhNy4wOSw3LjA5LDAsMCwwLDMuNSwxLjNWNzkuMkEzNS40NCwzNS40NCwwLDAsMSwxOS4xLDcxWm0yMy40LDguM1Y0OS40QTYuMjUsNi4yNSwwLDAsMCw0Niw0OC4xTDY1LjEsNzFBMzYsMzYsMCwwLDEsNDIuNSw3OS4zWm02LTM1LjVWNDMuMWE3LjI3LDcuMjcsMCwwLDAtLjctMi45TDczLjYsMjUuM2EzNS44MiwzNS44MiwwLDAsMSw0LjcsMTcuOCwzOC4wOCwzOC4wOCwwLDAsMS0uNSw1LjlaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMC45IC0xLjgpIi8+PC9zdmc+");';
8
+ case 1:
9
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiNmZjJkMjk7fS5jbHMtM3tmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItMTwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjQxLjEgODAuMDcgNDEuMTUgNDYuMDkgMzcuMTEgNDUuNjUgMTUuOTIgNzEuMTEgNDEuMSA4MC4wNyIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTgzLjQsNDNBNDEuMjUsNDEuMjUsMCwxLDAsMjkuNSw4Mi4zTDQzLjQsOTYuNSw1NS4zLDgyLjFBNDEuMjksNDEuMjksMCwwLDAsODMuNCw0M1pNNjUuNyw3MC42TDQ2LjUsNDcuN2E2LDYsMCwwLDAsMS44LTMuMmwyOS40LDUuMkEzNi4zMywzNi4zMywwLDAsMSw2NS43LDcwLjZaTTYuNSw0OS43bDI5LjQtNS4yYTYuODMsNi44MywwLDAsMCwxLjgsMy4yTDE4LjUsNzAuNkEzNi4zMywzNi4zMywwLDAsMSw2LjUsNDkuN1pNNS45LDQzYTM1LjgyLDM1LjgyLDAsMCwxLDQuNy0xNy44TDM2LjQsNDAuMWE1LjkyLDUuOTIsMCwwLDAtLjcsMi45djAuN0w2LjMsNDlBMzkuNDUsMzkuNDUsMCwwLDEsNS45LDQzWk0yOS40LDkuMWwxMC4yLDI4YTYsNiwwLDAsMC0yLjgsMi40TDEwLjksMjQuNkEzNi42MiwzNi42MiwwLDAsMSwyOS40LDkuMVpNNDIuMSw2LjhhMzQsMzQsMCwwLDEsMTIsMi4xbC0xMC4yLDI4YTUuNjYsNS42NiwwLDAsMC0xLjgtLjMsNS4yMyw1LjIzLDAsMCwwLTEuOC4zTDMwLjEsOC45QTM0LDM0LDAsMCwxLDQyLjEsNi44Wk03My4zLDI0LjZMNDcuNSwzOS41YTcuMiw3LjIsMCwwLDAtMi44LTIuNGwxMC4yLTI4QTM2LjgsMzYuOCwwLDAsMSw3My4zLDI0LjZaTTE5LjEsNzFMMzguMyw0OC4xYTcuMDksNy4wOSwwLDAsMCwzLjUsMS4zVjc5LjJBMzUuNDQsMzUuNDQsMCwwLDEsMTkuMSw3MVptMjMuNCw4LjNWNDkuNEE2LjI1LDYuMjUsMCwwLDAsNDYsNDguMUw2NS4xLDcxQTM2LDM2LDAsMCwxLDQyLjUsNzkuM1ptNi0zNS41VjQzLjFhNy4yNyw3LjI3LDAsMCwwLS43LTIuOUw3My42LDI1LjNhMzUuODIsMzUuODIsMCwwLDEsNC43LDE3LjgsMzguMDgsMzguMDgsMCwwLDEtLjUsNS45WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTAuOSAtMS44KSIvPjwvc3ZnPg==");';
10
+ case 2:
11
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiNmZjJkMjk7fS5jbHMtM3tmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItMjwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjQxLjEgODAuMDcgNDEuMTUgNDYuMDkgMzcuMTEgNDUuNjUgMTUuOTIgNzEuMTEgNDEuMSA4MC4wNyIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSIxNS45NiA3MC43OCAzNy45MSA0NC44NSAzNS4xMSA0MS45MSAyLjQ5IDQ3LjY5IDE1Ljk2IDcwLjc4Ii8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNODMuNCw0M0E0MS4yNSw0MS4yNSwwLDEsMCwyOS41LDgyLjNMNDMuNCw5Ni41LDU1LjMsODIuMUE0MS4yOSw0MS4yOSwwLDAsMCw4My40LDQzWk02NS43LDcwLjZMNDYuNSw0Ny43YTYsNiwwLDAsMCwxLjgtMy4ybDI5LjQsNS4yQTM2LjMzLDM2LjMzLDAsMCwxLDY1LjcsNzAuNlpNNi41LDQ5LjdsMjkuNC01LjJhNi44Myw2LjgzLDAsMCwwLDEuOCwzLjJMMTguNSw3MC42QTM2LjMzLDM2LjMzLDAsMCwxLDYuNSw0OS43Wk01LjksNDNhMzUuODIsMzUuODIsMCwwLDEsNC43LTE3LjhMMzYuNCw0MC4xYTUuOTIsNS45MiwwLDAsMC0uNywyLjl2MC43TDYuMyw0OUEzOS40NSwzOS40NSwwLDAsMSw1LjksNDNaTTI5LjQsOS4xbDEwLjIsMjhhNiw2LDAsMCwwLTIuOCwyLjRMMTAuOSwyNC42QTM2LjYyLDM2LjYyLDAsMCwxLDI5LjQsOS4xWk00Mi4xLDYuOGEzNCwzNCwwLDAsMSwxMiwyLjFsLTEwLjIsMjhhNS42Niw1LjY2LDAsMCwwLTEuOC0uMyw1LjIzLDUuMjMsMCwwLDAtMS44LjNMMzAuMSw4LjlBMzQsMzQsMCwwLDEsNDIuMSw2LjhaTTczLjMsMjQuNkw0Ny41LDM5LjVhNy4yLDcuMiwwLDAsMC0yLjgtMi40bDEwLjItMjhBMzYuOCwzNi44LDAsMCwxLDczLjMsMjQuNlpNMTkuMSw3MUwzOC4zLDQ4LjFhNy4wOSw3LjA5LDAsMCwwLDMuNSwxLjNWNzkuMkEzNS40NCwzNS40NCwwLDAsMSwxOS4xLDcxWm0yMy40LDguM1Y0OS40QTYuMjUsNi4yNSwwLDAsMCw0Niw0OC4xTDY1LjEsNzFBMzYsMzYsMCwwLDEsNDIuNSw3OS4zWm02LTM1LjVWNDMuMWE3LjI3LDcuMjcsMCwwLDAtLjctMi45TDczLjYsMjUuM2EzNS44MiwzNS44MiwwLDAsMSw0LjcsMTcuOCwzOC4wOCwzOC4wOCwwLDAsMS0uNSw1LjlaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMC45IC0xLjgpIi8+PC9zdmc+")';
12
+ case 3:
13
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiNmZjJkMjk7fS5jbHMtM3tmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItMzwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjQxLjEgODAuMDcgNDEuMTUgNDYuMDkgMzcuMTEgNDUuNjUgMTUuOTIgNzEuMTEgNDEuMSA4MC4wNyIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSIxNS45NiA3MC43OCAzNy45MSA0NC44NSAzNS4xMSA0MS45MSAyLjQ5IDQ3LjY5IDE1Ljk2IDcwLjc4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjIuOTIgNDguMDcgMzYuMzggNDIuMTcgMzYuMSAzOC4xMiA3LjMzIDIxLjcgMi45MiA0OC4wNyIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTgzLjQsNDNBNDEuMjUsNDEuMjUsMCwxLDAsMjkuNSw4Mi4zTDQzLjQsOTYuNSw1NS4zLDgyLjFBNDEuMjksNDEuMjksMCwwLDAsODMuNCw0M1pNNjUuNyw3MC42TDQ2LjUsNDcuN2E2LDYsMCwwLDAsMS44LTMuMmwyOS40LDUuMkEzNi4zMywzNi4zMywwLDAsMSw2NS43LDcwLjZaTTYuNSw0OS43bDI5LjQtNS4yYTYuODMsNi44MywwLDAsMCwxLjgsMy4yTDE4LjUsNzAuNkEzNi4zMywzNi4zMywwLDAsMSw2LjUsNDkuN1pNNS45LDQzYTM1LjgyLDM1LjgyLDAsMCwxLDQuNy0xNy44TDM2LjQsNDAuMWE1LjkyLDUuOTIsMCwwLDAtLjcsMi45djAuN0w2LjMsNDlBMzkuNDUsMzkuNDUsMCwwLDEsNS45LDQzWk0yOS40LDkuMWwxMC4yLDI4YTYsNiwwLDAsMC0yLjgsMi40TDEwLjksMjQuNkEzNi42MiwzNi42MiwwLDAsMSwyOS40LDkuMVpNNDIuMSw2LjhhMzQsMzQsMCwwLDEsMTIsMi4xbC0xMC4yLDI4YTUuNjYsNS42NiwwLDAsMC0xLjgtLjMsNS4yMyw1LjIzLDAsMCwwLTEuOC4zTDMwLjEsOC45QTM0LDM0LDAsMCwxLDQyLjEsNi44Wk03My4zLDI0LjZMNDcuNSwzOS41YTcuMiw3LjIsMCwwLDAtMi44LTIuNGwxMC4yLTI4QTM2LjgsMzYuOCwwLDAsMSw3My4zLDI0LjZaTTE5LjEsNzFMMzguMyw0OC4xYTcuMDksNy4wOSwwLDAsMCwzLjUsMS4zVjc5LjJBMzUuNDQsMzUuNDQsMCwwLDEsMTkuMSw3MVptMjMuNCw4LjNWNDkuNEE2LjI1LDYuMjUsMCwwLDAsNDYsNDguMUw2NS4xLDcxQTM2LDM2LDAsMCwxLDQyLjUsNzkuM1ptNi0zNS41VjQzLjFhNy4yNyw3LjI3LDAsMCwwLS43LTIuOUw3My42LDI1LjNhMzUuODIsMzUuODIsMCwwLDEsNC43LDE3LjgsMzguMDgsMzguMDgsMCwwLDEtLjUsNS45WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTAuOSAtMS44KSIvPjwvc3ZnPg==");';
14
+ case 4:
15
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiNmZjJkMjk7fS5jbHMtM3tmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItNDwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjQxLjEgODAuMDcgNDEuMTUgNDYuMDkgMzcuMTEgNDUuNjUgMTUuOTIgNzEuMTEgNDEuMSA4MC4wNyIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSIxNS45NiA3MC43OCAzNy45MSA0NC44NSAzNS4xMSA0MS45MSAyLjQ5IDQ3LjY5IDE1Ljk2IDcwLjc4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjIuOTIgNDguMDcgMzYuMzggNDIuMTcgMzYuMSAzOC4xMiA3LjMzIDIxLjcgMi45MiA0OC4wNyIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSI3LjExIDIxLjcxIDM2LjY4IDM4LjQ1IDM5LjA0IDM1LjE1IDI3LjI5IDQuMTggNy4xMSAyMS43MSIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTgzLjQsNDNBNDEuMjUsNDEuMjUsMCwxLDAsMjkuNSw4Mi4zTDQzLjQsOTYuNSw1NS4zLDgyLjFBNDEuMjksNDEuMjksMCwwLDAsODMuNCw0M1pNNjUuNyw3MC42TDQ2LjUsNDcuN2E2LDYsMCwwLDAsMS44LTMuMmwyOS40LDUuMkEzNi4zMywzNi4zMywwLDAsMSw2NS43LDcwLjZaTTYuNSw0OS43bDI5LjQtNS4yYTYuODMsNi44MywwLDAsMCwxLjgsMy4yTDE4LjUsNzAuNkEzNi4zMywzNi4zMywwLDAsMSw2LjUsNDkuN1pNNS45LDQzYTM1LjgyLDM1LjgyLDAsMCwxLDQuNy0xNy44TDM2LjQsNDAuMWE1LjkyLDUuOTIsMCwwLDAtLjcsMi45djAuN0w2LjMsNDlBMzkuNDUsMzkuNDUsMCwwLDEsNS45LDQzWk0yOS40LDkuMWwxMC4yLDI4YTYsNiwwLDAsMC0yLjgsMi40TDEwLjksMjQuNkEzNi42MiwzNi42MiwwLDAsMSwyOS40LDkuMVpNNDIuMSw2LjhhMzQsMzQsMCwwLDEsMTIsMi4xbC0xMC4yLDI4YTUuNjYsNS42NiwwLDAsMC0xLjgtLjMsNS4yMyw1LjIzLDAsMCwwLTEuOC4zTDMwLjEsOC45QTM0LDM0LDAsMCwxLDQyLjEsNi44Wk03My4zLDI0LjZMNDcuNSwzOS41YTcuMiw3LjIsMCwwLDAtMi44LTIuNGwxMC4yLTI4QTM2LjgsMzYuOCwwLDAsMSw3My4zLDI0LjZaTTE5LjEsNzFMMzguMyw0OC4xYTcuMDksNy4wOSwwLDAsMCwzLjUsMS4zVjc5LjJBMzUuNDQsMzUuNDQsMCwwLDEsMTkuMSw3MVptMjMuNCw4LjNWNDkuNEE2LjI1LDYuMjUsMCwwLDAsNDYsNDguMUw2NS4xLDcxQTM2LDM2LDAsMCwxLDQyLjUsNzkuM1ptNi0zNS41VjQzLjFhNy4yNyw3LjI3LDAsMCwwLS43LTIuOUw3My42LDI1LjNhMzUuODIsMzUuODIsMCwwLDEsNC43LDE3LjgsMzguMDgsMzguMDgsMCwwLDEtLjUsNS45WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTAuOSAtMS44KSIvPjwvc3ZnPg==");';
16
+ case 5:
17
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiNmZjJkMjk7fS5jbHMtM3tmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItNTwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjU0LjU1IDQuNjkgNDIuODYgMzYuNTkgMzguOTIgMzUuNiAyNy44MyA0LjM5IDU0LjU1IDQuNjkiLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iNDEuMSA4MC4wNyA0MS4xNSA0Ni4wOSAzNy4xMSA0NS42NSAxNS45MiA3MS4xMSA0MS4xIDgwLjA3Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjE1Ljk2IDcwLjc4IDM3LjkxIDQ0Ljg1IDM1LjExIDQxLjkxIDIuNDkgNDcuNjkgMTUuOTYgNzAuNzgiLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iMi45MiA0OC4wNyAzNi4zOCA0Mi4xNyAzNi4xIDM4LjEyIDcuMzMgMjEuNyAyLjkyIDQ4LjA3Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjcuMTEgMjEuNzEgMzYuNjggMzguNDUgMzkuMDQgMzUuMTUgMjcuMjkgNC4xOCA3LjExIDIxLjcxIi8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNODMuNCw0M0E0MS4yNSw0MS4yNSwwLDEsMCwyOS41LDgyLjNMNDMuNCw5Ni41LDU1LjMsODIuMUE0MS4yOSw0MS4yOSwwLDAsMCw4My40LDQzWk02NS43LDcwLjZMNDYuNSw0Ny43YTYsNiwwLDAsMCwxLjgtMy4ybDI5LjQsNS4yQTM2LjMzLDM2LjMzLDAsMCwxLDY1LjcsNzAuNlpNNi41LDQ5LjdsMjkuNC01LjJhNi44Myw2LjgzLDAsMCwwLDEuOCwzLjJMMTguNSw3MC42QTM2LjMzLDM2LjMzLDAsMCwxLDYuNSw0OS43Wk01LjksNDNhMzUuODIsMzUuODIsMCwwLDEsNC43LTE3LjhMMzYuNCw0MC4xYTUuOTIsNS45MiwwLDAsMC0uNywyLjl2MC43TDYuMyw0OUEzOS40NSwzOS40NSwwLDAsMSw1LjksNDNaTTI5LjQsOS4xbDEwLjIsMjhhNiw2LDAsMCwwLTIuOCwyLjRMMTAuOSwyNC42QTM2LjYyLDM2LjYyLDAsMCwxLDI5LjQsOS4xWk00Mi4xLDYuOGEzNCwzNCwwLDAsMSwxMiwyLjFsLTEwLjIsMjhhNS42Niw1LjY2LDAsMCwwLTEuOC0uMyw1LjIzLDUuMjMsMCwwLDAtMS44LjNMMzAuMSw4LjlBMzQsMzQsMCwwLDEsNDIuMSw2LjhaTTczLjMsMjQuNkw0Ny41LDM5LjVhNy4yLDcuMiwwLDAsMC0yLjgtMi40bDEwLjItMjhBMzYuOCwzNi44LDAsMCwxLDczLjMsMjQuNlpNMTkuMSw3MUwzOC4zLDQ4LjFhNy4wOSw3LjA5LDAsMCwwLDMuNSwxLjNWNzkuMkEzNS40NCwzNS40NCwwLDAsMSwxOS4xLDcxWm0yMy40LDguM1Y0OS40QTYuMjUsNi4yNSwwLDAsMCw0Niw0OC4xTDY1LjEsNzFBMzYsMzYsMCwwLDEsNDIuNSw3OS4zWm02LTM1LjVWNDMuMWE3LjI3LDcuMjcsMCwwLDAtLjctMi45TDczLjYsMjUuM2EzNS44MiwzNS44MiwwLDAsMSw0LjcsMTcuOCwzOC4wOCwzOC4wOCwwLDAsMS0uNSw1LjlaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMC45IC0xLjgpIi8+PC9zdmc+");';
18
+ case 6:
19
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiNmZjJkMjk7fS5jbHMtM3tmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItNjwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9Ijc1LjI0IDIxLjkyIDQ1LjY3IDM4LjY2IDQzLjMxIDM1LjM2IDU1LjA2IDQuMzkgNzUuMjQgMjEuOTIiLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iNTQuNTUgNC42OSA0Mi44NiAzNi41OSAzOC45MiAzNS42IDI3LjgzIDQuMzkgNTQuNTUgNC42OSIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSI0MS4xIDgwLjA3IDQxLjE1IDQ2LjA5IDM3LjExIDQ1LjY1IDE1LjkyIDcxLjExIDQxLjEgODAuMDciLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iMTUuOTYgNzAuNzggMzcuOTEgNDQuODUgMzUuMTEgNDEuOTEgMi40OSA0Ny42OSAxNS45NiA3MC43OCIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSIyLjkyIDQ4LjA3IDM2LjM4IDQyLjE3IDM2LjEgMzguMTIgNy4zMyAyMS43IDIuOTIgNDguMDciLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iNy4xMSAyMS43MSAzNi42OCAzOC40NSAzOS4wNCAzNS4xNSAyNy4yOSA0LjE4IDcuMTEgMjEuNzEiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik04My40LDQzQTQxLjI1LDQxLjI1LDAsMSwwLDI5LjUsODIuM0w0My40LDk2LjUsNTUuMyw4Mi4xQTQxLjI5LDQxLjI5LDAsMCwwLDgzLjQsNDNaTTY1LjcsNzAuNkw0Ni41LDQ3LjdhNiw2LDAsMCwwLDEuOC0zLjJsMjkuNCw1LjJBMzYuMzMsMzYuMzMsMCwwLDEsNjUuNyw3MC42Wk02LjUsNDkuN2wyOS40LTUuMmE2LjgzLDYuODMsMCwwLDAsMS44LDMuMkwxOC41LDcwLjZBMzYuMzMsMzYuMzMsMCwwLDEsNi41LDQ5LjdaTTUuOSw0M2EzNS44MiwzNS44MiwwLDAsMSw0LjctMTcuOEwzNi40LDQwLjFhNS45Miw1LjkyLDAsMCwwLS43LDIuOXYwLjdMNi4zLDQ5QTM5LjQ1LDM5LjQ1LDAsMCwxLDUuOSw0M1pNMjkuNCw5LjFsMTAuMiwyOGE2LDYsMCwwLDAtMi44LDIuNEwxMC45LDI0LjZBMzYuNjIsMzYuNjIsMCwwLDEsMjkuNCw5LjFaTTQyLjEsNi44YTM0LDM0LDAsMCwxLDEyLDIuMWwtMTAuMiwyOGE1LjY2LDUuNjYsMCwwLDAtMS44LS4zLDUuMjMsNS4yMywwLDAsMC0xLjguM0wzMC4xLDguOUEzNCwzNCwwLDAsMSw0Mi4xLDYuOFpNNzMuMywyNC42TDQ3LjUsMzkuNWE3LjIsNy4yLDAsMCwwLTIuOC0yLjRsMTAuMi0yOEEzNi44LDM2LjgsMCwwLDEsNzMuMywyNC42Wk0xOS4xLDcxTDM4LjMsNDguMWE3LjA5LDcuMDksMCwwLDAsMy41LDEuM1Y3OS4yQTM1LjQ0LDM1LjQ0LDAsMCwxLDE5LjEsNzFabTIzLjQsOC4zVjQ5LjRBNi4yNSw2LjI1LDAsMCwwLDQ2LDQ4LjFMNjUuMSw3MUEzNiwzNiwwLDAsMSw0Mi41LDc5LjNabTYtMzUuNVY0My4xYTcuMjcsNy4yNywwLDAsMC0uNy0yLjlMNzMuNiwyNS4zYTM1LjgyLDM1LjgyLDAsMCwxLDQuNywxNy44LDM4LjA4LDM4LjA4LDAsMCwxLS41LDUuOVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0wLjkgLTEuOCkiLz48L3N2Zz4=");';
20
+ case 7:
21
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiNmZjJkMjk7fS5jbHMtM3tmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItNzwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9Ijc5LjQzIDQ4LjI4IDQ1Ljk3IDQyLjM4IDQ2LjI1IDM4LjMzIDc1LjAyIDIxLjkyIDc5LjQzIDQ4LjI4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9Ijc1LjI0IDIxLjkyIDQ1LjY3IDM4LjY2IDQzLjMxIDM1LjM2IDU1LjA2IDQuMzkgNzUuMjQgMjEuOTIiLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iNTQuNTUgNC42OSA0Mi44NiAzNi41OSAzOC45MiAzNS42IDI3LjgzIDQuMzkgNTQuNTUgNC42OSIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSI0MS4xIDgwLjA3IDQxLjE1IDQ2LjA5IDM3LjExIDQ1LjY1IDE1LjkyIDcxLjExIDQxLjEgODAuMDciLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iMTUuOTYgNzAuNzggMzcuOTEgNDQuODUgMzUuMTEgNDEuOTEgMi40OSA0Ny42OSAxNS45NiA3MC43OCIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSIyLjkyIDQ4LjA3IDM2LjM4IDQyLjE3IDM2LjEgMzguMTIgNy4zMyAyMS43IDIuOTIgNDguMDciLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iNy4xMSAyMS43MSAzNi42OCAzOC40NSAzOS4wNCAzNS4xNSAyNy4yOSA0LjE4IDcuMTEgMjEuNzEiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik04My40LDQzQTQxLjI1LDQxLjI1LDAsMSwwLDI5LjUsODIuM0w0My40LDk2LjUsNTUuMyw4Mi4xQTQxLjI5LDQxLjI5LDAsMCwwLDgzLjQsNDNaTTY1LjcsNzAuNkw0Ni41LDQ3LjdhNiw2LDAsMCwwLDEuOC0zLjJsMjkuNCw1LjJBMzYuMzMsMzYuMzMsMCwwLDEsNjUuNyw3MC42Wk02LjUsNDkuN2wyOS40LTUuMmE2LjgzLDYuODMsMCwwLDAsMS44LDMuMkwxOC41LDcwLjZBMzYuMzMsMzYuMzMsMCwwLDEsNi41LDQ5LjdaTTUuOSw0M2EzNS44MiwzNS44MiwwLDAsMSw0LjctMTcuOEwzNi40LDQwLjFhNS45Miw1LjkyLDAsMCwwLS43LDIuOXYwLjdMNi4zLDQ5QTM5LjQ1LDM5LjQ1LDAsMCwxLDUuOSw0M1pNMjkuNCw5LjFsMTAuMiwyOGE2LDYsMCwwLDAtMi44LDIuNEwxMC45LDI0LjZBMzYuNjIsMzYuNjIsMCwwLDEsMjkuNCw5LjFaTTQyLjEsNi44YTM0LDM0LDAsMCwxLDEyLDIuMWwtMTAuMiwyOGE1LjY2LDUuNjYsMCwwLDAtMS44LS4zLDUuMjMsNS4yMywwLDAsMC0xLjguM0wzMC4xLDguOUEzNCwzNCwwLDAsMSw0Mi4xLDYuOFpNNzMuMywyNC42TDQ3LjUsMzkuNWE3LjIsNy4yLDAsMCwwLTIuOC0yLjRsMTAuMi0yOEEzNi44LDM2LjgsMCwwLDEsNzMuMywyNC42Wk0xOS4xLDcxTDM4LjMsNDguMWE3LjA5LDcuMDksMCwwLDAsMy41LDEuM1Y3OS4yQTM1LjQ0LDM1LjQ0LDAsMCwxLDE5LjEsNzFabTIzLjQsOC4zVjQ5LjRBNi4yNSw2LjI1LDAsMCwwLDQ2LDQ4LjFMNjUuMSw3MUEzNiwzNiwwLDAsMSw0Mi41LDc5LjNabTYtMzUuNVY0My4xYTcuMjcsNy4yNywwLDAsMC0uNy0yLjlMNzMuNiwyNS4zYTM1LjgyLDM1LjgyLDAsMCwxLDQuNywxNy44LDM4LjA4LDM4LjA4LDAsMCwxLS41LDUuOVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0wLjkgLTEuOCkiLz48L3N2Zz4=");';
22
+ case 8:
23
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiNmZjJkMjk7fS5jbHMtM3tmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItODwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjY2LjM5IDcxIDQ0LjQ0IDQ1LjA2IDQ3LjI0IDQyLjEzIDc5Ljg2IDQ3LjkxIDY2LjM5IDcxIi8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9Ijc5LjQzIDQ4LjI4IDQ1Ljk3IDQyLjM4IDQ2LjI1IDM4LjMzIDc1LjAyIDIxLjkyIDc5LjQzIDQ4LjI4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9Ijc1LjI0IDIxLjkyIDQ1LjY3IDM4LjY2IDQzLjMxIDM1LjM2IDU1LjA2IDQuMzkgNzUuMjQgMjEuOTIiLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iNTQuNTUgNC42OSA0Mi44NiAzNi41OSAzOC45MiAzNS42IDI3LjgzIDQuMzkgNTQuNTUgNC42OSIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSI0MS4xIDgwLjA3IDQxLjE1IDQ2LjA5IDM3LjExIDQ1LjY1IDE1LjkyIDcxLjExIDQxLjEgODAuMDciLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iMTUuOTYgNzAuNzggMzcuOTEgNDQuODUgMzUuMTEgNDEuOTEgMi40OSA0Ny42OSAxNS45NiA3MC43OCIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSIyLjkyIDQ4LjA3IDM2LjM4IDQyLjE3IDM2LjEgMzguMTIgNy4zMyAyMS43IDIuOTIgNDguMDciLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iNy4xMSAyMS43MSAzNi42OCAzOC40NSAzOS4wNCAzNS4xNSAyNy4yOSA0LjE4IDcuMTEgMjEuNzEiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik04My40LDQzQTQxLjI1LDQxLjI1LDAsMSwwLDI5LjUsODIuM0w0My40LDk2LjUsNTUuMyw4Mi4xQTQxLjI5LDQxLjI5LDAsMCwwLDgzLjQsNDNaTTY1LjcsNzAuNkw0Ni41LDQ3LjdhNiw2LDAsMCwwLDEuOC0zLjJsMjkuNCw1LjJBMzYuMzMsMzYuMzMsMCwwLDEsNjUuNyw3MC42Wk02LjUsNDkuN2wyOS40LTUuMmE2LjgzLDYuODMsMCwwLDAsMS44LDMuMkwxOC41LDcwLjZBMzYuMzMsMzYuMzMsMCwwLDEsNi41LDQ5LjdaTTUuOSw0M2EzNS44MiwzNS44MiwwLDAsMSw0LjctMTcuOEwzNi40LDQwLjFhNS45Miw1LjkyLDAsMCwwLS43LDIuOXYwLjdMNi4zLDQ5QTM5LjQ1LDM5LjQ1LDAsMCwxLDUuOSw0M1pNMjkuNCw5LjFsMTAuMiwyOGE2LDYsMCwwLDAtMi44LDIuNEwxMC45LDI0LjZBMzYuNjIsMzYuNjIsMCwwLDEsMjkuNCw5LjFaTTQyLjEsNi44YTM0LDM0LDAsMCwxLDEyLDIuMWwtMTAuMiwyOGE1LjY2LDUuNjYsMCwwLDAtMS44LS4zLDUuMjMsNS4yMywwLDAsMC0xLjguM0wzMC4xLDguOUEzNCwzNCwwLDAsMSw0Mi4xLDYuOFpNNzMuMywyNC42TDQ3LjUsMzkuNWE3LjIsNy4yLDAsMCwwLTIuOC0yLjRsMTAuMi0yOEEzNi44LDM2LjgsMCwwLDEsNzMuMywyNC42Wk0xOS4xLDcxTDM4LjMsNDguMWE3LjA5LDcuMDksMCwwLDAsMy41LDEuM1Y3OS4yQTM1LjQ0LDM1LjQ0LDAsMCwxLDE5LjEsNzFabTIzLjQsOC4zVjQ5LjRBNi4yNSw2LjI1LDAsMCwwLDQ2LDQ4LjFMNjUuMSw3MUEzNiwzNiwwLDAsMSw0Mi41LDc5LjNabTYtMzUuNVY0My4xYTcuMjcsNy4yNywwLDAsMC0uNy0yLjlMNzMuNiwyNS4zYTM1LjgyLDM1LjgyLDAsMCwxLDQuNywxNy44LDM4LjA4LDM4LjA4LDAsMCwxLS41LDUuOVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0wLjkgLTEuOCkiLz48L3N2Zz4=");';
24
+ case 9:
25
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiNmZjJkMjk7fS5jbHMtM3tmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItOTwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjQxLjI1IDgwLjI4IDQxLjIgNDYuMzEgNDUuMjQgNDUuODcgNjYuNDMgNzEuMzIgNDEuMjUgODAuMjgiLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iNjYuMzkgNzEgNDQuNDQgNDUuMDYgNDcuMjQgNDIuMTMgNzkuODYgNDcuOTEgNjYuMzkgNzEiLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iNzkuNDMgNDguMjggNDUuOTcgNDIuMzggNDYuMjUgMzguMzMgNzUuMDIgMjEuOTIgNzkuNDMgNDguMjgiLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iNzUuMjQgMjEuOTIgNDUuNjcgMzguNjYgNDMuMzEgMzUuMzYgNTUuMDYgNC4zOSA3NS4yNCAyMS45MiIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSI1NC41NSA0LjY5IDQyLjg2IDM2LjU5IDM4LjkyIDM1LjYgMjcuODMgNC4zOSA1NC41NSA0LjY5Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjQxLjEgODAuMDcgNDEuMTUgNDYuMDkgMzcuMTEgNDUuNjUgMTUuOTIgNzEuMTEgNDEuMSA4MC4wNyIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSIxNS45NiA3MC43OCAzNy45MSA0NC44NSAzNS4xMSA0MS45MSAyLjQ5IDQ3LjY5IDE1Ljk2IDcwLjc4Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjIuOTIgNDguMDcgMzYuMzggNDIuMTcgMzYuMSAzOC4xMiA3LjMzIDIxLjcgMi45MiA0OC4wNyIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSI3LjExIDIxLjcxIDM2LjY4IDM4LjQ1IDM5LjA0IDM1LjE1IDI3LjI5IDQuMTggNy4xMSAyMS43MSIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTgzLjQsNDNBNDEuMjUsNDEuMjUsMCwxLDAsMjkuNSw4Mi4zTDQzLjQsOTYuNSw1NS4zLDgyLjFBNDEuMjksNDEuMjksMCwwLDAsODMuNCw0M1pNNjUuNyw3MC42TDQ2LjUsNDcuN2E2LDYsMCwwLDAsMS44LTMuMmwyOS40LDUuMkEzNi4zMywzNi4zMywwLDAsMSw2NS43LDcwLjZaTTYuNSw0OS43bDI5LjQtNS4yYTYuODMsNi44MywwLDAsMCwxLjgsMy4yTDE4LjUsNzAuNkEzNi4zMywzNi4zMywwLDAsMSw2LjUsNDkuN1pNNS45LDQzYTM1LjgyLDM1LjgyLDAsMCwxLDQuNy0xNy44TDM2LjQsNDAuMWE1LjkyLDUuOTIsMCwwLDAtLjcsMi45djAuN0w2LjMsNDlBMzkuNDUsMzkuNDUsMCwwLDEsNS45LDQzWk0yOS40LDkuMWwxMC4yLDI4YTYsNiwwLDAsMC0yLjgsMi40TDEwLjksMjQuNkEzNi42MiwzNi42MiwwLDAsMSwyOS40LDkuMVpNNDIuMSw2LjhhMzQsMzQsMCwwLDEsMTIsMi4xbC0xMC4yLDI4YTUuNjYsNS42NiwwLDAsMC0xLjgtLjMsNS4yMyw1LjIzLDAsMCwwLTEuOC4zTDMwLjEsOC45QTM0LDM0LDAsMCwxLDQyLjEsNi44Wk03My4zLDI0LjZMNDcuNSwzOS41YTcuMiw3LjIsMCwwLDAtMi44LTIuNGwxMC4yLTI4QTM2LjgsMzYuOCwwLDAsMSw3My4zLDI0LjZaTTE5LjEsNzFMMzguMyw0OC4xYTcuMDksNy4wOSwwLDAsMCwzLjUsMS4zVjc5LjJBMzUuNDQsMzUuNDQsMCwwLDEsMTkuMSw3MVptMjMuNCw4LjNWNDkuNEE2LjI1LDYuMjUsMCwwLDAsNDYsNDguMUw2NS4xLDcxQTM2LDM2LDAsMCwxLDQyLjUsNzkuM1ptNi0zNS41VjQzLjFhNy4yNyw3LjI3LDAsMCwwLS43LTIuOUw3My42LDI1LjNhMzUuODIsMzUuODIsMCwwLDEsNC43LDE3LjgsMzguMDgsMzguMDgsMCwwLDEtLjUsNS45WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTAuOSAtMS44KSIvPjwvc3ZnPg==");';
26
+ case 10:
27
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnNDYxOSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTUgMTcuMTQiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojZjk0YTE1O308L3N0eWxlPjwvZGVmcz48dGl0bGU+YmlrZXNoYXJlX291dF9vZl9odWI8L3RpdGxlPjxwYXRoIGlkPSJjaXJjbGU0NjA0IiBjbGFzcz0iY2xzLTEiIGQ9Ik0xMS42OSwxLjM4YTEsMSwwLDEsMCwxLDFBMSwxLDAsMCwwLDExLjY5LDEuMzhabS0xLjg2LDJhMC41LDAuNSwwLDAsMC0uMzUuMTRMNi44NCw2YTAuNSwwLjUsMCwwLDAsMCwuNzZsMS44LDEuMzV2Mi43NWEwLjUsMC41LDAsMSwwLDEsMHYtM2EwLjUsMC41LDAsMCwwLS4yLTAuNEw4LjcyLDYuOWwxLjgyLTEuNzIsMC43NSwxYTAuNSwwLjUsMCwwLDAsLjQuMmgxLjVhMC41LDAuNSwwLDEsMCwwLTFIMTEuOTRsLTAuNzUtMS0wLjYtLjhhMC41LDAuNSwwLDAsMC0uMzgtMC4yMUg5LjgzWm0tNS4xNCw0YTMsMywwLDEsMCwzLDNBMywzLDAsMCwwLDQuNjksNy4zOFptOSwwYTMsMywwLDEsMCwzLDNBMywzLDAsMCwwLDEzLjY5LDcuMzhabS05LDFhMiwyLDAsMSwxLTIsMkEyLDIsMCwwLDEsNC42OSw4LjM4Wm05LDBhMiwyLDAsMSwxLTIsMkEyLDIsMCwwLDEsMTMuNjksOC4zOFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xLjY5IC0xLjM4KSIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMSIgcG9pbnRzPSIzLjMgMTIuOTQgNy41IDE3LjE0IDExLjcgMTIuOTQgMy4zIDEyLjk0Ii8+PC9zdmc+");';
28
+ default:
29
+ return 'background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0id2hpdGVfYmciIGRhdGEtbmFtZT0id2hpdGUgYmciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDgyLjUgOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fS5jbHMtMntmaWxsOiMzMzM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5odWItMDwvdGl0bGU+PGNpcmNsZSBjbGFzcz0iY2xzLTEiIGN4PSI0MS4yNSIgY3k9IjQxLjIiIHI9IjM4LjE4Ii8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNODMuNCw0M0E0MS4yNSw0MS4yNSwwLDEsMCwyOS41LDgyLjNMNDMuNCw5Ni41LDU1LjMsODIuMUE0MS4yOSw0MS4yOSwwLDAsMCw4My40LDQzWk02NS43LDcwLjZMNDYuNSw0Ny43YTYsNiwwLDAsMCwxLjgtMy4ybDI5LjQsNS4yQTM2LjMzLDM2LjMzLDAsMCwxLDY1LjcsNzAuNlpNNi41LDQ5LjdsMjkuNC01LjJhNi44Myw2LjgzLDAsMCwwLDEuOCwzLjJMMTguNSw3MC42QTM2LjMzLDM2LjMzLDAsMCwxLDYuNSw0OS43Wk01LjksNDNhMzUuODIsMzUuODIsMCwwLDEsNC43LTE3LjhMMzYuNCw0MC4xYTUuOTIsNS45MiwwLDAsMC0uNywyLjl2MC43TDYuMyw0OUEzOS40NSwzOS40NSwwLDAsMSw1LjksNDNaTTI5LjQsOS4xbDEwLjIsMjhhNiw2LDAsMCwwLTIuOCwyLjRMMTAuOSwyNC42QTM2LjYyLDM2LjYyLDAsMCwxLDI5LjQsOS4xWk00Mi4xLDYuOGEzNCwzNCwwLDAsMSwxMiwyLjFsLTEwLjIsMjhhNS42Niw1LjY2LDAsMCwwLTEuOC0uMyw1LjIzLDUuMjMsMCwwLDAtMS44LjNMMzAuMSw4LjlBMzQsMzQsMCwwLDEsNDIuMSw2LjhaTTczLjMsMjQuNkw0Ny41LDM5LjVhNy4yLDcuMiwwLDAsMC0yLjgtMi40bDEwLjItMjhBMzYuOCwzNi44LDAsMCwxLDczLjMsMjQuNlpNMTkuMSw3MUwzOC4zLDQ4LjFhNy4wOSw3LjA5LDAsMCwwLDMuNSwxLjNWNzkuMkEzNS40NCwzNS40NCwwLDAsMSwxOS4xLDcxWm0yMy40LDguM1Y0OS40QTYuMjUsNi4yNSwwLDAsMCw0Niw0OC4xTDY1LjEsNzFBMzYsMzYsMCwwLDEsNDIuNSw3OS4zWm02LTM1LjVWNDMuMWE3LjI3LDcuMjcsMCwwLDAtLjctMi45TDczLjYsMjUuM2EzNS44MiwzNS44MiwwLDAsMSw0LjcsMTcuOCwzOC4wOCwzOC4wOCwwLDAsMS0uNSw1LjlaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMC45IC0xLjgpIi8+PC9zdmc+");';
30
+ }
31
+ };
32
+ /**
33
+ * Bike rental icons are different from other vehicle rental types since they
34
+ * typically have stations in addition to free-floating bikes. The stations are
35
+ * drawn as svgs marking how full the stations are while the floating bikes have
36
+ * their own unique icon.
37
+ */
38
+ export const BaseBikeRentalIcon = styled.div<{ percent: number }>`
39
+ background-size: contain;
40
+ background-position: center;
41
+ background-repeat: no-repeat;
42
+ margin: auto;
43
+ width: 24px;
44
+ height: 24px;
45
+ ${props => getPctIcon(props.percent)}
46
+ `;
47
+
48
+ export const StationMarker = styled(MapMarkerAlt)`
49
+ height: 12;
50
+ width: 12;
51
+ color: ${props => props.color};
52
+ `;
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "outDir": "./lib",
6
+ "rootDir": "./src",
7
+ "skipLibCheck": true
8
+ },
9
+ "include": ["src/**/*"],
10
+ "references": [
11
+ {
12
+ "path": "../types"
13
+ }
14
+ ]
15
+ }