diff --git a/assets/js/contentNavigation.js b/assets/js/contentNavigation.js index b61e21ed..8da2a51d 100644 --- a/assets/js/contentNavigation.js +++ b/assets/js/contentNavigation.js @@ -19,6 +19,8 @@ const initAside = () => { let isClosed = true; const closeSheet = () => { + if (isClosed) return; + isClosed = true; aside.classList.remove("o-aside--open"); asideContent.removeAttribute("role"); @@ -107,10 +109,9 @@ const initAside = () => { wasMobile = true; closeSheet(); } - if (!isMobile()) { + if (wasMobile && !isMobile()) { wasMobile = false; - closeOverlay(); - aside.classList.remove("o-aside--open"); + closeSheet(); } }; diff --git a/assets/js/dialog.js b/assets/js/dialog.js new file mode 100644 index 00000000..44e7f479 --- /dev/null +++ b/assets/js/dialog.js @@ -0,0 +1,68 @@ +import { + openOverlay, + closeOverlay, + addOverlayClickListener, +} from "./overlay.js"; + +function getCloseButton(dialog) { + return dialog.querySelector(".o-dialog__header > .a-button"); +} + +function openDialog(dialogId) { + const dialog = document.getElementById(dialogId); + if (!dialog) return; + + dialog.show(); + openOverlay("dialog"); + + const closeButton = getCloseButton(dialog); + if (closeButton) { + closeButton.addEventListener("click", () => closeDialog(dialog)); + } +} + +function closeDialog(dialog) { + const closeButton = getCloseButton(dialog); + if (closeButton) { + closeButton.replaceWith(closeButton.cloneNode(true)); + } + dialog.close(); + closeOverlay(); +} + +function closeAllDialogs() { + document.querySelectorAll("dialog[open]").forEach((dialog) => { + closeDialog(dialog); + }); +} + +function initDialogs() { + document.querySelectorAll("[data-dialog-trigger]").forEach((trigger) => { + const handler = (e) => { + if (e.type === "click" || (e.type === "keydown" && e.key === "Enter")) { + e.preventDefault(); + const dialogId = trigger.getAttribute("data-dialog-trigger"); + openDialog(dialogId); + } + }; + + trigger.addEventListener("click", handler); + trigger.addEventListener("keydown", handler); + }); + + addOverlayClickListener(() => { + closeAllDialogs(); + }); + + document.addEventListener("keydown", (e) => { + if (e.key === "Escape") { + closeAllDialogs(); + } + }); +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", initDialogs); +} else { + initDialogs(); +} diff --git a/assets/js/main.js b/assets/js/main.js index 964b1f43..830c8a4e 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -9,3 +9,4 @@ import "./dropdown.js"; import "./search.js"; import "./interactiveMap.js"; import "./expander.js"; +import "./dialog.js"; diff --git a/assets/js/mobileMenu.js b/assets/js/mobileMenu.js index 7190d65f..4ffe0c09 100644 --- a/assets/js/mobileMenu.js +++ b/assets/js/mobileMenu.js @@ -39,6 +39,8 @@ function closeMobileMenu() { const navContainer = document.querySelector(".o-header__nav"); const menuButton = document.querySelector(".o-nav__menu-button"); + if (!navContainer.classList.contains("o-header__nav--open")) return; + navContainer.classList.remove("o-header__nav--open"); menuButton.setAttribute("aria-expanded", false); closeOverlay(); diff --git a/assets/sass/button.scss b/assets/sass/button.scss index 7d6b7a82..a8b94a33 100644 --- a/assets/sass/button.scss +++ b/assets/sass/button.scss @@ -3,7 +3,6 @@ display: inline-flex; align-items: center; gap: 0.4rem; - border: 0.2rem solid var(--link-default); border-radius: var(--border-radius-m); font-size: 1.5rem; cursor: pointer; @@ -25,4 +24,12 @@ padding: 0.6rem; display: block; } + + &__external { + border: 0.2rem solid var(--link-default); + } + + &__internal { + border: none; + } } diff --git a/assets/sass/dialog.scss b/assets/sass/dialog.scss new file mode 100644 index 00000000..9c34cd6a --- /dev/null +++ b/assets/sass/dialog.scss @@ -0,0 +1,67 @@ +.o-dialog { + max-height: calc(100dvh - 14rem); + overflow: auto; + display: flex; + flex-direction: column; + + .a-anchorlink { + margin-bottom: 0; + + &__link { + display: none; + } + + > h2 { + margin-bottom: 1rem; + } + } + + .o-divider { + display: none; + } + + &__wrapper { + width: fit-content; + position: fixed; + top: 7rem; + border: none; + padding: 0; + background-color: var(--bg-default); + color: var(--color-body); + border-radius: var(--border-radius-m); + z-index: 12; + border: var(--border); + overflow: hidden; + } + + &__header { + position: sticky; + top: 0; + background-color: var(--bg-default); + display: flex; + column-gap: 1rem; + align-items: center; + justify-content: space-between; + padding: 1rem 1rem 1rem 2rem; + border-bottom: var(--border); + border-color: var(--color-table-border); + + .o-divider { + display: none; + } + + > h1 { + margin: 0; + font-size: 2.2rem; + } + } + + &__body { + padding: 2rem; + overflow: auto; + + > *:last-child { + margin-bottom: 0; + } + } +} diff --git a/assets/sass/header.scss b/assets/sass/header.scss index 12ab3273..0560fd74 100644 --- a/assets/sass/header.scss +++ b/assets/sass/header.scss @@ -102,6 +102,11 @@ z-index: 14; } +#header:has(.overlay--dialog), +.overlay--dialog { + z-index: 10; +} + body:has(.overlay--show) { overflow: hidden; } diff --git a/assets/sass/main.scss b/assets/sass/main.scss index 45cbf8c9..eb8db79a 100644 --- a/assets/sass/main.scss +++ b/assets/sass/main.scss @@ -23,3 +23,4 @@ @import "tag.scss"; @import "floatImage.scss"; @import "teamMember.scss"; +@import "dialog.scss"; diff --git a/assets/sass/styles.scss b/assets/sass/styles.scss index 53da76e6..e87f1b5e 100644 --- a/assets/sass/styles.scss +++ b/assets/sass/styles.scss @@ -57,13 +57,16 @@ button { @include focus-indicator(0.2rem); } -a { +a, +.o-link { color: var(--link-default); transition: color 0.3s ease, background-color 0.3s ease; text-underline-offset: 0.2rem; border-radius: var(--border-radius-s); + text-decoration: underline; + cursor: pointer; &:hover, &:focus { @@ -80,6 +83,10 @@ a { display: none; } } + + & > .material-symbols-rounded { + margin: 0 0.2rem; + } } main { diff --git a/content/country/belgium/index.en.md b/content/country/belgium/index.en.md index c5331490..66a531fd 100644 --- a/content/country/belgium/index.en.md +++ b/content/country/belgium/index.en.md @@ -17,7 +17,7 @@ Additionally, international [Eurostar](/operator/eurostar "Eurostar") trains ope Furthermore, international `TGV` trains of the [SNCF](/operator/sncf "SNCF") from France operate, for which the FIP Coupon of SNCB are not valid. Only special FIP Global Fares can be booked for these trains. For the Eurocity trains from Brussels to Paris operated by OUIGO, no FIP discounts apply. {{< identify-operator sources="db-website,vagonweb" >}} -Not all trains in the country (e. g. `ICE`) are shown in the [SNCB / NMBS online timetable](https://www.belgiantrain.be/en/). +Not all trains in the country (e.g. `ICE`) are shown in the [SNCB / NMBS online timetable](https://www.belgiantrain.be/en/). {{< /identify-operator >}} ## Interesting diff --git a/content/identify-operator/renfe-commuter-website/index.en.md b/content/identify-operator/renfe-commuter-website/index.en.md index da086cc4..deb30144 100644 --- a/content/identify-operator/renfe-commuter-website/index.en.md +++ b/content/identify-operator/renfe-commuter-website/index.en.md @@ -4,4 +4,4 @@ params: url: "https://www.renfe.com/es/en/suburban" --- -On the Renfe Cercanías website (suburban trains), you can search for train connections in any suburban train network. To do this, select the relevant region on the website and then click on _Timetables_. In suburban train networks with multiple operators (e. g. Barcelona), only Renfe trains are displayed. +On the Renfe Cercanías website (suburban trains), you can search for train connections in any suburban train network. To do this, select the relevant region on the website and then click on _Timetables_. In suburban train networks with multiple operators (e.g. Barcelona), only Renfe trains are displayed. diff --git a/i18n/de.yaml b/i18n/de.yaml index a2361e11..165bf5ea 100644 --- a/i18n/de.yaml +++ b/i18n/de.yaml @@ -39,6 +39,9 @@ country: many: Länder one: Land other: Länder +dialog: + close: Schließen + open: Öffnet Dialog discord: FIP Guide Community donation: Spenden editPage: Seite bearbeiten diff --git a/i18n/en.yaml b/i18n/en.yaml index 0064abea..a05a2510 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -38,6 +38,9 @@ country: many: countries one: country other: countries +dialog: + close: Close + open: Opens dialog discord: FIP Guide Community donation: Donate editPage: Edit page diff --git a/i18n/fr.yaml b/i18n/fr.yaml index e7d45673..12c0d752 100644 --- a/i18n/fr.yaml +++ b/i18n/fr.yaml @@ -39,6 +39,9 @@ country: one: pays other: pays countryselection: Choisir un pays +dialog: + close: Fermer + open: Ouvre le dialogue discord: Communauté FIP Guide donation: Donation editPage: Modifier la page diff --git a/layouts/partials/booking.html b/layouts/partials/booking.html index d28991f0..e59b5139 100644 --- a/layouts/partials/booking.html +++ b/layouts/partials/booking.html @@ -85,7 +85,6 @@ {{- $content := partial "increase-headings" (dict "content" .page.Content "offset" 2) -}} {{- $content := partial "prefix-footnotes" (dict "content" $content "prefix" .page.File.ContentBaseName) -}} {{- $content := partial "prefix-heading-ids" (dict "content" $content "prefix" .page.File.ContentBaseName) -}} - {{- $content := partial "remove-newlines" $content -}} {{- $content | safeHTML -}} diff --git a/layouts/partials/button.html b/layouts/partials/button.html index 647ca8d7..0c781999 100644 --- a/layouts/partials/button.html +++ b/layouts/partials/button.html @@ -1,8 +1,17 @@ - - {{- .Text -}}{{- partial "icon" "arrow_outward" -}} - +{{ if .Destination }} + + {{- .Text -}}{{- partial "icon" "arrow_outward" -}} + +{{ else }} + + {{- .Text -}} + +{{ end }} diff --git a/layouts/partials/dialog.html b/layouts/partials/dialog.html new file mode 100644 index 00000000..2ad259d5 --- /dev/null +++ b/layouts/partials/dialog.html @@ -0,0 +1,15 @@ + + + + {{ .Title }} + {{- partial "button" (dict "Text" (partial "icon" "close") "Title" (i18n "dialog.close")) -}} + + + {{- partial "increase-headings" (dict "content" .Content) | safeHTML -}} + + + diff --git a/layouts/partials/link.html b/layouts/partials/link.html index 841a93ab..767e9f31 100644 --- a/layouts/partials/link.html +++ b/layouts/partials/link.html @@ -1,6 +1,6 @@ {{- $url := .Destination -}} {{- if and (strings.HasPrefix $url "http") (not (strings.HasPrefix $url site.BaseURL)) -}} - {{- /* Absolute links pointing to external pages, e. g. `https://example.com` */ -}} + {{- /* Absolute links pointing to external pages, e.g. `https://example.com` */ -}} {{- .Text -}}{{- partial "icon" "arrow_outward" -}} +{{- else if strings.HasPrefix $url "dialog:" -}} + {{- /* Dialog triggers, e.g. `dialog:test` */ -}} + + {{- .Text -}}{{- partial "icon" "open_in_browser" -}} + {{- else if strings.HasPrefix $url "mailto:" -}} - {{- /* Email links, e. g. `mailto:example@example.com` */ -}} + {{- /* Email links, e.g. `mailto:example@example.com` */ -}} {{- else if strings.HasPrefix $url "tel:" -}} - {{- /* Telephone links, e. g. `tel:+1234567890` */ -}} + {{- /* Telephone links, e.g. `tel:+1234567890` */ -}} {{- else if .Page.Ref (dict "path" $url) -}} - {{- /* Internal links, referenced by path (e. g. `/news/1`) */ -}} + {{- /* Internal links, referenced by path (e.g. `/news/1`) */ -}} {{- else if or (strings.HasPrefix $url "/") (strings.HasPrefix $url site.BaseURL) -}} - {{- /* Internal links, referenced by URL (e. g. `/en/news/1`) */ -}} + {{- /* Internal links, referenced by URL (e.g. `/en/news/1`) */ -}}