UNPKG

clean-react-directives

Version:
263 lines (243 loc) 9.35 kB
/** * MIT License * * Copyright (c) 2019 Dejan Sandic - Deyo * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import React, { createElement, Fragment, Children } from 'react'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var classnames = createCommonjsModule(function (module) { /*! Copyright (c) 2017 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ /* global define */ (function () { var hasOwn = {}.hasOwnProperty; function classNames () { var classes = []; for (var i = 0; i < arguments.length; i++) { var arg = arguments[i]; if (!arg) continue; var argType = typeof arg; if (argType === 'string' || argType === 'number') { classes.push(arg); } else if (Array.isArray(arg) && arg.length) { var inner = classNames.apply(null, arg); if (inner) { classes.push(inner); } } else if (argType === 'object') { for (var key in arg) { if (hasOwn.call(arg, key) && arg[key]) { classes.push(key); } } } } return classes.join(' '); } if (module.exports) { classNames.default = classNames; module.exports = classNames; } else { window.classNames = classNames; } }()); }); function CleanReact(_a) { var children = _a.children, deep = _a.deep; return createElement(Fragment, {}, applyDirectives(children, deep)); } /** * Main function */ function applyDirectives(children, deep) { var last = false; var lastCon = ''; return Children.map(children, function (child) { if (!React.isValidElement(child)) return child; /** * When checking children, we want to stop when we run into CleanReact * component because it does its directive checking */ if (child.type === CleanReact) return child; var props = __assign({}, child.props); var children = props.children; var cloningRequired = false; /** * If deep flag is true check all children of the component */ if (deep && children) { children = applyDirectives(children, deep); cloningRequired = true; } /** * Handle r-html directive * */ if ('r-html' in props) { var __html = props['r-html']; if (typeof __html !== 'string') throw new Error('r-html expects a string as its value.'); props = __assign({}, props, { dangerouslySetInnerHTML: { __html: __html } }); cloningRequired = true; } /** * Handle r-class directive * */ if ('r-class' in props) { var rclass = props['r-class']; var className = props.className ? classnames(props.className, rclass) : classnames(rclass); props = __assign({}, props, { className: className }); cloningRequired = true; } /** * Handle r-show directive * */ if ('r-show' in props && !props['r-show']) { var style = child.props.style || {}; props = __assign({}, props, { style: __assign({}, style, { display: 'none' }) }); cloningRequired = true; } /** * Handle r-if r-else-if and r-else directives * */ var rif = 'r-if' in props && 'r-if'; var relseif = 'r-else-if' in props && 'r-else-if'; var relse = 'r-else' in props && 'r-else'; if ((rif && relseif) || (rif && relse) || (relseif && relse)) { throw new Error('You cannot combine r-if, r-else-if and r-else on the same component'); } if (rif) { lastCon = rif; last = props[rif]; if (!last) return null; } if (relseif || relse) { if (lastCon !== 'r-if' && lastCon !== 'r-else-if') { lastCon = relseif || relse; throw new Error(lastCon + " can only be placed after r-if or r-else-if"); } if ((!relse && !props[relseif]) || last) return null; last = true; } /** * If cloningRequired is set to true, meaning the child component has been modified, * strip down library-related props and clone the child using createElement function * * We use createElement and not cloneElement because cloneElement keeps the original * props of the child component */ if (cloningRequired) { libProps.forEach(function (prop) { return delete props[prop]; }); child = createElement(child.type, __assign({}, props), children); } return child; }); } /** * Library related props */ var libProps = ['r-if', 'r-else-if', 'r-else', 'r-show', 'r-class', 'r-html']; /** * In the create-react-app in DEVELOPMENT mode, when passing a prop in the format `prop-name` * the react component, React expects the value of that prop to be a string * * Since we are passing non-boolean values to the directives r-if, r-else-if, r-else, * and r-show, React will throw an error ( Received `true` for a non-boolean attribute `r-if` ) * * For this reason, we need to ignore the thrown error message if it is related to our library. * This is not ass clean as we would like it to be, but unfortunately, it is * the only solution we have at this point */ if (process && process.env && !process.env.rif && process.env.NODE_ENV !== 'production') { var error_1 = console.error; console.error = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } function includesString(text, string) { if (typeof text !== 'string') return false; return text.includes(string); } var nonBoolean = args.some(function (arg) { return includesString(arg, 'non-boolean'); }); var libRelated = args.some(function (arg) { return libProps.some(function (prop) { return includesString(arg, prop); }); }); if (!nonBoolean || !libRelated) error_1.apply(void 0, __spread(args)); }; process.env.rif = 'true'; } export default CleanReact;