Skip to content

Instantly share code, notes, and snippets.

@wookiehangover
Created November 11, 2022 20:20
Show Gist options
  • Save wookiehangover/7c96994845f5d7d8fbb654564f1ab89e to your computer and use it in GitHub Desktop.
Save wookiehangover/7c96994845f5d7d8fbb654564f1ab89e to your computer and use it in GitHub Desktop.

useExtendedStyles() hook

This hook is used to modify an object of Tailwind classNames.

Usage

import { useExtendedStyles } from '@sutterhill/hooks'

const defaultStyles = {
  body: 'bg-gray-100',
  container: 'bg-white text-black',
  header: 'bg-gray-200 border-b',
  link: 'text-blue-500 hover:text-blue-700',
}

const extendedStyles = {
  body: 'bg-red-300',
  container: {
    replace: ['bg-white', 'bg-gray-100'],
  },
  header: {
    without: 'border-b',
  },
  link: {
    add: 'underline',
  }
}

const styles = useExtendedStyles(defaultStyles, extendedStyles)
// styles = {
//   body: 'bg-red-300',
//   container: 'bg-gray-100 text-black',
//   header: 'bg-gray-200',
//   link: 'text-blue-500 hover:text-blue-700 underline',
// }
import { useMemo } from "react";
import { RequireAtLeastOne } from "type-fest";
export type StyleUpdate =
| string
| RequireAtLeastOne<{
add: string;
replace: [string, string];
without: string;
}>;
export type ExtendedStyles<T> = Partial<{ [k in keyof T]: StyleUpdate }>;
type useExtendedStyles = <
T extends Record<string, string>,
K extends ExtendedStyles<T>
>(
defaultStyles: T,
extendedStyles?: K
) => Record<keyof T, string>;
export const useExtendedStyles: useExtendedStyles = (
defaultStyles,
extendedStyles
) =>
useMemo(() => {
if (!extendedStyles) return defaultStyles;
const result: Record<string, string> = {};
const keys = Object.keys(defaultStyles);
for (const key of keys) {
let value = defaultStyles[key];
const update = extendedStyles[key];
if (update !== undefined) {
if (typeof update === "string") {
value = update;
} else {
if (update.without) {
const remove = update.without.split(/\s+/);
value = value
.split(/\s+/)
.filter((item) => !remove.includes(item))
.join(" ");
}
if (update.replace) {
const [search, replace] = update.replace;
const searches = search.split(/\s+/);
const values = new Set(
value.split(/\s+/).map((item) => {
if (searches.includes(item)) {
return replace;
}
return item;
})
);
value = Array.from(values).join(" ");
}
if (update.add) {
value = `${value} ${update.add}`;
}
}
}
result[key] = value;
}
return result as typeof defaultStyles;
}, [defaultStyles, extendedStyles]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment