From c5773b9488adfa68085fd12956483e32463c7dc8 Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Thu, 21 Mar 2024 19:59:25 +0100 Subject: [PATCH] feat: Add admin bar --- .../gatsby-browser.js | 9 +- .../gatsby-ssr.js | 9 +- .../src/contexts/adminContext.js | 3 + .../src/wsui/components/AdminBar.jsx | 75 +++++++ .../src/wsui/components/AdminProvider.jsx | 60 ++++++ .../wsui/components/RootElementWrapper.jsx | 194 ++++++++++-------- .../src/wsui/components/SiteLayout.jsx | 12 +- 7 files changed, 268 insertions(+), 94 deletions(-) create mode 100644 packages/gatsby-theme-wordpress-basic/src/contexts/adminContext.js create mode 100644 packages/gatsby-theme-wordpress-basic/src/wsui/components/AdminBar.jsx create mode 100644 packages/gatsby-theme-wordpress-basic/src/wsui/components/AdminProvider.jsx diff --git a/packages/gatsby-theme-wordpress-basic/gatsby-browser.js b/packages/gatsby-theme-wordpress-basic/gatsby-browser.js index 96983c10..957a4a80 100644 --- a/packages/gatsby-theme-wordpress-basic/gatsby-browser.js +++ b/packages/gatsby-theme-wordpress-basic/gatsby-browser.js @@ -10,9 +10,14 @@ import { HTMLProcessorProvider } from "./src/hooks"; const RootElementWrapper = require(GATSBY_ROOT_ELEMENT_WRAPPER_PATH).default; -export function wrapRootElement({ element }, { enableSEO }) { +export function wrapRootElement( + { element }, + { enableSEO, adminIframeUrl, dashboardUrl, editPostUrl }, +) { return ( - + {element} diff --git a/packages/gatsby-theme-wordpress-basic/gatsby-ssr.js b/packages/gatsby-theme-wordpress-basic/gatsby-ssr.js index ab1b9881..081290b3 100644 --- a/packages/gatsby-theme-wordpress-basic/gatsby-ssr.js +++ b/packages/gatsby-theme-wordpress-basic/gatsby-ssr.js @@ -10,9 +10,14 @@ import { HTMLProcessorProvider } from "./src/hooks"; const RootElementWrapper = require(GATSBY_ROOT_ELEMENT_WRAPPER_PATH).default; -export function wrapRootElement({ element }, { enableSEO }) { +export function wrapRootElement( + { element }, + { enableSEO, adminIframeUrl, dashboardUrl, editPostUrl }, +) { return ( - + {element} diff --git a/packages/gatsby-theme-wordpress-basic/src/contexts/adminContext.js b/packages/gatsby-theme-wordpress-basic/src/contexts/adminContext.js new file mode 100644 index 00000000..5a8c2e3e --- /dev/null +++ b/packages/gatsby-theme-wordpress-basic/src/contexts/adminContext.js @@ -0,0 +1,3 @@ +import { createContext } from "react"; + +export default createContext({ user: null }); diff --git a/packages/gatsby-theme-wordpress-basic/src/wsui/components/AdminBar.jsx b/packages/gatsby-theme-wordpress-basic/src/wsui/components/AdminBar.jsx new file mode 100644 index 00000000..27c1e715 --- /dev/null +++ b/packages/gatsby-theme-wordpress-basic/src/wsui/components/AdminBar.jsx @@ -0,0 +1,75 @@ +/** @jsx jsx */ +import { css, jsx } from "@emotion/react"; +import styled from "@emotion/styled"; +import { Fragment, useContext } from "react"; + +import adminContext from "../../contexts/adminContext"; +import { usePageContext } from "../../hooks"; +import { useTranslation } from "react-i18next"; +import { Icon } from "@wsui/base"; + +const Item = styled("span")` + display: inline-flex; + gap: 0.375em; + padding: 0.5rem; + color: inherit; + &:is(a) { + text-decoration: none; + &:hover { + background-color: #fff1; + text-decoration: underline; + } + } +`; + +export default function AdminBar({ ...restProps }) { + const { user, editPostUrl, getEditPostUrl, dashboardUrl } = + useContext(adminContext); + const page = usePageContext(); + const { t } = useTranslation(); + if (!user) return
; + return ( +
+ {!!dashboardUrl && ( + + {t("adminBar.dashboard")} + + )} + {!!page && !!page.databaseId && !!editPostUrl && ( + + {" "} + {t("adminBar.editPost", { + contentType: t( + [ + `contentTypes.${page.contentType?.name || "page"}.name`, + page.contentType.labels?.singularName ?? + page.contentType?.name ?? + "page", + ], + { count: 1 }, + ), + })} + + )} + + {user?.nicename} + +
+ ); +} diff --git a/packages/gatsby-theme-wordpress-basic/src/wsui/components/AdminProvider.jsx b/packages/gatsby-theme-wordpress-basic/src/wsui/components/AdminProvider.jsx new file mode 100644 index 00000000..ed09d1e6 --- /dev/null +++ b/packages/gatsby-theme-wordpress-basic/src/wsui/components/AdminProvider.jsx @@ -0,0 +1,60 @@ +/** @jsx jsx */ +import { jsx } from "@emotion/react"; +import { useEffect, useRef, useState } from "react"; + +import adminContext from "../../contexts/adminContext"; + +export default function AdminProvider({ + children, + iframeUrl, + dashboardUrl, + editPostUrl, +}) { + const { Provider } = adminContext; + const [user, setUser] = useState(null); + const iframeRef = useRef(null); + let value = { + user, + dashboardUrl, + editPostUrl, + getEditPostUrl: (id) => + (!!editPostUrl && + !!id && + editPostUrl.replace(/\{\{id\}\}/, encodeURIComponent(id))) || + null, + }; + + // const iframeRef = useRef(null); + const [iframeId] = useState(() => + Math.random().toString(36).substring(2, 11), + ); + const iframeOrigin = iframeUrl && new URL(iframeUrl).origin; + const iframeLoadHandler = () => { + iframeRef.current.contentWindow.postMessage( + { + type: "getUser", + iframeId, + }, + iframeOrigin, + ); + window.addEventListener("message", (event) => { + if (event.data?.iframeId === iframeId) { + setUser(event.data.user); + } + }); + }; + + useEffect(() => { + const iframe = document.createElement("iframe"); + iframeRef.current = iframe; + document.body.appendChild(iframe); + iframe.addEventListener("load", iframeLoadHandler); + iframe.src = iframeUrl; + return () => { + iframe.removeEventListener("load", iframeLoadHandler); + document.body.removeChild(iframe); + }; + }, [iframeUrl]); + + return {children}; +} diff --git a/packages/gatsby-theme-wordpress-basic/src/wsui/components/RootElementWrapper.jsx b/packages/gatsby-theme-wordpress-basic/src/wsui/components/RootElementWrapper.jsx index 94c7913f..65569159 100644 --- a/packages/gatsby-theme-wordpress-basic/src/wsui/components/RootElementWrapper.jsx +++ b/packages/gatsby-theme-wordpress-basic/src/wsui/components/RootElementWrapper.jsx @@ -1,105 +1,121 @@ import { H, Link, UrlTransformerProvider } from "@wsui/base"; -import React from "react"; +import React, { useContext } from "react"; import HtmlProcessorExtensionProvider from "../../components/HtmlProcessorExtensionProvider"; import WpCaption from "./WpCaption.jsx"; import WpImage from "./WpImage.jsx"; +import AdminProvider from "./AdminProvider.jsx"; +import pluginOptionsContext from "../../contexts/pluginOptionsContext.js"; export default function RootElementWrapper({ children }) { + const { adminIframeUrl, dashboardUrl, editPostUrl } = + useContext(pluginOptionsContext); return ( - { - let baseHeadingLevel; - let headingCount = 0; - visit(tree, isHeadingElement(), (node) => { - if (semanticHeadings) { - let headingLevel = Number(node.tagName[1]); - baseHeadingLevel = baseHeadingLevel || headingLevel; - node.properties.className = clsx( - node.properties.className, - `wsui-h${headingLevel}`, - ); - node.properties.adjustLevel = headingLevel - baseHeadingLevel; - node.tagName = "heading"; - } - if (ensureHeadingIds) { - node.properties.id ??= `${ - typeof ensureHeadingIds === "string" - ? ensureHeadingIds - : "heading" - }-${headingCount++}`; - } - }); - }, - (tree, { visit, contentMedia }) => { - if (contentMedia) { - visit(tree, { tagName: "img" }, (node) => { - let attachmentId; - if (node.properties && node.properties.className) { - node.properties.className.some((className) => { - let matches = className.match(/^wp-image-(\d+)$/); - if (matches) { - attachmentId = matches[1]; - return true; - } - }); + + { + let baseHeadingLevel; + let headingCount = 0; + visit(tree, isHeadingElement(), (node) => { + if (semanticHeadings) { + let headingLevel = Number(node.tagName[1]); + baseHeadingLevel = baseHeadingLevel || headingLevel; + node.properties.className = clsx( + node.properties.className, + `wsui-h${headingLevel}`, + ); + node.properties.adjustLevel = headingLevel - baseHeadingLevel; + node.tagName = "heading"; } - if (!attachmentId) { - return; + if (ensureHeadingIds) { + node.properties.id ??= `${ + typeof ensureHeadingIds === "string" + ? ensureHeadingIds + : "heading" + }-${headingCount++}`; } - node.tagName = "wp-image"; - node.properties = { - ...node.properties, - attachment: attachmentId, - sizes: null, - }; - // if (parent.tagName === "p") { - // parent.tagName = "div"; - // parent.properties = { - // ...parent.properties, - // className: [ - // ...((parent.properties && parent.properties.className) || - // []), - // "paragraph", - // ], - // }; - // } }); - } - }, - ]} - stringifierComponents={{ - a: Link, - heading: H, - "wp-caption": WpCaption, - "wp-image": WpImage, - }} - > - { - url = url?.replace(/^http:/, "https:"); - url = - process.env.GATSBY_WORDPRESS_UPLOADS_URL && - url?.startsWith( - process.env.GATSBY_WORDPRESS_URL + "/wp-content/uploads/", - ) - ? url.replace( - process.env.GATSBY_WORDPRESS_URL + "/wp-content/uploads", - process.env.GATSBY_WORDPRESS_UPLOADS_URL, - ) - : url?.startsWith(process.env.GATSBY_WORDPRESS_URL + "/wp-") - ? url - : url?.replace(process.env.GATSBY_WORDPRESS_URL, ""); - return url; + }, + (tree, { visit, contentMedia }) => { + if (contentMedia) { + visit(tree, { tagName: "img" }, (node) => { + let attachmentId; + if (node.properties && node.properties.className) { + node.properties.className.some((className) => { + let matches = className.match(/^wp-image-(\d+)$/); + if (matches) { + attachmentId = matches[1]; + return true; + } + }); + } + if (!attachmentId) { + return; + } + node.tagName = "wp-image"; + node.properties = { + ...node.properties, + attachment: attachmentId, + sizes: null, + }; + // if (parent.tagName === "p") { + // parent.tagName = "div"; + // parent.properties = { + // ...parent.properties, + // className: [ + // ...((parent.properties && parent.properties.className) || + // []), + // "paragraph", + // ], + // }; + // } + }); + } + }, + ]} + stringifierComponents={{ + a: Link, + heading: H, + "wp-caption": WpCaption, + "wp-image": WpImage, }} > - {children} - - + { + url = url?.replace(/^http:/, "https:"); + url = + process.env.GATSBY_WORDPRESS_UPLOADS_URL && + url?.startsWith( + process.env.GATSBY_WORDPRESS_URL + "/wp-content/uploads/", + ) + ? url.replace( + process.env.GATSBY_WORDPRESS_URL + "/wp-content/uploads", + process.env.GATSBY_WORDPRESS_UPLOADS_URL, + ) + : url?.startsWith(process.env.GATSBY_WORDPRESS_URL + "/wp-") + ? url + : url?.replace(process.env.GATSBY_WORDPRESS_URL, ""); + return url; + }} + > + {children} + + + ); } diff --git a/packages/gatsby-theme-wordpress-basic/src/wsui/components/SiteLayout.jsx b/packages/gatsby-theme-wordpress-basic/src/wsui/components/SiteLayout.jsx index bd92649b..ece20520 100644 --- a/packages/gatsby-theme-wordpress-basic/src/wsui/components/SiteLayout.jsx +++ b/packages/gatsby-theme-wordpress-basic/src/wsui/components/SiteLayout.jsx @@ -11,6 +11,7 @@ import AlertBanner from "./AlertBanner.jsx"; import DefaultFooter from "./Footer.jsx"; import DefaultHeader from "./Header.jsx"; import Html from "./Html.jsx"; +import AdminBar from "./AdminBar.jsx"; export default function SiteLayout(props) { const theme = useTheme(); @@ -26,10 +27,19 @@ export default function SiteLayout(props) {
+