generated from Kurocado-Studio/styleguide-remix-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): creates useColorThemeResolver hook (#24)
* feat(ui): adds ColorContextResolver
- Loading branch information
1 parent
c84b698
commit d9e6065
Showing
9 changed files
with
370 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { motion, useInView } from 'framer-motion'; | ||
import { debounce } from 'lodash-es'; | ||
import * as React from 'react'; | ||
|
||
import { ColorContext } from '~/context/ColorContext'; | ||
import type { ColorThemeContextEnum } from '~/context/types'; | ||
|
||
export interface ColorChangeContainerProps { | ||
children: React.ReactNode; | ||
className?: string; | ||
colorContext: ColorThemeContextEnum; | ||
tag?: string; | ||
} | ||
|
||
export function ColorContextChangerContainer({ | ||
colorContext, | ||
className, | ||
tag, | ||
children, | ||
}: ColorChangeContainerProps): React.ReactNode { | ||
const ref = React.useRef(null); | ||
|
||
const { setColorContext } = React.useContext(ColorContext); | ||
|
||
const isInView = useInView(ref, { once: false, margin: '-500px' }); | ||
|
||
const debouncedColorContextHandler = debounce( | ||
(debounceColorContext: ColorThemeContextEnum) => { | ||
setColorContext(debounceColorContext); | ||
}, | ||
100, | ||
); | ||
|
||
React.useEffect(() => { | ||
if (isInView) { | ||
debouncedColorContextHandler(colorContext); | ||
} | ||
}, [isInView, colorContext, debouncedColorContextHandler]); | ||
|
||
const CurrentTag = tag || 'div'; | ||
// @ts-expect-error Element mismatch between motion | ||
const ColorContextChanger = motion[CurrentTag]; | ||
|
||
return ( | ||
<ColorContextChanger | ||
ref={ref} | ||
initial={{ opacity: 0 }} | ||
animate={{ opacity: 1 }} | ||
className={className} | ||
> | ||
{children} | ||
</ColorContextChanger> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { motion } from 'framer-motion'; | ||
import * as React from 'react'; | ||
|
||
export function StaggerSplitText(props: { text: string }): React.ReactNode { | ||
const { text } = props; | ||
|
||
return ( | ||
<motion.div | ||
initial='hidden' | ||
animate='visible' | ||
style={{ display: 'inline-flex', flexWrap: 'wrap' }} | ||
> | ||
{text.split('').map((individualLetter, individualLetterIndex) => ( | ||
<motion.span | ||
key={String(individualLetterIndex + 1)} | ||
style={{ | ||
display: 'inline-block', | ||
overflow: 'hidden', | ||
whiteSpace: 'pre', | ||
}} | ||
custom={individualLetterIndex} | ||
variants={{ | ||
hidden: { opacity: 0, y: 12 }, | ||
visible: (currentIndex) => ({ | ||
opacity: 1, | ||
y: 0, | ||
transition: { | ||
duration: 0.6, | ||
delay: currentIndex * 0.04, | ||
}, | ||
}), | ||
}} | ||
> | ||
{individualLetter} | ||
</motion.span> | ||
))} | ||
</motion.div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { motion } from 'framer-motion'; | ||
import { get } from 'lodash-es'; | ||
import React, { createContext, useState } from 'react'; | ||
|
||
import { ColorThemeContextEnum } from '~/context/types'; | ||
import type { ColorContextState } from '~/context/types'; | ||
import { useColorThemeResolver } from '~/hooks/useColorThemeResolver'; | ||
|
||
export const ColorContext = createContext({ | ||
colorContext: ColorThemeContextEnum.DEFAULT, | ||
setColorContext: (colorContext: ColorThemeContextEnum): void => { | ||
/** | ||
* as we need a default action to handle the param | ||
*/ | ||
// eslint-disable-next-line no-console | ||
console.debug(colorContext); | ||
}, | ||
}); | ||
|
||
export function BodyHTMLTagColorProvider({ | ||
children, | ||
}: { | ||
children: React.ReactNode; | ||
}): React.ReactNode { | ||
const { resolveColorTheme, colorThemeMap, colorTheme } = | ||
useColorThemeResolver(); | ||
|
||
const [colors, setColors] = useState<ColorContextState>( | ||
get(colorThemeMap, [ColorThemeContextEnum.DEFAULT]), | ||
); | ||
|
||
const setColorsHandler = ( | ||
selectedColorContext: ColorThemeContextEnum, | ||
): void => { | ||
const { background, foreground } = resolveColorTheme(selectedColorContext); | ||
|
||
setColors({ background, foreground }); | ||
}; | ||
|
||
const providerValue = React.useMemo( | ||
() => ({ | ||
colorContext: colorTheme, | ||
setColorContext: setColorsHandler, | ||
}), | ||
[colorTheme], | ||
); | ||
|
||
return ( | ||
<ColorContext.Provider value={providerValue}> | ||
<motion.body | ||
style={{ | ||
backgroundColor: get(colors, ['background']), | ||
color: get(colors, ['foreground']), | ||
transition: 'background-color 0.9s, color 1.2s', | ||
}} | ||
className='selection:bg-lime-200 selection:text-[#f52891cc]' | ||
data-testid='root-body-test-id' | ||
> | ||
{children} | ||
</motion.body> | ||
</ColorContext.Provider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
export type ColorContextState = { | ||
background: string; | ||
foreground: string; | ||
}; | ||
|
||
export enum BaseColorsEnum { | ||
WHITE = 'WHITE', | ||
BLACK = 'BLACK', | ||
} | ||
|
||
export enum PrimaryColorsEnum { | ||
RED = 'RED', | ||
BLUE = 'BLUE', | ||
YELLOW = 'YELLOW', | ||
} | ||
|
||
export enum SecondaryColorsEnum { | ||
GREEN = 'GREEN', | ||
ORANGE = 'ORANGE', | ||
PURPLE = 'PURPLE', | ||
} | ||
|
||
type PrimaryColorsObject = { | ||
[K in keyof typeof PrimaryColorsEnum]: string; | ||
}; | ||
|
||
type SecondaryColorsObject = { | ||
[K in keyof typeof SecondaryColorsEnum]: string; | ||
}; | ||
|
||
type BaseColorsObject = { | ||
[K in keyof typeof BaseColorsEnum]: string; | ||
}; | ||
|
||
export type CombinedColorsObject = PrimaryColorsObject & | ||
SecondaryColorsObject & | ||
BaseColorsObject; | ||
|
||
export enum ColorThemeContextEnum { | ||
BLUE = PrimaryColorsEnum.BLUE, | ||
DEFAULT = BaseColorsEnum.BLACK, | ||
WHITE = BaseColorsEnum.WHITE, | ||
RED = PrimaryColorsEnum.RED, | ||
YELLOW = PrimaryColorsEnum.YELLOW, | ||
|
||
GREEN = SecondaryColorsEnum.GREEN, | ||
ORANGE = SecondaryColorsEnum.ORANGE, | ||
PURPLE = SecondaryColorsEnum.PURPLE, | ||
} | ||
|
||
export type ColorThemeContextMap = { | ||
[K in ColorThemeContextEnum]: ColorContextState; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { get } from 'lodash-es'; | ||
import { useState } from 'react'; | ||
|
||
import { | ||
BaseColorsEnum, | ||
ColorThemeContextEnum, | ||
PrimaryColorsEnum, | ||
SecondaryColorsEnum, | ||
} from '~/context/types'; | ||
import type { | ||
ColorContextState, | ||
ColorThemeContextMap, | ||
CombinedColorsObject, | ||
} from '~/context/types'; | ||
|
||
export type UseColorThemeResolver = () => { | ||
colorTheme: ColorThemeContextEnum; | ||
colorThemeMap: ColorThemeContextMap; | ||
resolveColorTheme: (theme: ColorThemeContextEnum) => ColorContextState; | ||
}; | ||
|
||
export const useColorThemeResolver: UseColorThemeResolver = () => { | ||
const [colorTheme, setColorTheme] = useState<ColorThemeContextEnum>( | ||
ColorThemeContextEnum.DEFAULT, | ||
); | ||
|
||
const colors: { | ||
[K in keyof CombinedColorsObject]: string; | ||
} = { | ||
[BaseColorsEnum.BLACK]: 'Black', | ||
[BaseColorsEnum.WHITE]: 'GhostWhite', | ||
[PrimaryColorsEnum.BLUE]: 'DarkBlue', | ||
[PrimaryColorsEnum.RED]: 'Crimson', | ||
[PrimaryColorsEnum.YELLOW]: 'Khaki', | ||
[SecondaryColorsEnum.GREEN]: 'MediumSeaGreen', | ||
[SecondaryColorsEnum.ORANGE]: 'DarkOrange', | ||
[SecondaryColorsEnum.PURPLE]: 'Indigo', | ||
}; | ||
const colorThemeMap: ColorThemeContextMap = { | ||
[ColorThemeContextEnum.DEFAULT]: { | ||
background: get(colors, [BaseColorsEnum.BLACK]), | ||
foreground: get(colors, [PrimaryColorsEnum.RED]), | ||
}, | ||
[ColorThemeContextEnum.WHITE]: { | ||
background: get(colors, [BaseColorsEnum.WHITE]), | ||
foreground: get(colors, [BaseColorsEnum.BLACK]), | ||
}, | ||
[ColorThemeContextEnum.RED]: { | ||
background: get(colors, [PrimaryColorsEnum.RED]), | ||
foreground: get(colors, [SecondaryColorsEnum.GREEN]), | ||
}, | ||
[ColorThemeContextEnum.GREEN]: { | ||
background: get(colors, [SecondaryColorsEnum.GREEN]), | ||
foreground: get(colors, [PrimaryColorsEnum.RED]), | ||
}, | ||
[ColorThemeContextEnum.BLUE]: { | ||
background: get(colors, [PrimaryColorsEnum.BLUE]), | ||
foreground: get(colors, [SecondaryColorsEnum.ORANGE]), | ||
}, | ||
[ColorThemeContextEnum.ORANGE]: { | ||
background: get(colors, [SecondaryColorsEnum.ORANGE]), | ||
foreground: get(colors, [PrimaryColorsEnum.BLUE]), | ||
}, | ||
[ColorThemeContextEnum.PURPLE]: { | ||
background: get(colors, [SecondaryColorsEnum.PURPLE]), | ||
foreground: get(colors, [PrimaryColorsEnum.YELLOW]), | ||
}, | ||
[ColorThemeContextEnum.YELLOW]: { | ||
background: get(colors, [PrimaryColorsEnum.YELLOW]), | ||
foreground: get(colors, [SecondaryColorsEnum.PURPLE]), | ||
}, | ||
}; | ||
|
||
const resolveColorTheme = ( | ||
selectedColorContext: ColorThemeContextEnum, | ||
): ColorContextState => { | ||
setColorTheme(selectedColorContext); | ||
|
||
return get( | ||
colorThemeMap, | ||
[selectedColorContext], | ||
get(colorThemeMap, [ColorThemeContextEnum.DEFAULT]), | ||
); | ||
}; | ||
|
||
return { | ||
resolveColorTheme, | ||
colorThemeMap, | ||
colorTheme, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.