Skip to content

Commit

Permalink
feat: Add admin bar
Browse files Browse the repository at this point in the history
  • Loading branch information
jeanfredrik committed Mar 21, 2024
1 parent 2d978b3 commit c5773b9
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 94 deletions.
9 changes: 7 additions & 2 deletions packages/gatsby-theme-wordpress-basic/gatsby-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<pluginOptionsContext.Provider value={{ enableSEO }}>
<pluginOptionsContext.Provider
value={{ enableSEO, adminIframeUrl, dashboardUrl, editPostUrl }}
>
<RootElementWrapper>
<HTMLProcessorProvider rehypeParse={rehypeParse}>
{element}
Expand Down
9 changes: 7 additions & 2 deletions packages/gatsby-theme-wordpress-basic/gatsby-ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<pluginOptionsContext.Provider value={{ enableSEO }}>
<pluginOptionsContext.Provider
value={{ enableSEO, adminIframeUrl, dashboardUrl, editPostUrl }}
>
<RootElementWrapper>
<HTMLProcessorProvider rehypeParse={rehypeParse}>
{element}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createContext } from "react";

export default createContext({ user: null });
Original file line number Diff line number Diff line change
@@ -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 <div />;
return (
<div
css={css`
background: #000d;
color: #fffd;
display: flex;
gap: 0.25rem;
font-size: 0.875rem;
font-weight: 500;
align-items: center;
backdrop-filter: blur(5px);
`}
{...restProps}
>
{!!dashboardUrl && (
<Item as="a" href={dashboardUrl} target="_blank">
<Icon name="wordpress" size="1.375em" /> {t("adminBar.dashboard")}
</Item>
)}
{!!page && !!page.databaseId && !!editPostUrl && (
<Item as="a" href={getEditPostUrl(page.databaseId)} target="_blank">
<Icon name="edit" size="1.375em" />{" "}
{t("adminBar.editPost", {
contentType: t(
[
`contentTypes.${page.contentType?.name || "page"}.name`,
page.contentType.labels?.singularName ??
page.contentType?.name ??
"page",
],
{ count: 1 },
),
})}
</Item>
)}
<Item
css={css`
margin-left: auto;
`}
>
{user?.nicename} <Icon name="person" size="1.375em" />
</Item>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -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 <Provider value={value}>{children}</Provider>;
}
Original file line number Diff line number Diff line change
@@ -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 (
<HtmlProcessorExtensionProvider
treeTransforms={[
(
tree,
{ visit, semanticHeadings, ensureHeadingIds, isHeadingElement, clsx },
) => {
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;
}
});
<AdminProvider
iframeUrl={adminIframeUrl}
dashboardUrl={dashboardUrl}
editPostUrl={editPostUrl}
>
<HtmlProcessorExtensionProvider
treeTransforms={[
(
tree,
{
visit,
semanticHeadings,
ensureHeadingIds,
isHeadingElement,
clsx,
},
) => {
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,
}}
>
<UrlTransformerProvider
transformUrl={(url) => {
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}
</UrlTransformerProvider>
</HtmlProcessorExtensionProvider>
<UrlTransformerProvider
transformUrl={(url) => {
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}
</UrlTransformerProvider>
</HtmlProcessorExtensionProvider>
</AdminProvider>
);
}
Loading

0 comments on commit c5773b9

Please sign in to comment.