├── .babelrc ├── .editorconfig ├── .gitignore ├── .npmignore ├── .prettierrc ├── .travis.yml ├── LICENSE ├── README.md ├── _config.yml ├── example ├── package.json ├── public │ ├── index.html │ └── manifest.json ├── src │ ├── App.js │ ├── components │ │ ├── ForLoop.js │ │ ├── Helper │ │ │ └── MySomeComponent.js │ │ ├── IfElse.js │ │ └── SwitchCase.js │ ├── index.css │ └── index.js └── yarn.lock ├── package.json ├── rollup.config.js ├── src ├── __test__ │ └── .gitkeep ├── index.js └── lib │ ├── Elements │ ├── DOMElements.js │ ├── Fragment.js │ └── index.js │ ├── helperFns │ ├── doForLoopOperation.js │ ├── doIfElseOperation.js │ ├── doSwitchCaseOperation.js │ └── index.js │ ├── utils │ ├── html-tags.js │ └── index.js │ └── withTemplating.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false 5 | }], 6 | "stage-0", 7 | "react" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # See https://help.github.com/ignore-files/ for more about ignoring files. 3 | 4 | # dependencies 5 | node_modules 6 | 7 | # builds 8 | build 9 | dist 10 | .rpt2_cache 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | 24 | *.tgz -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | example 3 | node_modules 4 | *.tgz -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": false, 10 | "fluid": false 11 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 10.15.1 3 | 4 | cache: 5 | directories: 6 | - node_modules 7 | 8 | install: 9 | - npm install 10 | 11 | script: 12 | - npm run build 13 | 14 | deploy: 15 | provider: npm 16 | skip_cleanup: true 17 | email: ritwickdey@outlook.com 18 | api_key: $NPM_TOKEN 19 | name: Ritwick Dey 20 | on: 21 | tags: true -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ritwick Dey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Jsx Templating 2 | 3 | > React Jsx Templating is a simple library, inspired by Angular :) 4 | 5 | [![NPM](https://img.shields.io/npm/v/react-jsx-templating.svg)](https://www.npmjs.com/package/react-jsx-templating) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) [![Build Status](https://travis-ci.com/ritwickdey/react-jsx-templating.svg?branch=master)](https://travis-ci.com/ritwickdey/react-jsx-templating) 6 | 7 | `React Jsx Templating` will give you templating syntax like Angular `*ngIf` 8 | 9 | **_Live Example: [Open in Codesandbox](https://codesandbox.io/s/j312l1m2x9)_** 10 | 11 | ## Install 12 | 13 | ```bash 14 | npm install --save react-jsx-templating 15 | ``` 16 | 17 | ## Usage 18 | 19 | - **Use HOC** 20 | 21 | ```jsx 22 | import withTemplating from 'react-jsx-templating'; //Note: default import 23 | class MyComponent extends Component { 24 | render() 25 | return "foo"; 26 | } 27 | } 28 | 29 | export default withTemplating(MyComponent); 30 | ``` 31 | 32 | - Use Wrapper HTML tags 33 | 34 | ```jsx 35 | // Note: named import. There are total 118 Elements 36 | import { Fragment, Div, P, Button, Br, Span } from 'react-jsx-templating'; 37 | ``` 38 | 39 | ## Syntax 40 | 41 | - If-else 42 | 43 | ```diff 44 | let isEnglish = false; 45 | + 46 | ``` 47 | 48 | - switch-case 49 | 50 | ```diff 51 | let testValue = 'c'; 52 | +
53 | +
A
54 | +
B
55 | +
C
56 | +
No Match
57 | +
58 | ``` 59 | 60 | - for-loop 61 | 62 | ```diff 63 | let people = [{id: 1, name:'Ritwick'}, {id:2, name:'Akash'}] 64 | +
person.id}> 65 | + {person =>
{person.name}
} 66 | +
67 | ``` 68 | 69 | ## Examples 70 | 71 | - **Switch-Case Templating** 72 | 73 | ```jsx 74 | import React, { useState } from 'react'; 75 | import { Div } from 'react-jsx-templating'; 76 | 77 | function ExampleSwitchCase() { 78 | const [animal, setAnimal] = useState(''); 79 | return ( 80 |
81 | setAnimal(e.target.value)} 85 | /> 86 |
Please type!!
}> 87 |
88 |
woof-woof 🐕
89 |
meows meows 🐈
90 |
Ops!! No Match!
91 |
92 |
93 |
94 | ); 95 | } 96 | ``` 97 | 98 | - **If-Else Templating** 99 | 100 | ```jsx 101 | import React, { Component } from 'react'; 102 | import { Div, Fragment, Button, Br } from 'react-jsx-templating'; 103 | import { EnglishNewsPaper, SpanishNewsPaper } from './Components'; 104 | 105 | class ExampleIfElse extends Component { 106 | state = { 107 | isEngLang: true 108 | }; 109 | 110 | toogleLanguage = () => { 111 | this.setState(oldState => ({ isEngLang: !oldState.isEngLang })); 112 | }; 113 | 114 | render() { 115 | const { isEngLang } = this.state; 116 | return ( 117 |
118 | Hola!}> 119 | Hello! 120 | 121 | 122 | 123 |
124 | ); 125 | } 126 | } 127 | ``` 128 | 129 | - **For Loop** 130 | 131 | ```jsx 132 | import React from 'react'; 133 | import { Div } from 'react-jsx-templating'; 134 | import { Article } from './Components'; 135 | 136 | function ExampleForLoop() { 137 | return ( 138 |
id}> 139 | {article =>
} 140 |
141 | ); 142 | } 143 | ``` 144 | 145 | ## What's next ? 146 | 147 | - ~~Switch Case~~ (added) 148 | - ~~Loop~~ (added) 149 | - ~~Fragment~~ (added) 150 | 151 | ## License 152 | 153 | MIT © [ritwickdey](https://github.com/ritwickdey) 154 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | 3 | repository: ritwickdey/react-jsx-templating 4 | 5 | gems: 6 | - jekyll-seo-tag 7 | 8 | title: React Jsx Templating 9 | description : React Jsx Templating will give you templating syntax like Angular *ngIf 10 | author : Ritwick Dey 11 | twitter:username: Dey_Ritwick 12 | social: 13 | name: React Jsx Templating 14 | links: 15 | - https://github.com/ritwickdey/react-jsx-templating 16 | lang: en_IN 17 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-jsx-templating-example", 3 | "homepage": "https://ritwickdey.github.io/react-jsx-templating", 4 | "version": "0.0.0", 5 | "license": "MIT", 6 | "private": true, 7 | "dependencies": { 8 | "prop-types": "^15.6.2", 9 | "react": "^16.4.1", 10 | "react-dom": "^16.4.1", 11 | "react-jsx-templating": "link:..", 12 | "react-scripts": "^2.1.8" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test --env=jsdom", 18 | "eject": "react-scripts eject" 19 | }, 20 | "browserslist": [ 21 | ">0.2%", 22 | "not dead", 23 | "not ie <= 11", 24 | "not op_mini all" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | react-jsx-templating 11 | 12 | 13 | 14 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /example/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "react-jsx-templating", 3 | "name": "react-jsx-templating", 4 | "start_url": "./index.html", 5 | "display": "standalone", 6 | "theme_color": "#000000", 7 | "background_color": "#ffffff" 8 | } 9 | -------------------------------------------------------------------------------- /example/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import IfElse from './components/IfElse'; 3 | import SwitchCase from './components/SwitchCase'; 4 | import ForLoop from './components/ForLoop'; 5 | 6 | function App() { 7 | return ( 8 |
9 |
10 |

If-Else Example

11 | 12 |
13 |
14 | 15 |
16 |

Switch-Case Example

17 | 18 |
19 |
20 | 21 |
22 |

For Loop Example

23 | 24 |
25 |
26 | ); 27 | } 28 | 29 | export default App; 30 | -------------------------------------------------------------------------------- /example/src/components/ForLoop.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Div } from 'react-jsx-templating'; 4 | 5 | const Articles = [ 6 | { 7 | id: 1, 8 | head: 'Article 1', 9 | body: 'This is body of Article 1' 10 | }, 11 | { 12 | id: 2, 13 | head: 'Article 2', 14 | body: 'This is body of Article 2' 15 | }, 16 | { 17 | id: 3, 18 | head: 'Article 3', 19 | body: 'This is body of Article 3' 20 | } 21 | ]; 22 | 23 | function Article(props) { 24 | const { article } = props; 25 | return ( 26 |
27 |
{article.head}
28 |

{article.body}

29 |
30 | ); 31 | } 32 | 33 | function ForLoop() { 34 | return ( 35 |
article.id}> 36 | {article =>
} 37 |
38 | ); 39 | } 40 | 41 | export default ForLoop; 42 | -------------------------------------------------------------------------------- /example/src/components/Helper/MySomeComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import withTemplating from 'react-jsx-templating'; 3 | 4 | function MySomeComponent(props) { 5 | return

{props.title || props.children}

; 6 | } 7 | 8 | export default withTemplating(MySomeComponent); 9 | -------------------------------------------------------------------------------- /example/src/components/IfElse.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import { Div, P, Button, Br, Fragment } from 'react-jsx-templating'; 4 | import MySomeComponent from './Helper/MySomeComponent'; 5 | 6 | function IfElse() { 7 | const [show, setShow] = useState(true); 8 | const [showToggler, setShowToggler] = useState(true); 9 | return ( 10 |
11 |
Hola ^_^
}> 12 | Hello! :D 13 |
14 |

Yay! It works

}> 15 | Welcome here 16 |
17 | 22 |
23 | 26 |
27 | ); 28 | } 29 | 30 | export default IfElse; 31 | -------------------------------------------------------------------------------- /example/src/components/SwitchCase.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Div } from 'react-jsx-templating'; 3 | 4 | function SwitchCase() { 5 | const [animal, setAnimal] = useState(''); 6 | return ( 7 |
8 | setAnimal(e.target.value)} 12 | /> 13 |
14 |
15 |
Please type!!
}> 16 |
17 |
woof-woof 🐕
18 |
meows meows 🐈
19 |
Ops!! No Match with `{`${animal}`} `
20 |
21 |
22 |
23 | ); 24 | } 25 | 26 | export default SwitchCase; 27 | -------------------------------------------------------------------------------- /example/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 18px; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | 7 | .App { 8 | margin-top: 18px; 9 | font-family: sans-serif; 10 | /* text-align: center; */ 11 | } 12 | -------------------------------------------------------------------------------- /example/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | 4 | import './index.css' 5 | import App from './App' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-jsx-templating", 3 | "version": "0.0.7", 4 | "description": "React Jsx Templating, inspired by Angular :)", 5 | "author": "ritwickdey", 6 | "license": "MIT", 7 | "repository": "ritwickdey/react-jsx-templating", 8 | "main": "dist/index.js", 9 | "module": "dist/index.es.js", 10 | "jsnext:main": "dist/index.es.js", 11 | "engines": { 12 | "node": ">=8", 13 | "npm": ">=5" 14 | }, 15 | "scripts": { 16 | "test": "cross-env CI=1 react-scripts test --env=jsdom", 17 | "test:watch": "react-scripts test --env=jsdom", 18 | "build": "rollup -c", 19 | "start": "rollup -c -w", 20 | "prepare": "yarn run build", 21 | "predeploy": "cd example && yarn install && yarn run build", 22 | "deploy": "gh-pages -d example/build" 23 | }, 24 | "peerDependencies": { 25 | "prop-types": "^15.5.4", 26 | "react": "^15.0.0 || ^16.0.0", 27 | "react-dom": "^15.0.0 || ^16.0.0" 28 | }, 29 | "devDependencies": { 30 | "@svgr/rollup": "^2.4.1", 31 | "babel-core": "^6.26.3", 32 | "babel-eslint": "^8.2.5", 33 | "babel-plugin-external-helpers": "^6.22.0", 34 | "babel-preset-env": "^1.7.0", 35 | "babel-preset-react": "^6.24.1", 36 | "babel-preset-stage-0": "^6.24.1", 37 | "cross-env": "^5.1.4", 38 | "eslint": "^5.0.1", 39 | "eslint-config-standard": "^11.0.0", 40 | "eslint-config-standard-react": "^6.0.0", 41 | "eslint-plugin-import": "^2.13.0", 42 | "eslint-plugin-node": "^7.0.1", 43 | "eslint-plugin-promise": "^4.0.0", 44 | "eslint-plugin-react": "^7.10.0", 45 | "eslint-plugin-standard": "^3.1.0", 46 | "gh-pages": "^1.2.0", 47 | "react": "^16.4.1", 48 | "react-dom": "^16.4.1", 49 | "react-scripts": "^1.1.4", 50 | "rollup": "^0.64.1", 51 | "rollup-plugin-babel": "^3.0.7", 52 | "rollup-plugin-commonjs": "^9.1.3", 53 | "rollup-plugin-node-resolve": "^3.3.0", 54 | "rollup-plugin-peer-deps-external": "^2.2.0", 55 | "rollup-plugin-postcss": "^1.6.2", 56 | "rollup-plugin-url": "^1.4.0" 57 | }, 58 | "files": [ 59 | "dist" 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel' 2 | import commonjs from 'rollup-plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import postcss from 'rollup-plugin-postcss' 5 | import resolve from 'rollup-plugin-node-resolve' 6 | import url from 'rollup-plugin-url' 7 | import svgr from '@svgr/rollup' 8 | 9 | import pkg from './package.json' 10 | 11 | export default { 12 | input: 'src/index.js', 13 | output: [ 14 | { 15 | file: pkg.main, 16 | format: 'cjs', 17 | sourcemap: true, 18 | exports: 'named', 19 | }, 20 | { 21 | file: pkg.module, 22 | format: 'es', 23 | sourcemap: true, 24 | exports: 'named', 25 | } 26 | ], 27 | plugins: [ 28 | external(), 29 | postcss({ 30 | modules: true 31 | }), 32 | url(), 33 | svgr(), 34 | babel({ 35 | exclude: 'node_modules/**', 36 | plugins: [ 'external-helpers' ] 37 | }), 38 | resolve(), 39 | commonjs() 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /src/__test__/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ritwickdey/react-jsx-templating/72329a16670043b75ecdea5ce01c54ef6bd47283/src/__test__/.gitkeep -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import withTemplating from './lib/withTemplating'; 2 | export * from './lib/Elements'; 3 | 4 | export default withTemplating; 5 | -------------------------------------------------------------------------------- /src/lib/Elements/DOMElements.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { HTML_TAGS } from '../utils/html-tags'; 3 | import withTemplating from '../withTemplating'; 4 | 5 | const DOMElements = {}; 6 | 7 | HTML_TAGS.forEach(tag => { 8 | const Fn = props => { 9 | const { $if, $switch, $case, $default, $else, $key, $for, ...rest } = props; 10 | return React.createElement(tag, rest); 11 | }; 12 | const Tag = capitalCase(tag); 13 | Fn.displayName = Tag; 14 | DOMElements[Tag] = withTemplating(Fn); 15 | }); 16 | 17 | function capitalCase(str) { 18 | return str.charAt(0).toUpperCase() + str.slice(1); 19 | } 20 | 21 | export default DOMElements; 22 | -------------------------------------------------------------------------------- /src/lib/Elements/Fragment.js: -------------------------------------------------------------------------------- 1 | import withTemplating from "../withTemplating"; 2 | 3 | const Fragment = ({ children }) => { 4 | return children; 5 | }; 6 | 7 | export default withTemplating(Fragment); 8 | -------------------------------------------------------------------------------- /src/lib/Elements/index.js: -------------------------------------------------------------------------------- 1 | import DOMElements from './DOMElements'; 2 | export { default as Fragment } from './Fragment'; 3 | 4 | export const { 5 | A, 6 | Abbr, 7 | Address, 8 | Area, 9 | Article, 10 | Aside, 11 | Audio, 12 | B, 13 | Base, 14 | Bdi, 15 | Bdo, 16 | Blockquote, 17 | Body, 18 | Br, 19 | Button, 20 | Canvas, 21 | Caption, 22 | Cite, 23 | Code, 24 | Col, 25 | Colgroup, 26 | Data, 27 | Datalist, 28 | Dd, 29 | Del, 30 | Details, 31 | Dfn, 32 | Dialog, 33 | Div, 34 | Dl, 35 | Dt, 36 | Em, 37 | Embed, 38 | Fieldset, 39 | Figcaption, 40 | Figure, 41 | Footer, 42 | Form, 43 | H1, 44 | H2, 45 | H3, 46 | H4, 47 | H5, 48 | H6, 49 | Head, 50 | Header, 51 | Hgroup, 52 | Hr, 53 | Html, 54 | I, 55 | Iframe, 56 | Img, 57 | Input, 58 | Ins, 59 | Kbd, 60 | Keygen, 61 | Label, 62 | Legend, 63 | Li, 64 | Link, 65 | Main, 66 | Map, 67 | Mark, 68 | Math, 69 | Menu, 70 | Menuitem, 71 | Meta, 72 | Meter, 73 | Nav, 74 | Noscript, 75 | Object, 76 | Ol, 77 | Optgroup, 78 | Option, 79 | Output, 80 | P, 81 | Param, 82 | Picture, 83 | Pre, 84 | Progress, 85 | Q, 86 | Rb, 87 | Rp, 88 | Rt, 89 | Rtc, 90 | Ruby, 91 | S, 92 | Samp, 93 | Script, 94 | Section, 95 | Select, 96 | Slot, 97 | Small, 98 | Source, 99 | Span, 100 | Strong, 101 | Style, 102 | Sub, 103 | Summary, 104 | Sup, 105 | Svg, 106 | Table, 107 | Tbody, 108 | Td, 109 | Template, 110 | Textarea, 111 | Tfoot, 112 | Th, 113 | Thead, 114 | Time, 115 | Title, 116 | Tr, 117 | Track, 118 | U, 119 | Ul, 120 | Var, 121 | Video, 122 | wbr 123 | } = DOMElements; 124 | -------------------------------------------------------------------------------- /src/lib/helperFns/doForLoopOperation.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function doForLoopOperation(props, Component) { 4 | const { $for: items, $key: keyFn, ...restProps } = props; 5 | 6 | const children = items.map((item, index, array) => { 7 | const child = props.children(item, index, array); 8 | const key = keyFn ? keyFn(item) : child.key; 9 | return React.cloneElement(child, { key, ...child.props }); 10 | }); 11 | 12 | return React.createElement(Component, restProps, children); 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/helperFns/doIfElseOperation.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { isTrusy } from '../utils'; 3 | 4 | export function doIfElseOperation(props, Component) { 5 | const { $if: condition, $else: elseBlock, ...restProps } = props; 6 | 7 | if (isTrusy(condition)) { 8 | return React.createElement(Component, restProps); 9 | } 10 | 11 | if (typeof elseBlock === 'function') { 12 | return React.createElement(elseBlock, restProps); 13 | } 14 | return elseBlock || null; 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/helperFns/doSwitchCaseOperation.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { isTrusy, isUndefined } from '../utils'; 3 | 4 | export function doSwitchCaseOperation(props, Component) { 5 | const { $switch: switchValue, children, ...restProps } = props; 6 | 7 | let targetChildNode = null; 8 | 9 | const totalNoOfNode = React.Children.count(children); 10 | for (let i = 0; i < totalNoOfNode; i++) { 11 | const child = totalNoOfNode === 1 ? children : children[i]; 12 | 13 | if (!React.isValidElement(child)) continue; 14 | 15 | const { $case: caseValue, $default: defaultValue } = child.props; 16 | 17 | if (isUndefined(caseValue) && isUndefined(defaultValue)) { 18 | throw new Error('$case or $default is missing', child); 19 | } 20 | 21 | if (isTrusy(defaultValue) || caseValue === switchValue) { 22 | targetChildNode = child; 23 | break; 24 | } 25 | } 26 | 27 | if (targetChildNode) { 28 | const { $case, $default, ...childProps } = targetChildNode.props; 29 | targetChildNode = React.cloneElement(targetChildNode, childProps); 30 | } 31 | 32 | return React.createElement(Component, restProps, targetChildNode); 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/helperFns/index.js: -------------------------------------------------------------------------------- 1 | export { doIfElseOperation } from './doIfElseOperation'; 2 | export { doSwitchCaseOperation } from './doSwitchCaseOperation'; 3 | export { doForLoopOperation } from './doForLoopOperation'; 4 | -------------------------------------------------------------------------------- /src/lib/utils/html-tags.js: -------------------------------------------------------------------------------- 1 | // Thanks @sindresorhus 2 | // https://raw.githubusercontent.com/sindresorhus/html-tags/master/html-tags.json 3 | 4 | export const HTML_TAGS = [ 5 | 'a', 6 | 'abbr', 7 | 'address', 8 | 'area', 9 | 'article', 10 | 'aside', 11 | 'audio', 12 | 'b', 13 | 'base', 14 | 'bdi', 15 | 'bdo', 16 | 'blockquote', 17 | 'body', 18 | 'br', 19 | 'button', 20 | 'canvas', 21 | 'caption', 22 | 'cite', 23 | 'code', 24 | 'col', 25 | 'colgroup', 26 | 'data', 27 | 'datalist', 28 | 'dd', 29 | 'del', 30 | 'details', 31 | 'dfn', 32 | 'dialog', 33 | 'div', 34 | 'dl', 35 | 'dt', 36 | 'em', 37 | 'embed', 38 | 'fieldset', 39 | 'figcaption', 40 | 'figure', 41 | 'footer', 42 | 'form', 43 | 'h1', 44 | 'h2', 45 | 'h3', 46 | 'h4', 47 | 'h5', 48 | 'h6', 49 | 'head', 50 | 'header', 51 | 'hgroup', 52 | 'hr', 53 | 'html', 54 | 'i', 55 | 'iframe', 56 | 'img', 57 | 'input', 58 | 'ins', 59 | 'kbd', 60 | 'keygen', 61 | 'label', 62 | 'legend', 63 | 'li', 64 | 'link', 65 | 'main', 66 | 'map', 67 | 'mark', 68 | 'math', 69 | 'menu', 70 | 'menuitem', 71 | 'meta', 72 | 'meter', 73 | 'nav', 74 | 'noscript', 75 | 'object', 76 | 'ol', 77 | 'optgroup', 78 | 'option', 79 | 'output', 80 | 'p', 81 | 'param', 82 | 'picture', 83 | 'pre', 84 | 'progress', 85 | 'q', 86 | 'rb', 87 | 'rp', 88 | 'rt', 89 | 'rtc', 90 | 'ruby', 91 | 's', 92 | 'samp', 93 | 'script', 94 | 'section', 95 | 'select', 96 | 'slot', 97 | 'small', 98 | 'source', 99 | 'span', 100 | 'strong', 101 | 'style', 102 | 'sub', 103 | 'summary', 104 | 'sup', 105 | 'svg', 106 | 'table', 107 | 'tbody', 108 | 'td', 109 | 'template', 110 | 'textarea', 111 | 'tfoot', 112 | 'th', 113 | 'thead', 114 | 'time', 115 | 'title', 116 | 'tr', 117 | 'track', 118 | 'u', 119 | 'ul', 120 | 'var', 121 | 'video', 122 | 'wbr' 123 | ]; 124 | -------------------------------------------------------------------------------- /src/lib/utils/index.js: -------------------------------------------------------------------------------- 1 | export function isTrusy(obj) { 2 | return !!obj; 3 | } 4 | 5 | export function isUndefined(obj) { 6 | return typeof obj === 'undefined'; 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/withTemplating.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { isUndefined } from './utils'; 3 | import { doIfElseOperation, doForLoopOperation, doSwitchCaseOperation } from './helperFns'; 4 | 5 | const withTemplating = Component => { 6 | const Templating = props => { 7 | const { $if, $switch, $for, ...restProps } = props; 8 | 9 | if (!isUndefined($if)) { 10 | return doIfElseOperation(props, Component); 11 | } 12 | 13 | if (!isUndefined($switch)) { 14 | return doSwitchCaseOperation(props, Component); 15 | } 16 | 17 | if (!isUndefined($for)) { 18 | return doForLoopOperation(props, Component); 19 | } 20 | 21 | return React.createElement(Component, restProps); 22 | }; 23 | 24 | Templating.displayName = `withTemplating(${Component.displayName || Component.name})`; 25 | return Templating; 26 | }; 27 | 28 | export default withTemplating; 29 | --------------------------------------------------------------------------------