├── .env ├── .eslintrc.js ├── .fixpackrc ├── .github ├── CODEOWNERS └── workflows │ ├── conventional-pr-title.yml │ ├── cypress-chrome.yml │ ├── cypress-edge.yml │ ├── cypress-firefox.yml │ └── test.yml ├── .gitignore ├── .husky ├── commit-msg ├── post-checkout ├── post-merge ├── post-rebase └── pre-commit ├── .imgbotconfig ├── .lintstagedrc.js ├── .nvmrc ├── .prettierrc.js ├── .stylelintrc.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.json ├── commitlint.config.js ├── cypress.config.js ├── cypress ├── e2e │ ├── Barrierfree.cy.js │ ├── DataLink.cy.js │ ├── FloorSwitcher.cy.js │ ├── Header.cy.js │ ├── MapControls.cy.js │ ├── Search.cy.js │ ├── ch.sbb.funkmesswagen.cy.js │ ├── ch.sbb.geltungsbereiche-iframe.cy.js │ ├── ch.sbb.handicap.cy.js │ ├── ch.sbb.infrastruktur.cy.js │ ├── ch.sbb.netzkarte.cy.js │ ├── ch.sbb.tarifverbundkarte.public.cy.js │ ├── consent.cy.js │ ├── metadata.cy.js │ └── permalink.cy.js ├── fixtures │ ├── busAtZoom14.png │ ├── example.json │ ├── fullTrajectory.png │ └── noBusAtZoom9.png ├── plugins │ └── index.js └── support │ ├── commands.js │ └── e2e.js ├── dependabot.yml ├── package.json ├── packages ├── es │ ├── .babelrc │ └── index.js └── wc │ └── config-overrides.js ├── public ├── README.md ├── data │ ├── pois.geojson │ └── tours.json ├── embed.html ├── favicon.png ├── iframe.html ├── index.html └── manifest.json ├── pull_request_template.md ├── scripts ├── build-es.sh ├── prepare-package.js ├── process_contentful.py ├── read-pkg-json.js └── requirements.txt ├── src ├── WebComponent.js ├── WebComponent.scss ├── __mocks__ │ ├── @jonkoops │ │ └── matomo-tracker-react.js │ ├── mapbox-gl.js │ ├── maplibre-gl.js │ └── resize-observer-polyfill.js ├── components │ ├── Autocomplete │ │ ├── Autocomplete.js │ │ ├── Autocomplete.md.scss │ │ ├── Autocomplete.scss │ │ ├── Autocomplete.test.js │ │ ├── __snapshots__ │ │ │ └── Autocomplete.test.js.snap │ │ └── index.js │ ├── Button │ │ ├── Button.js │ │ ├── Button.md.scss │ │ ├── Button.scss │ │ ├── Button.test.js │ │ ├── __snapshots__ │ │ │ └── Button.test.js.snap │ │ └── index.js │ ├── CloseButton │ │ ├── CloseButton.js │ │ └── index.js │ ├── Collapsible │ │ ├── Collapsible.js │ │ ├── Collapsible.scss │ │ └── index.js │ ├── Copyright │ │ ├── Copyright.js │ │ └── index.js │ ├── DataLink │ │ ├── DataLink.js │ │ ├── DataLink.test.js │ │ └── index.js │ ├── Dialog │ │ ├── Dialog.js │ │ ├── Dialog.test.js │ │ ├── __snapshots__ │ │ │ └── Dialog.test.js.snap │ │ └── index.js │ ├── Draw │ │ ├── Draw.js │ │ ├── Draw.test.js │ │ └── index.js │ ├── DrawButton │ │ ├── DrawButton.js │ │ ├── DrawButton.test.js │ │ └── index.js │ ├── DrawEditLinkInput │ │ ├── DrawEditLinkInput.js │ │ ├── DrawEditLinkInput.test.js │ │ └── index.js │ ├── DrawLayerMenu │ │ ├── DrawLayerMenu.js │ │ ├── DrawLayerMenu.scss │ │ ├── DrawLayerMenu.test.js │ │ ├── __snapshots__ │ │ │ └── DrawLayerMenu.test.js.snap │ │ └── index.js │ ├── DrawPermalinkButton │ │ ├── DrawPermalinkButton.js │ │ ├── DrawPermalinkButton.test.js │ │ └── index.js │ ├── DrawRemoveDialog │ │ ├── DrawRemoveDialog.js │ │ ├── DrawRemoveDialog.test.js │ │ ├── __snapshots__ │ │ │ └── DrawRemoveDialog.test.js.snap │ │ └── index.js │ ├── ExportButton │ │ ├── ExportButton.js │ │ ├── ExportButton.test.js │ │ ├── index.js │ │ ├── loader.svg │ │ └── northArrowCircle.png │ ├── FadeShadow │ │ ├── FadeShadow.js │ │ └── index.js │ ├── FeatureInformation │ │ ├── FeatureInformation.js │ │ ├── FeatureInformation.scss │ │ ├── FeatureInformation.test.js │ │ ├── FeaturePagination.scss │ │ ├── __snapshots__ │ │ │ └── FeatureInformation.test.js.snap │ │ └── index.js │ ├── FeatureMenu │ │ ├── FeatureMenu.js │ │ └── index.js │ ├── FloorSwitcher │ │ ├── FloorSwitcher.js │ │ └── index.js │ ├── Footer │ │ ├── Footer.js │ │ ├── Footer.scss │ │ ├── Footer.test.js │ │ └── index.js │ ├── Head │ │ ├── Head.js │ │ ├── Head.test.js │ │ └── index.js │ ├── Header │ │ ├── Header.js │ │ ├── Header.scss │ │ ├── Header.test.js │ │ ├── __snapshots__ │ │ │ └── Header.test.js.snap │ │ └── index.js │ ├── IconList │ │ ├── IconList.js │ │ ├── IconList.scss │ │ └── index.js │ ├── InfosButton │ │ ├── InfosButton.js │ │ ├── InfosButton.test.js │ │ └── index.js │ ├── InputIcon │ │ ├── InputIcon.js │ │ ├── InputIcon.test.js │ │ ├── __snapshots__ │ │ │ └── InputIcon.test.js.snap │ │ └── index.js │ ├── LanguageSelect │ │ ├── LanguageSelect.js │ │ ├── LanguageSelect.test.js │ │ └── index.js │ ├── LayerInfosDialog │ │ ├── LayerInfosDialog.js │ │ ├── LayerInfosDialog.test.js │ │ ├── __snapshots__ │ │ │ └── LayerInfosDialog.test.js.snap │ │ └── index.js │ ├── LegalLines │ │ ├── Contact │ │ │ ├── Contact.js │ │ │ └── index.js │ │ ├── Imprint │ │ │ ├── Imprint.js │ │ │ └── index.js │ │ ├── Legal │ │ │ ├── Legal.js │ │ │ └── index.js │ │ ├── LegalLines.js │ │ ├── LegalLines.test.js │ │ ├── __snapshots__ │ │ │ └── LegalLines.test.js.snap │ │ └── index.js │ ├── LegendCircle │ │ └── LegendCircle.js │ ├── Link │ │ ├── Link.js │ │ ├── Link.svg │ │ ├── Link.test.js │ │ ├── __snapshots__ │ │ │ └── Link.test.js.snap │ │ └── index.js │ ├── List │ │ ├── List.js │ │ ├── List.scss │ │ ├── List.test.js │ │ ├── __snapshots__ │ │ │ └── List.test.js.snap │ │ └── index.js │ ├── ListItem │ │ ├── ListItem.js │ │ ├── ListItem.test.js │ │ ├── __snapshots__ │ │ │ └── ListItem.test.js.snap │ │ └── index.js │ ├── Login │ │ ├── Login.js │ │ ├── Login.scss │ │ ├── Login.test.js │ │ ├── __snapshots__ │ │ │ └── Login.test.js.snap │ │ └── index.js │ ├── MainDialog │ │ ├── MainDialog.js │ │ └── index.js │ ├── Map │ │ ├── Map.js │ │ └── index.js │ ├── MapAccessibility │ │ ├── MapAccessibility.js │ │ ├── MapAccessibility.test.js │ │ ├── __snapshots__ │ │ │ └── MapAccessibility.test.js.snap │ │ └── index.js │ ├── MapButton │ │ ├── MapButton.js │ │ └── index.js │ ├── MapControls │ │ ├── FitExtent │ │ │ ├── FitExtent.js │ │ │ └── index.js │ │ ├── Geolocation │ │ │ ├── Geolocation.js │ │ │ └── index.js │ │ ├── MapControls.js │ │ ├── MapControls.test.js │ │ ├── MenuToggler │ │ │ ├── MenuToggler.js │ │ │ └── index.js │ │ └── index.js │ ├── MatomoTracker │ │ ├── MatomoTracker.js │ │ ├── MatomoTracker.test.js │ │ └── index.js │ ├── Menu │ │ ├── Menu.js │ │ ├── Menu.scss │ │ ├── MenuItem.js │ │ ├── MenuItem.scss │ │ ├── MenuItemHeader.js │ │ ├── MenuItemHeader.scss │ │ └── index.js │ ├── MessageListener │ │ ├── MessageListener.js │ │ └── index.js │ ├── NoDragPanWarning │ │ ├── NoDragPanWarning.js │ │ ├── NoDragPanWarning.test.js │ │ └── index.js │ ├── NoMouseWheelWarning │ │ ├── NoMouseWheelWarning.js │ │ ├── NoMouseWheelWarning.test.js │ │ └── index.js │ ├── Overlay │ │ ├── Overlay.js │ │ └── index.js │ ├── Permalink │ │ ├── Permalink.js │ │ ├── Permalink.test.js │ │ └── index.js │ ├── PermalinkButton │ │ ├── PermalinkButton.js │ │ ├── PermalinkButton.test.js │ │ ├── __snapshots__ │ │ │ └── PermalinkButton.test.js.snap │ │ └── index.js │ ├── PermalinkInput │ │ ├── PermalinkInput.js │ │ ├── PermalinkInput.test.js │ │ ├── __snapshots__ │ │ │ └── PermalinkInput.test.js.snap │ │ └── index.js │ ├── PermalinkInputCore │ │ ├── PermalinkInput.js │ │ ├── PermalinkInput.test.js │ │ ├── __snapshots__ │ │ │ └── PermalinkInput.test.js.snap │ │ └── index.js │ ├── PersonCard │ │ ├── PersonCard.js │ │ └── index.js │ ├── PhotoCarusel │ │ ├── PhotoCarusel.js │ │ ├── PhotoCarusel.test.js │ │ └── index.js │ ├── Popup │ │ ├── Popup.js │ │ └── index.js │ ├── ProjectionSelect │ │ ├── ProjectionSelect.js │ │ └── index.js │ ├── ResizeHandler │ │ ├── ResizeHandler.js │ │ ├── ResizeHandler.test.js │ │ └── index.js │ ├── SBBSwitch │ │ ├── SBBSwitch.js │ │ └── index.js │ ├── Search │ │ ├── Search.js │ │ ├── Search.scss │ │ ├── Search.svg │ │ ├── Search.test.js │ │ ├── SearchInfo.js │ │ ├── SearchInput.js │ │ ├── SearchInput.scss │ │ ├── SearchService.js │ │ ├── SearchToggle.js │ │ ├── SearchToggle.scss │ │ └── index.js │ ├── SearchInput │ │ ├── SearchInput.js │ │ ├── SearchInput.md.scss │ │ ├── SearchInput.scss │ │ ├── SearchInput.test.js │ │ ├── __snapshots__ │ │ │ └── SearchInput.test.js.snap │ │ └── index.js │ ├── Select │ │ ├── Select.js │ │ └── index.js │ ├── Share │ │ ├── Share.js │ │ ├── Share.scss │ │ ├── Share.test.js │ │ └── index.js │ ├── SharePermalinkButton │ │ ├── SharePermalinkButton.js │ │ ├── SharePermalinkButton.test.js │ │ └── index.js │ ├── StopSearchResult │ │ ├── StopSearchResult.js │ │ ├── StopSearchResult.test.js │ │ └── index.js │ ├── TarifverbundPartner │ │ ├── TarifverbundPartner.js │ │ └── index.js │ ├── TopicElements │ │ ├── TopicElements.js │ │ ├── TopicElements.test.js │ │ └── index.js │ ├── TopicInfosButton │ │ ├── TopicInfosButton.js │ │ ├── TopicInfosButton.test.js │ │ └── index.js │ ├── TopicLoader │ │ ├── TopicLoader.js │ │ ├── TopicLoader.test.js │ │ └── index.js │ ├── TopicMenu │ │ ├── TopicMenu.js │ │ ├── TopicMenu.scss │ │ ├── TopicMenu.test.js │ │ └── index.js │ ├── TopicTelephoneInfos │ │ ├── TopicTelephoneInfos.js │ │ └── index.js │ ├── TopicsMenu │ │ ├── TopicsMenu.js │ │ ├── TopicsMenu.scss │ │ ├── TopicsMenu.test.js │ │ └── index.js │ ├── TopicsMenuHeader │ │ ├── TopicsMenuHeader.js │ │ ├── TopicsMenuHeader.scss │ │ ├── TopicsMenuHeader.test.js │ │ └── index.js │ ├── TrafimageMaps │ │ ├── TrafimageMaps.js │ │ ├── TrafimageMaps.scss │ │ ├── TrafimageMaps.test.js │ │ └── index.js │ └── withResizing │ │ ├── index.js │ │ └── withResizing.js ├── config │ ├── ch.railplus.mitglieder │ │ ├── RailplusExportButton.js │ │ └── index.js │ ├── ch.sbb.beleuchtungsstaerken │ │ └── index.js │ ├── ch.sbb.casa │ │ └── index.js │ ├── ch.sbb.construction │ │ └── index.js │ ├── ch.sbb.direktverbindungen │ │ ├── DvFeatureInfo │ │ │ ├── DvFeatureInfo.js │ │ │ └── index.js │ │ ├── DvFeatureInfoTitle │ │ │ ├── DvFeatureInfoTitle.js │ │ │ └── index.js │ │ ├── DvLegendLine │ │ │ ├── DvLegendLine.js │ │ │ └── index.js │ │ ├── DvLineInfo │ │ │ ├── DvLineInfo.js │ │ │ └── index.js │ │ ├── DvLineTitle │ │ │ ├── DvLineTitle.js │ │ │ └── index.js │ │ ├── DvListButton │ │ │ ├── DvListButton.js │ │ │ ├── DvListButton.test.js │ │ │ ├── __snapshots__ │ │ │ │ └── DvListButton.test.js.snap │ │ │ └── index.js │ │ ├── dvParseFeatures.js │ │ └── index.js │ ├── ch.sbb.energie │ │ └── index.js │ ├── ch.sbb.funkmesswagen │ │ ├── MesswagenFollowButton │ │ │ ├── MesswagenFollowButton.js │ │ │ └── index.js │ │ └── index.js │ ├── ch.sbb.geltungsbereiche.iframe │ │ └── index.js │ ├── ch.sbb.geltungsbereiche.mvp │ │ └── index.js │ ├── ch.sbb.handicap │ │ ├── index.js │ │ └── index.test.js │ ├── ch.sbb.immobilien │ │ └── index.js │ ├── ch.sbb.infrastruktur │ │ └── index.js │ ├── ch.sbb.isb │ │ ├── index.js │ │ └── index.test.js │ ├── ch.sbb.netzkarte.sandbox │ │ └── index.js │ ├── ch.sbb.netzkarte │ │ └── index.js │ ├── ch.sbb.regionenkarte.public │ │ └── index.js │ ├── ch.sbb.sts │ │ ├── StsMenuToggler │ │ │ ├── StsMenuToggler.js │ │ │ └── index.js │ │ └── index.js │ ├── ch.sbb.tarifverbundkarte.public │ │ └── index.js │ ├── ch.sbb.zweitausbildung │ │ └── index.js │ ├── doc-config.json │ ├── proj4.js │ ├── searches.js │ └── topics.js ├── examples │ ├── Angular │ │ └── README.md │ ├── Betriebsregionen │ │ └── README.md │ ├── Construction │ │ └── README.md │ ├── CustomTopic │ │ ├── ExampleCode.txt │ │ └── README.md │ ├── DirektVerbindung │ │ └── README.md │ ├── DocForm.js │ ├── ExternalUpdate │ │ └── README.md │ ├── Geltungsbereiche │ │ └── README.md │ ├── OverrideTopic │ │ ├── ExampleCode.txt │ │ └── README.md │ ├── Punctuality │ │ ├── ExampleCode.txt │ │ └── README.md │ ├── Schulzug │ │ ├── ExampleCode.txt │ │ ├── README.md │ │ └── marker.png │ ├── WebComponent │ │ ├── README.md │ │ ├── getWcAttributesFromUrl.js │ │ ├── getWcCodeFromUrl.js │ │ └── webComponentAttributes.js │ ├── getCodeWithApiKey.js │ ├── getHtmlPageCode.js │ └── iframe │ │ ├── Geltungsbereiche │ │ └── README.md │ │ ├── README.md │ │ ├── Railplus │ │ └── README.md │ │ ├── getHtmlPageCode.js │ │ ├── getIframeCodeFromUrl.js │ │ └── iframeSearchParams.js ├── filters │ ├── AusbauFilters │ │ ├── AusbauFilters.js │ │ ├── AusbauFilters.test.js │ │ ├── __snapshots__ │ │ │ └── AusbauFilters.test.js.snap │ │ └── index.js │ └── index.js ├── globals.scss ├── i18n.js ├── img │ ├── Geolocate │ │ ├── Geolocate.js │ │ └── index.js │ ├── arrow.png │ ├── arrow.svg │ ├── bahnhofplanLayerIcon.png │ ├── chevronLeft.svg │ ├── circleQuestionMark.svg │ ├── clock_10_large.svg │ ├── energie_legend_pub.url.svg │ ├── favicon.png │ ├── finish_flag.png │ ├── finish_flag.svg │ ├── geltungsbereicheLegends │ │ ├── ga_legend_a3_de.svg │ │ ├── ga_legend_a3_en.svg │ │ ├── ga_legend_a3_fr.svg │ │ ├── ga_legend_a3_it.svg │ │ ├── ga_legend_a4_de.svg │ │ ├── ga_legend_a4_en.svg │ │ ├── ga_legend_a4_fr.svg │ │ ├── ga_legend_a4_it.svg │ │ ├── hta_legend_a3_de.svg │ │ ├── hta_legend_a3_en.svg │ │ ├── hta_legend_a3_fr.svg │ │ ├── hta_legend_a3_it.svg │ │ ├── hta_legend_a4_de.svg │ │ ├── hta_legend_a4_en.svg │ │ ├── hta_legend_a4_fr.svg │ │ ├── hta_legend_a4_it.svg │ │ ├── index.js │ │ ├── index.test.js │ │ ├── tk_legend_a3_de.svg │ │ ├── tk_legend_a3_en.svg │ │ ├── tk_legend_a3_fr.svg │ │ ├── tk_legend_a3_it.svg │ │ ├── tk_legend_a4_de.svg │ │ ├── tk_legend_a4_en.svg │ │ ├── tk_legend_a4_fr.svg │ │ └── tk_legend_a4_it.svg │ ├── geolocate_marker.svg │ ├── geolocate_marker_direction.svg │ ├── layers │ │ ├── BahnhofplanLayer │ │ │ ├── interactiveStationplans.png │ │ │ └── printproducts.png │ │ ├── Betriebsregionen │ │ │ ├── mitte.png │ │ │ ├── ost.png │ │ │ ├── other.png │ │ │ ├── sud.png │ │ │ └── west.png │ │ ├── NetzkartePointLayer │ │ │ ├── layer.png │ │ │ └── stations.png │ │ ├── PassagierfrequenzenLayer │ │ │ └── layer.png │ │ ├── RouteLayer │ │ │ └── layer.png │ │ └── ZoneLayer │ │ │ └── layer.png │ ├── list-icon-sbb.svg │ ├── loader.svg │ ├── mail.svg │ ├── menu.png │ ├── menu_closed.png │ ├── menu_open.png │ ├── minus.svg │ ├── northArrowCircle.png │ ├── pencil.svg │ ├── pencil_add.svg │ ├── person.svg │ ├── phone.svg │ ├── plus.svg │ ├── popups │ │ └── NetzentwicklungPopup │ │ │ ├── mail.svg │ │ │ ├── person.svg │ │ │ └── phone.svg │ ├── railplus_legend.svg │ ├── sbb-logo.svg │ ├── sbb │ │ ├── 040_hamburgermenu_102_36.svg │ │ ├── 040_schliessen_104_36.svg │ │ ├── globe_210_large.svg │ │ ├── gps-large.svg │ │ ├── gps-medium.svg │ │ ├── gps-small.svg │ │ ├── location-pin-a-medium.svg │ │ ├── location-pin-m-medium-bg-red.url.svg │ │ ├── two-finger-tap-large.svg │ │ └── user_92_large.svg │ ├── search.svg │ ├── swissbounds.svg │ ├── tarifverbund_legend.url.svg │ ├── train-day.svg │ └── train-night.svg ├── index.js ├── lang │ ├── de.json │ ├── en.json │ ├── fr.json │ └── it.json ├── layerInfos │ ├── BeleuchtungLayerInfo │ │ ├── BeleuchtungLayerInfo.js │ │ ├── index.js │ │ └── lightingMapping.js │ ├── BeleuchtungTopicInfo │ │ ├── BeleuchtungLegende.js │ │ ├── BeleuchtungTopicInfo.js │ │ └── index.js │ ├── BetriebsRegionenLayerInfo │ │ ├── BetriebsRegionenLayerInfo.js │ │ ├── BetriebsRegionenLayerInfo.scss │ │ └── index.js │ ├── BuslinesLayerInfo │ │ ├── BuslinesLayerInfo.js │ │ └── index.js │ ├── ConstructionFertigstellungLayerInfo │ │ ├── ConstructionFertigstellungLayerInfo.js │ │ └── index.js │ ├── ConstructionLayerInfo │ │ ├── ConstructionLayerInfo.js │ │ ├── ConstructionLayerInfo.scss │ │ └── index.js │ ├── ConstructionTopicInfo │ │ ├── ConstructionTopicInfo.js │ │ ├── ConstructionTopicInfo.test.js │ │ └── index.js │ ├── DrawLayerInfo │ │ ├── DrawLayerInfo.js │ │ └── index.js │ ├── DvLayerInfo │ │ ├── DvLayerInfo.js │ │ └── index.js │ ├── DvTopicInfo │ │ ├── DvTopicInfo.js │ │ └── index.js │ ├── EnergieLayerInfo │ │ ├── EnergieLayerInfo.js │ │ └── index.js │ ├── EnergiePublicTopicInfo │ │ ├── EnergiePublicTopicInfo.js │ │ └── index.js │ ├── EnergieTopicInfo │ │ ├── EnergieTopicInfo.js │ │ └── index.js │ ├── GeltungsbereicheLayerInfo │ │ ├── GeltungsbereicheLayerInfo.js │ │ └── index.js │ ├── GeltungsbereicheTopicInfo │ │ ├── GeltungsbereicheTopicInfo.js │ │ └── index.js │ ├── HandicapLayerInfo │ │ ├── HandicapLayerInfo.js │ │ └── index.js │ ├── HandicapTopicInfo │ │ ├── HandicapTopicInfo.js │ │ └── index.js │ ├── InfoFPWTopicInfo │ │ └── InfoFPWTopicInfo.js │ ├── InfrastrukturTopicInfo │ │ ├── InfrastrukturTopicInfo.js │ │ └── index.js │ ├── IsbNormalspurLayerInfo │ │ ├── IsbNormalspurLayerInfo.js │ │ ├── IsbNormalspurLayerInfo.test.js │ │ └── index.js │ ├── IsbOtherLayerInfo │ │ ├── IsbOtherLayerInfo.js │ │ ├── IsbOtherLayerInfo.test.js │ │ └── index.js │ ├── IsbSchmalspurLayerInfo │ │ ├── IsbSchmalspurLayerInfo.js │ │ ├── IsbSchmalspurLayerInfo.test.js │ │ └── index.js │ ├── IsbTVSLayerInfo │ │ ├── IsbTVSLayerInfo.js │ │ ├── IsbTVSLayerInfo.test.js │ │ ├── OperatorLegend.js │ │ ├── OperatorShortAndLongName.js │ │ └── index.js │ ├── IsbTopicInfo │ │ ├── IsbTopicInfo.js │ │ └── index.js │ ├── MapsGeoAdminLayerInfo │ │ ├── MapsGeoAdminLayerInfo.js │ │ ├── MapsGeoAdminLayerInfo.scss │ │ └── index.js │ ├── MesswagenPhotosLayerInfo │ │ ├── MesswagenPhotosLayerInfo.js │ │ └── index.js │ ├── MesswagenTopicInfo │ │ ├── MesswagenTopicInfo.js │ │ └── index.js │ ├── MobzTopicInfo │ │ └── MobzTopicInfo.js │ ├── NetzkarteTopicInfo │ │ ├── NetzkarteTopicInfo.js │ │ └── index.js │ ├── PassagierFrequenzenLayerInfo │ │ ├── PassagierFrequenzenLayerInfo.js │ │ ├── PassagierFrequenzenLayerInfo.test.js │ │ └── index.js │ ├── PunctualityLayerInfo │ │ ├── PunctualityLayerInfo.js │ │ └── index.js │ ├── RegionenkartePublicLayerInfo │ │ ├── RegionenkarteLegend.js │ │ ├── RegionenkarteLegend.test.js │ │ ├── RegionenkartePublicLayerInfo.js │ │ ├── boundary.png │ │ ├── gelb.png │ │ ├── grun.png │ │ ├── index.js │ │ ├── lila.png │ │ └── rot.png │ ├── RegionenkartePublicTopicInfo │ │ ├── RegionenkartePublicTopicInfo.js │ │ └── index.js │ ├── SandboxTopicInfo │ │ ├── SandboxTopicInfo.js │ │ └── index.js │ ├── TarifverbundkarteTopicInfo │ │ ├── TarifVerbundLegend.js │ │ ├── TarifverbundLegend.test.js │ │ ├── TarifverbundkarteTopicInfo.js │ │ └── index.js │ ├── ZweitausbildungLayerInfo │ │ ├── ZweitausbildungLayerInfo.js │ │ └── index.js │ ├── ZweitausbildungRoutesSubLayerInfo │ │ ├── ZweitausbildungRoutesSubLayerInfo.js │ │ └── index.js │ ├── ZweitausbildungSubLayerInfo │ │ ├── ZweitausbildungSubLayerInfo.js │ │ └── index.js │ ├── ZweitausbildungTopicInfo │ │ ├── ZweitausbildungTopicInfo.js │ │ └── index.js │ └── index.js ├── layers │ ├── AusbauLayer │ │ ├── AusbauLayer.js │ │ ├── AusbauLayer.test.js │ │ └── index.js │ ├── BeleuchtungsLayer │ │ ├── BeleuchtungsLayer.js │ │ └── index.js │ ├── DirektverbindungenLayer │ │ ├── DirektverbindungenLayer.js │ │ ├── DirektverbindungenLayer.test.js │ │ └── index.js │ ├── DrawLayer │ │ ├── DrawLayer.js │ │ └── index.js │ ├── GeltungsbereicheLayer │ │ ├── GeltungsbereicheLayer.js │ │ ├── GeltungsbereicheLayer.test.js │ │ └── index.js │ ├── HandicapLayer │ │ ├── HandicapLayer.js │ │ └── index.js │ ├── KilometrageLayer │ │ ├── KilometrageLayer.js │ │ └── index.js │ ├── LevelLayer │ │ ├── LevelLayer.js │ │ └── index.js │ ├── MapboxStyleLayer │ │ ├── MapboxStyleLayer.js │ │ ├── MapboxStyleLayer.test.js │ │ └── index.js │ ├── MapsGeoAdminLayer │ │ ├── MapsGeoAdminLayer.js │ │ └── index.js │ ├── MesswagenLayer │ │ ├── MesswagenLayer.js │ │ ├── MesswagenLayer.test.js │ │ └── index.js │ ├── PlatformsLayer │ │ ├── PlatformsLayer.js │ │ ├── PlatformsLayer.test.js │ │ └── index.js │ ├── RailplusLayer │ │ ├── RailplusLayer.js │ │ ├── RailplusLayer.test.js │ │ └── index.js │ ├── RegionenkarteLayer │ │ ├── RegionenkarteLayer.js │ │ ├── RegionenkarteLayer.test.js │ │ └── index.js │ ├── SchmalspurLayer │ │ ├── SchmalspurLayer.js │ │ ├── SchmalspurLayer.test.js │ │ └── index.js │ ├── StationsLayer │ │ ├── StationsLayer.js │ │ └── index.js │ ├── StsHighlightRoutesLayer │ │ ├── StsHighlightRoutesLayer.js │ │ └── index.js │ ├── StsPoisLayer │ │ ├── StsPoisLayer.js │ │ ├── StsPoisLayer.test.js │ │ ├── img │ │ │ ├── poi.png │ │ │ └── poi_hl.png │ │ └── index.js │ ├── TarifverbundkarteLayer │ │ ├── TarifverbundkarteLayer.js │ │ └── index.js │ ├── TrafimageMapboxLayer │ │ ├── TrafimageMapboxLayer.js │ │ └── index.js │ ├── TralisLayer │ │ ├── TralisLayer.js │ │ └── index.js │ ├── ZweitausbildungAbroadLayer │ │ ├── FeatureCollection.json │ │ ├── ZweitausbildungAbroadLayer.js │ │ └── index.js │ ├── ZweitausbildungPoisLayer │ │ ├── ZweitausbildungPoisLayer.js │ │ └── index.js │ ├── ZweitausbildungRoutesHighlightLayer │ │ ├── ZweitausbildungRoutesHighlightLayer.js │ │ └── index.js │ ├── ZweitausbildungRoutesLayer │ │ ├── GS100_Gotthard_Panorama_Expresses_shiplines.json │ │ ├── GS150_Gotthard_Panorama_Expresses_shiplines.json │ │ ├── ZweitausbildungRoutesLayer.js │ │ ├── getStyleLayers.js │ │ ├── index.js │ │ ├── lines.js │ │ └── lines.test.js │ └── index.js ├── menus │ ├── DirektverbindungenMenu │ │ ├── DvLayerSwitcher.js │ │ ├── DvMenu.js │ │ ├── DvMenu.test.js │ │ └── index.js │ ├── DrawMenu │ │ ├── DrawMenu.js │ │ ├── DrawMenu.test.js │ │ └── index.js │ ├── ExportMenu │ │ ├── ExportMenu.js │ │ ├── ExportResolutionSelect.js │ │ └── index.js │ ├── GaExportMenu │ │ ├── GaExportMapButton.js │ │ ├── GaExportMenu.js │ │ └── index.js │ ├── GeltungsbereicheTopicMenu │ │ ├── GeltungsbereicheTopicMenu.js │ │ ├── GeltungsbereicheTopicMenu.test.js │ │ └── index.js │ ├── IframeMenu │ │ ├── IframeMenu.js │ │ └── index.js │ ├── RailplusMenu │ │ ├── RailplusMenu.js │ │ └── index.js │ ├── ShareMenu │ │ ├── ShareMenu.js │ │ └── index.js │ ├── StsMenu │ │ ├── StsMenu.js │ │ ├── StsMenu.test.js │ │ ├── StsValidityFeatureInfo.js │ │ ├── StsValidityLayerSwitcher.js │ │ ├── index.js │ │ └── stsParseFeatureInfo.js │ └── TrackerMenu │ │ ├── TrackerMenu.js │ │ ├── TrackerMenu.scss │ │ └── index.js ├── model │ ├── app │ │ ├── actions.js │ │ ├── actions.test.js │ │ ├── reducers.js │ │ └── reducers.test.js │ ├── map │ │ ├── actions.js │ │ └── reducers.js │ └── store.js ├── ol │ └── interaction │ │ ├── DblClickDragZoom.js │ │ └── DblPointerClickZoomOut.js ├── popups │ ├── BahnhofplanPopup │ │ ├── BahnhofplanPopup.js │ │ ├── BahnhofplanPopup.scss │ │ └── index.js │ ├── BeleuchtungsPopup │ │ ├── BeleuchtungsPopup.js │ │ └── index.js │ ├── BetriebsRegionenPopup │ │ ├── BetriebsRegionenPopup.js │ │ └── index.js │ ├── BusLinePopup │ │ ├── BusLinePopup.js │ │ ├── BusLinePopup.scss │ │ └── index.js │ ├── CasaRoutePopup │ │ ├── CasaRoutePopup.js │ │ ├── CasaRoutePopup.scss │ │ └── index.js │ ├── ConstructionPopup │ │ ├── ConstructionPopup.js │ │ ├── ConstructionPopup.scss │ │ └── index.js │ ├── DeparturePopup │ │ ├── DeparturePopup.js │ │ ├── DeparturePopupContent.js │ │ ├── DeparturePopupContent.scss │ │ ├── DestinationInput.js │ │ ├── DestinationInput.scss │ │ └── index.js │ ├── DirektverbindungenPopup │ │ ├── DvPopup.js │ │ └── index.js │ ├── DrawPopup │ │ ├── DrawPopup.js │ │ └── index.js │ ├── EnergiePopup │ │ ├── EnergiePopup.js │ │ └── index.js │ ├── GeltungsbereicheGaPopup │ │ ├── GeltungsbereicheGaPopup.js │ │ ├── GeltungsbereicheLegend.js │ │ ├── GeltungsbereicheLegend.test.js │ │ └── index.js │ ├── HandicapPopup │ │ ├── HandicapPopup.js │ │ ├── HandicapPopup.scss │ │ ├── HandicapPopupElement.js │ │ ├── HandicapPopupElement.test.js │ │ ├── README.md │ │ ├── __snapshots__ │ │ │ └── HandicapPopupElement.test.js.snap │ │ └── index.js │ ├── IsbPopup │ │ ├── IsbPopup.js │ │ └── index.js │ ├── KilometragePopup │ │ ├── KilometragePopup.js │ │ └── index.js │ ├── MapsGeoAdminPopup │ │ ├── MapsGeoAdminPopup.js │ │ ├── MapsGeoAdminPopup.scss │ │ └── index.js │ ├── MesswagenPhotosPopup │ │ ├── MesswagenPhotosPopup.js │ │ └── index.js │ ├── MesswagenPopup │ │ ├── MesswagenPopup.js │ │ └── index.js │ ├── NetzkartePopup │ │ ├── NetzkartePopup.js │ │ ├── NetzkartePopup.scss │ │ ├── NetzkartePopup.test.js │ │ └── index.js │ ├── PassagierFrequenzenPopup │ │ ├── PassagierFrequenzenPopup.js │ │ └── index.js │ ├── PunctualityPopup │ │ ├── PunctualityPopup.js │ │ └── index.js │ ├── RailplusPopup │ │ ├── RailplusPopup.js │ │ ├── RailplusPopup.test.js │ │ └── index.js │ ├── RegionenkarteIntersectionPopup │ │ ├── RegionenkarteIntersectionPopup.js │ │ └── index.js │ ├── RegionenkarteSegmentPopup │ │ ├── Av.js │ │ ├── Line.js │ │ ├── LineData.js │ │ ├── Nl.js │ │ ├── Person.js │ │ ├── Region.js │ │ ├── RegionenkarteSegmentPopup.js │ │ └── index.js │ ├── SchmalspurPopup │ │ ├── SchmalspurPopup.js │ │ ├── SchmalspurPopup.test.js │ │ └── index.js │ ├── StationPopup │ │ ├── StationPopup.js │ │ └── index.js │ ├── StopPlacePopup │ │ ├── StopPlacePopup.js │ │ ├── StopPlacePopup.test.js │ │ └── index.js │ ├── TarifverbundkartePopup │ │ ├── TarifverbundkartePopup.js │ │ └── index.js │ ├── ZweitausbildungAbroadPopup │ │ ├── ZweitausbildungAbroadPopup.js │ │ └── index.js │ ├── ZweitausbildungPoisPopup │ │ ├── ZweitausbildungPoisPopup.js │ │ ├── ZweitausbildungPoisPopup.scss │ │ └── index.js │ ├── ZweitausbildungRoutesPopup │ │ ├── ZweitausbildungRoutesPopup.js │ │ ├── ZweitausbildungRoutesPopup.scss │ │ └── index.js │ └── index.js ├── searches │ ├── Betriebspunkte │ │ ├── Betriebspunkte.js │ │ └── index.js │ ├── HandicapStopFinder │ │ ├── HandicapStopFinder.js │ │ └── index.js │ ├── Lines │ │ ├── Lines.js │ │ ├── Lines.test.js │ │ └── index.js │ ├── Locations │ │ ├── Locations.js │ │ └── index.js │ ├── Municipalities │ │ ├── Municipalities.js │ │ └── index.js │ ├── Search.js │ ├── Search.test.js │ ├── StopFinder │ │ ├── StopFinder.js │ │ ├── StopFinder.test.js │ │ └── index.js │ └── index.js ├── setupTests.js ├── styleguidist │ ├── ComponentsList.js │ ├── StyleGuide.js │ └── styleguidist.css ├── themes │ ├── default.js │ └── react-ui │ │ ├── components.scss │ │ ├── index.scss │ │ ├── mixins.scss │ │ └── variables.scss └── utils │ ├── LayerService.js │ ├── LayerService.test.js │ ├── applyPermalinkVisibility.js │ ├── applyPermalinkVisibility.test.js │ ├── capitalizeFirstLetter.js │ ├── constants.js │ ├── coordinateHelper.js │ ├── coordinateHelper.test.js │ ├── exportUtils.js │ ├── exportUtils.test.js │ ├── fontUtils.js │ ├── fonts │ ├── SBBWeb-Bold.eot │ ├── SBBWeb-Bold.ttf │ ├── SBBWeb-Bold.woff │ ├── SBBWeb-Bold.woff2 │ ├── SBBWeb-Italic.eot │ ├── SBBWeb-Italic.ttf │ ├── SBBWeb-Italic.woff │ ├── SBBWeb-Italic.woff2 │ ├── SBBWeb-Light.eot │ ├── SBBWeb-Light.ttf │ ├── SBBWeb-Light.woff │ ├── SBBWeb-Light.woff2 │ ├── SBBWeb-Roman.eot │ ├── SBBWeb-Roman.ttf │ ├── SBBWeb-Roman.woff │ ├── SBBWeb-Roman.woff2 │ ├── SBBWeb-Thin.eot │ ├── SBBWeb-Thin.ttf │ ├── SBBWeb-Thin.woff │ ├── SBBWeb-Thin.woff2 │ ├── SBBWeb-UltraLight.eot │ ├── SBBWeb-UltraLight.ttf │ ├── SBBWeb-UltraLight.woff │ └── SBBWeb-UltraLight.woff2 │ ├── formatPhone.js │ ├── getDelayString.js │ ├── getDelayString.test.js │ ├── getFeatureInfoAtCoordinate.js │ ├── getGreaterNumber.js │ ├── getGreaterNumber.test.js │ ├── getIsMobileDevice.js │ ├── getLayersAsFlatArray.js │ ├── getPopupComponent.js │ ├── getQueryableLayers.js │ ├── getTrafimageFilter.js │ ├── highlightPointFeatures.js │ ├── highlightPointStyle.js │ ├── highlightPointStyle.test.js │ ├── isoToIntlVehicleCode.js │ ├── isoToIntlVehicleCode.test.js │ ├── panCenterFeature.js │ ├── redirectHelper.js │ ├── redirectHelper.test.js │ ├── removeDuplicateFeatures.js │ ├── toBase64.js │ ├── trackingUtils.js │ ├── useDisableIosElasticScrollEffect.js │ ├── useExportPrintOptions.js │ ├── useFetch.js │ ├── useHasScreenSize.js │ ├── useHighlightLayer.js │ ├── useHighlightSomeFeatures.js │ ├── useHighlightSomeFeatures.test.js │ ├── useIndexedFeatureInfo.js │ ├── useMaxCanvasSize.js │ ├── useOverlayWidth.js │ ├── usePanCenterFeature.js │ ├── usePrevious.js │ ├── useUpdateFeatureInfoOnLayerToggle.js │ └── useUpdateFeatureInfoOnLayerToggle.test.js ├── styleguide.config.js └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const eslintConfig = { 2 | env: { 3 | node: true, 4 | browser: true, 5 | es6: true, 6 | jest: true, 7 | "cypress/globals": true, 8 | }, 9 | parserOptions: { 10 | ecmaVersion: "latest", 11 | jsx: true, 12 | impliedStrict: true, 13 | }, 14 | extends: ["airbnb", "airbnb/hooks", "prettier"], 15 | plugins: ["cypress", "prettier"], 16 | rules: { 17 | "linebreak-style": 0, 18 | "arrow-body-style": 0, 19 | "default-param-last": 0, 20 | "no-restricted-exports": 0, 21 | "react/jsx-filename-extension": [ 22 | 1, 23 | { 24 | extensions: [".js", ".jsx"], 25 | }, 26 | ], 27 | "react/forbid-prop-types": "Off", 28 | "react/jsx-props-no-spreading": "Off", 29 | "react/require-default-props": "Off", 30 | "prettier/prettier": "error", 31 | }, 32 | settings: { 33 | react: { 34 | version: "detect", 35 | }, 36 | }, 37 | }; 38 | 39 | module.exports = eslintConfig; 40 | -------------------------------------------------------------------------------- /.fixpackrc: -------------------------------------------------------------------------------- 1 | { 2 | "sortToTop": [ 3 | "name", 4 | "license", 5 | "description", 6 | "version", 7 | "author", 8 | "homepage", 9 | "private", 10 | "main", 11 | "module", 12 | "files", 13 | "exports", 14 | "proxy", 15 | "dependencies", 16 | "peerDependencies", 17 | "devDependencies", 18 | "resolutions", 19 | "scripts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/conventional-pr-title.yml: -------------------------------------------------------------------------------- 1 | name: Check PR title 2 | on: 3 | pull_request: 4 | types: 5 | - opened 6 | - reopened 7 | - edited 8 | - synchronize 9 | 10 | # permissions for dependabot PRs 11 | permissions: 12 | actions: read 13 | checks: read 14 | contents: read 15 | deployments: read 16 | id-token: write 17 | issues: read 18 | discussions: read 19 | packages: read 20 | pages: read 21 | pull-requests: read 22 | repository-projects: read 23 | security-events: read 24 | statuses: write 25 | 26 | jobs: 27 | lint: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: aslafy-z/conventional-pr-title-action@v3 31 | with: 32 | success-state: Title follows the specification. 33 | failure-state: Title does not follow the specification. 34 | context-name: conventional-pr-title 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | -------------------------------------------------------------------------------- /.github/workflows/cypress-chrome.yml: -------------------------------------------------------------------------------- 1 | name: Cypress Chrome 2 | 3 | on: [push] 4 | 5 | jobs: 6 | run: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v3 11 | 12 | - name: Setup node 13 | uses: actions/setup-node@v3 14 | with: 15 | node-version-file: '.nvmrc' 16 | 17 | - name: Cypress install 18 | uses: cypress-io/github-action@v5 19 | with: 20 | runTests: false 21 | # report machine parameters 22 | - run: yarn cypress info 23 | - run: node --version 24 | - run: node -p 'os.cpus()' 25 | - run: yarn cy:test:chrome 26 | -------------------------------------------------------------------------------- /.github/workflows/cypress-edge.yml: -------------------------------------------------------------------------------- 1 | name: Cypress Edge 2 | 3 | on: [push] 4 | 5 | jobs: 6 | run: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v3 11 | 12 | - name: Setup node 13 | uses: actions/setup-node@v3 14 | with: 15 | node-version-file: '.nvmrc' 16 | 17 | - name: Cypress install 18 | uses: cypress-io/github-action@v5 19 | with: 20 | runTests: false 21 | # report machine parameters 22 | - run: yarn cypress info 23 | - run: node --version 24 | - run: node -p 'os.cpus()' 25 | - run: yarn cy:test:edge 26 | -------------------------------------------------------------------------------- /.github/workflows/cypress-firefox.yml: -------------------------------------------------------------------------------- 1 | name: Cypress Firefox 2 | 3 | on: [push] 4 | 5 | jobs: 6 | run: 7 | runs-on: ubuntu-latest 8 | container: 9 | # See images at https://github.com/cypress-io/cypress-docker-images/tree/master/browsers 10 | image: cypress/browsers:latest 11 | options: --user 1001 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup node 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version-file: '.nvmrc' 20 | 21 | - name: Cypress install 22 | uses: cypress-io/github-action@v5 23 | with: 24 | runTests: false 25 | # report machine parameters 26 | - run: yarn cypress info 27 | - run: node --version 28 | - run: node -p 'os.cpus()' 29 | - run: yarn cy:test:firefox 30 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Lint / Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version-file: '.nvmrc' 14 | - run: npm install -g yarn 15 | - run: yarn install --frozen-lockfile 16 | - run: yarn lint 17 | - run: yarn test 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # others 7 | /doc 8 | /docjs 9 | 10 | # testing 11 | /coverage 12 | 13 | # production 14 | /build 15 | 16 | #scripts 17 | /scripts/venv 18 | 19 | # misc 20 | .vscode 21 | .DS_Store 22 | .env.local 23 | .env.development.local 24 | .env.test.local 25 | .env.production.local 26 | 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | *.swp 32 | *.swo 33 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | yarn commitlint --edit $1 2 | -------------------------------------------------------------------------------- /.husky/post-checkout: -------------------------------------------------------------------------------- 1 | yarn install --frozen-lockfile 2 | -------------------------------------------------------------------------------- /.husky/post-merge: -------------------------------------------------------------------------------- 1 | yarn install --frozen-lockfile 2 | -------------------------------------------------------------------------------- /.husky/post-rebase: -------------------------------------------------------------------------------- 1 | yarn install --frozen-lockfile 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | CI=true npx lint-staged 2 | -------------------------------------------------------------------------------- /.imgbotconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignoredFiles": ["cypress/*"], 3 | "aggressiveCompression": "false" 4 | } 5 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | const lintStaged = { 2 | "*.md": ["prettier --write"], 3 | "src/lang/*.json": ["yarn sort", "git add"], 4 | "(src|__mocks__)/**/*.js": [ 5 | "eslint --fix", 6 | "prettier --write", 7 | "git add", 8 | "yarn test --bail --findRelatedTests", 9 | ], 10 | "src/**/*.scss": ["stylelint --fix", "git add"], 11 | "package.json": ["yarn fixpack", "git add"], 12 | }; 13 | module.exports = lintStaged; 14 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | const stylelint = { 2 | plugins: ["stylelint-scss"], 3 | extends: ["stylelint-config-standard", "stylelint-config-recommended-scss"], 4 | rules: { 5 | "import-notation": "string", 6 | "scss/at-import-partial-extension": "always", 7 | "scss/load-partial-extension": null, 8 | "selector-pseudo-class-no-unknown": [ 9 | true, 10 | { 11 | ignorePseudoClasses: ["export"], 12 | }, 13 | ], 14 | "property-no-unknown": [ 15 | true, 16 | { 17 | ignoreProperties: ["/transitiondelay/"], 18 | }, 19 | ], 20 | }, 21 | }; 22 | 23 | module.exports = stylelint; 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 geOps 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | // This babel config is used by babel-eslint (using yarn lint and yarn build) 2 | { 3 | "presets": ["@babel/preset-env", "@babel/preset-react"] 4 | } 5 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ["@commitlint/config-conventional"] }; 2 | -------------------------------------------------------------------------------- /cypress.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | const { defineConfig } = require("cypress"); 3 | 4 | module.exports = defineConfig({ 5 | requestTimeout: 20000, 6 | defaultCommandTimeout: 20000, 7 | retries: { 8 | // Configure retry attempts for `cypress run` 9 | // Default is 0 10 | runMode: 3, 11 | // Configure retry attempts for `cypress open` 12 | // Default is 0 13 | openMode: 0, 14 | }, 15 | e2e: { 16 | baseUrl: "http://localhost:3000", 17 | numTestsKeptInMemory: 0, 18 | chromeWebSecurity: false, 19 | video: false, 20 | 21 | // We've imported your old cypress plugins here. 22 | // You may want to clean this up later by importing these. 23 | setupNodeEvents(on, config) { 24 | // eslint-disable-next-line global-require, import/extensions 25 | return require("./cypress/plugins/index.js")(on, config); 26 | }, 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /cypress/e2e/ch.sbb.geltungsbereiche-iframe.cy.js: -------------------------------------------------------------------------------- 1 | describe("Geltungsbereiche iframe topic", () => { 2 | beforeEach(() => { 3 | cy.consent(); 4 | cy.visit("/ch.sbb.geltungsbereiche-iframe"); 5 | }); 6 | 7 | it("should show/hide some elements", () => { 8 | cy.viewport(1440, 900); 9 | cy.get(".tm-trafimage-maps").should("exist"); 10 | 11 | // header + telephoneinfos 12 | cy.get(".wkp-header").should("not.exist"); 13 | // search 14 | cy.get(".wkp-search").should("not.exist"); 15 | // telephoneInfos 16 | cy.get('[data-testid="wkp-tel-infos"]').should("not.exist"); 17 | // menu 18 | cy.get(".wkp-topics-menu").should("not.exist"); 19 | // baseLayerSwitcher 20 | cy.get(".rs-base-layer-switcher").should("not.exist"); 21 | // mapControls 22 | cy.get(".wkp-map-controls").should("exist"); 23 | // geolocationButton 24 | cy.get(".wkp-fit-extent").should("exist"); 25 | // fitExtent 26 | cy.get(".wkp-geolocation").should("not.exist"); 27 | // footer 28 | cy.get(".wkp-footer").should("not.exist"); 29 | 30 | // topicMenu 31 | cy.get(".wkp-gb-topic-menu").should("exist"); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /cypress/e2e/ch.sbb.handicap.cy.js: -------------------------------------------------------------------------------- 1 | import lang from "../../src/lang/de.json"; 2 | 3 | describe("Handicap Topic", () => { 4 | beforeEach(() => { 5 | cy.consent(); 6 | cy.visit("/ch.sbb.handicap"); 7 | cy.get(".wkp-menu-header ").click(); 8 | }); 9 | 10 | it("should open a popup on station search.", () => { 11 | cy.viewport(1440, 900); 12 | cy.get(".wkp-feature-information").should("not.exist"); 13 | 14 | // In handicap topic 15 | cy.get(".wkp-menu-header .wkp-menu-title", { timeout: 10000 }).contains( 16 | lang["ch.sbb.handicap"], 17 | ); 18 | 19 | cy.get(".wkp-search-input input") 20 | .focus() 21 | .type("B") 22 | .type("e") 23 | .type("r") 24 | .type("n"); 25 | 26 | cy.wait(1000); 27 | 28 | cy.get("#react-autowhatever-1-section-0-item-0", { timeout: 10000 }).click({ 29 | force: true, 30 | }); 31 | 32 | // Popup is opened. 33 | cy.get(".wkp-feature-information", { timeout: 20000 }).should("be.visible"); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /cypress/e2e/ch.sbb.infrastruktur.cy.js: -------------------------------------------------------------------------------- 1 | describe("Infrastruktur topic", () => { 2 | beforeEach(() => { 3 | cy.consent(); 4 | }); 5 | 6 | [ 7 | "/ch.sbb.infrastruktur", 8 | "/ch.sbb.infrastruktur?layers=", // TRAFKLEIN-726 9 | ].forEach((url) => { 10 | describe(`when loading directly the topic witht url ${url}`, () => { 11 | beforeEach(() => { 12 | cy.visit(url); 13 | }); 14 | 15 | it("should display the baselayer", () => { 16 | cy.viewport(1440, 900); 17 | cy.get(".maplibregl-map").should("exist"); 18 | }); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /cypress/e2e/ch.sbb.tarifverbundkarte.public.cy.js: -------------------------------------------------------------------------------- 1 | describe("Tarifverbundkarte topic", () => { 2 | beforeEach(() => { 3 | cy.consent(); 4 | }); 5 | 6 | [ 7 | "/ch.sbb.tarifverbundkarte.public", 8 | "/ch.sbb.tarifverbundkarte.public?layers=", // TRAFKLEIN-726 9 | ].forEach((url) => { 10 | describe(`when loading directly the topic witht url ${url}`, () => { 11 | beforeEach(() => { 12 | cy.visit(url); 13 | }); 14 | 15 | it("should display the baselayer", () => { 16 | cy.viewport(1440, 900); 17 | cy.get(".maplibregl-map").should("exist"); 18 | }); 19 | 20 | it("should open a popup on click", () => { 21 | cy.viewport(1440, 900); 22 | cy.get(".maplibregl-map canvas").then(() => { 23 | cy.wait(5000); 24 | cy.get(".rs-map").click("center"); 25 | cy.get(".wkp-feature-information").should("be.visible"); 26 | }); 27 | }); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /cypress/e2e/consent.cy.js: -------------------------------------------------------------------------------- 1 | describe("consent", () => { 2 | it(`opens consent banner`, () => { 3 | cy.visit(""); 4 | cy.get("#onetrust-accept-btn-handler", { timeout: 10000 }).click(); 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /cypress/fixtures/busAtZoom14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/cypress/fixtures/busAtZoom14.png -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /cypress/fixtures/fullTrajectory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/cypress/fixtures/fullTrajectory.png -------------------------------------------------------------------------------- /cypress/fixtures/noBusAtZoom9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/cypress/fixtures/noBusAtZoom9.png -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | /// 3 | /// 4 | // @ts-check 5 | const resemble = require("resemblejs"); 6 | 7 | /** 8 | * @type {Cypress.PluginConfig} 9 | */ 10 | module.exports = (on) => { 11 | on("task", { 12 | log(message) { 13 | // eslint-disable-next-line no-console 14 | console.log(message); 15 | return Promise.resolve(null); 16 | }, 17 | comparePng({ current, fixture }) { 18 | const diff = resemble(fixture).compareTo(current); 19 | // eslint-disable-next-line no-promise-executor-return 20 | return new Promise((resolve) => diff.onComplete(resolve)); 21 | }, 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /dependabot.yml: -------------------------------------------------------------------------------- 1 | # Cnfiguration file that: 2 | # - Enables security updates 3 | # - Disables version-updates 4 | 5 | version: 2 6 | updates: 7 | - package-ecosystem: 'npm' 8 | directory: '/' 9 | schedule: 10 | interval: 'daily' 11 | # Disable version updates for npm dependencies 12 | open-pull-requests-limit: 0 13 | -------------------------------------------------------------------------------- /packages/es/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "modules": false }], 4 | "@babel/preset-react" 5 | ], 6 | "ignore": ["**/*.test.js"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/es/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-unresolved 2 | import TrafimageMaps from './components/TrafimageMaps'; 3 | 4 | export default TrafimageMaps; 5 | -------------------------------------------------------------------------------- /public/embed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | Trafimage Webkarten 10 | 11 | 18 | 19 | 20 |
21 |

Test web component in custom page

22 |
23 |
24 | 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/public/favicon.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Geops Starter", 3 | "name": "geops-react-spatial-starter", 4 | "icons": [ 5 | { 6 | "src": "favicon.png", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | # How to 2 | 3 | 4 | 5 | # Others 6 | 7 | 8 | 9 | - [ ] It's not a hack or at least an unauthorized hack :). 10 | - [ ] The images added are optimized. 11 | - [ ] Everything in ticket description has been fixed. 12 | - [ ] The author of the MR has made its own review before assigning the reviewer. 13 | - [ ] The title means something for a human being and follows the [conventional commits](https://www.conventionalcommits.org/) specification. 14 | - [ ] The title contains [WIP] if it's necessary. 15 | - [ ] Labels applied. if it's a release? a hotfix? 16 | - [ ] Check the preview app on iOS before merge: Is everything working? SVGs visible? 17 | - [ ] Tests added. 18 | -------------------------------------------------------------------------------- /scripts/build-es.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### 4 | # This script build a folder you can use via import es6 module. 5 | ### 6 | 7 | # Remove the old files. 8 | if rm -rf build/es; then 9 | echo "Build folder emptied." 10 | else 11 | echo "Empty build/es folder failed." 12 | exit 1 13 | fi 14 | 15 | # Transform all es6 files to es5. 16 | # Transform jsx to js. 17 | # Transform .scss import to .css import. 18 | # Creates also js sourcemaps. 19 | if NODE_ENV=production ./node_modules/.bin/babel src --config-file './packages/es/.babelrc' --out-dir build/es --source-maps --copy-files; then 20 | # if NODE_ENV=production yarn esbuild; then 21 | echo "Transformation suceeds." 22 | else 23 | echo "Compile the 'src' directory failed." 24 | exit 1 25 | fi 26 | 27 | # Remove all tests files. 28 | if find build/es -regextype posix-extended -regex '.*(test).*js.?(map|snap)?$' -type f | xargs rm -f; then 29 | echo "All test files removed." 30 | else 31 | echo "Remove tests files failed." 32 | exit 1 33 | fi 34 | 35 | # Move the index.js file to build/es 36 | cp -f packages/es/index.js build/es 37 | -------------------------------------------------------------------------------- /scripts/prepare-package.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const pkg = require("../package.json"); 4 | 5 | const buildDir = path.resolve(__dirname, "../build/"); 6 | 7 | // Write out simplified package.json to be able to publish only the build 8 | // folder content. 9 | delete pkg.scripts; 10 | delete pkg.devDependencies; 11 | delete pkg.style; 12 | delete pkg.eslintConfig; 13 | delete pkg.private; 14 | delete pkg.dependencies["react-styleguidist"]; 15 | 16 | // Write the good index file import for es6 module loading. 17 | pkg.main = "bundle.js"; 18 | fs.writeFileSync( 19 | path.join(buildDir, "package.json"), 20 | JSON.stringify(pkg, null, 2), 21 | "utf-8", 22 | ); 23 | -------------------------------------------------------------------------------- /scripts/read-pkg-json.js: -------------------------------------------------------------------------------- 1 | const pjson = require("../package.json"); 2 | 3 | const { peerDependencies } = pjson; 4 | 5 | const packageKeys = Object.keys(peerDependencies || {}); 6 | 7 | const arg = process.argv[2]; 8 | 9 | if (arg === "add" && packageKeys.length > 0) { 10 | console.log( 11 | `yarn add ${packageKeys 12 | .map((p) => `${p}@${peerDependencies[p]}`) 13 | .join(" ")}`, 14 | ); 15 | } else if (arg === "remove" && packageKeys.length > 0) { 16 | console.log( 17 | `rm -rf ${packageKeys.map((p) => `node_modules/${p}`).join(" ")}`, 18 | ); 19 | } else { 20 | console.log('echo "wrong argument."'); 21 | } 22 | -------------------------------------------------------------------------------- /scripts/requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2022.12.7 2 | charset-normalizer==2.0.7 3 | geojson==2.5.0 4 | idna==3.3 5 | psycopg2-binary==2.9.1 6 | requests==2.26.0 7 | urllib3==1.26.7 8 | -------------------------------------------------------------------------------- /src/__mocks__/@jonkoops/matomo-tracker-react.js: -------------------------------------------------------------------------------- 1 | const { 2 | MatomoProvider: MP, 3 | MatomoContext: MC, 4 | useMatomo: uM, 5 | createInstance: cI, 6 | } = require("@jonkoops/matomo-tracker-react"); 7 | 8 | const createInstanceTmp = jest.fn((params) => { 9 | const inst = cI(params); 10 | inst.pushInstruction = jest.fn(); 11 | 12 | // Store the lastinstance for testing 13 | createInstanceTmp.lastInstance = inst; 14 | return inst; 15 | }); 16 | 17 | export const createInstance = createInstanceTmp; 18 | export const useMatomo = uM; 19 | export const MatomoContext = MC; 20 | export const MatomoProvider = MP; 21 | -------------------------------------------------------------------------------- /src/__mocks__/mapbox-gl.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | class Map { 3 | isStyleLoaded() {} 4 | 5 | loaded() {} 6 | 7 | getCanvas() {} 8 | 9 | on() {} 10 | 11 | off() {} 12 | 13 | once() {} 14 | 15 | getLayer() {} 16 | } 17 | module.exports = { 18 | Map, 19 | }; 20 | -------------------------------------------------------------------------------- /src/__mocks__/maplibre-gl.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | class Map { 3 | isStyleLoaded() {} 4 | 5 | loaded() {} 6 | 7 | getCanvas() {} 8 | 9 | on() {} 10 | 11 | off() {} 12 | 13 | once() {} 14 | 15 | getLayer() {} 16 | } 17 | module.exports = { 18 | Map, 19 | }; 20 | -------------------------------------------------------------------------------- /src/__mocks__/resize-observer-polyfill.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default class ResizeObserver { 3 | constructor(onResize) { 4 | ResizeObserver.onResize = onResize; 5 | } 6 | observe() {} 7 | unobserve() {} 8 | disconnect() {} 9 | } 10 | -------------------------------------------------------------------------------- /src/components/Autocomplete/Autocomplete.md.scss: -------------------------------------------------------------------------------- 1 | .tm-autocomplete-example { 2 | position: relative; 3 | width: 420px; 4 | height: 44px; 5 | 6 | .tm-button { 7 | padding: 0 $padding-base; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/Autocomplete/Autocomplete.scss: -------------------------------------------------------------------------------- 1 | .tm-autocomplete { 2 | .tm-search { 3 | color: $gray-dark; 4 | } 5 | 6 | .tm-autocomplete-results, 7 | .tm-search-input { 8 | border: 2px solid $gray-lighter; 9 | } 10 | 11 | .tm-bt-clear { 12 | color: black; 13 | } 14 | 15 | .tm-autocomplete-results { 16 | position: absolute; 17 | margin: auto; 18 | border-top: none; 19 | top: calc(100% - 1px); 20 | left: 0; 21 | right: 0; 22 | background-color: white; 23 | overflow: auto; 24 | max-height: 400px; 25 | z-index: $zindex-higher; 26 | 27 | p { 28 | font-size: 0.9em; 29 | font-weight: bold; 30 | color: $gray-light; 31 | padding: 5px 15px 2px; 32 | margin: 0; 33 | } 34 | 35 | ul:first-child { 36 | margin-top: 8px; 37 | } 38 | 39 | li { 40 | &:hover, 41 | &:focus { 42 | color: $btn-primary-color; 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/Autocomplete/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Autocomplete"; 2 | -------------------------------------------------------------------------------- /src/components/Button/Button.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | const propTypes = { 5 | children: PropTypes.node.isRequired, 6 | onClick: PropTypes.func.isRequired, 7 | disabled: PropTypes.bool, 8 | }; 9 | 10 | /** 11 | * This component displays a simple button. 12 | * @ignore 13 | */ 14 | function Button({ children, disabled = false, onClick, ...props }) { 15 | return ( 16 |
{ 21 | if (disabled) { 22 | return; 23 | } 24 | onClick(e); 25 | }} 26 | onKeyPress={(e) => e.which === 13 && onClick(e)} 27 | > 28 | {children} 29 |
30 | ); 31 | } 32 | 33 | Button.propTypes = propTypes; 34 | 35 | export default Button; 36 | -------------------------------------------------------------------------------- /src/components/Button/Button.md.scss: -------------------------------------------------------------------------------- 1 | .tm-button-example { 2 | cursor: pointer; 3 | color: $btn-primary-color; 4 | border-radius: 15px; 5 | width: 100px; 6 | text-align: center; 7 | border: 3px solid $btn-primary-color; 8 | padding: 10px; 9 | transition: background-color 0.1s ease; 10 | 11 | &:hover { 12 | background-color: $gray-lighter; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Button/Button.scss: -------------------------------------------------------------------------------- 1 | .tm-button { 2 | cursor: pointer; 3 | height: $btn-size-base; 4 | width: $btn-size-base; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | transition: background-color 0.5s ease, color 0.5s ease; 9 | } 10 | 11 | .tm-round-blue { 12 | background: $btn-secondary-color; 13 | border-radius: 50%; 14 | color: white; 15 | } 16 | 17 | .tm-square-white { 18 | border: 1px solid #aaa; 19 | background: white; 20 | color: #aaa; 21 | 22 | &.tm-active { 23 | background-color: $btn-secondary-color-active; 24 | } 25 | } 26 | 27 | .tm-round-grey-hover-primary { 28 | background-color: $gray-light; 29 | border-radius: 50%; 30 | color: white; 31 | 32 | &:hover { 33 | background-color: $btn-primary-color-hover; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Button/Button.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { fireEvent, render } from "@testing-library/react"; 3 | import Button from "./Button"; 4 | 5 | const funcs = { 6 | onClick: () => {}, 7 | }; 8 | 9 | test("Button should match snapshot.", () => { 10 | const { container } = render( 11 | , 18 | ); 19 | expect(container.innerHTML).toMatchSnapshot(); 20 | }); 21 | 22 | test("Button should update.", () => { 23 | const spy = jest.spyOn(funcs, "onClick"); 24 | const { container } = render( 25 | , 28 | ); 29 | 30 | fireEvent.click(container.querySelector(".tm-class")); 31 | fireEvent.keyPress(container.querySelector(".tm-class"), { 32 | key: "Enter", 33 | code: "Enter", 34 | charCode: 13, 35 | which: 13, 36 | }); 37 | 38 | expect(spy).toHaveBeenCalledTimes(2); 39 | }); 40 | -------------------------------------------------------------------------------- /src/components/Button/__snapshots__/Button.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Button should match snapshot. 1`] = ` 4 |
9 | + 10 |
11 | `; 12 | -------------------------------------------------------------------------------- /src/components/Button/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Button"; 2 | -------------------------------------------------------------------------------- /src/components/CloseButton/CloseButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types"; 2 | import { IconButton } from "@mui/material"; 3 | import React from "react"; 4 | import { MdClose } from "react-icons/md"; 5 | import { useTranslation } from "react-i18next"; 6 | 7 | function CloseButton({ children, ...props }) { 8 | const { t } = useTranslation(); 9 | return ( 10 | 20 | {children || } 21 | 22 | ); 23 | } 24 | 25 | CloseButton.propTypes = { 26 | children: PropTypes.node, 27 | }; 28 | 29 | export default CloseButton; 30 | -------------------------------------------------------------------------------- /src/components/CloseButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CloseButton"; 2 | -------------------------------------------------------------------------------- /src/components/Collapsible/Collapsible.scss: -------------------------------------------------------------------------------- 1 | .wkp-collapsible-vertical { 2 | overflow: hidden auto; 3 | transition: max-height 0.3s ease; 4 | } 5 | 6 | .wkp-collapsible-hide-scrollbar::-webkit-scrollbar { 7 | display: none; 8 | } 9 | 10 | .wkp-collapsible-horizontal { 11 | overflow: hidden; 12 | transition: max-width 0.3s ease; 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Collapsible/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Collapsible"; 2 | -------------------------------------------------------------------------------- /src/components/Copyright/Copyright.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTranslation } from "react-i18next"; 3 | import { useSelector } from "react-redux"; 4 | import RsCopyright from "react-spatial/components/Copyright"; 5 | import { makeStyles } from "@mui/styles"; 6 | 7 | const useStyles = makeStyles(() => ({ 8 | wrapper: { 9 | position: "absolute", 10 | right: 5, 11 | paddingLeft: 5, 12 | fontSize: 12, 13 | bottom: ({ footer }) => (footer ? 42 : 2), 14 | "& a:not(.MuiIconButton-root)": { 15 | whiteSpace: "nowrap", 16 | textDecoration: "none !important", 17 | }, 18 | }, 19 | })); 20 | 21 | function Copyright() { 22 | const { t } = useTranslation(); 23 | const topic = useSelector((state) => state.app.activeTopic); 24 | const map = useSelector((state) => state.app.map); 25 | const classes = useStyles({ footer: topic.elements.footer }); 26 | 27 | return ( 28 |
29 | `${t("Geodaten")} ${f.join(", ")}`} 32 | /> 33 |
34 | ); 35 | } 36 | 37 | export default Copyright; 38 | -------------------------------------------------------------------------------- /src/components/Copyright/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Copyright"; 2 | -------------------------------------------------------------------------------- /src/components/DataLink/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DataLink"; 2 | -------------------------------------------------------------------------------- /src/components/Dialog/Dialog.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Provider } from "react-redux"; 4 | 5 | import { Map, View } from "ol"; 6 | import { render } from "@testing-library/react"; 7 | import { ThemeProvider } from "@mui/material"; 8 | import theme from "../../themes/default"; 9 | import Dialog from "./Dialog"; 10 | 11 | describe("Dialog", () => { 12 | let store; 13 | let map; 14 | 15 | beforeEach(() => { 16 | store = global.mockStore({ 17 | map: {}, 18 | app: {}, 19 | }); 20 | map = new Map({ view: new View({}) }); 21 | }); 22 | 23 | test("should match snapshot.", () => { 24 | const component = render( 25 | 26 | 27 | 28 | 29 | , 30 | ); 31 | expect(component.container.innerHTML).toMatchSnapshot(); 32 | }); 33 | 34 | // TODO: test focus document.activeElement on popup close 35 | }); 36 | -------------------------------------------------------------------------------- /src/components/Dialog/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Dialog"; 2 | -------------------------------------------------------------------------------- /src/components/Draw/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Draw"; 2 | -------------------------------------------------------------------------------- /src/components/DrawButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DrawButton"; 2 | -------------------------------------------------------------------------------- /src/components/DrawEditLinkInput/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DrawEditLinkInput"; 2 | -------------------------------------------------------------------------------- /src/components/DrawLayerMenu/DrawLayerMenu.scss: -------------------------------------------------------------------------------- 1 | .wkp-draw-layer-menu { 2 | .wkp-layer-tree, 3 | .wkp-layer-tree .rs-layer-tree-item { 4 | margin: 0; 5 | 6 | .rs-layer-tree-toggle { 7 | max-width: none; 8 | } 9 | 10 | .rs-checkbox { 11 | margin-right: 7px; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/DrawLayerMenu/__snapshots__/DrawLayerMenu.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`DrawLayerMenu should match snapshot. should return null 1`] = `""`; 4 | 5 | exports[`DrawLayerMenu should match snapshot. using the layerService property 1`] = ` 6 |
7 |
8 |
9 |
10 |
11 |
12 | `; 13 | -------------------------------------------------------------------------------- /src/components/DrawLayerMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DrawLayerMenu"; 2 | -------------------------------------------------------------------------------- /src/components/DrawPermalinkButton/DrawPermalinkButton.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Provider } from "react-redux"; 3 | 4 | import { ThemeProvider } from "@mui/material"; 5 | import { render } from "@testing-library/react"; 6 | import theme from "../../themes/default"; 7 | import DrawPermalinkButton from "."; 8 | 9 | describe("DrawPermalinkButton", () => { 10 | let store; 11 | test("should match snapshot.", () => { 12 | store = global.mockStore({ 13 | map: {}, 14 | app: { drawIds: {} }, 15 | }); 16 | 17 | const component = render( 18 | 19 | 20 | 21 | 22 | , 23 | ); 24 | expect( 25 | component.container.querySelectorAll(".wkp-permalink-bt").length, 26 | ).toBe(1); 27 | expect(component.container.querySelectorAll("button").length).toBe(1); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/components/DrawPermalinkButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DrawPermalinkButton"; 2 | -------------------------------------------------------------------------------- /src/components/DrawRemoveDialog/index.js: -------------------------------------------------------------------------------- 1 | export { default, NAME } from "./DrawRemoveDialog"; 2 | -------------------------------------------------------------------------------- /src/components/ExportButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ExportButton"; 2 | -------------------------------------------------------------------------------- /src/components/ExportButton/northArrowCircle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/components/ExportButton/northArrowCircle.png -------------------------------------------------------------------------------- /src/components/FadeShadow/FadeShadow.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { makeStyles } from "@mui/styles"; 3 | 4 | const useStyles = makeStyles(() => { 5 | return { 6 | topShadow: { 7 | position: "absolute", 8 | overflow: "hidden", 9 | width: "100%", 10 | height: 80, 11 | pointerEvents: "none", 12 | "&::after": { 13 | content: '""', 14 | position: "absolute", 15 | left: 0, 16 | right: 0, 17 | top: -20, 18 | bottom: 0, 19 | zIndex: 1, 20 | height: 20, 21 | background: "#18191B", 22 | opacity: 0.2, 23 | borderRadius: "600px / 50px", 24 | filter: "blur(15px)", 25 | }, 26 | }, 27 | }; 28 | }); 29 | 30 | // Unused component for now (does not work on iOS), but we keep it for now in case it becomes iOS compatible 31 | function FadeShadow() { 32 | const classes = useStyles(); 33 | return
; 34 | } 35 | 36 | export default FadeShadow; 37 | -------------------------------------------------------------------------------- /src/components/FadeShadow/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./FadeShadow"; 2 | -------------------------------------------------------------------------------- /src/components/FeatureInformation/FeatureInformation.scss: -------------------------------------------------------------------------------- 1 | .wkp-feature-information { 2 | .wkp-feature-information-header { 3 | display: flex; 4 | align-items: center; 5 | justify-content: space-between; 6 | background-color: #f5f5f5; 7 | font-family: SBBWeb-Bold, Arial, sans-serif; 8 | min-height: 40px; 9 | max-height: 60px; 10 | 11 | > span { 12 | display: flex; 13 | align-items: center; 14 | padding: 10px 0 10px 10px; 15 | height: 100%; 16 | overflow: hidden; 17 | } 18 | } 19 | 20 | .wkp-feature-information-body { 21 | > div:first-child { 22 | padding: 10px; 23 | min-width: 220px; 24 | overflow: auto; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/FeatureInformation/FeaturePagination.scss: -------------------------------------------------------------------------------- 1 | .wkp-pagination-wrapper { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | height: 35px; 6 | 7 | .wkp-pagination-button-wrapper { 8 | padding: 0 10px; 9 | height: 25px; 10 | width: 25px; 11 | 12 | .wkp-pagination-button { 13 | height: 25px; 14 | width: 25px; 15 | cursor: pointer; 16 | 17 | svg { 18 | height: 25px; 19 | width: 25px; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/FeatureInformation/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./FeatureInformation"; 2 | -------------------------------------------------------------------------------- /src/components/FeatureMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./FeatureMenu"; 2 | -------------------------------------------------------------------------------- /src/components/FloorSwitcher/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./FloorSwitcher"; 2 | -------------------------------------------------------------------------------- /src/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Footer"; 2 | -------------------------------------------------------------------------------- /src/components/Head/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Head"; 2 | -------------------------------------------------------------------------------- /src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import Login from "../Login"; 4 | import { ReactComponent as SBBLogo } from "../../img/sbb-logo.svg"; 5 | import LanguageSelect from "../LanguageSelect"; 6 | 7 | import "./Header.scss"; 8 | 9 | const propTypes = { 10 | loginUrl: PropTypes.string, 11 | }; 12 | 13 | function Header({ loginUrl }) { 14 | return ( 15 |
16 |
17 | 18 | 19 |
20 | 21 |
22 |
23 |
24 | ); 25 | } 26 | 27 | Header.propTypes = propTypes; 28 | 29 | export default React.memo(Header); 30 | -------------------------------------------------------------------------------- /src/components/Header/Header.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { Provider } from "react-redux"; 4 | 5 | import { render } from "@testing-library/react"; 6 | import { ThemeProvider } from "@mui/material"; 7 | import theme from "../../themes/default"; 8 | import Header from "./Header"; 9 | 10 | describe("Header", () => { 11 | test("match snapshots", () => { 12 | const store = global.mockStore({ 13 | map: {}, 14 | app: { language: "fr" }, 15 | }); 16 | const { container } = render( 17 | 18 | 19 |
20 | 21 | , 22 | ); 23 | expect(container.innerHTML).toMatchSnapshot(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Header"; 2 | -------------------------------------------------------------------------------- /src/components/IconList/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./IconList"; 2 | -------------------------------------------------------------------------------- /src/components/InfosButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./InfosButton"; 2 | -------------------------------------------------------------------------------- /src/components/InputIcon/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./InputIcon"; 2 | -------------------------------------------------------------------------------- /src/components/LanguageSelect/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./LanguageSelect"; 2 | -------------------------------------------------------------------------------- /src/components/LayerInfosDialog/index.js: -------------------------------------------------------------------------------- 1 | export { default, NAME } from "./LayerInfosDialog"; 2 | -------------------------------------------------------------------------------- /src/components/LegalLines/Contact/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Contact"; 2 | -------------------------------------------------------------------------------- /src/components/LegalLines/Imprint/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Imprint"; 2 | -------------------------------------------------------------------------------- /src/components/LegalLines/Legal/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Legal"; 2 | -------------------------------------------------------------------------------- /src/components/LegalLines/LegalLines.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import Contact from "./Contact"; 4 | import Legal from "./Legal"; 5 | import Imprint from "./Imprint"; 6 | 7 | const kontakt = Contact; 8 | const rechtliches = Legal; 9 | const impressum = Imprint; 10 | 11 | const docs = { 12 | kontakt, 13 | rechtliches, 14 | impressum, 15 | }; 16 | 17 | const propTypes = { 18 | language: PropTypes.string, 19 | doc: PropTypes.oneOf(Object.keys(docs)), 20 | }; 21 | 22 | function LegalLines({ language = "de", doc = Object.keys(docs)[0] }) { 23 | const Comp = docs[doc]; 24 | return ; 25 | } 26 | 27 | LegalLines.propTypes = propTypes; 28 | 29 | export default LegalLines; 30 | -------------------------------------------------------------------------------- /src/components/LegalLines/LegalLines.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import LegalLines from "./LegalLines"; 4 | 5 | describe("LegalLines", () => { 6 | test("uses default properties", () => { 7 | window.console.error = jest.fn().mockImplementation(() => {}); 8 | const component = render(); 9 | expect(component.container.innerHTML).toMatchSnapshot(); 10 | }); 11 | 12 | ["rechtliches", "impressum", "kontakt"].forEach((doc) => { 13 | ["de", "fr", "en", "it"].forEach((lng) => { 14 | test(`should match snapshot with doc=${doc} and language=${lng}`, () => { 15 | const component = render(); 16 | expect(component.container.innerHTML).toMatchSnapshot(); 17 | }); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/components/LegalLines/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./LegalLines"; 2 | -------------------------------------------------------------------------------- /src/components/Link/Link.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { Link as MuiLink } from "@mui/material"; 4 | import { ReactComponent as LinkIcon } from "./Link.svg"; 5 | 6 | const propTypes = { 7 | href: PropTypes.string.isRequired, 8 | children: PropTypes.node.isRequired, 9 | className: PropTypes.string, 10 | }; 11 | 12 | function Link({ href, children, className = "" }) { 13 | return ( 14 | 32 | {children} 33 | 34 | 35 | ); 36 | } 37 | 38 | Link.propTypes = propTypes; 39 | 40 | export default Link; 41 | -------------------------------------------------------------------------------- /src/components/Link/Link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Link/Link.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import Link from "."; 4 | 5 | describe("Link", () => { 6 | test("should match snapshot.", () => { 7 | const component = render(geOps); 8 | expect(component.container.innerHTML).toMatchSnapshot(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/components/Link/__snapshots__/Link.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Link should match snapshot. 1`] = ` 4 | 9 | 10 | geOps 11 | 12 | 13 | Link.svg 14 | 15 | 16 | `; 17 | -------------------------------------------------------------------------------- /src/components/Link/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Link"; 2 | -------------------------------------------------------------------------------- /src/components/List/List.scss: -------------------------------------------------------------------------------- 1 | .tm-list { 2 | list-style-type: none; 3 | padding: 0; 4 | margin: 0; 5 | 6 | li { 7 | line-height: 25px; 8 | padding: 4px 15px; 9 | cursor: pointer; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/components/List/__snapshots__/List.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`List when no properties are set matches snapshot 1`] = `""`; 4 | 5 | exports[`List when properties are set matches snapshot with defaultItems 1`] = `""`; 6 | 7 | exports[`List when properties are set matches snapshot with items 1`] = ` 8 | 30 | `; 31 | -------------------------------------------------------------------------------- /src/components/List/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./List"; 2 | -------------------------------------------------------------------------------- /src/components/ListItem/__snapshots__/ListItem.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ListItem when no properties are set matches snapshot 1`] = ` 4 | 9 | `; 10 | 11 | exports[`ListItem when properties are set matches snapshot 1`] = ` 12 | 20 | `; 21 | -------------------------------------------------------------------------------- /src/components/ListItem/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ListItem"; 2 | -------------------------------------------------------------------------------- /src/components/Login/Login.scss: -------------------------------------------------------------------------------- 1 | @import '../../globals.scss'; 2 | 3 | .wkp-login { 4 | margin-right: 6px; 5 | padding-top: 2px; 6 | color: $brand-grey; 7 | display: flex; 8 | align-items: center; 9 | 10 | .wkp-login-icon { 11 | float: left; 12 | margin-bottom: 2px; 13 | height: 28px; 14 | width: 28px; 15 | 16 | path { 17 | stroke: $brand-grey; 18 | } 19 | } 20 | 21 | &:hover { 22 | color: $brand-primary-hover; 23 | 24 | svg { 25 | path { 26 | stroke: $brand-primary-hover; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Login/Login.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import { Provider } from "react-redux"; 4 | import Login from "."; 5 | 6 | describe("Login", () => { 7 | describe("matches snapshot", () => { 8 | test("displaying default text", () => { 9 | const store = global.global.mockStore({ 10 | app: { appBaseUrl: "http://foo.de" }, 11 | }); 12 | const component = render( 13 | 14 | 15 | , 16 | ); 17 | expect(component.container.innerHTML).toMatchSnapshot(); 18 | }); 19 | 20 | test("displaying user name", () => { 21 | const store = global.global.mockStore({ 22 | app: { permissionInfos: { user: "bar" }, appBaseUrl: "http://foo.de" }, 23 | }); 24 | const component = render( 25 | 26 | 27 | , 28 | ); 29 | expect(component.container.innerHTML).toMatchSnapshot(); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/components/Login/__snapshots__/Login.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Login matches snapshot displaying default text 1`] = ` 4 | 17 | `; 18 | 19 | exports[`Login matches snapshot displaying user name 1`] = ` 20 | 34 | `; 35 | -------------------------------------------------------------------------------- /src/components/Login/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Login"; 2 | -------------------------------------------------------------------------------- /src/components/MainDialog/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MainDialog"; 2 | -------------------------------------------------------------------------------- /src/components/Map/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Map"; 2 | -------------------------------------------------------------------------------- /src/components/MapAccessibility/__snapshots__/MapAccessibility.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`MapAccessibility should return null 1`] = `""`; 4 | -------------------------------------------------------------------------------- /src/components/MapAccessibility/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MapAccessibility"; 2 | -------------------------------------------------------------------------------- /src/components/MapButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MapButton"; 2 | -------------------------------------------------------------------------------- /src/components/MapControls/FitExtent/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./FitExtent"; 2 | -------------------------------------------------------------------------------- /src/components/MapControls/Geolocation/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Geolocation"; 2 | -------------------------------------------------------------------------------- /src/components/MapControls/MenuToggler/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MenuToggler"; 2 | -------------------------------------------------------------------------------- /src/components/MapControls/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MapControls"; 2 | -------------------------------------------------------------------------------- /src/components/MatomoTracker/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MatomoTracker"; 2 | -------------------------------------------------------------------------------- /src/components/Menu/Menu.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { useSelector } from "react-redux"; 4 | 5 | import "./Menu.scss"; 6 | 7 | const propTypes = { 8 | children: PropTypes.node.isRequired, 9 | }; 10 | 11 | function Menu({ children }) { 12 | const menuOpen = useSelector((state) => state.app.menuOpen); 13 | const className = useMemo(() => { 14 | return `wkp-menu-wrapper${menuOpen ? " wkp-menu-wrapper-open" : ""}`; 15 | }, [menuOpen]); 16 | 17 | return
{children}
; 18 | } 19 | 20 | Menu.propTypes = propTypes; 21 | 22 | export default React.memo(Menu); 23 | -------------------------------------------------------------------------------- /src/components/Menu/Menu.scss: -------------------------------------------------------------------------------- 1 | .wkp-menu-wrapper { 2 | position: absolute; 3 | top: 12px; 4 | left: 10px; 5 | max-width: 371px; 6 | z-index: 1; 7 | right: 10px; 8 | } 9 | 10 | .map-controls .wkp-menu-wrapper { 11 | right: 75px; // map-controls + margin*2 12 | } 13 | 14 | // Specific zu geltungsbereiche iframe topic 15 | .tm-trafimage-maps.ch-sbb-geltungsbereiche-iframe .wkp-menu-wrapper { 16 | max-width: 406px; 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Menu/MenuItem.scss: -------------------------------------------------------------------------------- 1 | .wkp-topic-menu-item { 2 | position: relative; 3 | 4 | .wkp-menu-title { 5 | display: flex; 6 | justify-content: space-between; 7 | padding: 15px 10px 15px 15px; 8 | align-items: center; 9 | 10 | .wkp-menu-title-left { 11 | display: flex; 12 | align-items: center; 13 | } 14 | } 15 | } 16 | 17 | .wkp-menu-item { 18 | border: 2px solid #666; 19 | background: white; 20 | margin-top: 5px; 21 | 22 | .wkp-menu-item { 23 | border: none; 24 | } 25 | 26 | .wkp-menu-item-body { 27 | min-width: 250px; 28 | max-height: 1000px; 29 | overflow-y: auto; 30 | padding: 5px 0 0; 31 | } 32 | 33 | &.closed { 34 | border-width: 0; 35 | } 36 | 37 | .wkp-collapsible-vertical { 38 | overflow: hidden auto; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/components/Menu/MenuItemHeader.scss: -------------------------------------------------------------------------------- 1 | .wkp-menu-item-header { 2 | height: 15px; 3 | border: none; 4 | display: flex; 5 | align-items: center; 6 | padding: 15px 10px 15px 15px; 7 | 8 | &.open { 9 | border-bottom: 1px solid #eee; 10 | } 11 | 12 | .wkp-menu-item-header-icon { 13 | margin-right: 10px; 14 | width: 15px; 15 | height: 15px; 16 | } 17 | 18 | .wkp-menu-item-header-toggler { 19 | width: 15px; 20 | height: 15px; 21 | } 22 | 23 | .wkp-menu-item-header-icon, 24 | .wkp-menu-item-header-toggler { 25 | svg { 26 | width: 100%; 27 | height: 100%; 28 | } 29 | } 30 | 31 | .wkp-menu-item-header-title { 32 | flex-grow: 2; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/components/Menu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Menu"; 2 | -------------------------------------------------------------------------------- /src/components/MessageListener/MessageListener.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { useSelector } from "react-redux"; 3 | 4 | function MessageListener() { 5 | const messageEvents = useSelector( 6 | (state) => state.app.activeTopic?.messageEvents, 7 | ); 8 | 9 | useEffect(() => { 10 | const unlistens = []; 11 | (messageEvents || []).forEach((messageEvent) => { 12 | const callback = (evt) => { 13 | if ( 14 | evt.type === messageEvent.eventType || 15 | evt.data === messageEvent.eventType 16 | ) { 17 | messageEvent.callback(evt); 18 | } 19 | }; 20 | window.addEventListener("message", callback); 21 | unlistens.push(() => { 22 | window.removeEventListener("message", callback); 23 | }); 24 | }); 25 | 26 | return () => { 27 | unlistens.forEach((unlisten) => { 28 | unlisten(); 29 | }); 30 | }; 31 | }, [messageEvents]); 32 | 33 | return null; 34 | } 35 | 36 | export default React.memo(MessageListener); 37 | -------------------------------------------------------------------------------- /src/components/MessageListener/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MessageListener"; 2 | -------------------------------------------------------------------------------- /src/components/NoDragPanWarning/NoDragPanWarning.test.js: -------------------------------------------------------------------------------- 1 | // import NoDragPanWarning from '.'; 2 | 3 | describe("NoDragPanWarning", () => { 4 | test("display warning message", () => { 5 | expect(true).toBe(true); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/components/NoDragPanWarning/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./NoDragPanWarning"; 2 | -------------------------------------------------------------------------------- /src/components/NoMouseWheelWarning/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./NoMouseWheelWarning"; 2 | -------------------------------------------------------------------------------- /src/components/Overlay/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Overlay"; 2 | -------------------------------------------------------------------------------- /src/components/Permalink/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Permalink"; 2 | -------------------------------------------------------------------------------- /src/components/PermalinkButton/PermalinkButton.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Provider } from "react-redux"; 3 | 4 | import { render } from "@testing-library/react"; 5 | import PermalinkButton from "."; 6 | 7 | describe("PermalinkButton", () => { 8 | let store; 9 | test("should match snapshot.", () => { 10 | store = global.mockStore({ 11 | map: {}, 12 | app: { drawIds: {} }, 13 | }); 14 | 15 | const component = render( 16 | 17 | 18 | {() => { 19 | return
; 20 | }} 21 | 22 | , 23 | ); 24 | expect(component.container.innerHTML).toMatchSnapshot(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/components/PermalinkButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PermalinkButton"; 2 | -------------------------------------------------------------------------------- /src/components/PermalinkInput/PermalinkInput.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import PermalinkInput from "."; 4 | 5 | describe("PermalinkInput", () => { 6 | test("should match snapshot.", () => { 7 | const component = render(); 8 | expect(component.container.innerHTML).toMatchSnapshot(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/components/PermalinkInput/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PermalinkInput"; 2 | -------------------------------------------------------------------------------- /src/components/PermalinkInputCore/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PermalinkInput"; 2 | -------------------------------------------------------------------------------- /src/components/PersonCard/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PersonCard"; 2 | -------------------------------------------------------------------------------- /src/components/PhotoCarusel/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PhotoCarusel"; 2 | -------------------------------------------------------------------------------- /src/components/Popup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Popup"; 2 | -------------------------------------------------------------------------------- /src/components/ProjectionSelect/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ProjectionSelect"; 2 | -------------------------------------------------------------------------------- /src/components/ResizeHandler/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ResizeHandler"; 2 | -------------------------------------------------------------------------------- /src/components/SBBSwitch/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SBBSwitch"; 2 | -------------------------------------------------------------------------------- /src/components/Search/Search.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from "react"; 2 | import SearchInput from "./SearchInput"; 3 | import SearchToggle from "./SearchToggle"; 4 | 5 | import "./Search.scss"; 6 | 7 | function Search() { 8 | const searchContainerRef = useRef(); 9 | 10 | return ( 11 |
12 | 13 | 14 | 15 |
16 | ); 17 | } 18 | 19 | export default React.memo(Search); 20 | -------------------------------------------------------------------------------- /src/components/Search/Search.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-class-pattern */ 2 | @import "../../globals.scss"; 3 | 4 | .wkp-search { 5 | position: absolute; 6 | top: 25px; 7 | z-index: 2; /* To be above header div, in order to be clickable. */ 8 | right: 480px; 9 | left: 400px; 10 | } 11 | -------------------------------------------------------------------------------- /src/components/Search/Search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Search/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Search"; 2 | -------------------------------------------------------------------------------- /src/components/SearchInput/SearchInput.md.scss: -------------------------------------------------------------------------------- 1 | .tm-search-input-example { 2 | .tm-search-input { 3 | position: relative; 4 | width: 300px; 5 | height: 40px; 6 | transition: width 0.3s ease; 7 | 8 | &.tm-focus { 9 | width: 600px; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/components/SearchInput/SearchInput.scss: -------------------------------------------------------------------------------- 1 | .tm-search-input { 2 | display: flex; 3 | justify-content: flex-end; 4 | width: calc(100% - 4px); 5 | height: calc(100% - 4px); 6 | position: absolute; 7 | top: 0; 8 | bottom: 0; 9 | margin: auto; 10 | border: 1px solid $gray; 11 | background-color: white; 12 | 13 | input, 14 | button, 15 | svg { 16 | border: none; 17 | } 18 | 19 | input { 20 | padding: 0 0 0 15px; 21 | flex-grow: 2; 22 | text-overflow: ellipsis; 23 | min-width: 0; 24 | 25 | &::-ms-clear { 26 | display: none; 27 | } 28 | } 29 | 30 | div[role='button'] { 31 | width: $btn-size-base; 32 | height: 100%; 33 | flex-shrink: 0; 34 | } 35 | 36 | div[role='button']:last-child { 37 | background-color: $btn-primary-color; 38 | color: white; 39 | 40 | &:hover { 41 | background-color: $btn-primary-color-hover; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/components/SearchInput/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SearchInput"; 2 | -------------------------------------------------------------------------------- /src/components/Select/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Select"; 2 | -------------------------------------------------------------------------------- /src/components/Share/Share.scss: -------------------------------------------------------------------------------- 1 | .wkp-share { 2 | display: flex; 3 | flex-wrap: wrap; 4 | gap: 5px; 5 | padding: 10px 16px; 6 | 7 | > div, > button, > a { 8 | min-width: 55px; 9 | height: 30px; 10 | padding-top: 0; 11 | padding-bottom: 0; 12 | margin: 0; 13 | display: flex; 14 | align-items: center; 15 | justify-content: center; 16 | } 17 | 18 | // .ta-facebook-icon { 19 | // padding: 0 5px; 20 | // } 21 | 22 | .rs-canvas-save-button { 23 | padding-top: 2px; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Share/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Share"; 2 | -------------------------------------------------------------------------------- /src/components/SharePermalinkButton/SharePermalinkButton.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Provider } from "react-redux"; 3 | 4 | import { render } from "@testing-library/react"; 5 | import { ThemeProvider } from "@mui/material"; 6 | import theme from "../../themes/default"; 7 | import SharePermalinkButton from "."; 8 | 9 | describe("SharePermalinkButton", () => { 10 | let store; 11 | test("should match snapshot.", () => { 12 | store = global.mockStore({ 13 | map: {}, 14 | app: { drawIds: {} }, 15 | }); 16 | 17 | const component = render( 18 | 19 | 20 | 21 | 22 | , 23 | ); 24 | expect( 25 | component.container.querySelectorAll(".wkp-permalink-bt").length, 26 | ).toBe(1); 27 | expect(component.container.querySelectorAll("button").length).toBe(1); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/components/SharePermalinkButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SharePermalinkButton"; 2 | -------------------------------------------------------------------------------- /src/components/StopSearchResult/StopSearchResult.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | 4 | import StopSearchResult from "./StopSearchResult"; 5 | 6 | describe("StopSearchResult", () => { 7 | test("returns null when no properties defined", () => { 8 | const { queryByTestId } = render(); 9 | expect(queryByTestId("stopfinder-search-result")).toBeNull(); 10 | }); 11 | 12 | test("returns null when no property title defined", () => { 13 | const { queryByTestId } = render( 14 | , 15 | ); 16 | expect(queryByTestId("stopfinder-search-result")).toBeNull(); 17 | }); 18 | 19 | test("returns component with name when property title defined", () => { 20 | const { queryByTestId } = render( 21 | , 22 | ); 23 | expect(queryByTestId("stopfinder-search-result")).toBeDefined(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/components/StopSearchResult/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StopSearchResult"; 2 | -------------------------------------------------------------------------------- /src/components/TarifverbundPartner/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TarifverbundPartner"; 2 | -------------------------------------------------------------------------------- /src/components/TopicElements/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TopicElements"; 2 | -------------------------------------------------------------------------------- /src/components/TopicInfosButton/TopicInfosButton.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import { useSelector } from "react-redux"; 3 | import PropTypes from "prop-types"; 4 | import InfosButton from "../InfosButton"; 5 | 6 | function TopicInfosButton({ topic }) { 7 | const activeTopic = useSelector((state) => state.app.activeTopic); 8 | 9 | const className = useMemo(() => { 10 | const classes = ["wkp-info-bt"]; 11 | 12 | if (activeTopic?.key === topic.key) { 13 | classes.push("wkp-active"); 14 | } 15 | return classes.join(" "); 16 | }, [activeTopic.key, topic]); 17 | 18 | if (!topic) { 19 | return null; 20 | } 21 | 22 | return ; 23 | } 24 | 25 | TopicInfosButton.propTypes = { 26 | topic: PropTypes.object.isRequired, 27 | }; 28 | 29 | export default React.memo(TopicInfosButton); 30 | -------------------------------------------------------------------------------- /src/components/TopicInfosButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TopicInfosButton"; 2 | -------------------------------------------------------------------------------- /src/components/TopicLoader/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TopicLoader"; 2 | -------------------------------------------------------------------------------- /src/components/TopicMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TopicMenu"; 2 | -------------------------------------------------------------------------------- /src/components/TopicTelephoneInfos/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TopicTelephoneInfos"; 2 | -------------------------------------------------------------------------------- /src/components/TopicsMenu/TopicsMenu.scss: -------------------------------------------------------------------------------- 1 | .wkp-topics-menu { 2 | border: 2px solid #666; 3 | max-height: 1500px; 4 | 5 | .wkp-menu-item { 6 | border: none; 7 | margin-top: 0; 8 | } 9 | 10 | .wkp-menu-item-header { 11 | border-top: 1px solid #eee; 12 | } 13 | 14 | .wkp-topics-menu-title { 15 | display: flex; 16 | align-items: center; 17 | } 18 | 19 | .wkp-topics-menu-body { 20 | min-width: 250px; 21 | max-height: 2000px; 22 | overflow-y: auto; 23 | background: white; 24 | padding: 10px 15px; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/TopicsMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TopicsMenu"; 2 | -------------------------------------------------------------------------------- /src/components/TopicsMenuHeader/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TopicsMenuHeader"; 2 | -------------------------------------------------------------------------------- /src/components/TrafimageMaps/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TrafimageMaps"; 2 | -------------------------------------------------------------------------------- /src/components/withResizing/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./withResizing"; 2 | -------------------------------------------------------------------------------- /src/config/ch.sbb.direktverbindungen/DvFeatureInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DvFeatureInfo"; 2 | -------------------------------------------------------------------------------- /src/config/ch.sbb.direktverbindungen/DvFeatureInfoTitle/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DvFeatureInfoTitle"; 2 | -------------------------------------------------------------------------------- /src/config/ch.sbb.direktverbindungen/DvLegendLine/DvLegendLine.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | function DvLegendLine({ color, width = 50 }) { 5 | return
; 6 | } 7 | 8 | DvLegendLine.propTypes = { 9 | color: PropTypes.string.isRequired, 10 | width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 11 | }; 12 | 13 | export default DvLegendLine; 14 | -------------------------------------------------------------------------------- /src/config/ch.sbb.direktverbindungen/DvLegendLine/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DvLegendLine"; 2 | -------------------------------------------------------------------------------- /src/config/ch.sbb.direktverbindungen/DvLineInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DvLineInfo"; 2 | -------------------------------------------------------------------------------- /src/config/ch.sbb.direktverbindungen/DvLineTitle/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DvLineTitle"; 2 | -------------------------------------------------------------------------------- /src/config/ch.sbb.direktverbindungen/DvListButton/__snapshots__/DvListButton.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`DvListButton should match snapshot and be disabled on load. 1`] = `","`; 4 | -------------------------------------------------------------------------------- /src/config/ch.sbb.direktverbindungen/DvListButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DvListButton"; 2 | -------------------------------------------------------------------------------- /src/config/ch.sbb.direktverbindungen/dvParseFeatures.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert Direktverbindungen features for popup 3 | */ 4 | const parseDvFeatures = (featuresArray) => { 5 | return featuresArray.map((feature) => { 6 | const { vias } = feature.getProperties(); 7 | const parsedVias = Array.isArray(vias) ? vias : JSON.parse(vias); 8 | 9 | const switchVias = vias 10 | ? parsedVias.filter( 11 | (via) => via.via_type === "switch" || via.via_type === "visible", 12 | ) 13 | : []; 14 | feature.set("vias", [ 15 | parsedVias[0], 16 | ...switchVias, 17 | parsedVias[parsedVias.length - 1], 18 | ]); 19 | return feature; 20 | }); 21 | }; 22 | 23 | export default parseDvFeatures; 24 | -------------------------------------------------------------------------------- /src/config/ch.sbb.funkmesswagen/MesswagenFollowButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MesswagenFollowButton"; 2 | -------------------------------------------------------------------------------- /src/config/ch.sbb.geltungsbereiche.iframe/index.js: -------------------------------------------------------------------------------- 1 | import layers from "../ch.sbb.geltungsbereiche.mvp"; 2 | 3 | export default layers; 4 | -------------------------------------------------------------------------------- /src/config/ch.sbb.handicap/index.test.js: -------------------------------------------------------------------------------- 1 | import { getLayersAsFlatArray } from "mobility-toolbox-js/common"; 2 | import layers from "./index"; 3 | 4 | describe("ch.sbb.handicap", () => { 5 | it("should always have the same layers key for layers available via permalink", () => { 6 | expect(true).toBe(true); 7 | const permalinkKeys = getLayersAsFlatArray(layers) 8 | .filter((l) => { 9 | return !l.get("hideInLegend") && !l.get("isBaseLayer"); 10 | }) 11 | .map((l) => l.key) 12 | .join(","); 13 | expect(permalinkKeys).toBe( 14 | "ch.sbb.bahnhofplaene,ch.sbb.bahnhofplaene.interaktiv,ch.sbb.bahnhofplaene.printprodukte,ch.sbb.status_unbekannt,ch.sbb.nichtbarrierfreierbahnhoefe,ch.sbb.teilbarrierefreiebahnhoefe,ch.sbb.barrierfreierbahnhoefe", 15 | ); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/config/ch.sbb.immobilien/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/config/ch.sbb.immobilien/index.js -------------------------------------------------------------------------------- /src/config/ch.sbb.isb/index.test.js: -------------------------------------------------------------------------------- 1 | import { isbOther, isbTVS } from "./index"; 2 | 3 | describe("ch.sbb.isb", () => { 4 | describe("isbOther", () => { 5 | test("has a shortToLongName property set and only unique keys, it's important for the layer infos", () => { 6 | expect(isbOther.get("shortToLongName")).toBeDefined(); 7 | expect(isbOther.get("defaultColor")).toBeDefined(); 8 | }); 9 | }); 10 | describe("isbTVS", () => { 11 | test("has a shortToLongName property set and only unique keys, it's important for the layer infos", () => { 12 | expect(isbTVS.get("shortToLongName")).toBeDefined(); 13 | expect(isbTVS.get("colors")).toBeDefined(); 14 | expect(isbTVS.get("defaultColor")).toBeDefined(); 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/config/ch.sbb.sts/StsMenuToggler/StsMenuToggler.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTranslation } from "react-i18next"; 3 | import { useSelector } from "react-redux"; 4 | import MenuToggler from "../../../components/MapControls/MenuToggler"; 5 | import useHasScreenSize from "../../../utils/useHasScreenSize"; 6 | import { ReactComponent as MenuOpen } from "../../../img/sbb/040_hamburgermenu_102_36.svg"; 7 | import { ReactComponent as SearchIcon } from "../../../components/Search/Search.svg"; 8 | 9 | function StsMenuToggler() { 10 | const { t } = useTranslation(); 11 | const isMobile = useHasScreenSize(["xs"]); 12 | const displayMenu = useSelector((state) => state.app.displayMenu); 13 | 14 | return ( 15 | : , 19 | title: !displayMenu ? t("Menü") : t("Suchen"), 20 | style: { padding: 8 }, 21 | } 22 | : {})} 23 | /> 24 | ); 25 | } 26 | 27 | export default StsMenuToggler; 28 | -------------------------------------------------------------------------------- /src/config/ch.sbb.sts/StsMenuToggler/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StsMenuToggler"; 2 | -------------------------------------------------------------------------------- /src/config/ch.sbb.tarifverbundkarte.public/index.js: -------------------------------------------------------------------------------- 1 | import TrafimageMapboxLayer from "../../layers/TrafimageMapboxLayer"; 2 | import TarifverbundkarteLayer from "../../layers/TarifverbundkarteLayer"; 3 | 4 | export const tarifverbundkarteDataLayer = new TrafimageMapboxLayer({ 5 | name: "ch.sbb.tarifverbundkarte.data", 6 | visible: true, 7 | zIndex: -1, // Add zIndex as the MapboxLayer would block tiled layers (buslines) 8 | style: "ch.sbb.tarifverbund", 9 | properties: { 10 | hideInLegend: true, 11 | isBaseLayer: true, 12 | }, 13 | mapOptions: { 14 | preserveDrawingBuffer: true, 15 | }, 16 | }); 17 | 18 | export const tarifverbundkarteLayer = new TarifverbundkarteLayer({ 19 | mapboxLayer: tarifverbundkarteDataLayer, 20 | visible: true, 21 | properties: { 22 | isQueryable: true, 23 | hideInLegend: true, 24 | useOverlay: true, 25 | popupComponent: "TarifverbundkartePopup", 26 | }, 27 | }); 28 | export default [tarifverbundkarteDataLayer, tarifverbundkarteLayer]; 29 | -------------------------------------------------------------------------------- /src/config/doc-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": "Trafimage-maps", 3 | "githubRepo": "https://github.com/geops/trafimage-maps" 4 | } 5 | -------------------------------------------------------------------------------- /src/config/proj4.js: -------------------------------------------------------------------------------- 1 | import proj4 from "proj4"; 2 | import { register } from "ol/proj/proj4"; 3 | 4 | proj4.defs( 5 | "EPSG:21781", 6 | "+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 " + 7 | "+x_0=600000 +y_0=200000 +ellps=bessel " + 8 | "+towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs", 9 | ); 10 | 11 | proj4.defs( 12 | "EPSG:2056", 13 | "+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 " + 14 | "+x_0=2600000 +y_0=1200000 +ellps=bessel " + 15 | "+towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs", 16 | ); 17 | 18 | register(proj4); 19 | -------------------------------------------------------------------------------- /src/config/searches.js: -------------------------------------------------------------------------------- 1 | import Betriebspunkte from "../searches/Betriebspunkte"; 2 | import Lines from "../searches/Lines"; 3 | import Locations from "../searches/Locations"; 4 | import Municipalities from "../searches/Municipalities"; 5 | import StopFinder from "../searches/StopFinder"; 6 | import HandicapStopFinder from "../searches/HandicapStopFinder"; 7 | 8 | export const betriebspunkte = new Betriebspunkte(); 9 | 10 | export const lines = new Lines(); 11 | 12 | export const locations = new Locations(); 13 | 14 | export const municipalities = new Municipalities(); 15 | 16 | export const stopFinder = new StopFinder(); 17 | 18 | export const handicapStopFinder = new HandicapStopFinder(); 19 | 20 | export default { 21 | Stationen: stopFinder, 22 | Gemeinden: municipalities, 23 | Orte: locations, 24 | Betriebspunkte: betriebspunkte, 25 | Linien: lines, 26 | }; 27 | -------------------------------------------------------------------------------- /src/examples/Angular/README.md: -------------------------------------------------------------------------------- 1 | For Angular, we provide this [CodeSandBox example](https://codesandbox.io/p/devbox/angular-trafimage-maps-4jkf9x) to demonstrate the usage of _trafimage-maps_ web component in an Angular app. 2 | 3 | 9 | -------------------------------------------------------------------------------- /src/examples/Betriebsregionen/README.md: -------------------------------------------------------------------------------- 1 | Example for the _Operating regions_ topic. 2 | 3 | ```js 4 | import "trafimage-maps"; 5 | import Editor from "react-styleguidist/lib/client/rsg-components/Editor"; 6 | import getHtmlPageCode from "../getHtmlPageCode"; 7 | 8 | const App = () => { 9 | return ( 10 | <> 11 |
12 | 18 |
19 |
20 | \n `, 23 | )} 24 | onChange={(code) => null} //setCode(code)} 25 | /> 26 | 27 | ); 28 | }; 29 | ; 30 | ``` 31 | -------------------------------------------------------------------------------- /src/examples/Construction/README.md: -------------------------------------------------------------------------------- 1 | Example how to load _trafimage-maps_ with a specific topic without using React. 2 | 3 | ```js 4 | import "trafimage-maps"; 5 | import Editor from "react-styleguidist/lib/client/rsg-components/Editor"; 6 | import getHtmlPageCode from "../getHtmlPageCode"; 7 | 8 | const App = () => { 9 | return ( 10 | <> 11 |
12 | 18 |
19 |
20 | \n `, 23 | )} 24 | onChange={(code) => null} //setCode(code)} 25 | /> 26 | 27 | ); 28 | }; 29 | ; 30 | ``` 31 | -------------------------------------------------------------------------------- /src/examples/CustomTopic/ExampleCode.txt: -------------------------------------------------------------------------------- 1 | import 'trafimage-maps'; 2 | import React, { useRef, useEffect } from 'react'; 3 | import TrafimageMapboxLayer from 'trafimage-maps/es/layers/TrafimageMapboxLayer'; 4 | 5 | const topic = { 6 | name: 'Default', 7 | key: 'default', 8 | elements: { 9 | menu: false, 10 | header: true, 11 | footer: true, 12 | permalink: false, 13 | }, 14 | layers: [ 15 | new TrafimageMapboxLayer({ 16 | name: 'Netzkarte', 17 | visible: true, 18 | style: 'base_bright_v2', 19 | }), 20 | ], 21 | }; 22 | 23 | const App = () => { 24 | const ref = useRef(); 25 | 26 | useEffect(() => { 27 | const map = ref.current; 28 | map.topics = [topic]; 29 | 30 | return () => { 31 | map.topics = null; 32 | }; 33 | }, []); 34 | 35 | return ( 36 |
37 | 38 | 42 | /> 43 |
44 | ); 45 | }; 46 | 47 | ; -------------------------------------------------------------------------------- /src/examples/OverrideTopic/ExampleCode.txt: -------------------------------------------------------------------------------- 1 | import 'trafimage-maps'; 2 | import React, { useRef, useEffect } from 'react'; 3 | import { netzkarte } from 'trafimage-maps/es/config/topics'; 4 | 5 | const topic = { 6 | ...netzkarte, 7 | elements: { 8 | ...netzkarte.elements, 9 | popup: false, 10 | }, 11 | }; 12 | 13 | const App = () => { 14 | const ref = useRef(); 15 | 16 | useEffect(() => { 17 | const map = ref.current; 18 | map.topics = [topic]; 19 | 20 | return () => { 21 | map.topics = null; 22 | }; 23 | }, []); 24 | 25 | return ( 26 |
27 | 28 | 32 | /> 33 |
34 | ); 35 | }; 36 | 37 | ; 38 | -------------------------------------------------------------------------------- /src/examples/Schulzug/marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/examples/Schulzug/marker.png -------------------------------------------------------------------------------- /src/examples/getCodeWithApiKey.js: -------------------------------------------------------------------------------- 1 | const getCodeWithApiKey = async (pathToFile, apiKey) => { 2 | const text = await fetch(pathToFile).then((res) => res.text()); 3 | return text.replace("", `"${apiKey}"`); 4 | }; 5 | 6 | export default getCodeWithApiKey; 7 | -------------------------------------------------------------------------------- /src/examples/getHtmlPageCode.js: -------------------------------------------------------------------------------- 1 | const getHtmlPageCode = (wcCode, scriptCode) => { 2 | if (!wcCode) { 3 | return null; 4 | } 5 | return ` 6 | 7 | 8 | ${ 9 | scriptCode ? `\n ${scriptCode}` : "" 10 | } 11 | 12 | 13 |
14 | ${wcCode} 15 |
16 | 17 | 18 | `; 19 | }; 20 | 21 | export default getHtmlPageCode; 22 | -------------------------------------------------------------------------------- /src/examples/iframe/getHtmlPageCode.js: -------------------------------------------------------------------------------- 1 | const getHtmlPageCode = (iframeCode, extraCode = "") => { 2 | if (!iframeCode) { 3 | return null; 4 | } 5 | return ` 6 | 7 | 8 | 9 |
10 | ${iframeCode} 11 |
${extraCode} 12 | 13 | 14 | `; 15 | }; 16 | 17 | export default getHtmlPageCode; 18 | -------------------------------------------------------------------------------- /src/examples/iframe/getIframeCodeFromUrl.js: -------------------------------------------------------------------------------- 1 | const getIframeCodeFromUrl = (url) => { 2 | if (!url) { 3 | return null; 4 | } 5 | return ``; 6 | }; 7 | 8 | export default getIframeCodeFromUrl; 9 | -------------------------------------------------------------------------------- /src/filters/AusbauFilters/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AusbauFilters"; 2 | -------------------------------------------------------------------------------- /src/filters/index.js: -------------------------------------------------------------------------------- 1 | import AusbauFilters from "./AusbauFilters"; 2 | 3 | export { default as AusbauFilters } from "./AusbauFilters"; 4 | 5 | export default { AusbauFilters }; 6 | -------------------------------------------------------------------------------- /src/img/Geolocate/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Geolocate"; 2 | -------------------------------------------------------------------------------- /src/img/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/arrow.png -------------------------------------------------------------------------------- /src/img/arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/bahnhofplanLayerIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/bahnhofplanLayerIcon.png -------------------------------------------------------------------------------- /src/img/chevronLeft.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/clock_10_large.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/favicon.png -------------------------------------------------------------------------------- /src/img/finish_flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/finish_flag.png -------------------------------------------------------------------------------- /src/img/finish_flag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/geltungsbereicheLegends/index.test.js: -------------------------------------------------------------------------------- 1 | import geltungsbereicheLegendConfigs from "."; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | 6 | describe("Geltungsbereiche legends: ", () => { 7 | geltungsbereicheLegendConfigs.forEach((item) => { 8 | it(`${item.legend} should contain a scaleline tag for dynamic scaleline placement`, () => { 9 | const svgPath = path.join(__dirname, "./", item.legend); 10 | const fdr = fs.readFileSync(svgPath, "utf8", (err, data) => { 11 | return data; 12 | }); 13 | expect(fdr).toContain(`id="scaleline"`); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/img/layers/BahnhofplanLayer/interactiveStationplans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/BahnhofplanLayer/interactiveStationplans.png -------------------------------------------------------------------------------- /src/img/layers/BahnhofplanLayer/printproducts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/BahnhofplanLayer/printproducts.png -------------------------------------------------------------------------------- /src/img/layers/Betriebsregionen/mitte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/Betriebsregionen/mitte.png -------------------------------------------------------------------------------- /src/img/layers/Betriebsregionen/ost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/Betriebsregionen/ost.png -------------------------------------------------------------------------------- /src/img/layers/Betriebsregionen/other.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/Betriebsregionen/other.png -------------------------------------------------------------------------------- /src/img/layers/Betriebsregionen/sud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/Betriebsregionen/sud.png -------------------------------------------------------------------------------- /src/img/layers/Betriebsregionen/west.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/Betriebsregionen/west.png -------------------------------------------------------------------------------- /src/img/layers/NetzkartePointLayer/layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/NetzkartePointLayer/layer.png -------------------------------------------------------------------------------- /src/img/layers/NetzkartePointLayer/stations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/NetzkartePointLayer/stations.png -------------------------------------------------------------------------------- /src/img/layers/PassagierfrequenzenLayer/layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/PassagierfrequenzenLayer/layer.png -------------------------------------------------------------------------------- /src/img/layers/RouteLayer/layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/RouteLayer/layer.png -------------------------------------------------------------------------------- /src/img/layers/ZoneLayer/layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/layers/ZoneLayer/layer.png -------------------------------------------------------------------------------- /src/img/list-icon-sbb.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/mail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/menu.png -------------------------------------------------------------------------------- /src/img/menu_closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/menu_closed.png -------------------------------------------------------------------------------- /src/img/menu_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/menu_open.png -------------------------------------------------------------------------------- /src/img/minus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/northArrowCircle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/img/northArrowCircle.png -------------------------------------------------------------------------------- /src/img/pencil.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/pencil_add.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/person.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/phone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/popups/NetzentwicklungPopup/mail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/popups/NetzentwicklungPopup/person.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/popups/NetzentwicklungPopup/phone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/sbb/040_hamburgermenu_102_36.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/img/sbb/040_schliessen_104_36.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/img/sbb/globe_210_large.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/sbb/gps-large.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/img/sbb/gps-medium.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/img/sbb/gps-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/img/sbb/location-pin-a-medium.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/sbb/location-pin-m-medium-bg-red.url.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | M 5 | -------------------------------------------------------------------------------- /src/img/sbb/two-finger-tap-large.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/img/sbb/user_92_large.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/swissbounds.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/train-day.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/img/train-night.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import "react-app-polyfill/stable"; 2 | // import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'; 3 | 4 | // Import web-components polyfills for ie 11, see https://github.com/webcomponents/polyfills. 5 | // If you need to test the component with { shadow: true }, you have to add: 6 | // import '@webcomponents/shadydom' 7 | // import '@webcomponents/shadycss' 8 | // import '@webcomponents/webcomponents-platform'; 9 | // import '@webcomponents/custom-elements'; 10 | // import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter'; 11 | // import 'proxy-polyfill'; 12 | import ReactWebComponent from "@geops/create-react-web-component"; 13 | import WebComponent from "./WebComponent"; 14 | 15 | ReactWebComponent.setAttributes(WebComponent.attributes); 16 | ReactWebComponent.setProperties(WebComponent.defaultProps); 17 | ReactWebComponent.render(WebComponent, "trafimage-maps", { shadow: false }); 18 | -------------------------------------------------------------------------------- /src/layerInfos/BeleuchtungLayerInfo/BeleuchtungLayerInfo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { PropTypes } from "prop-types"; 3 | import { BeleuchtungLegendeRow } from "../BeleuchtungTopicInfo/BeleuchtungLegende"; 4 | 5 | function BeleuchtungLayerInfo({ properties }) { 6 | const lightClass = properties.name.split("beleuchtungsstaerken")[1]; 7 | return ; 8 | } 9 | 10 | BeleuchtungLayerInfo.propTypes = { 11 | properties: PropTypes.shape({ 12 | name: PropTypes.string, 13 | }).isRequired, 14 | }; 15 | 16 | export default BeleuchtungLayerInfo; 17 | -------------------------------------------------------------------------------- /src/layerInfos/BeleuchtungLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./BeleuchtungLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/BeleuchtungLayerInfo/lightingMapping.js: -------------------------------------------------------------------------------- 1 | export const lightingMapping = { 2 | 1: { 3 | color: "#fdd815", 4 | info: [">= 20000", "Hohes Personenaufkommen"], 5 | }, 6 | "2a": { 7 | color: "#e8a60a", 8 | info: ["10000 - 19999", "Mittleres Personenaufkommen"], 9 | }, 10 | "2b": { 11 | color: "#9c7c2e", 12 | info: ["1500 - 9999", "Mittleres Personenaufkommen"], 13 | }, 14 | 3: { 15 | color: "#674512", 16 | info: ["50 - 1499", "Geringes Personenaufkommen"], 17 | }, 18 | 4: { 19 | color: "#000000", 20 | info: ["< 50", "Sehr geringes Personenaufkommen"], 21 | }, 22 | }; 23 | 24 | export default lightingMapping; 25 | -------------------------------------------------------------------------------- /src/layerInfos/BeleuchtungTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./BeleuchtungTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/BetriebsRegionenLayerInfo/BetriebsRegionenLayerInfo.scss: -------------------------------------------------------------------------------- 1 | .wkp-betriebsregionen { 2 | padding-top: 5px; 3 | 4 | .wkp-betriebsregion { 5 | display: flex; 6 | padding: 5px 0; 7 | align-items: center; 8 | 9 | img { 10 | padding-right: 7px; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/layerInfos/BetriebsRegionenLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./BetriebsRegionenLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/BuslinesLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./BuslinesLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/ConstructionFertigstellungLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ConstructionFertigstellungLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/ConstructionLayerInfo/ConstructionLayerInfo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { withTranslation } from "react-i18next"; 4 | 5 | import "./ConstructionLayerInfo.scss"; 6 | 7 | const propTypes = { 8 | t: PropTypes.func.isRequired, 9 | properties: PropTypes.object.isRequired, 10 | staticFilesUrl: PropTypes.string.isRequired, 11 | }; 12 | 13 | function ConstructionLayerInfo({ t, properties, staticFilesUrl }) { 14 | const config = properties.get("construction"); 15 | const filename = `${config.art}_${config.ort}`.replace( 16 | /[^A-Z,^0-9,-_]/gi, 17 | "", 18 | ); 19 | 20 | return ( 21 |
22 | {t("Kein 27 | {t(`${properties.key}-desc`)} 28 |
29 | ); 30 | } 31 | 32 | ConstructionLayerInfo.propTypes = propTypes; 33 | 34 | export default withTranslation()(ConstructionLayerInfo); 35 | -------------------------------------------------------------------------------- /src/layerInfos/ConstructionLayerInfo/ConstructionLayerInfo.scss: -------------------------------------------------------------------------------- 1 | .wkp-construction-layer-info { 2 | img { 3 | float: left; 4 | padding-right: 10px; 5 | padding-bottom: 10px; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/layerInfos/ConstructionLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ConstructionLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/ConstructionTopicInfo/ConstructionTopicInfo.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import ConstructionTopicInfo from "."; 4 | 5 | describe("ConstructionTopicInfo", () => { 6 | test("should display link to data", () => { 7 | const { container } = render(); 8 | const link = container.querySelector("a.wkp-link"); 9 | expect(link.href).toBe( 10 | "https://data.sbb.ch/explore/dataset/construction-projects/information/", 11 | ); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/layerInfos/ConstructionTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ConstructionTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/DrawLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DrawLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/DvLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DvLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/DvTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DvTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/EnergieLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./EnergieLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/EnergiePublicTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./EnergiePublicTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/EnergieTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./EnergieTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/GeltungsbereicheLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./GeltungsbereicheLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/GeltungsbereicheTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./GeltungsbereicheTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/HandicapLayerInfo/HandicapLayerInfo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { Typography } from "@mui/material"; 4 | import { makeStyles } from "@mui/styles"; 5 | import { useTranslation } from "react-i18next"; 6 | import HandicapLayer from "../../layers/HandicapLayer"; 7 | 8 | const propTypes = { 9 | properties: PropTypes.instanceOf(HandicapLayer).isRequired, 10 | }; 11 | 12 | const useStyles = makeStyles({ 13 | grid: { 14 | display: "flex", 15 | gap: 10, 16 | }, 17 | circle: { 18 | height: 15, 19 | minWidth: 15, 20 | borderRadius: "50%", 21 | }, 22 | }); 23 | 24 | function HandicapLayerInfo({ properties: layer }) { 25 | const { t } = useTranslation(); 26 | const classes = useStyles(); 27 | 28 | return ( 29 |
30 |
34 | {t(`${layer.key}.layerinfo`)} 35 |
36 | ); 37 | } 38 | 39 | HandicapLayerInfo.propTypes = propTypes; 40 | 41 | export default HandicapLayerInfo; 42 | -------------------------------------------------------------------------------- /src/layerInfos/HandicapLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./HandicapLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/HandicapTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./HandicapTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/InfrastrukturTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./InfrastrukturTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/IsbNormalspurLayerInfo/IsbNormalspurLayerInfo.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import { Layer } from "mobility-toolbox-js/ol"; 4 | import IsbNormalspurLayerInfo from "."; 5 | 6 | describe("IsbNormalspurLayerInfo", () => { 7 | test("render opertaors in alphabetical order", () => { 8 | const { container } = render( 9 | , 22 | ); 23 | // Test important operator 24 | expect(container.textContent.includes("aSm aSmASM ASMzB zB")).toBe(true); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/layerInfos/IsbNormalspurLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./IsbNormalspurLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/IsbOtherLayerInfo/IsbOtherLayerInfo.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "@testing-library/react"; 3 | import { isbOther } from "../../config/ch.sbb.isb"; 4 | import IsbOtherLayerInfo from "."; 5 | 6 | describe("IsbOtherLayerInfo", () => { 7 | test("render something", () => { 8 | render(); 9 | // Test important operator 10 | expect(screen.getByText(/DB, /)).toBeInTheDocument(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/layerInfos/IsbOtherLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./IsbOtherLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/IsbSchmalspurLayerInfo/IsbSchmalspurLayerInfo.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import { Layer } from "mobility-toolbox-js/ol"; 4 | import IsbSchmalspurLayerInfo from "."; 5 | 6 | describe("IsbSchmalspurLayerInfo", () => { 7 | test("render something", () => { 8 | const { container } = render( 9 | , 22 | ); 23 | // Test important operator 24 | expect(container.textContent.includes("aSm aSmASM ASMzB zB")).toBe(true); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/layerInfos/IsbSchmalspurLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./IsbSchmalspurLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/IsbTVSLayerInfo/IsbTVSLayerInfo.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "@testing-library/react"; 3 | import { isbTVS } from "../../config/ch.sbb.isb"; 4 | import IsbTVSLayerInfo from "."; 5 | 6 | describe("IsbTVSLayerInfo", () => { 7 | test("render something", () => { 8 | render( a} language="de" properties={isbTVS} />); 9 | // Test important operator 10 | expect(screen.getByText("SBB")).toBeInTheDocument(); 11 | expect(screen.getByText("SBB Infrastruktur")).toBeInTheDocument(); 12 | // Test one of the other operators 13 | expect(screen.getByText(/TMR, /)).toBeInTheDocument(); 14 | expect(screen.getByText("www.tvs.ch")).toBeInTheDocument(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/layerInfos/IsbTVSLayerInfo/OperatorShortAndLongName.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTranslation } from "react-i18next"; 3 | import { makeStyles } from "@mui/styles"; 4 | import PropTypes from "prop-types"; 5 | 6 | const useStyles = makeStyles({ 7 | root: { 8 | display: "flex", 9 | alignItems: "center", 10 | fontSize: "0.8em", 11 | margin: "3px 0", 12 | }, 13 | shortName: { 14 | display: "inline-block", 15 | flex: "0 0 40px", 16 | minWidth: "40px", 17 | marginRight: 5, 18 | }, 19 | }); 20 | 21 | const propTypes = { 22 | shortName: PropTypes.string.isRequired, 23 | longName: PropTypes.string.isRequired, 24 | }; 25 | 26 | function OperatorShortAndLongName({ shortName, longName }) { 27 | const { t } = useTranslation(); 28 | const classes = useStyles(); 29 | return ( 30 |
31 | {shortName} 32 | {t(longName)} 33 |
34 | ); 35 | } 36 | 37 | OperatorShortAndLongName.propTypes = propTypes; 38 | 39 | export default React.memo(OperatorShortAndLongName); 40 | -------------------------------------------------------------------------------- /src/layerInfos/IsbTVSLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./IsbTVSLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/IsbTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./IsbTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/MapsGeoAdminLayerInfo/MapsGeoAdminLayerInfo.scss: -------------------------------------------------------------------------------- 1 | .wkp-maps-geo-admin-layer-info { 2 | max-height: 50vh; 3 | 4 | .bod-title { 5 | font-family: SBBWeb-Bold, Arial, sans-serif; 6 | } 7 | 8 | .legend-footer { 9 | display: none; 10 | } 11 | 12 | .wkp-maps-geo-admin-layer-info-footer { 13 | margin: 10px 0; 14 | padding-bottom: 20px; 15 | 16 | img { 17 | margin: 10px 0; 18 | } 19 | 20 | span { 21 | display: flex; 22 | align-items: baseline; 23 | white-space: pre; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/layerInfos/MapsGeoAdminLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MapsGeoAdminLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/MesswagenPhotosLayerInfo/MesswagenPhotosLayerInfo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Typography } from "@mui/material"; 3 | import { makeStyles } from "@mui/styles"; 4 | import { useTranslation } from "react-i18next"; 5 | import LegendCircle from "../../components/LegendCircle/LegendCircle"; 6 | 7 | const useStyles = makeStyles((theme) => ({ 8 | grid: { 9 | display: "grid", 10 | gridTemplateColumns: "1fr 8fr", 11 | gridGap: theme.spacing(1), 12 | }, 13 | })); 14 | 15 | function MesswagenPhotosLayerInfo() { 16 | const classes = useStyles(); 17 | const { t } = useTranslation(); 18 | return ( 19 |
20 | 21 | {t("SBB-Funkmesswagen")} 22 | 23 | {t("Verschiedenes")} 24 |
25 | ); 26 | } 27 | 28 | export default MesswagenPhotosLayerInfo; 29 | -------------------------------------------------------------------------------- /src/layerInfos/MesswagenPhotosLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MesswagenPhotosLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/MesswagenTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MesswagenTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/NetzkarteTopicInfo/NetzkarteTopicInfo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { withTranslation } from "react-i18next"; 4 | 5 | const propTypes = { 6 | t: PropTypes.func.isRequired, 7 | }; 8 | 9 | function NetzkarteTopicInfo({ t }) { 10 | return ( 11 |
12 |

{`${t("ch.sbb.netzkarte-desc")} ${t( 13 | "ch.sbb.netzkarte-desc-topic-info", 14 | )}`}

15 |

16 | {t("Verantwortlich")}: 17 |
18 | {t("SBB AG, Product Owner Trafimage")}, 19 |
20 | Daniel Hofstetter,  21 | {t("trafimage@sbb.ch")}. 22 |

23 |
24 | ); 25 | } 26 | 27 | NetzkarteTopicInfo.propTypes = propTypes; 28 | 29 | export default withTranslation()(NetzkarteTopicInfo); 30 | -------------------------------------------------------------------------------- /src/layerInfos/NetzkarteTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./NetzkarteTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/PassagierFrequenzenLayerInfo/PassagierFrequenzenLayerInfo.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import { passagierfrequenzen } from "../../config/ch.sbb.netzkarte"; 4 | import PassagierFrequenzenLayerInfo from "."; 5 | 6 | describe("PassagierFrequenzenLayerInfo", () => { 7 | test("should display link to data", () => { 8 | const { container } = render( 9 | , 10 | ); 11 | const link = container.querySelector("a.wkp-link"); 12 | expect(link.href).toBe( 13 | "https://reporting.sbb.ch/verkehr?highlighted=row-243", 14 | ); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/layerInfos/PassagierFrequenzenLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PassagierFrequenzenLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/PunctualityLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PunctualityLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/RegionenkartePublicLayerInfo/RegionenkarteLegend.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ThemeProvider } from "@mui/material"; 3 | import { render } from "@testing-library/react"; 4 | import theme from "../../themes/default"; 5 | import RegionenkarteLegend from "./RegionenkarteLegend"; 6 | 7 | const mapping = { 8 | ost: "grun", 9 | sud: "rot", 10 | mitte: "lila", 11 | west: "gelb", 12 | }; 13 | 14 | describe("RegionenkarteLegend", () => { 15 | test("should display all regions with correct colors", async () => { 16 | const { queryByTestId } = render( 17 | 18 | 19 | , 20 | ); 21 | Object.keys(mapping).forEach((region) => { 22 | const imgElement = queryByTestId(`regionenkartelegend-${region}`); 23 | expect(imgElement).toBeInTheDocument(); 24 | expect(imgElement).toHaveAttribute("src", `${mapping[region]}.png`); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/layerInfos/RegionenkartePublicLayerInfo/boundary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/layerInfos/RegionenkartePublicLayerInfo/boundary.png -------------------------------------------------------------------------------- /src/layerInfos/RegionenkartePublicLayerInfo/gelb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/layerInfos/RegionenkartePublicLayerInfo/gelb.png -------------------------------------------------------------------------------- /src/layerInfos/RegionenkartePublicLayerInfo/grun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/layerInfos/RegionenkartePublicLayerInfo/grun.png -------------------------------------------------------------------------------- /src/layerInfos/RegionenkartePublicLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./RegionenkartePublicLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/RegionenkartePublicLayerInfo/lila.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/layerInfos/RegionenkartePublicLayerInfo/lila.png -------------------------------------------------------------------------------- /src/layerInfos/RegionenkartePublicLayerInfo/rot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/layerInfos/RegionenkartePublicLayerInfo/rot.png -------------------------------------------------------------------------------- /src/layerInfos/RegionenkartePublicTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./RegionenkartePublicTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/SandboxTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SandboxTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/TarifverbundkarteTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TarifverbundkarteTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/ZweitausbildungLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/ZweitausbildungRoutesSubLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungRoutesSubLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/ZweitausbildungSubLayerInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungSubLayerInfo"; 2 | -------------------------------------------------------------------------------- /src/layerInfos/ZweitausbildungTopicInfo/ZweitausbildungTopicInfo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTranslation } from "react-i18next"; 3 | 4 | function ZweitausbildungTopicInfo() { 5 | const { t } = useTranslation(); 6 | return ( 7 |
8 |

{t("ch.sbb.zweitausbildung-desc")}

9 |

10 | {t("Datenstand")}: {t("ch.sbb.zweitausbildung-datastatus")} 11 |

12 |

13 | {t("Verantwortlich")}: 14 |
15 | HR-POK-SKK-PM 16 |
17 | pm.skk.kbc@sbb.ch. 18 |

19 |
20 | ); 21 | } 22 | 23 | export default ZweitausbildungTopicInfo; 24 | -------------------------------------------------------------------------------- /src/layerInfos/ZweitausbildungTopicInfo/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungTopicInfo"; 2 | -------------------------------------------------------------------------------- /src/layers/AusbauLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AusbauLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/BeleuchtungsLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./BeleuchtungsLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/DirektverbindungenLayer/DirektverbindungenLayer.test.js: -------------------------------------------------------------------------------- 1 | import { Layer } from "mobility-toolbox-js/ol"; 2 | import OLMap from "ol/Map"; 3 | import DirektverbindungenLayer from "./DirektverbindungenLayer"; 4 | 5 | describe("DirektverbindungenLayer", () => { 6 | test("should become visible when night or day layer are hidden then day layer becomes visible", () => { 7 | const map = new OLMap({}); 8 | const layer = new DirektverbindungenLayer({ 9 | visible: false, 10 | properties: { 11 | dayLayer: new Layer({ visible: false }), 12 | nightLayer: new Layer({ visible: false }), 13 | }, 14 | }); 15 | 16 | // Listen day/night layer events 17 | layer.attachToMap(map); 18 | 19 | expect(layer.visible).toBe(false); 20 | layer.get("dayLayer").visible = true; 21 | expect(layer.visible).toBe(true); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/layers/DirektverbindungenLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DirektverbindungenLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/DrawLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DrawLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/GeltungsbereicheLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./GeltungsbereicheLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/HandicapLayer/HandicapLayer.js: -------------------------------------------------------------------------------- 1 | import MapboxStyleLayer from "../MapboxStyleLayer"; 2 | 3 | /** 4 | * Layer for Handicap 5 | * Extends {@link https://mobility-toolbox-js.geops.io/doc/class/build/ol/layers/MapboxStyleLayer%20js~MapboxStyleLayer%20html-offset-anchor} 6 | * @private 7 | * @class 8 | * @param {Object} [options] Layer options. 9 | */ 10 | class HandicapLayer extends MapboxStyleLayer { 11 | getFeatureInfoAtCoordinate(coordinate) { 12 | return super.getFeatureInfoAtCoordinate(coordinate).then((data) => { 13 | // Remove duplicate features. 14 | return { 15 | ...data, 16 | features: data.features.reduce((acc, feature) => { 17 | return acc.find((f) => f.get("uic") === feature.get("uic")) 18 | ? acc 19 | : [...acc, feature]; 20 | }, []), 21 | }; 22 | }); 23 | } 24 | } 25 | 26 | export default HandicapLayer; 27 | -------------------------------------------------------------------------------- /src/layers/HandicapLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./HandicapLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/KilometrageLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./KilometrageLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/LevelLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./LevelLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/MapboxStyleLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MapboxStyleLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/MapsGeoAdminLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MapsGeoAdminLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/MesswagenLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MesswagenLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/PlatformsLayer/PlatformsLayer.test.js: -------------------------------------------------------------------------------- 1 | import MapboxStyleLayer from "../MapboxStyleLayer"; 2 | import PlatformsLayer from "./PlatformsLayer"; 3 | 4 | describe("PlatformsLayer", () => { 5 | test("return only one featureInfo to avoid duplicate data in der Popup [TRAFDATA-334]", (done) => { 6 | const feat1 = { id: "feat1" }; 7 | const feat2 = { id: "feat2" }; 8 | MapboxStyleLayer.prototype.getFeatureInfoAtCoordinate = jest.fn(() => 9 | Promise.resolve({ 10 | features: [feat1, feat2], 11 | }), 12 | ); 13 | const layer = new PlatformsLayer(); 14 | layer.getFeatureInfoAtCoordinate().then((featureInfo) => { 15 | expect(featureInfo.features.length).toBe(1); 16 | expect(featureInfo.features[0]).toBe(feat1); 17 | done(); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/layers/PlatformsLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PlatformsLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/RailplusLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./RailplusLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/RegionenkarteLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./RegionenkarteLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/SchmalspurLayer/SchmalspurLayer.js: -------------------------------------------------------------------------------- 1 | import MapboxStyleLayer from "../MapboxStyleLayer"; 2 | 3 | /** 4 | * Layer for SchmalspurLayer 5 | * Extends {@link https://mobility-toolbox-js.geops.io/doc/class/build/ol/layers/MapboxStyleLayer%20js~MapboxStyleLayer%20html-offset-anchor} 6 | * @private 7 | * @class 8 | * @param {Object} [options] Layer options. 9 | */ 10 | class SchmalspurLayer extends MapboxStyleLayer { 11 | onLoad() { 12 | super.onLoad(); 13 | this.fetchSource(); 14 | } 15 | 16 | fetchSource() { 17 | const { url } = 18 | this.mapboxLayer?.mbMap?.getSource("ch.sbb.isb.schmalspur") || {}; 19 | fetch(url) 20 | .then((res) => res.json()) 21 | .then((data) => { 22 | this.tuInfos = data["geops.isb.schmalspur.tu_info"]; 23 | }) 24 | // eslint-disable-next-line no-console 25 | .catch((err) => console.error(err)); 26 | } 27 | } 28 | 29 | export default SchmalspurLayer; 30 | -------------------------------------------------------------------------------- /src/layers/SchmalspurLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SchmalspurLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/StationsLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StationsLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/StsHighlightRoutesLayer/StsHighlightRoutesLayer.js: -------------------------------------------------------------------------------- 1 | import MapboxStyleLayer from "../MapboxStyleLayer"; 2 | 3 | const lineWidth = 15; 4 | 5 | class HighlightRoutesLayer extends MapboxStyleLayer { 6 | highlightRoutes(names = [], property = "route_names_premium") { 7 | const { mbMap, loaded } = this.mapboxLayer || {}; 8 | if (loaded && mbMap && mbMap.getStyle()) { 9 | let lineWidthProp = 0; 10 | if (names.length) { 11 | const any = ["any"]; 12 | names.forEach((name) => { 13 | any.push(["in", name, ["get", property]]); 14 | }); 15 | lineWidthProp = ["case", any, lineWidth, 0]; 16 | } 17 | mbMap 18 | .getStyle() 19 | .layers.filter(this.styleLayersFilter) 20 | .forEach((layer) => { 21 | mbMap.setPaintProperty(layer.id, "line-opacity", 0.8); 22 | mbMap.setPaintProperty(layer.id, "line-width", lineWidthProp); 23 | }); 24 | } 25 | } 26 | } 27 | 28 | export default HighlightRoutesLayer; 29 | -------------------------------------------------------------------------------- /src/layers/StsHighlightRoutesLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StsHighlightRoutesLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/StsPoisLayer/StsPoisLayer.test.js: -------------------------------------------------------------------------------- 1 | import StsPoisLayer from "./StsPoisLayer"; 2 | 3 | describe("StsPoisLayer", () => { 4 | test("sets up a correct ol layer", () => { 5 | const layer = new StsPoisLayer(); 6 | expect(layer.olLayer.getSource().getUrl()).toBe( 7 | "https://maps.trafimage.ch/sts-static/pois.geojson", 8 | ); 9 | expect(layer.olLayer.getSource().getFormat().dataProjection.getCode()).toBe( 10 | "EPSG:4326", 11 | ); 12 | expect( 13 | layer.olLayer.getSource().getFormat().defaultFeatureProjection.getCode(), 14 | ).toBe("EPSG:3857"); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/layers/StsPoisLayer/img/poi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/layers/StsPoisLayer/img/poi.png -------------------------------------------------------------------------------- /src/layers/StsPoisLayer/img/poi_hl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/layers/StsPoisLayer/img/poi_hl.png -------------------------------------------------------------------------------- /src/layers/StsPoisLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StsPoisLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/TarifverbundkarteLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TarifverbundkarteLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/TrafimageMapboxLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TrafimageMapboxLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/TralisLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TralisLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/ZweitausbildungAbroadLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungAbroadLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/ZweitausbildungPoisLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungPoisLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/ZweitausbildungRoutesHighlightLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungRoutesHighlightLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/ZweitausbildungRoutesLayer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungRoutesLayer"; 2 | -------------------------------------------------------------------------------- /src/layers/ZweitausbildungRoutesLayer/lines.test.js: -------------------------------------------------------------------------------- 1 | import lines from "./lines"; 2 | 3 | describe("lines", () => { 4 | test("must no have a string as key that is included in another key (that means style is probably broken)", () => { 5 | let hasWrongKey = false; 6 | Object.keys(lines).forEach((key) => { 7 | Object.keys(lines).forEach((key2) => { 8 | if (key !== key2 && key2.includes(key)) { 9 | // eslint-disable-next-line no-console 10 | console.log("Bad key key1:", key, "key2:", key2); 11 | hasWrongKey = true; 12 | } 13 | }); 14 | }); 15 | 16 | expect(hasWrongKey).toBe(false); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/menus/DirektverbindungenMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DvMenu"; 2 | -------------------------------------------------------------------------------- /src/menus/DrawMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DrawMenu"; 2 | -------------------------------------------------------------------------------- /src/menus/ExportMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ExportMenu"; 2 | -------------------------------------------------------------------------------- /src/menus/GaExportMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./GaExportMenu"; 2 | -------------------------------------------------------------------------------- /src/menus/GeltungsbereicheTopicMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./GeltungsbereicheTopicMenu"; 2 | -------------------------------------------------------------------------------- /src/menus/IframeMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./IframeMenu"; 2 | -------------------------------------------------------------------------------- /src/menus/RailplusMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./RailplusMenu"; 2 | -------------------------------------------------------------------------------- /src/menus/ShareMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ShareMenu"; 2 | -------------------------------------------------------------------------------- /src/menus/StsMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StsMenu"; 2 | -------------------------------------------------------------------------------- /src/menus/TrackerMenu/TrackerMenu.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTranslation } from "react-i18next"; 3 | import { TiVideo } from "react-icons/ti"; 4 | import FeatureMenu from "../../components/FeatureMenu"; 5 | 6 | /** 7 | * Menu use to display feature info from punctuality layers. 8 | */ 9 | function TrackerMenu(props) { 10 | const { t } = useTranslation(); 11 | 12 | return ( 13 | , 20 | }} 21 | /> 22 | ); 23 | } 24 | 25 | export default React.memo(TrackerMenu); 26 | -------------------------------------------------------------------------------- /src/menus/TrackerMenu/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TrackerMenu"; 2 | -------------------------------------------------------------------------------- /src/model/map/actions.js: -------------------------------------------------------------------------------- 1 | export const SET_LAYERS = "SET_LAYERS"; 2 | export const SET_LAYER_SERVICE = "SET_LAYER_SERVICE"; 3 | export const SET_CENTER = "SET_CENTER"; 4 | export const SET_RESOLUTION = "SET_RESOLUTION"; 5 | export const SET_ZOOM = "SET_ZOOM"; 6 | export const SET_MAX_EXTENT = "SET_MAX_EXTENT"; 7 | export const SET_MAX_ZOOM = "SET_MAX_ZOOM"; 8 | export const SET_MIN_ZOOM = "SET_MIN_ZOOM"; 9 | export const SET_ZOOM_TYPE = "SET_ZOOM_TYPE"; 10 | 11 | export const setLayers = (data) => ({ type: SET_LAYERS, data }); 12 | 13 | export const setCenter = (data) => ({ type: SET_CENTER, data }); 14 | 15 | export const setResolution = (data) => ({ type: SET_RESOLUTION, data }); 16 | 17 | export const setZoom = (data) => ({ type: SET_ZOOM, data }); 18 | 19 | export const setMaxExtent = (data) => ({ type: SET_MAX_EXTENT, data }); 20 | 21 | export const setMaxZoom = (data) => ({ type: SET_MAX_ZOOM, data }); 22 | 23 | export const setMinZoom = (data) => ({ type: SET_MIN_ZOOM, data }); 24 | 25 | export const setZoomType = (data) => ({ type: SET_ZOOM_TYPE, data }); 26 | -------------------------------------------------------------------------------- /src/model/store.js: -------------------------------------------------------------------------------- 1 | import { 2 | legacy_createStore as createStore, 3 | applyMiddleware, 4 | compose, 5 | combineReducers, 6 | } from "redux"; 7 | import { thunk } from "redux-thunk"; 8 | import createDebounce from "redux-debounced"; 9 | import map from "./map/reducers"; 10 | import app from "./app/reducers"; 11 | 12 | // reduxjstoolkit: 13 | 14 | // const store = configureStore({ 15 | // // Automatically calls `combineReducers` 16 | // reducer: { 17 | // app, 18 | // map, 19 | // }, 20 | // middleware: (getDefaultMiddleware) => { 21 | // return getDefaultMiddleware().concat(createDebounce()); 22 | // }, 23 | // }); 24 | /* eslint-disable */ 25 | const getStore = () => { 26 | const store = createStore( 27 | combineReducers({ 28 | app, 29 | map, 30 | }), 31 | compose(applyMiddleware(createDebounce(), thunk)), 32 | ); 33 | 34 | return store; 35 | }; 36 | 37 | export default getStore; 38 | -------------------------------------------------------------------------------- /src/popups/BahnhofplanPopup/BahnhofplanPopup.scss: -------------------------------------------------------------------------------- 1 | .wkp-bahnhofplan-popup { 2 | svg { 3 | margin-left: 5px; 4 | } 5 | 6 | .wkp-bahnhofplan-popup-title { 7 | font-weight: bold; 8 | } 9 | 10 | a { 11 | text-decoration: none !important; 12 | } 13 | 14 | div { 15 | padding: 3px 0; 16 | border-width: 0 0 1px; 17 | border-style: solid; 18 | border-color: #eee; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/popups/BahnhofplanPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./BahnhofplanPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/BeleuchtungsPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./BeleuchtungsPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/BetriebsRegionenPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./BetriebsRegionenPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/BusLinePopup/BusLinePopup.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import Feature from "ol/Feature"; 4 | 5 | const propTypes = { 6 | feature: PropTypes.instanceOf(Feature).isRequired, 7 | }; 8 | 9 | function BusLinePopup({ feature }) { 10 | const props = feature.getProperties(); 11 | return ( 12 |
13 | {Object.entries(props).map(([key, value]) => { 14 | if (!/^lines /.test(key)) { 15 | return null; 16 | } 17 | return ( 18 |
19 |
{key.replace("lines ", "")}
20 |
{value}
21 |
22 | ); 23 | })} 24 |
25 | ); 26 | } 27 | 28 | BusLinePopup.propTypes = propTypes; 29 | 30 | export default React.memo(BusLinePopup); 31 | -------------------------------------------------------------------------------- /src/popups/BusLinePopup/BusLinePopup.scss: -------------------------------------------------------------------------------- 1 | .wkp-bus-line-popup { 2 | > div { 3 | display: flex; 4 | align-content: center; 5 | margin: 10px 0; 6 | 7 | > div:first-child { 8 | font-weight: bold; 9 | width: 100px; 10 | margin-right: 10px; 11 | } 12 | 13 | > div:last-child { 14 | width: 200px; 15 | max-height: 100px; 16 | overflow-y: auto; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/popups/BusLinePopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./BusLinePopup"; 2 | -------------------------------------------------------------------------------- /src/popups/CasaRoutePopup/CasaRoutePopup.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import Feature from "ol/Feature"; 4 | 5 | const propTypes = { 6 | feature: PropTypes.instanceOf(Feature).isRequired, 7 | }; 8 | 9 | function CasaRoutePopup({ feature }) { 10 | const route = feature.get("route"); 11 | 12 | return ( 13 |
14 | {route.popupContent.map((item) => ( 15 |
16 |
{item}
17 |
18 | ))} 19 |
20 | ); 21 | } 22 | 23 | CasaRoutePopup.propTypes = propTypes; 24 | 25 | CasaRoutePopup.hideHeader = (feature) => { 26 | const route = feature.get("route"); 27 | return !route.popupTitle; 28 | }; 29 | 30 | CasaRoutePopup.renderTitle = (feature) => { 31 | const route = feature.get("route"); 32 | return route.popupTitle; 33 | }; 34 | 35 | export default CasaRoutePopup; 36 | -------------------------------------------------------------------------------- /src/popups/CasaRoutePopup/CasaRoutePopup.scss: -------------------------------------------------------------------------------- 1 | .wkp-casa-route-popup { 2 | .wkp-casa-route-popup-row { 3 | padding: 3px; 4 | 5 | pre { 6 | font-size: inherit; 7 | color: inherit; 8 | margin: inherit; 9 | font-family: inherit; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/popups/CasaRoutePopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CasaRoutePopup"; 2 | -------------------------------------------------------------------------------- /src/popups/ConstructionPopup/ConstructionPopup.scss: -------------------------------------------------------------------------------- 1 | .wkp-construction-popup { 2 | .wkp-construction-popup-subtitle { 3 | font-style: italic; 4 | padding-bottom: 7px; 5 | } 6 | 7 | .wkp-construction-popup-desc { 8 | padding-bottom: 4px; 9 | } 10 | 11 | .wkp-construction-popup-link { 12 | padding: 3px 0; 13 | border-width: 1px 0 0; 14 | border-style: solid; 15 | border-color: #eee; 16 | 17 | &:last-child { 18 | border-bottom-width: 1px; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/popups/ConstructionPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ConstructionPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/DeparturePopup/DeparturePopup.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import Feature from "ol/Feature"; 4 | import { withTranslation } from "react-i18next"; 5 | import DeparturePopupContent from "./DeparturePopupContent"; 6 | 7 | function DeparturePopup({ feature, children }) { 8 | const platform = feature.get("platform"); 9 | const uic = parseFloat(feature.get("sbb_id")); 10 | 11 | return ( 12 | 13 | {children} 14 | 15 | ); 16 | } 17 | 18 | DeparturePopup.propTypes = { 19 | feature: PropTypes.instanceOf(Feature).isRequired, 20 | children: PropTypes.node, 21 | }; 22 | DeparturePopup.defaultProps = { children: undefined }; 23 | 24 | const composed = withTranslation()(DeparturePopup); 25 | composed.renderTitle = (feat, layer, t) => { 26 | const platform = feat.get("platform"); 27 | if (platform) { 28 | return `${feat.get("name")} (${t("abfahrtszeiten_kante")} ${platform})`; 29 | } 30 | return feat.get("name"); 31 | }; 32 | 33 | export default composed; 34 | -------------------------------------------------------------------------------- /src/popups/DeparturePopup/DestinationInput.scss: -------------------------------------------------------------------------------- 1 | .tm-departure-input { 2 | position: relative; 3 | 4 | .tm-search-input { 5 | input { 6 | padding: 0 7px; 7 | } 8 | 9 | .tm-bt-search { 10 | display: none; 11 | } 12 | } 13 | 14 | .tm-autocomplete .tm-autocomplete-results { 15 | max-height: 140px; 16 | } 17 | 18 | .tm-list li { 19 | line-height: 20px; 20 | padding: 4px 7px; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/popups/DeparturePopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DeparturePopup"; 2 | -------------------------------------------------------------------------------- /src/popups/DirektverbindungenPopup/DvPopup.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { makeStyles } from "@mui/styles"; 3 | import DvFeatureInfo from "../../config/ch.sbb.direktverbindungen/DvFeatureInfo"; 4 | import { DvFeatureInfoTitleString } from "../../config/ch.sbb.direktverbindungen/DvFeatureInfoTitle/DvFeatureInfoTitle"; 5 | 6 | const useStyles = makeStyles({ 7 | container: { 8 | padding: "0 !important", 9 | }, 10 | }); 11 | 12 | function DvPopup() { 13 | const classes = useStyles(); 14 | 15 | return ( 16 |
17 | 18 |
19 | ); 20 | } 21 | 22 | const memoized = React.memo(DvPopup); 23 | memoized.renderTitle = () => ( 24 | 25 | 26 | 27 | ); 28 | memoized.hidePagination = true; 29 | 30 | export default memoized; 31 | -------------------------------------------------------------------------------- /src/popups/DirektverbindungenPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DvPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/DrawPopup/DrawPopup.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import Feature from "ol/Feature"; 4 | 5 | function DrawPopup({ feature }) { 6 | const descr = feature.get("description"); 7 | if (!descr) { 8 | return null; 9 | } 10 | 11 | return ( 12 |
16 | ); 17 | } 18 | 19 | DrawPopup.propTypes = { 20 | feature: PropTypes.instanceOf(Feature).isRequired, 21 | }; 22 | 23 | const composed = React.memo(DrawPopup); 24 | 25 | composed.renderTitle = (feature) => feature.get("name"); 26 | export default composed; 27 | -------------------------------------------------------------------------------- /src/popups/DrawPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DrawPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/EnergiePopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./EnergiePopup"; 2 | -------------------------------------------------------------------------------- /src/popups/GeltungsbereicheGaPopup/GeltungsbereicheLegend.test.js: -------------------------------------------------------------------------------- 1 | import { getLegends } from "./GeltungsbereicheLegend"; 2 | 3 | describe("GeltungsbereicheLegend", () => { 4 | test("exports legends that have the good mots order", () => { 5 | expect( 6 | Object.values(getLegends()).map(({ mots }) => mots && mots[0]), 7 | ).toEqual(["rail", "bus", "gondola", "ferry", null]); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/popups/GeltungsbereicheGaPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./GeltungsbereicheGaPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/HandicapPopup/HandicapPopup.scss: -------------------------------------------------------------------------------- 1 | .wkp-handicap-popup { 2 | p { 3 | margin-block: 0; 4 | } 5 | 6 | .wkp-handicap-popup-body { 7 | .wkp-handicap-popup-title { 8 | font-weight: bold; 9 | margin-bottom: 10px; 10 | } 11 | 12 | .wkp-handicap-popup-element { 13 | padding-bottom: 10px; 14 | flex-direction: column; 15 | display: flex; 16 | 17 | a { 18 | white-space: nowrap; 19 | } 20 | 21 | &:last-child { 22 | padding: 0; 23 | } 24 | 25 | .wkp-handicap-popup-field-title { 26 | font-weight: bold; 27 | vertical-align: top; 28 | padding-right: 20px; 29 | } 30 | 31 | .wkp-handicap-popup-field-body { 32 | word-break: break-word; 33 | } 34 | } 35 | 36 | .wkp-handicap-popup-bottom { 37 | display: flex; 38 | justify-content: space-between; 39 | flex-wrap: wrap; 40 | 41 | .wkp-handicap-popup-element { 42 | min-width: 180px; 43 | width: 50%; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/popups/HandicapPopup/README.md: -------------------------------------------------------------------------------- 1 | # 2 | 3 | Example of the Handicap topic together with its menu component. 4 | 5 | ```jsx 6 | import React from 'react'; 7 | import { handicap } from '../../config/topics'; 8 | import { HandicapMenu } from '../../config/menu'; 9 | 10 | import TrafimageMaps from '../../components/TrafimageMaps'; 11 | 12 | // The `apiKey` used here is for demonstration purposes only. 13 | // Please get your own api key at https://developer.geops.io/. 14 | const apiKey = window.apiKey; 15 | 16 |
17 | 38 |
; 39 | ``` 40 | -------------------------------------------------------------------------------- /src/popups/HandicapPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./HandicapPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/IsbPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./IsbPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/KilometragePopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./KilometragePopup"; 2 | -------------------------------------------------------------------------------- /src/popups/MapsGeoAdminPopup/MapsGeoAdminPopup.scss: -------------------------------------------------------------------------------- 1 | .wkp-maps-geo-admin-popup { 2 | .wkp-maps-geo-admin-popup-body { 3 | max-height: 200px; 4 | min-width: 300px; 5 | } 6 | 7 | .htmlpopup-header { 8 | font-family: SBBWeb-Bold, Arial, sans-serif; 9 | } 10 | 11 | .htmlpopup-content { 12 | table { 13 | border-spacing: 0 4px; 14 | padding: 10px 0; 15 | } 16 | 17 | .cell-left { 18 | vertical-align: top; 19 | min-width: 150px; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/popups/MapsGeoAdminPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MapsGeoAdminPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/MesswagenPhotosPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MesswagenPhotosPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/MesswagenPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./MesswagenPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/NetzkartePopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./NetzkartePopup"; 2 | -------------------------------------------------------------------------------- /src/popups/PassagierFrequenzenPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PassagierFrequenzenPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/PunctualityPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PunctualityPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/RailplusPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./RailplusPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/RegionenkarteIntersectionPopup/RegionenkarteIntersectionPopup.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import Feature from "ol/Feature"; 4 | import { makeStyles } from "@mui/styles"; 5 | 6 | const useStyles = makeStyles(() => ({ 7 | root: { 8 | flex: 1, 9 | display: "flex", 10 | alignItems: "center", 11 | justifyContent: "center", 12 | minWidth: "100px !important", 13 | }, 14 | })); 15 | 16 | function RegionenkarteIntersectionPopup({ feature }) { 17 | const classes = useStyles(); 18 | return
{feature.get("label")}
; 19 | } 20 | 21 | RegionenkarteIntersectionPopup.propTypes = { 22 | feature: PropTypes.instanceOf(Feature).isRequired, 23 | }; 24 | 25 | RegionenkarteIntersectionPopup.hideHeader = () => true; 26 | 27 | export default RegionenkarteIntersectionPopup; 28 | -------------------------------------------------------------------------------- /src/popups/RegionenkarteIntersectionPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./RegionenkarteIntersectionPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/RegionenkarteSegmentPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./RegionenkarteSegmentPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/SchmalspurPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SchmalspurPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/StationPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StationPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/StopPlacePopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StopPlacePopup"; 2 | -------------------------------------------------------------------------------- /src/popups/TarifverbundkartePopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./TarifverbundkartePopup"; 2 | -------------------------------------------------------------------------------- /src/popups/ZweitausbildungAbroadPopup/ZweitausbildungAbroadPopup.js: -------------------------------------------------------------------------------- 1 | import { PureComponent } from "react"; 2 | import PropTypes from "prop-types"; 3 | import Feature from "ol/Feature"; 4 | import { connect } from "react-redux"; 5 | import { compose } from "redux"; 6 | import { setFeatureInfo } from "../../model/app/actions"; 7 | 8 | const propTypes = { 9 | feature: PropTypes.instanceOf(Feature).isRequired, 10 | 11 | // mapDispatchToProps 12 | dispatchSetFeatureInfo: PropTypes.func.isRequired, 13 | }; 14 | 15 | class ZweitausbildungAbroadPopup extends PureComponent { 16 | componentDidMount() { 17 | const { feature, dispatchSetFeatureInfo } = this.props; 18 | window.open(feature.get("url"), "_blank"); 19 | dispatchSetFeatureInfo(); 20 | } 21 | 22 | render() { 23 | return null; 24 | } 25 | } 26 | 27 | ZweitausbildungAbroadPopup.propTypes = propTypes; 28 | 29 | const mapDispatchToProps = { 30 | dispatchSetFeatureInfo: setFeatureInfo, 31 | }; 32 | 33 | export default compose(connect(null, mapDispatchToProps))( 34 | ZweitausbildungAbroadPopup, 35 | ); 36 | -------------------------------------------------------------------------------- /src/popups/ZweitausbildungAbroadPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungAbroadPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/ZweitausbildungPoisPopup/ZweitausbildungPoisPopup.scss: -------------------------------------------------------------------------------- 1 | .wkp-zweitausbildung-pois-popup { 2 | overflow-y: auto; 3 | 4 | .wkp-zweitausbildung-pois-popup-row { 5 | border-bottom: dashed 1px #bebebe; 6 | padding-top: 5px; 7 | 8 | &:hover { 9 | background-color: #eaeaea; 10 | } 11 | } 12 | 13 | .wkp-zweitausbildung-pois-popup-railaway { 14 | font-style: italic; 15 | } 16 | 17 | .wkp-zweitausbildung-pois-popup-image { 18 | padding: 6px; 19 | 20 | img { 21 | max-width: 100%; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/popups/ZweitausbildungPoisPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungPoisPopup"; 2 | -------------------------------------------------------------------------------- /src/popups/ZweitausbildungRoutesPopup/ZweitausbildungRoutesPopup.scss: -------------------------------------------------------------------------------- 1 | .wkp-zweitausbildung-routes-popup { 2 | overflow-y: auto; 3 | 4 | .wkp-zweitausbildung-routes-popup-row { 5 | min-height: 50px; 6 | border-bottom: dashed 1px #bebebe; 7 | padding-top: 5px; 8 | 9 | &.highlight { 10 | background-color: #eaeaea; 11 | } 12 | } 13 | 14 | .wkp-zweitausbildung-routes-popup-image > img { 15 | vertical-align: middle; 16 | padding: 3px 5px 3px 0; 17 | } 18 | 19 | .wkp-zweitausbildung-routes-popup-desc { 20 | padding-top: 5px; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/popups/ZweitausbildungRoutesPopup/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ZweitausbildungRoutesPopup"; 2 | -------------------------------------------------------------------------------- /src/searches/Betriebspunkte/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Betriebspunkte"; 2 | -------------------------------------------------------------------------------- /src/searches/HandicapStopFinder/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./HandicapStopFinder"; 2 | -------------------------------------------------------------------------------- /src/searches/Lines/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Lines"; 2 | -------------------------------------------------------------------------------- /src/searches/Locations/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Locations"; 2 | -------------------------------------------------------------------------------- /src/searches/Municipalities/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Municipalities"; 2 | -------------------------------------------------------------------------------- /src/searches/Search.test.js: -------------------------------------------------------------------------------- 1 | import { Style } from "ol/style"; 2 | import Search from "./Search"; 3 | 4 | describe("Search", () => { 5 | describe("#getFeatures()", () => { 6 | test("applies the highlightStyle on each feature", () => { 7 | const search = new Search(); 8 | search.highlightStyle = new Style({}); 9 | const feat = search.getFeature({ 10 | type: "Feature", 11 | geometry: { 12 | type: "Point", 13 | coordinates: [0, 0], 14 | }, 15 | properties: {}, 16 | }); 17 | 18 | expect(feat.getStyle()).toBe(search.highlightStyle); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/searches/StopFinder/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StopFinder"; 2 | -------------------------------------------------------------------------------- /src/searches/index.js: -------------------------------------------------------------------------------- 1 | import Betriebspunkte from "./Betriebspunkte"; 2 | import HandicapStopFinder from "./HandicapStopFinder"; 3 | import Lines from "./Lines"; 4 | import Locations from "./Locations"; 5 | import Municipalities from "./Municipalities"; 6 | import StopFinder from "./StopFinder"; 7 | import Search from "./Search"; 8 | 9 | export { default as Betriebspunkte } from "./Betriebspunkte"; 10 | export { default as HandicapStopFinder } from "./HandicapStopFinder"; 11 | export { default as Lines } from "./Lines"; 12 | export { default as Locations } from "./Locations"; 13 | export { default as Municipalities } from "./Municipalities"; 14 | export { default as StopFinder } from "./StopFinder"; 15 | export { default as Search } from "./Search"; 16 | 17 | export default { 18 | Betriebspunkte, 19 | HandicapStopFinder, 20 | Lines, 21 | Locations, 22 | Municipalities, 23 | StopFinder, 24 | Search, 25 | }; 26 | -------------------------------------------------------------------------------- /src/themes/react-ui/components.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable import-notation */ 2 | /* stylelint-disable scss/at-import-partial-extension */ 3 | 4 | /** 5 | * This file load the css of components. 6 | */ 7 | @import '../../components/Autocomplete/Autocomplete.scss'; 8 | @import '../../components/Button/Button.scss'; 9 | @import '../../components/Checkbox/Checkbox.scss'; 10 | @import '../../components/Dialog/Dialog.scss'; 11 | @import '../../components/Footer/Footer.scss'; 12 | @import '../../components/Header/Header.scss'; 13 | @import '../../components/List/List.scss'; 14 | @import '../../components/Menu/Menu.scss'; 15 | @import '../../components/MenuItem/MenuItem.scss'; 16 | @import '../../components/Overlay/Overlay.scss'; 17 | @import '../../components/PermalinkInput/PermalinkInput.scss'; 18 | @import '../../components/SearchInput/SearchInput.scss'; 19 | @import '../../components/Sidebar/Sidebar.scss'; 20 | @import '../../components/SidebarMenuItem/SidebarMenuItem.scss'; 21 | @import '../../components/Tabs/Tabs.scss'; 22 | @import '../../components/Tab/Tab.scss'; 23 | -------------------------------------------------------------------------------- /src/themes/react-ui/index.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable scss/at-import-partial-extension */ 2 | /* stylelint-disable import-notation */ 3 | 4 | /** 5 | * This file load the default theme, for all the components. 6 | */ 7 | @import './variables.scss'; 8 | @import './mixins.scss'; 9 | @import './components.scss'; 10 | 11 | [role='button']:not([disable]), 12 | button:not([disable]), 13 | a:not([disable]) { 14 | cursor: pointer; 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/capitalizeFirstLetter.js: -------------------------------------------------------------------------------- 1 | const capitalizeFirstLetter = (string) => { 2 | return string.charAt(0).toUpperCase() + string.slice(1); 3 | }; 4 | 5 | export default capitalizeFirstLetter; 6 | -------------------------------------------------------------------------------- /src/utils/coordinateHelper.js: -------------------------------------------------------------------------------- 1 | function meterFormat(coords) { 2 | return coords.map((num) => 3 | Math.round(num) 4 | .toString() 5 | .replace(/\B(?=(\d{3})+(?!\d))/g, "'"), 6 | ); 7 | } 8 | 9 | function wgs84Format(coords, decimalSep = ".") { 10 | return coords.map((num) => num.toFixed(5).replace(".", decimalSep)); 11 | } 12 | 13 | export default { meterFormat, wgs84Format }; 14 | -------------------------------------------------------------------------------- /src/utils/coordinateHelper.test.js: -------------------------------------------------------------------------------- 1 | import coordinateHelper from "./coordinateHelper"; 2 | 3 | describe("coordinateHelper", () => { 4 | test("should format coordinates correcly.", () => { 5 | expect( 6 | coordinateHelper.wgs84Format([8.298163986976597, 47.041177354357444]), 7 | ).toEqual(["8.29816", "47.04118"]); 8 | expect( 9 | coordinateHelper.wgs84Format( 10 | [8.298163986976597, 47.041177354357444], 11 | ",", 12 | ), 13 | ).toEqual(["8,29816", "47,04118"]); 14 | expect( 15 | coordinateHelper.meterFormat([929977.0073545576, 5948635.427925528]), 16 | ).toEqual(["929'977", "5'948'635"]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Bold.eot -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Bold.ttf -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Bold.woff -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Bold.woff2 -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Italic.eot -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Italic.ttf -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Italic.woff -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Italic.woff2 -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Light.eot -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Light.ttf -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Light.woff -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Light.woff2 -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Roman.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Roman.eot -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Roman.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Roman.ttf -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Roman.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Roman.woff -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Roman.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Roman.woff2 -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Thin.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Thin.eot -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Thin.ttf -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Thin.woff -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-Thin.woff2 -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-UltraLight.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-UltraLight.eot -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-UltraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-UltraLight.ttf -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-UltraLight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-UltraLight.woff -------------------------------------------------------------------------------- /src/utils/fonts/SBBWeb-UltraLight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geops/trafimage-maps/d9e73607456380022fdc688a0cee789b53624cba/src/utils/fonts/SBBWeb-UltraLight.woff2 -------------------------------------------------------------------------------- /src/utils/formatPhone.js: -------------------------------------------------------------------------------- 1 | const formatPhone = (phone) => { 2 | try { 3 | return phone 4 | .split(/(\+41)(\d{2})(\d{3})(\d{2})(\d{2})/g) 5 | .join(" ") 6 | .trim(); 7 | } catch (e) { 8 | return phone; 9 | } 10 | }; 11 | 12 | export default formatPhone; 13 | -------------------------------------------------------------------------------- /src/utils/getDelayString.js: -------------------------------------------------------------------------------- 1 | // We round the minutes down for departure times and up for arrival times. 2 | export const getDelaySecure = (milliseconds, isArrival) => { 3 | let timeInMs = milliseconds; 4 | if (timeInMs < 0) { 5 | timeInMs = 0; 6 | } 7 | const h = Math.floor(timeInMs / 3600000); 8 | const m = (isArrival ? Math.ceil : Math.floor)((timeInMs % 3600000) / 60000); 9 | 10 | return h * 3600000 + m * 60000; 11 | }; 12 | 13 | // We round the minutes down for departure times and up for arrival times. 14 | const getDelayString = (milliseconds, isArrival) => { 15 | let timeInMs = milliseconds; 16 | if (timeInMs < 0) { 17 | timeInMs = 0; 18 | } 19 | timeInMs = getDelaySecure(timeInMs, isArrival); 20 | const h = Math.floor(timeInMs / 3600000); 21 | const m = (timeInMs % 3600000) / 60000; 22 | 23 | if (h === 0 && m === 0) { 24 | return "+0m"; 25 | } 26 | if (h === 0) { 27 | return `+${m}m`; 28 | } 29 | 30 | let str = `+`; 31 | if (h > 0) { 32 | str += `${h}h`; 33 | } 34 | 35 | if (m > 0) { 36 | str += `${m}m`; 37 | } 38 | return str; 39 | }; 40 | 41 | export default getDelayString; 42 | -------------------------------------------------------------------------------- /src/utils/getDelayString.test.js: -------------------------------------------------------------------------------- 1 | import getDelayString from "./getDelayString"; 2 | 3 | describe("getDelayString", () => { 4 | it('should return "+0m" when delay <= 0', () => { 5 | expect(getDelayString(0)).toBe("+0m"); 6 | expect(getDelayString(-1)).toBe("+0m"); 7 | }); 8 | 9 | it('should return "+Xm" when delay is between hours and 0', () => { 10 | expect(getDelayString(69000)).toBe("+1m"); 11 | expect(getDelayString(129000)).toBe("+2m"); 12 | }); 13 | 14 | it('should return "+XhXm" when delay is more than 1 hour', () => { 15 | expect(getDelayString(3659000)).toBe("+1h"); 16 | expect(getDelayString(3661000)).toBe("+1h1m"); 17 | expect(getDelayString(36610000)).toBe("+10h10m"); 18 | }); 19 | 20 | it("should ceil the result if it is an arrival time", () => { 21 | expect(getDelayString(3659000, true)).toBe("+1h1m"); 22 | expect(getDelayString(3659000)).toBe("+1h"); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/utils/getFeatureInfoAtCoordinate.js: -------------------------------------------------------------------------------- 1 | const getFeatureInfoAtCoordinate = (coordinate, layers, eventType) => { 2 | const promises = layers.map((layer) => { 3 | return layer 4 | .getFeatureInfoAtCoordinate(coordinate, eventType) 5 | .then((featureInfo) => { 6 | return featureInfo; 7 | }); 8 | }); 9 | return Promise.all(promises); 10 | }; 11 | 12 | export default getFeatureInfoAtCoordinate; 13 | -------------------------------------------------------------------------------- /src/utils/getGreaterNumber.js: -------------------------------------------------------------------------------- 1 | // Get the first number that is higher or equal than the given number. 2 | function getGreaterNumber(number, numbers) { 3 | const sorted = [...numbers].sort((a, b) => a - b); 4 | return ( 5 | sorted.find((nb) => { 6 | return number <= nb; 7 | }) || sorted.pop() 8 | ); 9 | } 10 | 11 | export default getGreaterNumber; 12 | -------------------------------------------------------------------------------- /src/utils/getGreaterNumber.test.js: -------------------------------------------------------------------------------- 1 | import getGreaterNumber from "./getGreaterNumber"; 2 | 3 | test(`#getGreaterNumber()`, () => { 4 | let gen = getGreaterNumber(20.01, [200, 50, 100, 20, 10, 5]); 5 | expect(gen).toBe(50); 6 | gen = getGreaterNumber(50, [200, 50, 100, 20, 10, 5]); 7 | expect(gen).toBe(50); 8 | gen = getGreaterNumber(300, [200, 50, 100, 20, 10, 5]); 9 | expect(gen).toBe(200); 10 | gen = getGreaterNumber(0, [200, 50, 100, 20, 10, 5]); 11 | expect(gen).toBe(5); 12 | }); 13 | -------------------------------------------------------------------------------- /src/utils/getIsMobileDevice.js: -------------------------------------------------------------------------------- 1 | const getIsMobileDevice = () => { 2 | return !!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( 3 | navigator.userAgent, 4 | ); 5 | }; 6 | 7 | export default getIsMobileDevice; 8 | -------------------------------------------------------------------------------- /src/utils/getLayersAsFlatArray.js: -------------------------------------------------------------------------------- 1 | const getLayersAsFlatArray = (optLayers = []) => { 2 | let layers = []; 3 | optLayers.forEach((l) => { 4 | layers.push(l); 5 | const { children } = l; 6 | layers = layers.concat(getLayersAsFlatArray(children)); 7 | }); 8 | return layers; 9 | }; 10 | 11 | export default getLayersAsFlatArray; 12 | -------------------------------------------------------------------------------- /src/utils/getPopupComponent.js: -------------------------------------------------------------------------------- 1 | import popups from "../popups"; 2 | 3 | const getPopupComponent = ({ popupComponent, layer }) => { 4 | const comp = popupComponent || layer.get("popupComponent"); 5 | return typeof comp === "string" ? popups[comp] : comp; 6 | }; 7 | 8 | export default getPopupComponent; 9 | -------------------------------------------------------------------------------- /src/utils/getQueryableLayers.js: -------------------------------------------------------------------------------- 1 | import getLayersAsFlatArray from "./getLayersAsFlatArray"; 2 | 3 | const getQueryableLayers = (featureInfoEventType, layers, map) => { 4 | return getLayersAsFlatArray(layers).filter((layer) => { 5 | let isQueryable = 6 | layer.visible && 7 | layer.get("isQueryable") && 8 | ( 9 | layer.get("featureInfoEventTypes") || ["pointermove", "singleclick"] 10 | ).includes(featureInfoEventType); 11 | 12 | if (typeof layer.get("isQueryable") === "function") { 13 | isQueryable = layer.get("isQueryable")(map); 14 | } 15 | return isQueryable; 16 | }); 17 | }; 18 | 19 | export default getQueryableLayers; 20 | -------------------------------------------------------------------------------- /src/utils/getTrafimageFilter.js: -------------------------------------------------------------------------------- 1 | const getTrafimageFilter = ( 2 | stylelayer, 3 | filterAttribute = "trafimage.filter", 4 | ) => { 5 | return stylelayer?.metadata && stylelayer.metadata[filterAttribute]; 6 | }; 7 | 8 | export default getTrafimageFilter; 9 | -------------------------------------------------------------------------------- /src/utils/highlightPointStyle.test.js: -------------------------------------------------------------------------------- 1 | import highlightPointStyle from "./highlightPointStyle"; 2 | 3 | let context = null; 4 | 5 | describe("higlightPointStyle", () => { 6 | beforeEach(() => { 7 | context = { 8 | createRadialGradient: jest.fn(() => ({ 9 | addColorStop: jest.fn(), 10 | })), 11 | beginPath: jest.fn(), 12 | fill: jest.fn(), 13 | stroke: jest.fn(), 14 | arc: jest.fn(), 15 | }; 16 | }); 17 | 18 | test("draw a gradient if the geometry is a point", () => { 19 | highlightPointStyle.getRenderer()([0, 0], { context }); 20 | expect(context.createRadialGradient).toHaveBeenCalled(); 21 | }); 22 | 23 | test("does not crash if the geometry is not a point", () => { 24 | highlightPointStyle.getRenderer()([[0, 0], 0], { context }); 25 | highlightPointStyle.getRenderer()([0, 0, 0], { context }); 26 | expect(context.createRadialGradient).not.toHaveBeenCalled(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/utils/isoToIntlVehicleCode.test.js: -------------------------------------------------------------------------------- 1 | import isoToIntlVehicleCode from "./isoToIntlVehicleCode"; 2 | 3 | describe("isoToIntlVehicleCode", () => { 4 | test("returns the international vehicle registration code from an ISO country code", () => { 5 | expect(isoToIntlVehicleCode("DE")).toBe("D"); 6 | expect(isoToIntlVehicleCode("FR")).toBe("F"); 7 | expect(isoToIntlVehicleCode("IT")).toBe("I"); 8 | expect(isoToIntlVehicleCode("CH")).toBe("CH"); 9 | expect(isoToIntlVehicleCode("ZW")).toBe("ZW"); 10 | }); 11 | test("returns the ISO country code if the vehicle code is not found", () => { 12 | expect(isoToIntlVehicleCode("balbnlba")).toBe("balbnlba"); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/utils/removeDuplicateFeatures.js: -------------------------------------------------------------------------------- 1 | export const getId = (feat) => feat?.getId() || feat?.get("id"); 2 | 3 | export const removeDuplicateFeatures = (featureArray = []) => { 4 | return featureArray.reduce((final, feat) => { 5 | return final.find((f) => getId(f) === getId(feat)) 6 | ? final 7 | : [...final, feat]; 8 | }, []); 9 | }; 10 | 11 | export default removeDuplicateFeatures; 12 | -------------------------------------------------------------------------------- /src/utils/toBase64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Encodes a file to base64 string 3 | * @param {*} file 4 | * @returns promise 5 | */ 6 | const toBase64 = (file) => 7 | new Promise((resolve, reject) => { 8 | const reader = new FileReader(); 9 | reader.readAsDataURL(file); 10 | reader.onload = () => resolve(reader.result); 11 | reader.onerror = reject; 12 | }); 13 | 14 | export default toBase64; 15 | -------------------------------------------------------------------------------- /src/utils/useHasScreenSize.js: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | import { useSelector } from "react-redux"; 3 | 4 | const useHasScreenSize = (screenSizes = ["xs"]) => { 5 | const screenWidth = useSelector((state) => state.app.screenWidth); 6 | const isMobile = useMemo(() => { 7 | return screenSizes.includes(screenWidth); 8 | }, [screenWidth, screenSizes]); 9 | return isMobile; 10 | }; 11 | 12 | export default useHasScreenSize; 13 | -------------------------------------------------------------------------------- /src/utils/useHighlightSomeFeatures.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useSelector } from "react-redux"; 3 | 4 | /** 5 | * This hook will only highlight specific set of features that are part 6 | * of the current feature infos. See iSB popup for exmaple of use. 7 | * 8 | * Works only with MapboxStyleLayer. 9 | */ 10 | const useHighlightSomeFeatures = (features, layer) => { 11 | const featureInfo = useSelector((state) => state.app.featureInfo); 12 | // Highlight only one feature at a time. 13 | useEffect(() => { 14 | if (!layer || !features?.length) { 15 | return () => {}; 16 | } 17 | 18 | featureInfo?.forEach(({ layer: l }) => { 19 | if (l !== layer) { 20 | l?.highlight?.(); 21 | } 22 | }); 23 | layer?.highlight(features); 24 | return () => { 25 | layer?.highlight(); 26 | }; 27 | }, [layer, features, featureInfo]); 28 | }; 29 | 30 | export default useHighlightSomeFeatures; 31 | -------------------------------------------------------------------------------- /src/utils/useOverlayWidth.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { useSelector } from "react-redux"; 3 | 4 | const useOverlayWidth = () => { 5 | const overlayElement = useSelector((state) => state.app.overlayElement); 6 | const [width, setWidth] = useState(0); 7 | useEffect(() => { 8 | const resizeObserver = new ResizeObserver((entries) => { 9 | setWidth(entries[0].contentRect.width); 10 | }); 11 | if (!overlayElement) { 12 | setWidth(0); 13 | return () => resizeObserver.disconnect(); 14 | } 15 | resizeObserver.observe(overlayElement); 16 | return () => resizeObserver.disconnect(); 17 | }, [overlayElement]); 18 | return width; 19 | }; 20 | 21 | export default useOverlayWidth; 22 | -------------------------------------------------------------------------------- /src/utils/usePrevious.js: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from "react"; 2 | 3 | const usePrevious = (value) => { 4 | const ref = useRef(); 5 | useEffect(() => { 6 | ref.current = value; 7 | }, [value]); 8 | return ref.current; 9 | }; 10 | 11 | export default usePrevious; 12 | -------------------------------------------------------------------------------- /src/utils/useUpdateFeatureInfoOnLayerToggle.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import { unByKey } from "ol/Observable"; 4 | 5 | import { setFeatureInfo } from "../model/app/actions"; 6 | 7 | const useUpdateFeatureInfoOnLayerToggle = (layers) => { 8 | const featureInfo = useSelector((state) => state.app.featureInfo); 9 | const dispatch = useDispatch(); 10 | useEffect(() => { 11 | const listeners = layers.map((l) => { 12 | return l?.on("change:visible", (evt) => { 13 | const layer = evt.target; 14 | if (!layer.visible) { 15 | dispatch( 16 | setFeatureInfo(featureInfo.filter((info) => info.layer !== layer)), 17 | ); 18 | } 19 | }); 20 | }); 21 | return () => { 22 | unByKey(listeners); 23 | }; 24 | }, [layers, featureInfo, dispatch]); 25 | }; 26 | 27 | export default useUpdateFeatureInfoOnLayerToggle; 28 | --------------------------------------------------------------------------------