Skip to content

Commit

Permalink
v0.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
evankirkiles committed Jun 6, 2023
1 parent 0b52d5d commit fe636cb
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 56 deletions.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "meta-theme-swap",
"version": "0.1.1",
"version": "0.0.1",
"description": "Synchronizes WebKit meta theme color with elements on the page.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"scripts": {
"build": "tsc",
"format": "prettier --write \"src/**/*.(js|ts)\"",
"lint": "eslint src --ext .js,.ts",
"format": "prettier --write \"src/**/*.(js|ts|tsx)\"",
"lint": "eslint src --ext .js,.ts,.tsx",
"lint:fix": "eslint src --fix --ext .js,.ts",
"test": "jest --config jest.config.js",
"prepare": "npm run build",
Expand Down Expand Up @@ -35,8 +35,8 @@
"@typescript-eslint/eslint-plugin": "5.54.0",
"@typescript-eslint/parser": "5.52.0",
"eslint": "8.35.0",
"eslint-config-next": "^13.4.4",
"eslint-plugin-jest": "27.2.1",
"eslint-plugin-react": "^7.32.2",
"jest": "29.4.3",
"prettier": "2.8.4",
"ts-jest": "29.0.5",
Expand Down
77 changes: 48 additions & 29 deletions src/MetaThemeContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,71 @@
* created on Sun Jun 04 2023
* 2023 the nobot space
*/

import { PropsWithChildren, createContext } from 'react';
import React from 'react';
import { PropsWithChildren, createContext, useRef } from 'react';

interface IMetaThemeContext {
observer: IntersectionObserver;
observerTop: IntersectionObserver;
observerBottom: IntersectionObserver;
}

export const MetaThemeContext = createContext<IMetaThemeContext>({
observer: new IntersectionObserver(() => {
return;
}),
observerTop: new IntersectionObserver(() => void 0),
observerBottom: new IntersectionObserver(() => void 0),
});

/**
* A HOC for providing the Meta Theme context, so elements may control the
* colors of the WebKit navigation and address bars. Example usage:
*
* ```
* <MetaThemeProvider>
* ...your app
* </MetaThemeProvider>
* ```
*/
export default function MetaThemeProvider({ children }: PropsWithChildren) {
const metaTag = useRef<HTMLMetaElement>(document.querySelector('meta[name="theme-color"'));
const currThemeColor = useRef<string | null>(metaTag.current?.getAttribute('content') ?? null);
return (
<MetaThemeContext.Provider
value={{
observer: new IntersectionObserver(
(entries) => {
const selectedEntry = entries.reduce<
[IntersectionObserverEntry | null, number]
>(
(acc, entry) =>
entry.intersectionRatio > acc[1]
? [entry, entry.intersectionRatio]
: acc,
[null, 0],
);
// Top of screen intersection observer (controls theme-color)
observerTop: new IntersectionObserver(
(es) => {
if (!metaTag.current) return;
const selectedEntry = es.filter((e) => e.isIntersecting);
const target = selectedEntry[0]?.target;
if (target) {
const color = target.getAttribute('data-metathemeswap-color');
const transition = target.getAttribute(
'data-metathemeswap-timeout',
);
if (color) {
document.body.style.backgroundColor = color;
if (transition) {
document.body.style.transition = `background-color ${transition} ease-in-out`;
}
}
}
if (!target) return;
const color = target.getAttribute('data-metathemeswap-color');
if (!color) return;
currThemeColor.current = color;
metaTag.current.setAttribute('content', currThemeColor.current);
},
{
rootMargin: '-0.05% 0px -99.9% 0px',
},
),
// Bottom of screen intersection observer (controls background-color)
observerBottom: new IntersectionObserver(
(es) => {
if (!metaTag.current) return;
const selectedEntry = es.filter((e) => e.isIntersecting);
const target = selectedEntry[0]?.target;
if (!target) return;
const color = target.getAttribute('data-metathemeswap-color');
if (!color) return;
document.body.style.backgroundColor = color;
metaTag.current.setAttribute('content', currThemeColor.current + 'fe');
const meta = metaTag.current;
requestAnimationFrame(() => {
meta.setAttribute('content', currThemeColor.current || '');
});
},
{
rootMargin: '-99.9% 0px -0.05% 0px',
},
),
}}
>
{children}
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* index.test.ts
* author: evan kirkiles
* created on Sun Jun 04 2023
* 2023 the nobot space
* 2023 the nobot space
*/

// TODO: Write tests
// TODO: Write tests
11 changes: 4 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
* index.ts
* author: evan kirkiles
* created on Mon Jun 05 2023
* 2023 the nobot space
* 2023 the nobot space
*/
import MetaThemeProvider from "./MetaThemeContext";
import useMetaTheme from "./useMetaTheme";
import MetaThemeProvider from './MetaThemeContext';
import useMetaTheme from './useMetaTheme';

export {
MetaThemeProvider,
useMetaTheme
};
export { MetaThemeProvider, useMetaTheme };
31 changes: 17 additions & 14 deletions src/useMetaTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,29 @@
* created on Sun Jun 04 2023
* 2023 meta-theme-swap
*/
import { RefObject, useContext, useEffect, useId } from 'react';
import { RefObject, useContext, useEffect } from 'react';
import { MetaThemeContext } from './MetaThemeContext';

export default function useMetaTheme(
ref: RefObject<Element>,
color: string,
priority: number,
timeout?: string,
) {
const id = useId();
const { observer } = useContext(MetaThemeContext);
/**
* A hook for adding a meta theme color to an HTML element. When the element
* reaches the top of the screen, the navigation bar will change color to
* match the color specified. When the element reaches the bottom of the screen,
* the address bar changes color to match the color specified.
*
* @param ref A ref object for the element to check intersections
* @param color The color represented by the element.
*/
export default function useMetaTheme(ref: RefObject<Element>, color: string) {
const { observerTop, observerBottom } = useContext(MetaThemeContext);
useEffect(() => {
const node = ref.current;
if (!node) return;
node.setAttribute('data-metathemeswap-id', id);
node.setAttribute('data-metathemeswap-color', color);
node.setAttribute('data-metathemeswap-timeout', timeout ?? '0ms');
observer.observe(node);
observerTop.observe(node);
observerBottom.observe(node);
return () => {
observer.unobserve(node);
observerTop.unobserve(node);
observerBottom.unobserve(node);
};
}, [id, observer, color, timeout, ref]);
}, [observerTop, observerBottom, color, ref]);
}

0 comments on commit fe636cb

Please sign in to comment.