You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

65 lines
1.6 KiB

  1. import React, { useEffect, useRef } from 'react'
  2. interface UseStyleOptions {
  3. selector?: string;
  4. styles: React.CSSProperties & { [key: `--${string}`]: string };
  5. }
  6. const applyStyles = (element: HTMLElement, styles: React.CSSProperties) => {
  7. Object.entries(styles).forEach(([ key, value ]) => {
  8. element.style.setProperty(key, value as string)
  9. })
  10. }
  11. const removeStyles = (element: HTMLElement, styles: React.CSSProperties) => {
  12. Object.keys(styles).forEach(key => {
  13. element.style.removeProperty(key)
  14. })
  15. }
  16. function useInlineStyle({ selector, styles }: UseStyleOptions) {
  17. const elementRef = useRef<HTMLElement | null>(null)
  18. useEffect(() => {
  19. let targetElement: HTMLElement | null = null
  20. const manageStyles = () => {
  21. targetElement = selector ? document.querySelector<HTMLElement>(selector) : elementRef.current
  22. if (!targetElement) return
  23. applyStyles(targetElement, styles)
  24. return () => {
  25. if (targetElement) {
  26. removeStyles(targetElement, styles)
  27. }
  28. }
  29. }
  30. if (selector) {
  31. if (document.readyState === 'complete' || document.readyState === 'interactive') {
  32. manageStyles()
  33. } else {
  34. document.addEventListener('DOMContentLoaded', manageStyles)
  35. return () => {
  36. document.removeEventListener('DOMContentLoaded', manageStyles)
  37. }
  38. }
  39. } else {
  40. manageStyles()
  41. }
  42. return () => {
  43. if (targetElement) {
  44. removeStyles(targetElement, styles)
  45. }
  46. }
  47. }, [ selector, styles ])
  48. return elementRef
  49. }
  50. export default useInlineStyle