![JAR search and dependency download from the Maven repository](/logo.png)
package.create-component.js.map Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of react Show documentation
Show all versions of react Show documentation
A React component wrapper for web components.
The newest version!
{"version":3,"file":"create-component.js","sources":["src/create-component.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2018 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\nimport type React from 'react';\n\nconst NODE_MODE = false;\nconst DEV_MODE = true;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DistributiveOmit = T extends any\n ? K extends keyof T\n ? Omit\n : T\n : T;\ntype PropsWithoutRef = DistributiveOmit;\n\n/**\n * Creates a type to be used for the props of a web component used directly in\n * React JSX.\n *\n * Example:\n *\n * ```ts\n * declare module \"react\" {\n * namespace JSX {\n * interface IntrinsicElements {\n * 'x-foo': WebComponentProps;\n * }\n * }\n * }\n * ```\n */\nexport type WebComponentProps = React.DetailedHTMLProps<\n React.HTMLAttributes,\n I\n> &\n ElementProps;\n\n/**\n * Type of the React component wrapping the web component. This is the return\n * type of `createComponent`.\n */\nexport type ReactWebComponent<\n I extends HTMLElement,\n E extends EventNames = {},\n> = React.ForwardRefExoticComponent<\n // TODO(augustjk): Remove and use `React.PropsWithoutRef` when\n // https://github.com/preactjs/preact/issues/4124 is fixed.\n PropsWithoutRef> & React.RefAttributes\n>;\n\n// Props derived from custom element class. Currently has limitations of making\n// all properties optional and also surfaces life cycle methods in autocomplete.\n// TODO(augustjk) Consider omitting keyof LitElement to remove \"internal\"\n// lifecycle methods or allow user to explicitly provide props.\ntype ElementProps = Partial>;\n\n// Acceptable props to the React component.\ntype ComponentProps = Omit<\n React.HTMLAttributes,\n // Prefer type of provided event handler props or those on element over\n // built-in HTMLAttributes\n keyof E | keyof ElementProps\n> &\n EventListeners &\n ElementProps;\n\n/**\n * Type used to cast an event name with an event type when providing the\n * `events` option to `createComponent` for better typing of the event handler\n * prop.\n *\n * Example:\n *\n * ```ts\n * const FooComponent = createComponent({\n * ...\n * events: {\n * onfoo: 'foo' as EventName,\n * }\n * });\n * ```\n *\n * `onfoo` prop will have the type `(e: FooEvent) => void`.\n */\nexport type EventName = string & {\n __eventType: T;\n};\n\n// A key value map matching React prop names to event names.\ntype EventNames = Record;\n\n// A map of expected event listener types based on EventNames.\ntype EventListeners = {\n [K in keyof R]?: R[K] extends EventName\n ? (e: R[K]['__eventType']) => void\n : (e: Event) => void;\n};\n\nexport interface Options {\n react: typeof React;\n tagName: string;\n elementClass: Constructor;\n events?: E;\n displayName?: string;\n}\n\ntype Constructor = {new (): T};\n\nconst reservedReactProperties = new Set([\n 'children',\n 'localName',\n 'ref',\n 'style',\n 'className',\n]);\n\nconst listenedEvents = new WeakMap>();\n\n/**\n * Adds an event listener for the specified event to the given node. In the\n * React setup, there should only ever be one event listener. Thus, for\n * efficiency only one listener is added and the handler for that listener is\n * updated to point to the given listener function.\n */\nconst addOrUpdateEventListener = (\n node: Element,\n event: string,\n listener: (event?: Event) => void\n) => {\n let events = listenedEvents.get(node);\n if (events === undefined) {\n listenedEvents.set(node, (events = new Map()));\n }\n let handler = events.get(event);\n if (listener !== undefined) {\n // If necessary, add listener and track handler\n if (handler === undefined) {\n events.set(event, (handler = {handleEvent: listener}));\n node.addEventListener(event, handler);\n // Otherwise just update the listener with new value\n } else {\n handler.handleEvent = listener;\n }\n // Remove listener if one exists and value is undefined\n } else if (handler !== undefined) {\n events.delete(event);\n node.removeEventListener(event, handler);\n }\n};\n\n/**\n * Sets properties and events on custom elements. These properties and events\n * have been pre-filtered so we know they should apply to the custom element.\n */\nconst setProperty = (\n node: E,\n name: string,\n value: unknown,\n old: unknown,\n events?: EventNames\n) => {\n const event = events?.[name];\n // Dirty check event value.\n if (event !== undefined) {\n if (value !== old) {\n addOrUpdateEventListener(node, event, value as (e?: Event) => void);\n }\n return;\n }\n // But don't dirty check properties; elements are assumed to do this.\n node[name as keyof E] = value as E[keyof E];\n\n // This block is to replicate React's behavior for attributes of native\n // elements where `undefined` or `null` values result in attributes being\n // removed.\n // https://github.com/facebook/react/blob/899cb95f52cc83ab5ca1eb1e268c909d3f0961e7/packages/react-dom-bindings/src/client/DOMPropertyOperations.js#L107-L141\n //\n // It's only needed here for native HTMLElement properties that reflect\n // attributes of the same name but don't have that behavior like \"id\" or\n // \"draggable\".\n if (\n (value === undefined || value === null) &&\n name in HTMLElement.prototype\n ) {\n node.removeAttribute(name);\n }\n};\n\n/**\n * Creates a React component for a custom element. Properties are distinguished\n * from attributes automatically, and events can be configured so they are added\n * to the custom element as event listeners.\n *\n * @param options An options bag containing the parameters needed to generate a\n * wrapped web component.\n *\n * @param options.react The React module, typically imported from the `react`\n * npm package.\n * @param options.tagName The custom element tag name registered via\n * `customElements.define`.\n * @param options.elementClass The custom element class registered via\n * `customElements.define`.\n * @param options.events An object listing events to which the component can\n * listen. The object keys are the event property names passed in via React\n * props and the object values are the names of the corresponding events\n * generated by the custom element. For example, given `{onactivate:\n * 'activate'}` an event function may be passed via the component's `onactivate`\n * prop and will be called when the custom element fires its `activate` event.\n * @param options.displayName A React component display name, used in debugging\n * messages. Default value is inferred from the name of custom element class\n * registered via `customElements.define`.\n */\nexport const createComponent = <\n I extends HTMLElement,\n E extends EventNames = {},\n>({\n react: React,\n tagName,\n elementClass,\n events,\n displayName,\n}: Options): ReactWebComponent => {\n const eventProps = new Set(Object.keys(events ?? {}));\n\n if (DEV_MODE) {\n for (const p of reservedReactProperties) {\n if (p in elementClass.prototype && !(p in HTMLElement.prototype)) {\n // Note, this effectively warns only for `ref` since the other\n // reserved props are on HTMLElement.prototype. To address this\n // would require crawling down the prototype, which doesn't feel worth\n // it since implementing these properties on an element is extremely\n // rare.\n console.warn(\n `${tagName} contains property ${p} which is a React reserved ` +\n `property. It will be used by React and not set on the element.`\n );\n }\n }\n }\n\n type Props = ComponentProps;\n\n const ReactComponent = React.forwardRef((props, ref) => {\n const prevElemPropsRef = React.useRef(new Map());\n const elementRef = React.useRef(null);\n\n // Props to be passed to React.createElement\n const reactProps: Record = {};\n // Props to be set on element with setProperty\n const elementProps: Record = {};\n\n for (const [k, v] of Object.entries(props)) {\n if (reservedReactProperties.has(k)) {\n // React does *not* handle `className` for custom elements so\n // coerce it to `class` so it's handled correctly.\n reactProps[k === 'className' ? 'class' : k] = v;\n continue;\n }\n\n if (eventProps.has(k) || k in elementClass.prototype) {\n elementProps[k] = v;\n continue;\n }\n\n reactProps[k] = v;\n }\n\n // useLayoutEffect produces warnings during server rendering.\n if (!NODE_MODE) {\n // This one has no dependency array so it'll run on every re-render.\n React.useLayoutEffect(() => {\n if (elementRef.current === null) {\n return;\n }\n const newElemProps = new Map();\n for (const key in elementProps) {\n setProperty(\n elementRef.current,\n key,\n props[key],\n prevElemPropsRef.current.get(key),\n events\n );\n prevElemPropsRef.current.delete(key);\n newElemProps.set(key, props[key]);\n }\n // \"Unset\" any props from previous render that no longer exist.\n // Setting to `undefined` seems like the correct thing to \"unset\"\n // but currently React will set it as `null`.\n // See https://github.com/facebook/react/issues/28203\n for (const [key, value] of prevElemPropsRef.current) {\n setProperty(elementRef.current, key, undefined, value, events);\n }\n prevElemPropsRef.current = newElemProps;\n });\n\n // Empty dependency array so this will only run once after first render.\n React.useLayoutEffect(() => {\n elementRef.current?.removeAttribute('defer-hydration');\n }, []);\n }\n\n if (NODE_MODE) {\n // If component is to be server rendered with `@lit/ssr-react`, pass\n // element properties in a special bag to be set by the server-side\n // element renderer.\n if (\n React.createElement.name === 'litPatchedCreateElement' &&\n Object.keys(elementProps).length\n ) {\n // This property needs to remain unminified.\n reactProps['_$litProps$'] = elementProps;\n }\n } else {\n // Suppress hydration warning for server-rendered attributes.\n // This property needs to remain unminified.\n reactProps['suppressHydrationWarning'] = true;\n }\n\n return React.createElement(tagName, {\n ...reactProps,\n ref: React.useCallback(\n (node: I) => {\n elementRef.current = node;\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref !== null) {\n ref.current = node;\n }\n },\n [ref]\n ),\n });\n });\n\n ReactComponent.displayName = displayName ?? elementClass.name;\n\n return ReactComponent;\n};\n"],"names":["reservedReactProperties","Set","listenedEvents","WeakMap","setProperty","node","name","value","old","events","event","undefined","HTMLElement","prototype","removeAttribute","listener","get","set","Map","handler","handleEvent","addEventListener","delete","removeEventListener","addOrUpdateEventListener","createComponent","react","React","tagName","elementClass","displayName","eventProps","Object","keys","ReactComponent","forwardRef","props","ref","prevElemPropsRef","useRef","elementRef","reactProps","elementProps","k","v","entries","has","useLayoutEffect","current","newElemProps","key","createElement","useCallback"],"mappings":";;;;;AAgHA,MAAMA,EAA0B,IAAIC,IAAI,CACtC,WACA,YACA,MACA,QACA,cAGIC,EAAiB,IAAIC,QAsCrBC,EAAc,CAClBC,EACAC,EACAC,EACAC,EACAC,KAEA,MAAMC,EAAQD,IAASH,QAETK,IAAVD,GAOJL,EAAKC,GAAmBC,EAWtB,MAACA,GACDD,KAAQM,YAAYC,WAEpBR,EAAKS,gBAAgBR,IApBjBC,IAAUC,GAxCe,EAC/BH,EACAK,EACAK,KAEA,IAAIN,EAASP,EAAec,IAAIX,QACjBM,IAAXF,GACFP,EAAee,IAAIZ,EAAOI,EAAS,IAAIS,KAEzC,IAAIC,EAAUV,EAAOO,IAAIN,QACRC,IAAbI,OAEcJ,IAAZQ,GACFV,EAAOQ,IAAIP,EAAQS,EAAU,CAACC,YAAaL,IAC3CV,EAAKgB,iBAAiBX,EAAOS,IAG7BA,EAAQC,YAAcL,OAGHJ,IAAZQ,IACTV,EAAOa,OAAOZ,GACdL,EAAKkB,oBAAoBb,EAAOS,GACjC,EAkBGK,CAAyBnB,EAAMK,EAAOH,EAoBzC,EA2BUkB,EAAkB,EAI7BC,MAAOC,EACPC,UACAC,eACApB,SACAqB,kBAEA,MAAMC,EAAa,IAAI9B,IAAI+B,OAAOC,KAAKxB,GAAU,CAAE,IAoB7CyB,EAAiBP,EAAMQ,YAAqB,CAACC,EAAOC,KACxD,MAAMC,EAAmBX,EAAMY,OAAO,IAAIrB,KACpCsB,EAAab,EAAMY,OAAiB,MAGpCE,EAAsC,CAAA,EAEtCC,EAAwC,CAAA,EAE9C,IAAK,MAAOC,EAAGC,KAAMZ,OAAOa,QAAQT,GAC9BpC,EAAwB8C,IAAIH,GAG9BF,EAAiB,cAANE,EAAoB,QAAUA,GAAKC,EAI5Cb,EAAWe,IAAIH,IAAMA,KAAKd,EAAahB,UACzC6B,EAAaC,GAAKC,EAIpBH,EAAWE,GAAKC,EAuDlB,OAjDEjB,EAAMoB,iBAAgB,KACpB,GAA2B,OAAvBP,EAAWQ,QACb,OAEF,MAAMC,EAAe,IAAI/B,IACzB,IAAK,MAAMgC,KAAOR,EAChBtC,EACEoC,EAAWQ,QACXE,EACAd,EAAMc,GACNZ,EAAiBU,QAAQhC,IAAIkC,GAC7BzC,GAEF6B,EAAiBU,QAAQ1B,OAAO4B,GAChCD,EAAahC,IAAIiC,EAAKd,EAAMc,IAM9B,IAAK,MAAOA,EAAK3C,KAAU+B,EAAiBU,QAC1C5C,EAAYoC,EAAWQ,QAASE,OAAKvC,EAAWJ,EAAOE,GAEzD6B,EAAiBU,QAAUC,CAAY,IAIzCtB,EAAMoB,iBAAgB,KACpBP,EAAWQ,SAASlC,gBAAgB,kBAAkB,GACrD,IAiBH2B,EAAqC,0BAAI,EAGpCd,EAAMwB,cAAcvB,EAAS,IAC/Ba,EACHJ,IAAKV,EAAMyB,aACR/C,IACCmC,EAAWQ,QAAU3C,EACF,mBAARgC,EACTA,EAAIhC,GACa,OAARgC,IACTA,EAAIW,QAAU3C,EACf,GAEH,CAACgC,KAEH,IAKJ,OAFAH,EAAeJ,YAAcA,GAAeD,EAAavB,KAElD4B,CAAc"}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy