Source: react/hooks.js

import { useEffect } from 'react';
import { subscribe, unsubscribe } from '../pubSub';
import { sendTrackEvent } from '../analytics';
import { getConfig } from '../config';

/**
 * A React hook that allows functional components to subscribe to application events.  This should
 * be used sparingly - for the most part, Context should be used higher-up in the application to
 * provide necessary data to a given component, rather than utilizing a non-React-like Pub/Sub
 * mechanism.
 *
 * @param {string} type
 * @param {function} callback
 *
 * @memberof module:React
 */
export const useAppEvent = (type, callback) => {
  useEffect(() => {
    const subscriptionToken = subscribe(type, callback);

    return function cleanup() {
      unsubscribe(subscriptionToken);
    };
  }, [callback, type]);
};

/**
 * A React hook that tracks user's preferred color scheme (light or dark) and sends respective
 * event to the tracking service.
 *
 * @memberof module:React
 */
export const useTrackColorSchemeChoice = () => {
  useEffect(() => {
    const trackColorSchemeChoice = ({ matches }) => {
      const preferredColorScheme = matches ? 'dark' : 'light';
      sendTrackEvent('openedx.ui.frontend-platform.prefers-color-scheme.selected', { preferredColorScheme });
    };
    const colorSchemeQuery = window.matchMedia?.('(prefers-color-scheme: dark)');
    if (colorSchemeQuery) {
      // send user's initial choice
      trackColorSchemeChoice(colorSchemeQuery);
      colorSchemeQuery.addEventListener('change', trackColorSchemeChoice);
    }
    return () => {
      if (colorSchemeQuery) {
        colorSchemeQuery.removeEventListener('change', trackColorSchemeChoice);
      }
    };
  }, []);
};

/**
 * @typedef {object} ComponentPropOverride
 * @property {string|boolean|Record<string, any>|Function} [key] - The custom prop value.
 */

/**
 * @typedef {object} ComponentPropOverrides
 * @property {Record<string, ComponentPropOverride>} targets - A mapping of component targets to custom props.
 */

/**
 * @typedef {object} AppConfigWithComponentPropOverrides
 * @property {ComponentPropOverrides} componentPropOverrides - The component prop overrides configuration.
 */

/**
 * @typedef {object} ComponentPropOverridesOptions
 * @property {string[]} [allowedPropNames=["className"]] - The list of prop names allowed to be overridden.
 * @property {boolean} [allowsDataAttributes=true] - Whether to allow `data-*` attributes to be applied.
 */

/**
 * A React hook that processes the given `target` to extend/merge component props
 * with any corresponding attributes/values based on configuration.
 *
 * This hook looks up the specified `target` in the `componentPropOverrides` configuration,
 * and if a match is found, it merges the component's props with the mapped attributes/values
 * per the configured component targets.
 *
 * @param {string} target - The component target used to identify custom props per configuration.
 * @param {Record<string, any>} props - The original props object passed to the component.
 * @param {ComponentPropOverridesOptions} [options] - Optional configuration for the hook.
 * @returns {Record<string, any>} An updated props object with custom props merged in.
 *
 * @example
 * // Given a configuration like:
 * {
 *   componentPropOverrides: {
 *     targets: {
 *       example: {
 *         'data-dd-privacy': 'mask',
 *         'data-hj-suppress': '',
 *         className: 'fs-mask',
 *       },
 *     },
 *   },
 * }
 *
 * // and calling the hook as follows:
 * const propOverrides = useComponentPropOverrides('example', { otherProp: 'value' });
 *
 * // The resulting `propOverrides` will be:
 * { otherProp: 'value', 'data-dd-privacy': 'mask', data-hj-suppress: '', className: 'fs-mask' }
 *
 * @memberof module:React
 * @see module:React.withComponentPropOverrides
 */
export function useComponentPropOverrides(target, props, options = {}) {
  /** @type {AppConfigWithComponentPropOverrides} */
  const { componentPropOverrides } = getConfig();
  const propOverridesForTarget = componentPropOverrides?.targets?.[target];
  if (!target || !propOverridesForTarget) {
    return props;
  }
  const overrideOptions = {
    // Allow for custom prop overrides to be applied to the component. These are
    // separate from any `data-*` attributes, which are always supported.
    allowedPropNames: ['className'],
    // Allow for any `data-*` attributes to be applied to the component.
    allowsDataAttributes: true,
    ...options,
  };

  const updatedProps = { ...props };
  // Apply the configured attributes/values/classes for the matched target
  Object.entries(propOverridesForTarget).forEach(([attributeName, attributeValue]) => {
    const isAllowedPropName = !!overrideOptions.allowedPropNames?.includes(attributeName);
    const isDataAttribute = !!overrideOptions.allowsDataAttributes && attributeName.startsWith('data-');
    const isAllowedPropOverride = isAllowedPropName || isDataAttribute;
    if (!isAllowedPropOverride) {
      // Skip applying the override prop if it's not allowed.
      return;
    }

    // Parse attributeValue as empty string if not provided, or falsey value is given (e.g., `undefined`).
    let transformedAttributeValue = !attributeValue ? '' : attributeValue;
    if (attributeName === 'className') {
      // Append the `className` to the existing `className` prop value (if any)
      transformedAttributeValue = [updatedProps.className, attributeValue].join(' ').trim();
    } else if (attributeName === 'style' && typeof attributeValue === 'object') {
      // Merge the `style` object with the existing `style` prop object (if any)
      transformedAttributeValue = { ...updatedProps.style, ...attributeValue };
    } else if (typeof attributeValue === 'function') {
      // Merge the function with the existing prop's function
      const oldFn = updatedProps[attributeName];
      transformedAttributeValue = oldFn ? (...args) => {
        oldFn(...args);
        attributeValue(...args);
      } : attributeValue;
    }

    // Update the props with the transformed attribute value
    updatedProps[attributeName] = transformedAttributeValue;
  });
  return updatedProps;
}