{"version":3,"sources":["../src/index.tsx","../src/utils.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\nimport { useId, IS_SERVER, useIsomorphicLayoutEffect } from './utils'\n\nconst SYMBOL_KEY = '__wrap_b'\nconst SYMBOL_NATIVE_KEY = '__wrap_n'\nconst SYMBOL_OBSERVER_KEY = '__wrap_o'\n\ninterface WrapperElement extends HTMLElement {\n  [SYMBOL_OBSERVER_KEY]?: ResizeObserver | undefined\n}\n\ntype RelayoutFn = (\n  id: string | number,\n  ratio: number,\n  wrapper?: WrapperElement\n) => void\n\ndeclare global {\n  interface Window {\n    [SYMBOL_KEY]: RelayoutFn\n    // A flag to indicate whether the browser supports text-balancing natively.\n    // undefined: not injected\n    // 1: injected and supported\n    // 2: injected but not supported\n    [SYMBOL_NATIVE_KEY]?: number\n  }\n}\n\nconst relayout: RelayoutFn = (id, ratio, wrapper) => {\n  wrapper =\n    wrapper || document.querySelector<WrapperElement>(`[data-br=\"${id}\"]`)\n  const container = wrapper?.parentElement\n\n  if (!container) { return; }\n\n  const update = (width: number) => (wrapper.style.maxWidth = width + 'px')\n\n  // Reset wrapper width\n  wrapper.style.maxWidth = ''\n\n  // Get the initial container size\n  const width = container.clientWidth\n  const height = container.clientHeight\n\n  // Synchronously do binary search and calculate the layout\n  let lower: number = width / 2 - 0.25\n  let upper: number = width + 0.5\n  let middle: number\n\n  if (width) {\n    // Ensure we don't search widths lower than when the text overflows\n    update(lower)\n    lower = Math.max(wrapper.scrollWidth, lower)\n\n    while (lower + 1 < upper) {\n      middle = Math.round((lower + upper) / 2)\n      update(middle)\n      if (container.clientHeight === height) {\n        upper = middle\n      } else {\n        lower = middle\n      }\n    }\n\n    // Update the wrapper width\n    update(upper * ratio + width * (1 - ratio))\n  }\n\n  // Create a new observer if we don't have one.\n  // Note that we must inline the key here as we use `toString()` to serialize\n  // the function.\n  if (!wrapper['__wrap_o']) {\n    if (typeof ResizeObserver !== 'undefined') {\n      ;(wrapper['__wrap_o'] = new ResizeObserver(() => {\n        self.__wrap_b(0, +wrapper.dataset.brr, wrapper)\n      })).observe(container)\n    } else {\n      // Silently ignore ResizeObserver for production builds\n      if (process.env.NODE_ENV === 'development') {\n        console.warn(\n          'The browser you are using does not support the ResizeObserver API. ' +\n            'Please consider add polyfill for this API to avoid potential layout shifts or upgrade your browser. ' +\n            'Read more: https://github.com/shuding/react-wrap-balancer#browser-support-information'\n        )\n      }\n    }\n  }\n}\n\nconst RELAYOUT_STR = relayout.toString()\n\nconst isTextWrapBalanceSupported = `(self.CSS&&CSS.supports(\"text-wrap\",\"balance\")?1:2)`\n\nconst createScriptElement = (\n  injected: boolean,\n  nonce?: string,\n  suffix: string = ''\n) => {\n  if (suffix) {\n    suffix = `self.${SYMBOL_NATIVE_KEY}!=1&&${suffix}`\n  }\n  return (\n    <script\n      suppressHydrationWarning\n      dangerouslySetInnerHTML={{\n        // Calculate the balance initially for SSR\n        __html:\n          (injected\n            ? ''\n            : `self.${SYMBOL_NATIVE_KEY}=self.${SYMBOL_NATIVE_KEY}||${isTextWrapBalanceSupported};self.${SYMBOL_KEY}=${RELAYOUT_STR};`) +\n          suffix,\n      }}\n      nonce={nonce}\n    />\n  )\n}\n\ninterface BalancerOwnProps<\n  ElementType extends React.ElementType = React.ElementType\n> extends React.HTMLAttributes<HTMLElement> {\n  /**\n   * The HTML tag to use for the wrapper element.\n   * @default 'span'\n   */\n  as?: ElementType\n  /**\n   * The balance ratio of the wrapper width (0 <= ratio <= 1).\n   * 0 means the wrapper width is the same as the container width (no balance, browser default).\n   * 1 means the wrapper width is the minimum (full balance, most compact).\n   * @default 1\n   */\n  ratio?: number\n  /**\n   * An option to skip the re-balance logic\n   * and use the native CSS text-balancing if supported.\n   * @default true\n   */\n  preferNative?: boolean\n  /**\n   * The nonce attribute to allowlist inline script injection by the component.\n   */\n  nonce?: string\n}\n\ntype BalancerProps<ElementType extends React.ElementType> =\n  BalancerOwnProps<ElementType> &\n    Omit<React.ComponentPropsWithoutRef<ElementType>, keyof BalancerOwnProps>\n\n/**\n * An optional provider to inject the global relayout function, so all children\n * Balancer components can share it.\n */\nconst BalancerContext = React.createContext<{\n  preferNative: boolean\n  hasProvider: boolean\n}>({ preferNative: true, hasProvider: false })\nconst Provider: React.FC<{\n  /**\n   * An option to skip the re-balance logic\n   * and use the native CSS text-balancing if supported.\n   * @default true\n   */\n  preferNative?: boolean\n  /**\n   * The nonce attribute to allowlist inline script injection by the component\n   */\n  nonce?: string\n  children?: React.ReactNode\n}> = ({ preferNative = true, nonce, children }) => {\n  const contextValue = React.useMemo(() => {\n    return {\n      preferNative,\n      hasProvider: true,\n    }\n  }, [preferNative])\n  return (\n    <BalancerContext.Provider value={contextValue}>\n      {createScriptElement(false, nonce)}\n      {children}\n    </BalancerContext.Provider>\n  )\n}\n\nconst Balancer = React.forwardRef(\n  <ElementType extends React.ElementType = React.ElementType>(\n    {\n      ratio = 1,\n      preferNative,\n      nonce,\n      children,\n      as,\n      ...props\n    }: BalancerProps<ElementType>,\n    ref\n  ) => {\n    const id = useId()\n    const wrapperRef = React.useRef<WrapperElement>()\n    const contextValue = React.useContext(BalancerContext)\n    const preferNativeBalancing = preferNative ?? contextValue.preferNative\n    const Wrapper: React.ElementType = as || 'span'\n\n    React.useImperativeHandle(ref, () => wrapperRef.current, [])\n\n    // Re-balance on content change and on mount/hydration.\n    useIsomorphicLayoutEffect(() => {\n      // Skip if the browser supports text-balancing natively.\n      if (preferNativeBalancing && self[SYMBOL_NATIVE_KEY] === 1) return\n\n      if (wrapperRef.current) {\n        // Re-assign the function here as the component can be dynamically rendered, and script tag won't work in that case.\n        ;(self[SYMBOL_KEY] = relayout)(0, ratio, wrapperRef.current)\n      }\n    }, [children, preferNativeBalancing, ratio])\n\n    // Remove the observer when unmounting.\n    useIsomorphicLayoutEffect(() => {\n      // Skip if the browser supports text-balancing natively.\n      if (preferNativeBalancing && self[SYMBOL_NATIVE_KEY] === 1) return\n\n      return () => {\n        if (!wrapperRef.current) return\n\n        const resizeObserver = wrapperRef.current[SYMBOL_OBSERVER_KEY]\n        if (!resizeObserver) return\n\n        resizeObserver.disconnect()\n        delete wrapperRef.current[SYMBOL_OBSERVER_KEY]\n      }\n    }, [preferNativeBalancing])\n\n    if (process.env.NODE_ENV === 'development') {\n      // In development, we check `children`'s type to ensure we are not wrapping\n      // elements like <p> or <h1> inside. Instead <Balancer> should directly\n      // wrap text nodes.\n      if (\n        children &&\n        !Array.isArray(children) &&\n        typeof children === 'object'\n      ) {\n        if (\n          'type' in children &&\n          typeof children.type === 'string' &&\n          children.type !== 'span'\n        ) {\n          console.warn(\n            `<Balancer> should not wrap <${children.type}> inside. Instead, it should directly wrap text or inline nodes.\n\nTry changing this:\n  <Balancer><${children.type}>content</${children.type}></Balancer>\nTo:\n  <${children.type}><Balancer>content</Balancer></${children.type}>`\n          )\n        }\n      }\n    }\n\n    return (\n      <>\n        <Wrapper\n          {...props}\n          data-br={id}\n          data-brr={ratio}\n          ref={wrapperRef}\n          style={{\n            display: 'inline-block',\n            verticalAlign: 'top',\n            textDecoration: 'inherit',\n            textWrap: preferNativeBalancing ? 'balance' : 'initial',\n          }}\n          suppressHydrationWarning\n        >\n          {children}\n        </Wrapper>\n        {createScriptElement(\n          contextValue.hasProvider,\n          nonce,\n          `self.${SYMBOL_KEY}(\"${id}\",${ratio})`\n        )}\n      </>\n    )\n  }\n)\n\n// As Next.js adds `display: none` to `body` for development, we need to trigger\n// a re-balance right after the style is removed, synchronously.\nif (!IS_SERVER && process.env.NODE_ENV !== 'production') {\n  const next_dev_style = document.querySelector<HTMLElement>(\n    '[data-next-hide-fouc]'\n  )\n  if (next_dev_style) {\n    const callback: MutationCallback = (mutationList) => {\n      for (const mutation of mutationList) {\n        for (const node of Array.from(mutation.removedNodes)) {\n          if (node !== next_dev_style) continue\n\n          observer.disconnect()\n          const elements =\n            document.querySelectorAll<WrapperElement>('[data-br]')\n\n          for (const element of Array.from(elements)) {\n            self[SYMBOL_KEY](0, +element.dataset.brr, element)\n          }\n        }\n      }\n    }\n    const observer = new MutationObserver(callback)\n    observer.observe(document.head, { childList: true })\n  }\n}\n\nexport default Balancer\nexport { Provider, Balancer, BalancerOwnProps }\n","import React from 'react'\n\nexport const IS_SERVER = typeof window === 'undefined'\nexport const useIsomorphicLayoutEffect = IS_SERVER\n  ? React.useEffect\n  : React.useLayoutEffect\n\nlet ID = 0\nconst genId = () => ++ID\nlet serverHandoffComplete = false\n\nfunction useIdPolyfill() {\n  const [id, setId] = React.useState(serverHandoffComplete ? genId : undefined)\n\n  useIsomorphicLayoutEffect(() => {\n    if (id === undefined) {\n      setId(genId())\n    }\n\n    serverHandoffComplete = true\n  }, [])\n\n  if (id === undefined) {\n    return id\n  }\n\n  return `rwb-${id.toString(32)}`\n}\n\n/**\n * A hook for generating unique IDs that are stable across the server and client,\n * while avoiding hydration mismatches. Compatible with React 16+ by using\n * [React 18's useId](https://reactjs.org/docs/hooks-reference.html#useid) if\n * it's available, and a polyfill implementation inspired by\n * [@accessible/use-id](https://github.com/accessible-ui/use-id) if it is not.\n *\n * \"rwb-\" is hard-coded as a prefix in the polyfill. When using React 18+,\n * a prefix can be provided with the `identifierPrefix` option in\n * [ReactDOMClient](https://reactjs.org/docs/react-dom-client.html).\n */\nexport function useId() {\n  const implementation = React.useMemo((): (() => string | number) => {\n    if ('useId' in React) return React.useId\n    return useIdPolyfill\n  }, [])\n\n  return implementation()\n}\n"],"mappings":"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","names":["React","React","IS_SERVER","useIsomorphicLayoutEffect","ID","genId","serverHandoffComplete","useIdPolyfill","id","setId","useId","SYMBOL_KEY","SYMBOL_NATIVE_KEY","SYMBOL_OBSERVER_KEY","relayout","id","ratio","wrapper","container","update","width","height","lower","upper","middle","RELAYOUT_STR","isTextWrapBalanceSupported","createScriptElement","injected","nonce","suffix","React","BalancerContext","Provider","preferNative","children","contextValue","Balancer","as","props","ref","useId","wrapperRef","preferNativeBalancing","Wrapper","useIsomorphicLayoutEffect","resizeObserver","IS_SERVER","next_dev_style","callback","mutationList","mutation","node","observer","elements","element","src_default"]}