From aeb6b6341cab100c8d9a27c2defc212dc5d48a9e Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 24 Dec 2022 22:43:42 +0800 Subject: [PATCH] fix: recover attributes removed by GitHub fixes #263 --- src/components/SideBar.tsx | 16 ++++-- src/components/SideBarResizeHandler.tsx | 8 ++- src/utils/DOMHelper.ts | 66 ++++++++++++++++++++++--- 3 files changed, 79 insertions(+), 11 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 8c77102..9d21088 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -183,8 +183,12 @@ function useFocusSidebarOnExpand(shouldExpand: boolean) { function useMarkGitakoReadyState() { React.useEffect(() => { + const detach = DOMHelper.attachStickyGitakoReadyState() DOMHelper.markGitakoReadyState(true) - return () => DOMHelper.markGitakoReadyState(false) + return () => { + detach() + DOMHelper.markGitakoReadyState(false) + } }, []) } @@ -199,9 +203,13 @@ function useLogoContainerElement() { function useUpdateBodyIndentOnStateUpdate(shouldExpand: boolean) { const { sidebarToggleMode } = useConfigs().value React.useEffect(() => { - if (sidebarToggleMode === 'persistent' && shouldExpand) { - DOMHelper.setBodyIndent(true) - return () => DOMHelper.setBodyIndent(false) + if (!(sidebarToggleMode === 'persistent' && shouldExpand)) return + + const detach = DOMHelper.attachStickyBodyIndent() + DOMHelper.setBodyIndent(true) + return () => { + detach() + DOMHelper.setBodyIndent(false) } }, [sidebarToggleMode, shouldExpand]) } diff --git a/src/components/SideBarResizeHandler.tsx b/src/components/SideBarResizeHandler.tsx index 3255f91..4d91b2a 100644 --- a/src/components/SideBarResizeHandler.tsx +++ b/src/components/SideBarResizeHandler.tsx @@ -1,6 +1,6 @@ import { useConfigs } from 'containers/ConfigsContext' import * as React from 'react' -import { useDebounce, useWindowSize } from 'react-use' +import { useDebounce, useLatest, useWindowSize } from 'react-use' import { getDefaultConfigs } from 'utils/config/helper' import * as DOMHelper from 'utils/DOMHelper' import { useAfterRedirect } from 'utils/hooks/useFastRedirect' @@ -27,6 +27,12 @@ function useSidebarWidth() { React.useLayoutEffect(() => DOMHelper.setGitakoWidthCSSVariable(width), [width]) + const widthRef = useLatest(width) + React.useEffect(() => { + const detach = DOMHelper.attachStickyGitakoWidthCSSVariable(() => widthRef.current) + return () => detach() + }, [widthRef]) + // Keep variable when directing from PR to repo home via meta bar useAfterRedirect(React.useCallback(() => DOMHelper.setGitakoWidthCSSVariable(width), [width])) diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 8b382e7..ba95c0a 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -7,20 +7,69 @@ import { $ } from './$' export const rootElementID = 'gitako-root' export const gitakoDescriptionTarget = document.documentElement +// Some custom attributes added to GitHub html would be removed by GitHub when some events happen +function attachStickyAttribute( + target: Node, + shouldAttach: (mutation: MutationRecord) => boolean, + attach: (mutation: MutationRecord) => void, + mutationOptions?: MutationObserverInit, +) { + const observer = new MutationObserver(mutations => { + for (const mutation of mutations) if (shouldAttach(mutation)) attach(mutation) + }) + + observer.observe(target, { + attributeOldValue: true, + attributes: true, + ...mutationOptions, + }) + + return () => observer.disconnect() +} + +export const attachStickyDataAttribute = ( + target: HTMLElement, + attributeName: string, + attach: (mutation: MutationRecord) => void, +) => + attachStickyAttribute(target, () => !target.getAttribute(attributeName), attach, { + attributeFilter: [attributeName], + }) + +export const attachStickyStyle = ( + target: HTMLElement, + styleName: string, + attach: (mutation: MutationRecord) => void, +) => + attachStickyAttribute( + target, + () => !target.style.getPropertyValue(styleName), // `''` if not exist + attach, + { attributeFilter: ['style'] }, + ) + /** - * when gitako is ready, make page's header narrower - * or cancel it + * when gitako is ready, attach attribute to activate CSS selectors + * e.g. make page's header narrower on pin sidebar */ +const readyDataAttributeName = 'data-gitako-ready' +export const attachStickyGitakoReadyState = () => + attachStickyDataAttribute(gitakoDescriptionTarget, readyDataAttributeName, ({ oldValue }) => + markGitakoReadyState(oldValue === 'true'), + ) export function markGitakoReadyState(ready: boolean) { - const readyAttributeName = 'data-gitako-ready' - return gitakoDescriptionTarget.setAttribute(readyAttributeName, `${ready}`) + return gitakoDescriptionTarget.setAttribute(readyDataAttributeName, `${ready}`) } /** * if should show gitako, then move body right to make space for showing gitako * otherwise, hide the space */ -export const spacingAttributeName = 'data-with-gitako-spacing' +const spacingAttributeName = 'data-with-gitako-spacing' +export const attachStickyBodyIndent = () => + attachStickyDataAttribute(gitakoDescriptionTarget, spacingAttributeName, ({ oldValue }) => + setBodyIndent(oldValue === 'true'), + ) export function setBodyIndent(shouldShowGitako: boolean) { gitakoDescriptionTarget.setAttribute(spacingAttributeName, `${shouldShowGitako}`) } @@ -122,8 +171,13 @@ export function setCSSVariable(name: string, value: string | undefined, element: else element.style.setProperty(name, value) } +const gitakoWidthVariable = '--gitako-width' +export const attachStickyGitakoWidthCSSVariable = (getLatestSize: () => number) => + attachStickyStyle(gitakoDescriptionTarget, gitakoWidthVariable, () => { + setGitakoWidthCSSVariable(getLatestSize()) + }) export const setGitakoWidthCSSVariable = (size: number) => { - setCSSVariable('--gitako-width', `${size}px`, gitakoDescriptionTarget) + setCSSVariable(gitakoWidthVariable, `${size}px`, gitakoDescriptionTarget) } export function formatID(id: string) {