From 62b74a54453482e5e98f4b4867d8ed874dd04b05 Mon Sep 17 00:00:00 2001 From: Emmanuel Pelletier Date: Fri, 14 Jun 2024 15:21:40 +0200 Subject: [PATCH] gaufre: fix popup placement issues, simplify styling this is done following up Tchap integration. - the popup placement script was really dumb and assumed the gaufre button was always placed at the top right of the page. Tchap can't do that and uses it at the bottom left. Now the popup places itself correctly wherever the button is on the page. On mobile now we have a "modal" mode for the popup where it takes all the viewport. - Tchap uses the gaufre inside their own popup component. This was not something we handled before. Now you can set up a 'lasuite--gaufre-borderless' class on your html or body tag so that the gaufre doesn't render its box shadow or blue border, making it easier to integrate in a already made popup. --- website/public/api/v1/gaufre.js | 127 ++++++++++++--- website/src/components/GaufrePage.astro | 155 +++++++++++-------- website/src/content/docs/guides/gaufre.mdx | 25 +++ website/src/pages/examples/gaufre/html.astro | 25 +++ 4 files changed, 246 insertions(+), 86 deletions(-) diff --git a/website/public/api/v1/gaufre.js b/website/public/api/v1/gaufre.js index 741704b..4381a73 100644 --- a/website/public/api/v1/gaufre.js +++ b/website/public/api/v1/gaufre.js @@ -1,5 +1,7 @@ ;(function () { const BUTTON_CLASS = "js-lasuite-gaufre-btn" + const DIMENSIONS = { width: 304, height: 352, margin: 8 } + let lastFocusedButton = null window.document.documentElement.classList.add("lasuite--gaufre-loaded") @@ -42,7 +44,7 @@ if (!button) { return } - popup.style.cssText = getPopupPositionStyle(button) + updatePopupStyle(popup, button) }) const appendPopup = () => { @@ -58,8 +60,6 @@ } const popup = document.createElement("div") popup.id = "lasuite-gaufre-popup" - popup.width = "304" - popup.height = "360" popup.style.cssText = "display: none !important" const { host, protocol, searchParams, origin } = new URL(scriptTag.src) @@ -79,35 +79,111 @@ }) } - const getPopupPositionStyle = (button) => { + const getPopupCoords = (button) => { const buttonCoords = button.getBoundingClientRect() - const isSmallScreen = window.innerWidth <= 400 - let leftPos = buttonCoords.right - 304 + document.documentElement.scrollLeft - leftPos = leftPos < 5 ? 5 : leftPos - return ` - position: absolute !important; - top: ${buttonCoords.top + buttonCoords.height + 8}px; - ${ - isSmallScreen - ? ` - left: 5px; - right: 5px; - margin: 0 auto; + + const documentWidth = document.body.clientWidth + const spaceLeft = buttonCoords.left + window.scrollX + const spaceRight = documentWidth - buttonCoords.right + window.scrollX + const hasHorizontalSpace = spaceLeft > DIMENSIONS.width || spaceRight > DIMENSIONS.width + let modalMode = false + + // by default, the popup is displayed anchored to the right of the button (taking space on the left) + let leftPos = buttonCoords.right - DIMENSIONS.width + document.documentElement.scrollLeft + let isAnchoredToRight = true + // if there is not enough space on the left, or if there is more space on the right, + // we anchor it to the left of the button, taking space on the right + if (spaceLeft < DIMENSIONS.width || spaceRight > spaceLeft) { + leftPos = buttonCoords.left + document.documentElement.scrollLeft + isAnchoredToRight = false + } + // if there is no space at all, we use a "modal" mode, taking all the screen space + if (!hasHorizontalSpace) { + modalMode = true + } + + const spaceTop = buttonCoords.top + const spaceBottom = window.innerHeight - buttonCoords.bottom + const hasVerticalSpace = spaceTop > DIMENSIONS.height || spaceBottom > DIMENSIONS.height + // by default, the popup is displayed anchored to the bottom of the button (taking space on the bottom) + let topPos = buttonCoords.bottom + 8 + document.documentElement.scrollTop + // if there is not enough space on the bottom, or if there is more space on the top, + // we anchor it to the top of the button, taking space on the top + if (spaceBottom < DIMENSIONS.height || spaceTop > spaceBottom) { + topPos = + buttonCoords.top - + DIMENSIONS.height - + DIMENSIONS.margin + + document.documentElement.scrollTop + } + + // if there is no space below or above the button, but there is space on the sides, + // we show the popup next to the button + if (!hasVerticalSpace && window.innerHeight > DIMENSIONS.height) { + topPos = (window.innerHeight - DIMENSIONS.height) / 2 + leftPos = isAnchoredToRight + ? leftPos - buttonCoords.width - DIMENSIONS.margin + : leftPos + buttonCoords.width + DIMENSIONS.margin + } + + // if there is no space at all, we use a "modal" mode, taking all the screen space + if (!hasVerticalSpace && window.innerHeight <= DIMENSIONS.height) { + modalMode = true + } + + return { + modalMode, + top: !modalMode ? topPos : null, + left: !modalMode ? leftPos : null, + } + } + + const defaultPopupStyle = ` + border: 0 !important; + display: block !important; + z-index: 100000; + box-sizing: border-box !important; + ` + + const getPopupPositionStyle = (coords) => { + if (coords.modalMode) { + return ` + ${defaultPopupStyle} + position: fixed !important; + top: 0; + left: 0; + bottom: 0; + right: 0; + width: 100vw; + height: 100vh; + margin: auto; ` - : ` - left: ${leftPos}px; - width: 304px;` - } - border: 0 !important; - display: block !important; - z-index: 100000; - ` + } + return ` + ${defaultPopupStyle} + position: absolute !important; + top: ${coords.top}px !important; + left: ${coords.left}px !important; + max-width: calc(100vw - 30px); + width: ${DIMENSIONS.width}px; + height: ${DIMENSIONS.height}px; + margin: 0; + ` + } + + const updatePopupStyle = (popup, button) => { + const popupCoords = getPopupCoords(button) + popup.style.cssText = getPopupPositionStyle(popupCoords) + popup.classList[popupCoords.modalMode ? "add" : "remove"]("lasuite--gaufre-modal") + window.document.documentElement.classList[popupCoords.modalMode ? "add" : "remove"]( + "lasuite--gaufre-modal-opened", + ) } const showPopup = (button) => { let popup = document.querySelector(`#lasuite-gaufre-popup`) const show = (el) => { - el.style.cssText = getPopupPositionStyle(button) + updatePopupStyle(popup, button) el.classList.add("lasuite--gaufre-opened") lastFocusedButton = button setTimeout(() => { @@ -132,5 +208,6 @@ lastFocusedButton.focus() lastFocusedButton = null } + window.document.documentElement.classList.remove("lasuite--gaufre-modal-opened") } })() diff --git a/website/src/components/GaufrePage.astro b/website/src/components/GaufrePage.astro index 41bb000..3e34b13 100644 --- a/website/src/components/GaufrePage.astro +++ b/website/src/components/GaufrePage.astro @@ -42,22 +42,62 @@ const { services } = Astro.props } .lagaufre { - height: 22rem !important; - max-height: 22rem !important; + --lagaufre-border-color: #01018f !important; + --lagaufre-bg-color: #fff !important; + height: 100% !important; + width: 100% !important; font-size: 100% !important; font-family: "La Gaufre", "La Gaufre fallback", BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji" !important; - margin: 0 0 0.5rem !important; - background-color: #01018fcc !important; - padding: 2px !important; - width: 100% !important; - /* border-radius: 8px !important; */ + margin: 0 0 8px !important; + border: 2px solid var(--lagaufre-border-color) !important; + background-color: var(--lagaufre-bg-color) !important; filter: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06)) !important; overflow: hidden !important; + display: flex !important; + flex-direction: column !important; } [data-fr-scheme="dark"] .lagaufre { - background-color: #8585f6 !important; + --lagaufre-bg-color: #1e1e1e !important; + --lagaufre-border-color: #8585f6 !important; + } + + .lasuite--gaufre-borderless .lagaufre { + border-color: var(--lagaufre-bg-color) !important; + filter: none !important; + margin-bottom: 0 !important; + } + + .lasuite--gaufre-modal-opened body { + overflow: hidden !important; + } + + .lasuite--gaufre-modal .lagaufre { + filter: none !important; + border: 0 !important; + padding: 0 !important; + margin: 0 !important; + font-weight: bold !important; + } + + .lagaufre-close-button-container { + display: flex !important; + justify-content: flex-end !important; + } + + .lagaufre-close-button { + display: none !important; + padding: 8px !important; + margin: 4px !important; + border: 1px solid rgb(221, 221, 221) !important; + color: rgb(0, 0, 145) !important; + font-weight: bold !important; + background: none !important; + } + + .lasuite--gaufre-modal .lagaufre-close-button { + display: block !important; } .lagaufre-sr-only { @@ -73,31 +113,20 @@ const { services } = Astro.props display: block !important; } - .lagaufre-border { - height: 100% !important; - /* border-radius: 6px !important; */ - overflow: hidden !important; - } - .lagaufre-list { margin: 0 !important; padding: 0 !important; list-style: none !important; /* border-radius: 2px !important; */ - background-color: white !important; height: 100% !important; overflow: auto !important; } - [data-fr-scheme="dark"] .lagaufre-list { - background-color: #1e1e1e !important; - } - .lagaufre-service { position: relative !important; display: flex !important; align-items: center !important; - padding: 1rem 2rem !important; + padding: 16px 32px !important; border-top: 1px solid transparent !important; border-bottom: 1px solid transparent !important; } @@ -139,6 +168,7 @@ const { services } = Astro.props } .lagaufre-service__icon { + flex-shrink: 0 !important; display: flex !important; align-items: center !important; width: 40px !important; @@ -157,7 +187,7 @@ const { services } = Astro.props } .lagaufre-service__name { - margin-left: 1.5rem !important; + margin-left: 24px !important; text-decoration: none !important; color: #161616 !important; } @@ -197,47 +227,50 @@ const { services } = Astro.props background-color: transparent !important; } -
-

- Liste des services de La Suite numérique -

- +

Liste des services de La Suite numérique

+
+
+
diff --git a/website/src/content/docs/guides/gaufre.mdx b/website/src/content/docs/guides/gaufre.mdx index f976d43..6c192e0 100644 --- a/website/src/content/docs/guides/gaufre.mdx +++ b/website/src/content/docs/guides/gaufre.mdx @@ -167,6 +167,31 @@ burger : aucun HTML n'est proposé pour ça, tant il dépend de votre site. même comportement sur votre propre implémentation, vous pouvez vous appuyer sur la présence de la classe `lasuite--gaufre-loaded` sur la balise `html`. +### Popup customisée + +Au delà du bouton, suivant votre stack technique, vous voudrez peut-être utiliser un composant de +popup déjà en place dans votre app. Dans ce cas, une solution est de ne pas charger le script +proposé, et de gérer vous-même le comportement du bouton. + +Le contenu à afficher dans la popup est requêtable directement sur +[{import.meta.env.PUBLIC_LASUITE_API_URL}/api/v1/gaufre](/api/v1/gaufre). La liste est déjà +construite pour vous en HTML/CSS car son affichage est amené à évoluer au fil du temps suivant le +nombre de services. Il n'est pas souhaitable de demander à chaque service de La Suite de devoir se +mettre à jour régulièrement. L'idée est donc de requêter cette URL, récupérer son `` et de +l'intégrer dans votre DOM dans le contenu de votre composant de popup. + +Appliquez la classe `lasuite--gaufre-borderless` sur votre balise `html` ou `body`. Ceci fera que la +liste fournie via l'URL ci-dessus, une fois dans votre DOM, n'affichera pas de bordure bleue avec +une ombre, et s'intégrera mieux dans votre composant de popup. + +:::caution + +Il est vivement déconseillé de surcharger les CSS du contenu de la popup de la Gaufre, car les +contenus de la popup et leur disposition visuelle évoluent avec le temps. +[Contactez-nous](/about/help) si vous avez des besoins particuliers par rapport à votre service. + +::: + ## Exemple [La Gaufre : exemple HTML](/examples/gaufre/html). diff --git a/website/src/pages/examples/gaufre/html.astro b/website/src/pages/examples/gaufre/html.astro index 570d8dd..dea9aa5 100644 --- a/website/src/pages/examples/gaufre/html.astro +++ b/website/src/pages/examples/gaufre/html.astro @@ -7,6 +7,7 @@ import gaufreCssUrl from "@gouvfr-lasuite/integration/dist/css/gaufre.css?url" Exemple intégration bouton La Gaufre - La Suite numérique + @@ -60,6 +67,13 @@ import gaufreCssUrl from "@gouvfr-lasuite/integration/dist/css/gaufre.css?url" +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac diam a libero posuere ornare facilisis in mi. Nullam eu vulputate augue, in auctor nibh. Praesent ac tempus dui. @@ -88,6 +102,17 @@ import gaufreCssUrl from "@gouvfr-lasuite/integration/dist/css/gaufre.css?url" Vestibulum feugiat pulvinar fermentum. Vivamus imperdiet dapibus ornare. Donec venenatis, lectus id faucibus tempus, sapien urna molestie augue, at egestas enim lectus quis nisi.

+ +