@opentripplanner/vehicle-rental-overlay 1.4.5 → 2.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.
@@ -0,0 +1,122 @@
1
+ import React from "react";
2
+ import { action } from "@storybook/addon-actions";
3
+ import { boolean } from "@storybook/addon-knobs";
4
+
5
+ import { Company, Station } from "@opentripplanner/types";
6
+ import bikeRentalStations from "../__mocks__/bike-rental-stations.json";
7
+ import carRentalStations from "../__mocks__/car-rental-stations.json";
8
+ import eScooterStations from "../__mocks__/e-scooter-rental-stations.json";
9
+ import { withMap } from "../../../.storybook/base-map-wrapper";
10
+ import VehicleRentalOverlay from ".";
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
+ <VehicleRentalOverlay
55
+ companies={companies}
56
+ configCompanies={configCompanies}
57
+ getStationName={getStationName}
58
+ id="test"
59
+ refreshVehicles={refreshVehicles}
60
+ setLocation={setLocation}
61
+ stations={stations}
62
+ visible={visible}
63
+ />
64
+ );
65
+
66
+ function customStationName(_, station) {
67
+ return `🛴 (ID: ${station.id})`;
68
+ }
69
+
70
+ export default {
71
+ title: "VehicleRentalOverlay",
72
+ component: VehicleRentalOverlay,
73
+ decorators: [withMap(center, INITIAL_ZOOM)]
74
+ };
75
+
76
+ export const RentalBicycles = () => (
77
+ <ZoomControlledMapWithVehicleRentalOverlay
78
+ companies={["BIKETOWN"]}
79
+ refreshVehicles={action("refresh bicycles")}
80
+ stations={bikeRentalStations}
81
+ />
82
+ );
83
+
84
+ export const RentalBicyclesVisibilityControlledByKnob = () => {
85
+ const isOverlayVisible = boolean(
86
+ "Toggle visibility of vehicle rental overlay",
87
+ false
88
+ );
89
+ return (
90
+ <ZoomControlledMapWithVehicleRentalOverlay
91
+ companies={["BIKETOWN"]}
92
+ refreshVehicles={action("refresh bicycles")}
93
+ stations={bikeRentalStations}
94
+ visible={isOverlayVisible}
95
+ />
96
+ );
97
+ };
98
+
99
+ export const RentalCars = () => (
100
+ <ZoomControlledMapWithVehicleRentalOverlay
101
+ companies={["CAR2GO"]}
102
+ refreshVehicles={action("refresh cars")}
103
+ stations={carRentalStations}
104
+ />
105
+ );
106
+
107
+ export const RentalEScooters = () => (
108
+ <ZoomControlledMapWithVehicleRentalOverlay
109
+ companies={["SHARED"]}
110
+ refreshVehicles={action("refresh E-scooters")}
111
+ stations={eScooterStations}
112
+ />
113
+ );
114
+
115
+ export const RentalEScootersWithCustomNaming = () => (
116
+ <ZoomControlledMapWithVehicleRentalOverlay
117
+ companies={["SHARED"]}
118
+ getStationName={customStationName}
119
+ refreshVehicles={action("refresh E-scooters")}
120
+ stations={eScooterStations}
121
+ />
122
+ );
package/src/index.tsx ADDED
@@ -0,0 +1,256 @@
1
+ import { MarkerWithPopup } from "@opentripplanner/base-map";
2
+ import {
3
+ Company,
4
+ ConfiguredCompany,
5
+ MapLocationActionArg,
6
+ Station
7
+ } from "@opentripplanner/types";
8
+ import { EventData } from "mapbox-gl";
9
+ import React, { useEffect, useState } from "react";
10
+ import { Layer, Popup, Source, useMap } from "react-map-gl";
11
+
12
+ import StationPopup from "./StationPopup";
13
+ import { BaseBikeRentalIcon, StationMarker } from "./styled";
14
+
15
+ // TODO: Make configurable?
16
+ const DETAILED_MARKER_CUTOFF = 16;
17
+
18
+ const getColorForStation = (v: Station) => {
19
+ if (v.isFloatingCar) return "#009cde";
20
+ if (v.isFloatingVehicle) return "#f5a729";
21
+ // TODO: nicer color to match transitive
22
+ if (v.bikesAvailable !== undefined || v.isFloatingBike) return "#f00";
23
+ return "gray";
24
+ };
25
+
26
+ const checkIfPositionInViewport = (
27
+ bounds: mapboxgl.LngLatBounds,
28
+ lat: number,
29
+ lng: number
30
+ ): boolean => {
31
+ const PADDING = 0.001;
32
+ // @ts-expect-error types appear to be wrong? version issue?
33
+ // eslint-disable-next-line no-underscore-dangle
34
+ const [sw, ne] = [bounds._sw, bounds._ne];
35
+ if (!sw || !ne) return false;
36
+
37
+ return (
38
+ lat >= sw.lat - PADDING &&
39
+ lat <= ne.lat + PADDING &&
40
+ lng >= sw.lng - PADDING &&
41
+ lng <= ne.lng + PADDING
42
+ );
43
+ };
44
+
45
+ type Props = {
46
+ /**
47
+ * A list of companies that are applicable to just this instance of the
48
+ * overlay.
49
+ */
50
+ companies?: string[];
51
+ /**
52
+ * The entire companies config array.
53
+ */
54
+ configCompanies: ConfiguredCompany[];
55
+ /**
56
+ * An id, used to make this layer uniquely identifiable
57
+ */
58
+ id: string;
59
+ /**
60
+ * An optional custom function to create a string name of a particular vehicle
61
+ * rental station. This function takes two arguments of the configCompanies
62
+ * prop and a vehicle rental station. The function must return a string.
63
+ */
64
+ getStationName?: (configCompanies: Company[], station: Station) => string;
65
+ /**
66
+ * If specified, a function that will be triggered every 30 seconds whenever this layer is
67
+ * visible.
68
+ */
69
+ refreshVehicles?: () => void;
70
+ /**
71
+ * A callback for when a user clicks on setting this stop as either the from
72
+ * or to location of a new search.
73
+ *
74
+ * This will be dispatched with the following argument:
75
+ *
76
+ * ```js
77
+ * {
78
+ * location: {
79
+ * lat: number,
80
+ * lon: number,
81
+ * name: string
82
+ * },
83
+ * locationType: "from" or "to"
84
+ * }
85
+ * ```
86
+ */
87
+ setLocation?: (arg: MapLocationActionArg) => void;
88
+ /**
89
+ * A list of the vehicle rental stations specific to this overlay instance.
90
+ */
91
+ stations: Station[];
92
+ /**
93
+ * Whether the overlay is currently visible.
94
+ */
95
+ visible?: boolean;
96
+ /**
97
+ * TODO: Add props for overriding symbols?
98
+ */
99
+ };
100
+
101
+ /**
102
+ * This vehicle rental overlay can be used to render vehicle rentals of various
103
+ * types. This layer can be configured to show different styles of markers at
104
+ * different zoom levels.
105
+ */
106
+ const VehicleRentalOverlay = ({
107
+ companies,
108
+ configCompanies,
109
+ getStationName,
110
+ id,
111
+ refreshVehicles,
112
+ setLocation,
113
+ stations,
114
+ visible
115
+ }: Props): JSX.Element => {
116
+ const { current: map } = useMap();
117
+ const zoom = map?.getZoom();
118
+ const bounds = map?.getBounds();
119
+
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
+ map?.on("mouseenter", stopLayer, () => {
137
+ map.getCanvas().style.cursor = "pointer";
138
+ });
139
+ map?.on("mouseleave", stopLayer, () => {
140
+ map.getCanvas().style.cursor = "";
141
+ });
142
+ map?.on("click", stopLayer, (event: EventData) => {
143
+ setClickedVehicle(event.features?.[0].properties);
144
+ });
145
+ });
146
+ }, [map]);
147
+
148
+ // Don't render if no map or no stops are defined.
149
+ if (visible === false || !stations || stations.length === 0) {
150
+ // Null can't be returned here -- react-map-gl dislikes null values as children
151
+ return <></>;
152
+ }
153
+
154
+ const vehiclesGeoJSON: GeoJSON.FeatureCollection = {
155
+ type: "FeatureCollection",
156
+ features: stations
157
+ .filter(
158
+ vehicle =>
159
+ // Include specified companies only if companies is specified and network info is available
160
+ !companies ||
161
+ !vehicle.networks ||
162
+ companies.includes(vehicle.networks[0])
163
+ )
164
+ .map(vehicle => ({
165
+ type: "Feature",
166
+ geometry: { type: "Point", coordinates: [vehicle.x, vehicle.y] },
167
+ properties: {
168
+ ...vehicle,
169
+ networks: JSON.stringify(vehicle.networks),
170
+ "stroke-width":
171
+ vehicle.isFloatingBike || vehicle.isFloatingVehicle ? 1 : 2,
172
+ color: getColorForStation(vehicle)
173
+ }
174
+ }))
175
+ };
176
+
177
+ return (
178
+ <>
179
+ {zoom < DETAILED_MARKER_CUTOFF && (
180
+ <Source type="geojson" data={vehiclesGeoJSON}>
181
+ <Layer
182
+ id={layerId}
183
+ paint={{
184
+ "circle-color": ["get", "color"],
185
+ "circle-opacity": 0.9,
186
+ "circle-stroke-color": "#333",
187
+ "circle-stroke-width": ["get", "stroke-width"]
188
+ }}
189
+ type="circle"
190
+ />
191
+ {/* this is where we add the symbols layer. add a second layer that gets swapped in and out dynamically */}
192
+ </Source>
193
+ )}
194
+ {zoom >= DETAILED_MARKER_CUTOFF &&
195
+ stations
196
+ .filter(station =>
197
+ checkIfPositionInViewport(bounds, station.y, station.x)
198
+ )
199
+ .map(station => (
200
+ <MarkerWithPopup
201
+ key={station.id}
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
+ position={[station.y, station.x]}
214
+ >
215
+ {station.bikesAvailable !== undefined &&
216
+ !station.isFloatingBike &&
217
+ !station.isFloatingVehicle &&
218
+ station.spacesAvailable !== undefined ? (
219
+ <BaseBikeRentalIcon
220
+ percent={
221
+ station?.bikesAvailable /
222
+ (station?.bikesAvailable + station?.spacesAvailable)
223
+ }
224
+ />
225
+ ) : (
226
+ <StationMarker width={12} color={getColorForStation(station)} />
227
+ )}
228
+ </MarkerWithPopup>
229
+ ))}
230
+ {clickedVehicle && (
231
+ <Popup
232
+ latitude={clickedVehicle.y}
233
+ longitude={clickedVehicle.x}
234
+ maxWidth="100%"
235
+ onClose={() => {
236
+ setClickedVehicle(null);
237
+ }}
238
+ >
239
+ <StationPopup
240
+ configCompanies={configCompanies}
241
+ getStationName={getStationName}
242
+ setLocation={location => {
243
+ setClickedVehicle(null);
244
+ setLocation(location);
245
+ }}
246
+ station={{
247
+ ...clickedVehicle,
248
+ networks: JSON.parse(clickedVehicle.networks)
249
+ }}
250
+ />
251
+ </Popup>
252
+ )}
253
+ </>
254
+ );
255
+ };
256
+ 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-position: center;
40
+ background-repeat: no-repeat;
41
+ background-size: contain;
42
+ height: 24px;
43
+ margin: auto;
44
+ width: 24px;
45
+ ${props => getPctIcon(props.percent)}
46
+ `;
47
+
48
+ export const StationMarker = styled(MapMarkerAlt)`
49
+ color: ${props => props.color};
50
+ height: 12;
51
+ width: 12;
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
+ }