import { render as preactRender, hydrate as preactHydrate, options, toChildArray, Component } from 'preact'; export const REACT_ELEMENT_TYPE = (typeof Symbol != 'undefined' && Symbol.for && Symbol.for('react.element')) || 0xeac7; const CAMEL_PROPS = /^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|marker(?!H|W|U)|overline|paint|stop|strikethrough|stroke|text(?!L)|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/; const IS_DOM = typeof document !== 'undefined'; // Input types for which onchange should not be converted to oninput. // type="file|checkbox|radio", plus "range" in IE11. // (IE11 doesn't support Symbol, which we use here to turn `rad` into `ra` which matches "range") const onChangeInputType = type => (typeof Symbol != 'undefined' && typeof Symbol() == 'symbol' ? /fil|che|rad/i : /fil|che|ra/i ).test(type); // Some libraries like `react-virtualized` explicitly check for this. Component.prototype.isReactComponent = {}; // `UNSAFE_*` lifecycle hooks // Preact only ever invokes the unprefixed methods. // Here we provide a base "fallback" implementation that calls any defined UNSAFE_ prefixed method. // - If a component defines its own `componentDidMount()` (including via defineProperty), use that. // - If a component defines `UNSAFE_componentDidMount()`, `componentDidMount` is the alias getter/setter. // - If anything assigns to an `UNSAFE_*` property, the assignment is forwarded to the unprefixed property. // See https://github.com/preactjs/preact/issues/1941 [ 'componentWillMount', 'componentWillReceiveProps', 'componentWillUpdate' ].forEach(key => { Object.defineProperty(Component.prototype, key, { configurable: true, get() { return this['UNSAFE_' + key]; }, set(v) { Object.defineProperty(this, key, { configurable: true, writable: true, value: v }); } }); }); /** * Proxy render() since React returns a Component reference. * @param {import('./internal').VNode} vnode VNode tree to render * @param {import('./internal').PreactElement} parent DOM node to render vnode tree into * @param {() => void} [callback] Optional callback that will be called after rendering * @returns {import('./internal').Component | null} The root component reference or null */ export function render(vnode, parent, callback) { // React destroys any existing DOM nodes, see #1727 // ...but only on the first render, see #1828 if (parent._children == null) { parent.textContent = ''; } preactRender(vnode, parent); if (typeof callback == 'function') callback(); return vnode ? vnode._component : null; } export function hydrate(vnode, parent, callback) { preactHydrate(vnode, parent); if (typeof callback == 'function') callback(); return vnode ? vnode._component : null; } let oldEventHook = options.event; options.event = e => { if (oldEventHook) e = oldEventHook(e); e.persist = empty; e.isPropagationStopped = isPropagationStopped; e.isDefaultPrevented = isDefaultPrevented; return (e.nativeEvent = e); }; function empty() {} function isPropagationStopped() { return this.cancelBubble; } function isDefaultPrevented() { return this.defaultPrevented; } let classNameDescriptor = { configurable: true, get() { return this.class; } }; let oldVNodeHook = options.vnode; options.vnode = vnode => { let type = vnode.type; let props = vnode.props; let normalizedProps = props; // only normalize props on Element nodes if (typeof type === 'string') { const nonCustomElement = type.indexOf('-') === -1; normalizedProps = {}; for (let i in props) { let value = props[i]; if (IS_DOM && i === 'children' && type === 'noscript') { // Emulate React's behavior of not rendering the contents of noscript tags on the client. continue; } else if (i === 'value' && 'defaultValue' in props && value == null) { // Skip applying value if it is null/undefined and we already set // a default value continue; } else if ( i === 'defaultValue' && 'value' in props && props.value == null ) { // `defaultValue` is treated as a fallback `value` when a value prop is present but null/undefined. // `defaultValue` for Elements with no value prop is the same as the DOM defaultValue property. i = 'value'; } else if (i === 'download' && value === true) { // Calling `setAttribute` with a truthy value will lead to it being // passed as a stringified value, e.g. `download="true"`. React // converts it to an empty string instead, otherwise the attribute // value will be used as the file name and the file will be called // "true" upon downloading it. value = ''; } else if (/ondoubleclick/i.test(i)) { i = 'ondblclick'; } else if ( /^onchange(textarea|input)/i.test(i + type) && !onChangeInputType(props.type) ) { i = 'oninput'; } else if (/^onfocus$/i.test(i)) { i = 'onfocusin'; } else if (/^onblur$/i.test(i)) { i = 'onfocusout'; } else if (/^on(Ani|Tra|Tou|BeforeInp|Compo)/.test(i)) { i = i.toLowerCase(); } else if (nonCustomElement && CAMEL_PROPS.test(i)) { i = i.replace(/[A-Z0-9]/, '-$&').toLowerCase(); } else if (value === null) { value = undefined; } // Add support for onInput and onChange, see #3561 // if we have an oninput prop already change it to oninputCapture if (/^oninput/i.test(i)) { i = i.toLowerCase(); if (normalizedProps[i]) { i = 'oninputCapture'; } } normalizedProps[i] = value; } // Add support for array select values: