{"version":3,"sources":["globals/wrappers/createReactCustomElementType.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAA+C,MAAM,OAAO,CAAC;AAIpE;;GAEG;AACH,UAAU,4BAA4B;IACpC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,CAAC;CAC1C;AAED;;GAEG;AACH,UAAU,2BAA2B;IACnC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAE3B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,4BAA4B,CAAC;IAE9C;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC;CAC3C;AAED;;GAEG;AACH,UAAU,4BAA4B;IACpC,CAAC,QAAQ,EAAE,MAAM,GAAG,2BAA2B,CAAC;CACjD;AAED;;GAEG;AACH,UAAU,sBAAsB;IAC9B;;OAEG;IACH,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;IAExB;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAsGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,QAAA,MAAM,4BAA4B,SAAU,MAAM,cAAc,4BAA4B,sHAoF3F,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,gCAAqC,CAAC;AAEpE;;;GAGG;AACH,eAAO,MAAM,gBAAgB,qBAAmD,CAAC;AAEjF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,qBAA2D,CAAC;AAEzF,eAAe,4BAA4B,CAAC","file":"createReactCustomElementType.d.ts","sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2019, 2021\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport React, { Component, createElement, forwardRef } from 'react';\nimport on from 'carbon-components/es/globals/js/misc/on';\nimport Handle from '../internal/handle';\n\n/**\n * A descriptor for a React event prop of a custom element.\n */\ninterface CustomElementEventDescriptor {\n  /**\n   * The event name.\n   */\n  name: string;\n\n  /**\n   * A boolean to detemine usage of capture mode or the event options.\n   */\n  options?: boolean | EventListenerOptions;\n}\n\n/**\n * A descriptor for a React prop for an attribute of a custom element.\n */\ninterface CustomElementPropDescriptor {\n  /**\n   * The attribute name for the prop.\n   */\n  attribute?: string | false;\n\n  /**\n   * The event name (or descriptor) for the prop.\n   */\n  event?: string | CustomElementEventDescriptor;\n\n  /**\n   * A function that takes a property value and returns the corresponding attribute value.\n   */\n  serialize?: (value: any) => string | void;\n}\n\n/**\n * A descriptor for a set of React props for attributes of a custom element.\n */\ninterface CustomElementPropsDescriptor {\n  [propName: string]: CustomElementPropDescriptor;\n}\n\n/**\n * React props for the component `createCustomElementType()` generates.\n */\ninterface CustomElementTypeProps {\n  /**\n   * Ordinal prop.\n   */\n  [propName: string]: any;\n\n  /**\n   * Child nodes.\n   */\n  children?: React.ReactNode;\n}\n\n/**\n * @param refs List of React refs to merge.\n * @returns Merged React ref.\n */\nconst mergeRefs =\n  <T>(...refs: React.Ref<T>[]) =>\n  el => {\n    refs.forEach(ref => {\n      // https://github.com/facebook/react/issues/13029#issuecomment-410002316\n      if (typeof ref === 'function') {\n        ref(el);\n      } else if (Object(ref) === ref) {\n        // `React.Ref.current` is read-only for regular use case, but we update it here\n        (ref as { current: T }).current = el;\n      }\n    });\n  };\n\n/**\n * @param prop A prop value.\n * @param descriptor A React prop descriptor.\n * @returns The corresponding attribute value for the given prop value.\n */\nconst convertProp = (prop: any, descriptor: CustomElementPropDescriptor) => {\n  if (!descriptor) {\n    return prop;\n  }\n  const { event, serialize } = descriptor;\n  if (event) {\n    // Events are not set as props, we use DOM `addEventListener()` instead\n    return undefined;\n  }\n  return !serialize ? prop : serialize(prop);\n};\n\n/**\n * @param props A set of React props.\n * @param descriptor A set of React prop desciptor.\n * @returns The set of React props to set to a custom element, corresponding to the given React props.\n */\nconst convertProps = (props: CustomElementTypeProps, descriptor: CustomElementPropsDescriptor) =>\n  Object.keys(props).reduce((acc, propName) => {\n    const { [propName]: descriptorItem } = descriptor;\n    const converted = convertProp(props[propName], descriptorItem);\n    const { attribute } = descriptorItem ?? {};\n    return attribute === false\n      ? acc\n      : {\n          ...acc,\n          [attribute || propName]: converted,\n        };\n  }, {});\n\n/**\n * Attaches listeners of custom events, to a custom element.\n * @param elem The custom element.\n * @param descriptor An object, keyed by prop name, of data that may have custom event names.\n * @param callback A callback function that runs as the custom events fire.\n * @returns A handle that allows to release all event listeners attached.\n */\nconst attachEventListeners = (\n  elem: HTMLElement,\n  descriptor: CustomElementPropsDescriptor,\n  callback: (name: string, event: Event) => void\n): Handle => {\n  const handles = new Set<Handle>();\n  Object.keys(descriptor).forEach(propName => {\n    if (descriptor[propName]) {\n      const { event: eventDescriptor } = descriptor[propName];\n      const name =\n        Object(eventDescriptor) !== eventDescriptor\n          ? (eventDescriptor as string)\n          : (eventDescriptor as CustomElementEventDescriptor).name;\n      const options =\n        Object(eventDescriptor) !== eventDescriptor ? undefined : (eventDescriptor as CustomElementEventDescriptor).options;\n      if (name) {\n        handles.add(\n          on(\n            elem,\n            name,\n            event => {\n              callback(propName, event);\n            },\n            options\n          )\n        );\n      }\n    }\n  });\n  return {\n    release() {\n      handles.forEach(handle => {\n        handle.release();\n        handles.delete(handle);\n      });\n      return null;\n    },\n  };\n};\n\n/**\n * @param name The tag name of the custom element.\n * @param descriptor A descriptor for a set of React props for attributes of a custom element.\n * @returns A React component working as a wrapper for the given custom element.\n * @example\n * import { render } from 'react-dom';\n * import createCustomElementType, { booleanSerializer } from '/path/to/createCustomElementType';\n *\n * const BXDropdown = createCustomElementType('bx-dropdown', {\n *   disabled: {\n *     // Sets `disabled` attribute when the React prop value is truthy, unsets otherwise\n *     serialize: booleanSerializer,\n *   },\n *   helperText: {\n *     // Maps `helperText` React prop to `helper-text` attribute\n *     attribute: 'helper-text',\n *   },\n *   onBeforeSelect: {\n *     // Sets `onBeforeSelect` React prop value as a listener of `bx-dropdown-beingselected` custom event\n *     event: 'bx-dropdown-beingselected',\n *   },\n * });\n *\n * render(\n *   (\n *     <BXDropdown\n *       disabled={true}\n *       helperText=\"some-helper-text\"\n *       onBeforeSelect={event => { console.log('bx-dropdown-beingselected is fired!', event); }}>\n *       <bx-dropdown-item value=\"all\">Option 1</bx-dropdown-item>\n *       <bx-dropdown-item value=\"cloudFoundry\">Option 2</bx-dropdown-item>\n *       <bx-dropdown-item value=\"staging\">Option 3</bx-dropdown-item>\n *     </BXDropdown>\n *   )\n *   document.body\n * );\n */\nconst createReactCustomElementType = (name: string, descriptor: CustomElementPropsDescriptor) => {\n  /**\n   * Array of React prop names that should be mapped to DOM properties instead of attributes.\n   */\n  const nonAttributeProps = Object.keys(descriptor).filter(propName => {\n    const { [propName]: descriptorItem } = descriptor;\n    const { attribute } = descriptorItem ?? {};\n    return attribute === false;\n  });\n\n  /**\n   * A React component working as a wrapper for the custom element.\n   */\n  class CustomElementType extends Component<CustomElementTypeProps> {\n    /**\n     * The element.\n     */\n    private _elem: HTMLElement | null = null;\n\n    /**\n     * The handle that allows to release all event listeners attached to this custom element.\n     */\n    private _eventListenersHandle: Handle | null = null;\n\n    /**\n     * The callback function that runs as the custom events fire.\n     * @param propName The React prop name associated with the event listener.\n     * @param event The event.\n     */\n    private _handleEvent = (propName: string, event: Event) => {\n      const { [propName]: listener } = this.props;\n      if (listener) {\n        listener.call(event.currentTarget, event);\n      }\n    };\n\n    /**\n     * Handles getting/losing the React `ref` object of this custom element.\n     * @param elem The custom element.\n     */\n    private _handleElemRef = (elem: HTMLElement) => {\n      this._elem = elem;\n      if (this._eventListenersHandle) {\n        this._eventListenersHandle.release();\n        this._eventListenersHandle = null;\n      }\n      if (elem) {\n        this._eventListenersHandle = attachEventListeners(elem, descriptor, this._handleEvent);\n      }\n    };\n\n    /**\n     * Reflects change in React props to DOM properties.\n     * @param prevProps The previous props.\n     */\n    updateProps(prevProps: { [key: string]: any } = {}) {\n      const { props, _elem: elem } = this;\n      nonAttributeProps.forEach(propName => {\n        const { [propName]: prevValue } = prevProps;\n        const { [propName]: value } = props;\n        if (prevValue !== value) {\n          elem![propName] = value;\n        }\n      });\n    }\n\n    componentDidMount() {\n      this.updateProps();\n    }\n\n    componentDidUpdate(prevProps) {\n      this.updateProps(prevProps);\n    }\n\n    render() {\n      const { children, innerRef, ...props } = this.props;\n      const mergedRef = mergeRefs<HTMLElement>(innerRef, this._handleElemRef);\n      return createElement(name, { ref: mergedRef, ...convertProps(props, descriptor) }, children);\n    }\n  }\n\n  return forwardRef<HTMLElement, CustomElementTypeProps>((props, ref) =>\n    createElement(CustomElementType, { ...props, innerRef: ref })\n  );\n};\n\n/**\n * @param value A React prop value.\n * @returns Serialized version of React prop value, as a boolean attribute in a custom element.\n */\nexport const booleanSerializer = value => (!value ? undefined : '');\n\n/**\n * @param value A React prop value.\n * @returns Serialized version of React prop value, as a number attribute in a custom element.\n */\nexport const numberSerializer = value => (value == null ? value : String(value));\n\n/**\n * @param value A React prop value.\n * @returns Serialized version of React prop value, as a object attribute in a custom element.\n */\nexport const objectSerializer = value => (value == null ? value : JSON.stringify(value));\n\nexport default createReactCustomElementType;\n"]}