├── .gitignore ├── README.md ├── dist ├── README.md ├── index.js └── package.json ├── package-lock.json ├── package.json ├── src ├── Cases.tsx ├── Conditional.tsx ├── Else.tsx ├── If.tsx ├── Repeat.tsx ├── index.tsx ├── interfaces │ ├── Component.ts │ └── Object.ts └── js │ ├── Cases.jsx │ ├── Conditional.jsx │ ├── Else.jsx │ ├── If.jsx │ ├── Repeat.jsx │ └── index.jsx └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | .cache/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Templates Components 2 | 3 | These components are not trying to re-invent the wheel or are anything new and spectacular. The main reason to use them 4 | is because you should strive to write your React components as humanly readable as possible. 5 | 6 | React components tend to become un-readable with two main issues affecting readability: 7 | 8 | - Multiple separate render functions which return JSX in the main render() function 9 | - One large render() function with multiple .map() functions or conditional operators and use of JavaScript in general 10 | 11 | In order to reduce boilerplate this library is introducing components that act like a DSL template language but in the 12 | form of React components. 13 | 14 | ## Suggestion 15 | 16 | React has an enormous ecosystem of libraries and frameworks. However, after many years of React and Vue projects, I realize 17 | that there are many "moving parts" to an application. Too many packages from various developers, some of which are no 18 | longer maintained. Even this package, might feel unmaintained, especially after so many months of inactivity. Although, this 19 | package is more or less feature complete, I strongly advise if you need to use it, then download the source code, instead of the 20 | npm package. The source code itself is just plain React code with nothing fancy. The only possible issue is TypeScript which might 21 | not be in your current tech-stack. However, you will find the JavaScript version at github under "src/js". 22 | 23 | Finally, personally I try not to install too many packages. If I have to, then I tend to choose based on whether the package 24 | is being developed by a corporate entity, or the community behind the package has enormous and consistent support. 25 | 26 | ## v2.2.6 Information 27 | 28 | The Repeat component is proving more of a hassle than it should. I have marked it as experimental since in order for the component to work 29 | it has to tamper the internals of a React component. Since this is quite volatile and bad practice I suggest using the usual .map() for iteration. 30 | I apologize for any inconvenience. 31 | 32 | ## v2.2.0 Breaking changes 33 | 34 | - Repeat component has been re-written in order to sort out issues with the key prop 35 | - Repeat useRandomKeyForIteration has been removed please use setKey as shown in the docs. 36 | 37 | **I apologize for any inconvenience but Repeat proved to be more complex than it should. Please READ CAREFULLY the updated 38 | examples in the documentation below.** 39 | 40 | ## v2.1.1 Release Information 41 | 42 | - Added 'useFragment' prop for all components, in case you do not want that extra HTML element added to the DOM. 43 | 44 | **Please note that in case you set 'useFragment' to true the 'tag' and/or 'className' props will not have any effect on the component. Therefore if you have styled 45 | the HTML element used in the 'tag' prop, you will get weird styling results.** 46 | 47 | ## v2.0.0 Important information 48 | 49 | When I started building this library I used JavaScript. However, I am the first to admit that TypeScript is starting 50 | to grow in me. This update brings the following changes: 51 | 52 | - TypeScript re-write 53 | - Functional components in order to be ready for the class based deprecation React will eventually bring upon us 54 | - ParcelJS for building from source. I cannot stress how much faster and easier it was and the minified size in also smaller 55 | - Repeat component has made some "hasty" assumptions about the key prop. New version and props are found in the documentation below 56 | 57 | ### Thoughts 58 | 59 | I would like to apologize to anyone that is using this package and hope that any breaking changes are easy and fast to refactor. 60 | As always, please do not hesitate to contact me in case you find a bug. 61 | 62 | ## Getting Started 63 | 64 | ### Prerequisites if building from source 65 | 66 | You should have an installation of NodeJS any version and npm or yarn 67 | 68 | More over React and PropTypes are MANDATORY for the components to work. 69 | 70 | If you want to build from source by including them in your project, please take a look at my tsconfig.json since v2.0.0 is using TypeScript. 71 | 72 | These components are using React features that were present from the early stages of the library therefore you should not 73 | encounter any issues. Nevertheless, I do suggest a version of React 15+ 74 | 75 | ### Installing 76 | 77 | Npm: 78 | 79 | ``` 80 | npm install --save react-templates-components 81 | ``` 82 | 83 | Yarn: 84 | 85 | ``` 86 | yarn add react-templates-components 87 | ``` 88 | 89 | ## Usage 90 | 91 | #### Global props 92 | 93 | **_The following props are available for all components:_** 94 | 95 | - **tag** 96 | - type: **string** 97 | - default: **'div'** 98 | - description: **You can use here any tag or React element to enclose the children in the same time supporting semantic web** 99 | - **className** 100 | - type: **string** 101 | - default: **" "** 102 | - description: **The usual value found in all React components that translate to class** 103 | - **useFragment** 104 | - type: **boolean** 105 | - default: **false** 106 | - description: **The component will use the 'React.Fragment' component. 107 | _Please note that in case you set 'useFragment' to true the 'tag' and/or 'className' props will not have any effect on the component. 108 | Therefore, if you have styled the HTML element used in the 'tag' prop, you will get weird styling results._** 109 | 110 | #### Components 111 | 112 | The following components props are available 113 | 114 | #### `` Component 115 | 116 | ```jsx 117 | 118 |
IF
119 |
120 | ``` 121 | 122 | Available props (see global props as well): 123 | 124 | - **show** 125 | 126 | - type: **boolean** 127 | - default: **true** 128 | - description: **The value which will show/hide the elements you enclose** 129 | 130 | #### `` Component 131 | 132 | ```jsx 133 | 134 |
ELSE
135 |
136 | ``` 137 | 138 | Available props (see global props as well): 139 | 140 | - **show** 141 | 142 | - type: **boolean** 143 | - default: **true** 144 | - description: **The value which will show/hide the elements you enclose** 145 | 146 | > \*\*It is important to understand that you could do the same thing with 2 If components but by using If and Else you 147 | 148 | are covering semantics as well. It is also easier to read** 149 | 150 | #### `` Component 151 | 152 | ```jsx 153 | 158 | 159 |
HI CONDITIONAL IF
160 |
161 | 162 |
HI CONDITIONAL ELSE
163 |
164 |
165 | ``` 166 | 167 | Available props (see global props as well): 168 | 169 | - **condition** 170 | - type: **boolean** 171 | - default: **true** 172 | - description: **depending on the 'condition' prop, it will toggle between if and else therefore the prop 'show' in if/else is 173 | redundant** 174 | 175 | #### `/` Component 176 | 177 | ```jsx 178 | 179 | Hi Jack 180 | Hi John 181 | Hi Jim 182 | Hi Jason 183 | Default 184 | 185 | ``` 186 | 187 | Available props for `` (see global props as well): 188 | 189 | - **expression** 190 | 191 | - type: **any** 192 | - description: **The expression to evaluate** 193 | 194 | Available props for `` (see global props as well): 195 | 196 | - **expressionValue** 197 | - type: **any** 198 | - description: **The expressionValue to evaluate with the expression in the `` component** 199 | 200 | #### `` Component **(_EXPERIMENTAL use .map() instead_)** 201 | 202 | ```jsx 203 | /* No setKey default index will be used */ 204 | 207 |
208 | {'@iterator'} 209 |
{'@iterator'}
210 |
211 |
HI REPEAT #2
212 | HI REPEAT #3 {'@iterator'} 213 |
214 | 215 | /* Array of objects */ 216 | 220 |
221 | {'@iterator.name'} 222 |
{'@iterator.id'}
223 |
224 |
HI REPEAT #2
225 | HI REPEAT #3 {'@iterator.name'} 226 |
227 | 228 | /* Array of primitives */ 229 | 233 |
234 | {'@iterator'} 235 |
{'@iterator'}
236 |
237 |
HI REPEAT #2
238 | HI REPEAT #3 {'@iterator'} 239 |
240 | ``` 241 | 242 | Available props (see global props as well): 243 | 244 | - **iterator** 245 | - type: **Array** || **Number** 246 | - default: **none this prop _is required_** 247 | - description: **Either pass an Array or a number for the component to work** 248 | - **stringInterpolationIdentifier** 249 | - type: **string** 250 | - default: **"@iterator"** 251 | - description: **Change the default identifier for when applying the value of each element with {'@iterator'} inside JSX from the Array** 252 | - **setKey** 253 | - type: **string** 254 | - default: **"index"**. If you want to access values in the array, then if it is an array of primitives values use "@iterator". 255 | If it is an array of objects then use "@iterator.PROP_NAME". See examples above. 256 | - description: **This prop will set the key for the element being iterated.** 257 | 258 | ## License 259 | 260 | This project is licensed under the MIT License 261 | -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | # React Templates Components 2 | 3 | These components are not trying to re-invent the wheel or are anything new and spectacular. The main reason to use them 4 | is because you should strive to write your React components as humanly readable as possible. 5 | 6 | React components tend to become un-readable with two main issues affecting readability: 7 | 8 | - Multiple separate render functions which return JSX in the main render() function 9 | - One large render() function with multiple .map() functions or conditional operators and use of JavaScript in general 10 | 11 | In order to reduce boilerplate this library is introducing components that act like a DSL template language but in the 12 | form of React components. 13 | 14 | ## Suggestion 15 | 16 | React has an enormous ecosystem of libraries and frameworks. However, after many years of React and Vue projects, I realize 17 | that there are many "moving parts" to an application. Too many packages from various developers, some of which are no 18 | longer maintained. Even this package, might feel unmaintained, especially after so many months of inactivity. Although, this 19 | package is more or less feature complete, I strongly advise if you need to use it, then download the source code, instead of the 20 | npm package. The source code itself is just plain React code with nothing fancy. The only possible issue is TypeScript which might 21 | not be in your current tech-stack. However, you will find the JavaScript version at github under "src/js". 22 | 23 | Finally, personally I try not to install too many packages. If I have to, then I tend to choose based on whether the package 24 | is being developed by a corporate entity, or the community behind the package has enormous and consistent support. 25 | 26 | ## v2.2.6 Information 27 | 28 | The Repeat component is proving more of a hassle than it should. I have marked it as experimental since in order for the component to work 29 | it has to tamper the internals of a React component. Since this is quite volatile and bad practice I suggest using the usual .map() for iteration. 30 | I apologize for any inconvenience. 31 | 32 | ## v2.2.0 Breaking changes 33 | 34 | - Repeat component has been re-written in order to sort out issues with the key prop 35 | - Repeat useRandomKeyForIteration has been removed please use setKey as shown in the docs. 36 | 37 | **I apologize for any inconvenience but Repeat proved to be more complex than it should. Please READ CAREFULLY the updated 38 | examples in the documentation below.** 39 | 40 | ## v2.1.1 Release Information 41 | 42 | - Added 'useFragment' prop for all components, in case you do not want that extra HTML element added to the DOM. 43 | 44 | **Please note that in case you set 'useFragment' to true the 'tag' and/or 'className' props will not have any effect on the component. Therefore if you have styled 45 | the HTML element used in the 'tag' prop, you will get weird styling results.** 46 | 47 | ## v2.0.0 Important information 48 | 49 | When I started building this library I used JavaScript. However, I am the first to admit that TypeScript is starting 50 | to grow in me. This update brings the following changes: 51 | 52 | - TypeScript re-write 53 | - Functional components in order to be ready for the class based deprecation React will eventually bring upon us 54 | - ParcelJS for building from source. I cannot stress how much faster and easier it was and the minified size in also smaller 55 | - Repeat component has made some "hasty" assumptions about the key prop. New version and props are found in the documentation below 56 | 57 | ### Thoughts 58 | 59 | I would like to apologize to anyone that is using this package and hope that any breaking changes are easy and fast to refactor. 60 | As always, please do not hesitate to contact me in case you find a bug. 61 | 62 | ## Getting Started 63 | 64 | ### Prerequisites if building from source 65 | 66 | You should have an installation of NodeJS any version and npm or yarn 67 | 68 | More over React and PropTypes are MANDATORY for the components to work. 69 | 70 | If you want to build from source by including them in your project, please take a look at my tsconfig.json since v2.0.0 is using TypeScript. 71 | 72 | These components are using React features that were present from the early stages of the library therefore you should not 73 | encounter any issues. Nevertheless, I do suggest a version of React 15+ 74 | 75 | ### Installing 76 | 77 | Npm: 78 | 79 | ``` 80 | npm install --save react-templates-components 81 | ``` 82 | 83 | Yarn: 84 | 85 | ``` 86 | yarn add react-templates-components 87 | ``` 88 | 89 | ## Usage 90 | 91 | #### Global props 92 | 93 | **_The following props are available for all components:_** 94 | 95 | - **tag** 96 | - type: **string** 97 | - default: **'div'** 98 | - description: **You can use here any tag or React element to enclose the children in the same time supporting semantic web** 99 | - **className** 100 | - type: **string** 101 | - default: **" "** 102 | - description: **The usual value found in all React components that translate to class** 103 | - **useFragment** 104 | - type: **boolean** 105 | - default: **false** 106 | - description: **The component will use the 'React.Fragment' component. 107 | _Please note that in case you set 'useFragment' to true the 'tag' and/or 'className' props will not have any effect on the component. 108 | Therefore, if you have styled the HTML element used in the 'tag' prop, you will get weird styling results._** 109 | 110 | #### Components 111 | 112 | The following components props are available 113 | 114 | #### `` Component 115 | 116 | ```jsx 117 | 118 |
IF
119 |
120 | ``` 121 | 122 | Available props (see global props as well): 123 | 124 | - **show** 125 | 126 | - type: **boolean** 127 | - default: **true** 128 | - description: **The value which will show/hide the elements you enclose** 129 | 130 | #### `` Component 131 | 132 | ```jsx 133 | 134 |
ELSE
135 |
136 | ``` 137 | 138 | Available props (see global props as well): 139 | 140 | - **show** 141 | 142 | - type: **boolean** 143 | - default: **true** 144 | - description: **The value which will show/hide the elements you enclose** 145 | 146 | > \*\*It is important to understand that you could do the same thing with 2 If components but by using If and Else you 147 | 148 | are covering semantics as well. It is also easier to read** 149 | 150 | #### `` Component 151 | 152 | ```jsx 153 | 158 | 159 |
HI CONDITIONAL IF
160 |
161 | 162 |
HI CONDITIONAL ELSE
163 |
164 |
165 | ``` 166 | 167 | Available props (see global props as well): 168 | 169 | - **condition** 170 | - type: **boolean** 171 | - default: **true** 172 | - description: **depending on the 'condition' prop, it will toggle between if and else therefore the prop 'show' in if/else is 173 | redundant** 174 | 175 | #### `/` Component 176 | 177 | ```jsx 178 | 179 | Hi Jack 180 | Hi John 181 | Hi Jim 182 | Hi Jason 183 | Default 184 | 185 | ``` 186 | 187 | Available props for `` (see global props as well): 188 | 189 | - **expression** 190 | 191 | - type: **any** 192 | - description: **The expression to evaluate** 193 | 194 | Available props for `` (see global props as well): 195 | 196 | - **expressionValue** 197 | - type: **any** 198 | - description: **The expressionValue to evaluate with the expression in the `` component** 199 | 200 | #### `` Component **(_EXPERIMENTAL use .map() instead_)** 201 | 202 | ```jsx 203 | /* No setKey default index will be used */ 204 | 207 |
208 | {'@iterator'} 209 |
{'@iterator'}
210 |
211 |
HI REPEAT #2
212 | HI REPEAT #3 {'@iterator'} 213 |
214 | 215 | /* Array of objects */ 216 | 220 |
221 | {'@iterator.name'} 222 |
{'@iterator.id'}
223 |
224 |
HI REPEAT #2
225 | HI REPEAT #3 {'@iterator.name'} 226 |
227 | 228 | /* Array of primitives */ 229 | 233 |
234 | {'@iterator'} 235 |
{'@iterator'}
236 |
237 |
HI REPEAT #2
238 | HI REPEAT #3 {'@iterator'} 239 |
240 | ``` 241 | 242 | Available props (see global props as well): 243 | 244 | - **iterator** 245 | - type: **Array** || **Number** 246 | - default: **none this prop _is required_** 247 | - description: **Either pass an Array or a number for the component to work** 248 | - **stringInterpolationIdentifier** 249 | - type: **string** 250 | - default: **"@iterator"** 251 | - description: **Change the default identifier for when applying the value of each element with {'@iterator'} inside JSX from the Array** 252 | - **setKey** 253 | - type: **string** 254 | - default: **"index"**. If you want to access values in the array, then if it is an array of primitives values use "@iterator". 255 | If it is an array of objects then use "@iterator.PROP_NAME". See examples above. 256 | - description: **This prop will set the key for the element being iterated.** 257 | 258 | ## License 259 | 260 | This project is licensed under the MIT License 261 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | (function () {var aa=this;var $=function(){return $=Object.assign||function(t){for(var e,r=1,o=arguments.length;rl.length&&l.push($)}function w($,r,e,a){var t=typeof $;"undefined"!==t&&"boolean"!==t||($=null);var i=!1;if(null===$)i=!0;else switch(t){case"string":case"number":i=!0;break;case"object":switch($.$$typeof){case j:case la:i=!0;}}if(i)return e(a,$,""===r?"."+y($,0):r),1;if(i=0,r=""===r?".":r+":",Array.isArray($))for(var o=0;o<$.length;o++){var n=r+y(t=$[o],o);i+=w(t,n,e,a)}else if(null===$||"object"!=typeof $?n=null:n="function"==typeof(n=N&&$[N]||$["@@iterator"])?n:null,"function"==typeof n)for($=n.call($),o=0;!(t=$.next()).done;)i+=w(t=t.value,n=r+y(t,o++),e,a);else if("object"===t)throw e=""+$,Error(k(31,"[object Object]"===e?"object with keys {"+Object.keys($).join(", ")+"}":e,""));return i}function x($,r,e){return null==$?0:w($,"",r,e)}function y($,r){return"object"==typeof $&&null!==$&&null!=$.key?$a($.key):r.toString(36)}function _a($,r){$.func.call($.context,r,$.count++)}function ab($,r,e){var a=$.result,t=$.keyPrefix;$=$.func.call($.context,r,$.count++),Array.isArray($)?z($,a,e,function($){return $}):null!=$&&(u($)&&($=Za($,t+(!$.key||r&&r.key===$.key?"":(""+$.key).replace(S,"$&/")+"/")+e)),a.push($))}function z($,r,e,a,t){var i="";null!=e&&(i=(""+e).replace(S,"$&/")+"/"),x($,ab,r=W(r,i,a,t)),X(r)}function d(){var $=T.current;if(null===$)throw Error(k(321));return $}function bb(){if(Ya)return;Ya=true;a={};s=(ka(),ea);c="function"==typeof Symbol&&Symbol.for;j=c?Symbol.for("react.element"):60103;la=c?Symbol.for("react.portal"):60106;ma=c?Symbol.for("react.fragment"):60107;na=c?Symbol.for("react.strict_mode"):60108;oa=c?Symbol.for("react.profiler"):60114;pa=c?Symbol.for("react.provider"):60109;qa=c?Symbol.for("react.context"):60110;ra=c?Symbol.for("react.forward_ref"):60112;sa=c?Symbol.for("react.suspense"):60113;ta=c?Symbol.for("react.memo"):60115;ua=c?Symbol.for("react.lazy"):60116;N="function"==typeof Symbol&&Symbol.iterator;O={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}};P={};e.prototype.isReactComponent={},e.prototype.setState=function($,r){if("object"!=typeof $&&"function"!=typeof $&&null!=$)throw Error(k(85));this.updater.enqueueSetState(this,$,r,"setState")},e.prototype.forceUpdate=function($){this.updater.enqueueForceUpdate(this,$,"forceUpdate")},U.prototype=e.prototype;n=o.prototype=new U;n.constructor=o,s(n,e.prototype),n.isPureReactComponent=!0;v={current:null};Q=Object.prototype.hasOwnProperty;R={key:!0,ref:!0,__self:!0,__source:!0};S=/\/+/g;l=[];T={current:null};va={ReactCurrentDispatcher:T,ReactCurrentBatchConfig:{suspense:null},ReactCurrentOwner:v,IsSomeRendererActing:{current:!1},assign:s};wa={map:function($,r,e){if(null==$)return $;var a=[];return z($,a,null,r,e),a},forEach:function($,r,e){if(null==$)return $;x($,_a,r=W(null,null,r,e)),X(r)},count:function($){return x($,function(){return null},null)},toArray:function($){var r=[];return z($,r,null,function($){return $}),r},only:function($){if(!u($))throw Error(k(143));return $}};a.Children=wa;xa=e;a.Component=xa;ya=ma;a.Fragment=ya;za=oa;a.Profiler=za;Aa=o;a.PureComponent=Aa;Ba=na;a.StrictMode=Ba;Ca=sa;a.Suspense=Ca;Da=va;a.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=Da;Ea=function($,r,e){if(null==$)throw Error(k(267,$));var a=s({},$.props),t=$.key,i=$.ref,o=$._owner;if(null!=r){if(void 0!==r.ref&&(i=r.ref,o=v.current),void 0!==r.key&&(t=""+r.key),$.type&&$.type.defaultProps)var n=$.type.defaultProps;for(u in r)Q.call(r,u)&&!R.hasOwnProperty(u)&&(a[u]=void 0===r[u]&&void 0!==n?n[u]:r[u])}var u=arguments.length-2;if(1===u)a.children=e;else if(1-1},$t.prototype.set=function(t,r){var e=this.__data__,n=kt(e,t);return n<0?e.push([t,r]):e[n][1]=r,this},mt.prototype.clear=function(){this.__data__={hash:new At,map:new(lt||$t),string:new At}},mt.prototype.delete=function(t){return Ut(this,t).delete(t)},mt.prototype.get=function(t){return Ut(this,t).get(t)},mt.prototype.has=function(t){return Ut(this,t).has(t)},mt.prototype.set=function(t,r){return Ut(this,t).set(t,r),this},xt.prototype.clear=function(){this.__data__=new $t},xt.prototype.delete=function(t){return this.__data__.delete(t)},xt.prototype.get=function(t){return this.__data__.get(t)},xt.prototype.has=function(t){return this.__data__.has(t)},xt.prototype.set=function(t,e){var n=this.__data__;if(n instanceof $t){var o=n.__data__;if(!lt||o.length-1&&t%1==0&&t-1&&t%1==0&&t<=n}(t.length)&&!Gt(t)}var Vt=it||function(){return!1};function Gt(t){var r=qt(t)?Y.call(t):"";return r==u||r==i}function qt(t){var r=typeof t;return!!t&&("object"==r||"function"==r)}function Ht(t){return Nt(t)?St(t):function(t){if(!Tt(t))return ft(t);var r=[];for(var e in Object(t))X.call(t,e)&&"constructor"!=e&&r.push(e);return r}(t)}return t.exports=function(t){return Et(t,!0,!0)},t.exports}.call({});var G={};Object.defineProperty(G,"__esModule",{value:!0});var H=void 0;G.Repeat=H;var I=g(h),Y=da(cb),p=function(e,r){if(r.split(".").length>1){for(var t=0,a=r.split(".");t1?o.props[z]=p(t,o.props[z].split($+".")[1]):o.props[z]=t);q(o.props.children,l,t,a)}else"string"==typeof l&&new RegExp("("+$+")","g").test(l)&&(l.split($+".").length>1?e[e.indexOf(l)]=p(t,l.split($+".")[1]):e[e.indexOf(l)]=t)}else new RegExp("("+$+")","g").test(e)&&(e=e.split($+".").length>1?p(t,e.split($+".")[1]):t);return e},Z=function(e,r,t,a){return"index"===e?a:e===t?r:p(r,e.split(t+".")[1])};H=function(e){var r=e.iterator,t=e.children,a=e.tag,$=void 0===a?"div":a,n=e.className,i=void 0===n?"":n,l=e.useFragment,o=void 0!==l&&l,z=e.setKey,p=void 0===z?"index":z,s=e.stringInterpolationIdentifier,d=void 0===s?"@iterator":s,c=$;if(void 0===r)throw new SyntaxError("The iterator prop is mandatory");var v="number"==typeof r?ba(new Array(r)):r;return o?v.map(function(r,a){return I.default.createElement(I.Fragment,{key:Z(p,r,d,a)},q(Y.default(t),{},r,e))}):v.map(function(r,a){return I.default.createElement(c,{className:i,key:Z(p,r,d,a)},q(Y.default(t),{},r,e))})},G.Repeat=H;var i={};Object.defineProperty(i,"__esModule",{value:!0});var J=(K=void 0,i.Case=K);i.Cases=J;var f=g(h),K=function(e){return e.children};i.Case=K,J=function(e){var r=e.children,t=e.condition,$=e.expression,a=e.tag,s=void 0===a?"div":a,o=e.className,n=void 0===o?"":o,C=e.useFragment,l=void 0!==C&&C,i=null,u=s;if(f.default.Children.count(r)<2)throw new SyntaxError("You must include at least two cases with one marked as default");if(f.default.Children.count(r)>2){var c=r.filter(function(e){return e.props.expressionValue===$});if(0===c.length)i=r[r.length-1];else{if(c.length>1)throw new SyntaxError("You most probably have set the same expressionValue in your Case components");i=c}}return l?f.default.createElement(f.Fragment,null,f.default.Children.count(r)>2?i:t?r[0]:r[1]):f.default.createElement(u,{className:n},f.default.Children.count(r)>2?i:t?r[0]:r[1])},i.Cases=J;var db,eb,fb,gb,hb,b={};Object.defineProperty(b,"__esModule",{value:!0});var ib=(hb=void 0,gb=b.Conditional=hb,fb=b.If=gb,eb=b.Else=fb,db=b.Repeat=eb,b.Cases=db);b.Case=ib;Object.defineProperty(b,"Conditional",{enumerable:!0,get:function(){return t}});Object.defineProperty(b,"If",{enumerable:!0,get:function(){return B}});Object.defineProperty(b,"Else",{enumerable:!0,get:function(){return E}});Object.defineProperty(b,"Repeat",{enumerable:!0,get:function(){return H}});Object.defineProperty(b,"Cases",{enumerable:!0,get:function(){return J}}),Object.defineProperty(b,"Case",{enumerable:!0,get:function(){return K}});if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=b}else if(typeof define==="function"&&define.amd){define(function(){return b})}})(); -------------------------------------------------------------------------------- /dist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-templates-components", 3 | "version": "2.2.8", 4 | "main": "index.js", 5 | "description": "Simple template based React Components", 6 | "keywords": [ 7 | "react", 8 | "templates" 9 | ], 10 | "author": "Antonios Liakakis", 11 | "license": "ISC", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/aliakakis/react-templates-components" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aliakakis/react-templates-components", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "description": "Simple template based React Components", 6 | "dependencies": { 7 | "@types/lodash.clonedeep": "^4.5.6", 8 | "@types/node": "^14.6.2", 9 | "@types/react": "^16.9.49", 10 | "@types/react-dom": "^16.9.8", 11 | "lodash.clonedeep": "^4.5.0", 12 | "react": "^16.13.1", 13 | "react-dom": "^16.13.1" 14 | }, 15 | "devDependencies": { 16 | "parcel-bundler": "^1.12.4", 17 | "prettier": "^2.1.1", 18 | "ts-loader": "^8.0.3", 19 | "tslib": "^2.0.1", 20 | "typescript": "^4.0.2" 21 | }, 22 | "keywords": [ 23 | "react", 24 | "templates" 25 | ], 26 | "scripts": { 27 | "build": "npx parcel build src/index.tsx --no-source-maps --experimental-scope-hoisting" 28 | }, 29 | "author": "Antonios Liakakis", 30 | "license": "ISC" 31 | } 32 | -------------------------------------------------------------------------------- /src/Cases.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import { IProps } from "./interfaces/Component"; 3 | import { IObject } from "./interfaces/Object"; 4 | 5 | interface ICaseProps extends IProps { 6 | expressionValue: any; 7 | } 8 | 9 | export const Case = ({ children }: ICaseProps) => children; 10 | 11 | interface ICasesProps extends IProps { 12 | condition?: unknown; 13 | expression: unknown; 14 | } 15 | 16 | export const Cases = ({ 17 | children, 18 | condition, 19 | expression, 20 | tag = "div", 21 | className = "", 22 | useFragment = false 23 | }: ICasesProps) => { 24 | let CaseItem = null; 25 | const Component: any = tag; 26 | 27 | if (React.Children.count(children) < 2) { 28 | throw new SyntaxError( 29 | "You must include at least two cases with one marked as default" 30 | ); 31 | } 32 | 33 | if (React.Children.count(children) > 2) { 34 | let filteredArrayElement = children.filter((childItem: IObject) => { 35 | return childItem.props.expressionValue === expression; 36 | }); 37 | 38 | if (filteredArrayElement.length === 0) { 39 | CaseItem = children[children.length - 1]; 40 | } else if (filteredArrayElement.length > 1) { 41 | throw new SyntaxError( 42 | "You most probably have set the same expressionValue in your Case components" 43 | ); 44 | } else { 45 | CaseItem = filteredArrayElement; 46 | } 47 | } 48 | 49 | return useFragment ? ( 50 | 51 | {React.Children.count(children) > 2 52 | ? CaseItem 53 | : condition 54 | ? children[0] 55 | : children[1]} 56 | 57 | ) : ( 58 | 59 | {React.Children.count(children) > 2 60 | ? CaseItem 61 | : condition 62 | ? children[0] 63 | : children[1]} 64 | 65 | ); 66 | }; 67 | -------------------------------------------------------------------------------- /src/Conditional.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import { IProps } from "./interfaces/Component"; 3 | 4 | interface IConditionalProps extends IProps { 5 | condition?: boolean; 6 | } 7 | 8 | export const Conditional = ({ 9 | children, 10 | condition = true, 11 | tag = "div", 12 | className = "", 13 | useFragment = false, 14 | }: IConditionalProps) => { 15 | const Component: any = tag; 16 | 17 | if (React.Children.count(children) < 2) { 18 | throw new SyntaxError( 19 | "You must include an If component and an Else component" 20 | ); 21 | } 22 | 23 | return useFragment ? ( 24 | {condition ? children[0] : children[1]} 25 | ) : ( 26 | 27 | {condition ? children[0] : children[1]} 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /src/Else.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import { IProps } from "./interfaces/Component"; 3 | 4 | interface IElseProps extends IProps { 5 | show?: boolean; 6 | } 7 | 8 | export const Else = ({ 9 | children, 10 | show = true, 11 | tag = "div", 12 | className = "", 13 | useFragment = false, 14 | }: IElseProps) => { 15 | const Component: any = tag; 16 | 17 | return ( 18 | show && 19 | (useFragment ? ( 20 | {children} 21 | ) : ( 22 | {children} 23 | )) 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/If.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import { IProps } from "./interfaces/Component"; 3 | 4 | interface IIfProps extends IProps { 5 | show?: boolean; 6 | } 7 | 8 | export const If = ({ 9 | children, 10 | show = true, 11 | tag = "div", 12 | className = "", 13 | useFragment = false, 14 | }: IIfProps) => { 15 | const Component: any = tag; 16 | 17 | return ( 18 | show && 19 | (useFragment ? ( 20 | {children} 21 | ) : ( 22 | {children} 23 | )) 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/Repeat.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import cloneDeep from "lodash.clonedeep"; 3 | import { IObject } from "./interfaces/Object"; 4 | import { IProps } from "./interfaces/Component"; 5 | 6 | const getNestedObjectValue = (obj: IObject, path: string) => { 7 | if (path.split(".").length > 1) { 8 | const deep = path.split("."); 9 | for (let value of deep) { 10 | obj = obj[value]; 11 | } 12 | return obj; 13 | } else { 14 | return obj[path]; 15 | } 16 | }; 17 | 18 | const changeValuePropsChildren = ( 19 | source: any, 20 | reactElement: IObject = {}, 21 | iteratorItem: any, 22 | props: IProps 23 | ) => { 24 | if (typeof source === "undefined") { 25 | throw new SyntaxError("Please add children inside Repeat"); 26 | } 27 | 28 | const { stringInterpolationIdentifier } = props; 29 | 30 | if (source instanceof Array) { 31 | for (let item of source) { 32 | if (item instanceof Array) { 33 | changeValuePropsChildren(item, {}, iteratorItem, props); 34 | } else if (item instanceof Object) { 35 | const element: IObject = item; 36 | 37 | for (let prop in element.props) { 38 | if (element.props.hasOwnProperty(prop) && prop !== "children") { 39 | if ( 40 | new RegExp("(" + stringInterpolationIdentifier + ")", "g").test( 41 | element.props[prop] 42 | ) 43 | ) { 44 | if ( 45 | element.props[prop].split(stringInterpolationIdentifier + ".") 46 | .length > 1 47 | ) { 48 | element.props[prop] = getNestedObjectValue( 49 | iteratorItem, 50 | element.props[prop].split( 51 | stringInterpolationIdentifier + "." 52 | )[1] 53 | ); 54 | } else { 55 | element.props[prop] = iteratorItem; 56 | } 57 | } 58 | } 59 | } 60 | 61 | changeValuePropsChildren( 62 | element.props.children, 63 | item, 64 | iteratorItem, 65 | props 66 | ); 67 | } else if (typeof item === "string") { 68 | if ( 69 | new RegExp("(" + stringInterpolationIdentifier + ")", "g").test(item) 70 | ) { 71 | if (item.split(stringInterpolationIdentifier + ".").length > 1) { 72 | source[source.indexOf(item)] = getNestedObjectValue( 73 | iteratorItem, 74 | item.split(stringInterpolationIdentifier + ".")[1] 75 | ); 76 | } else { 77 | source[source.indexOf(item)] = iteratorItem; 78 | } 79 | } 80 | } 81 | } 82 | } else { 83 | if ( 84 | new RegExp("(" + stringInterpolationIdentifier + ")", "g").test(source) 85 | ) { 86 | if (source.split(stringInterpolationIdentifier + ".").length > 1) { 87 | source = getNestedObjectValue( 88 | iteratorItem, 89 | source.split(stringInterpolationIdentifier + ".")[1] 90 | ); 91 | } else { 92 | source = iteratorItem; 93 | } 94 | } 95 | } 96 | return source; 97 | }; 98 | 99 | const calculateKey = ( 100 | setKey: string, 101 | item: any, 102 | stringInterpolationIdentifier: string, 103 | index: number 104 | ) => { 105 | let key: string | number; 106 | 107 | if (setKey === "index") { 108 | key = index; 109 | } else if (setKey === stringInterpolationIdentifier) { 110 | key = item; 111 | } else { 112 | key = getNestedObjectValue( 113 | item, 114 | setKey.split(stringInterpolationIdentifier + ".")[1] 115 | ); 116 | } 117 | 118 | return key; 119 | }; 120 | 121 | interface IRepeat extends IProps { 122 | iterator: IObject[] | number; 123 | setKey: string; 124 | stringInterpolationIdentifier: string; 125 | } 126 | 127 | export const Repeat = (props: IRepeat) => { 128 | const { 129 | iterator, 130 | children, 131 | tag = "div", 132 | className = "", 133 | useFragment = false, 134 | setKey = "index", 135 | stringInterpolationIdentifier = "@iterator", 136 | } = props; 137 | const Component: any = tag; 138 | 139 | if (typeof iterator === "undefined") { 140 | throw new SyntaxError("The iterator prop is mandatory"); 141 | } 142 | 143 | let iteratorProp: any[] = 144 | typeof iterator === "number" ? [...new Array(iterator)] : iterator; 145 | 146 | return useFragment 147 | ? iteratorProp.map((item: any, index: number) => { 148 | return ( 149 | 157 | {changeValuePropsChildren(cloneDeep(children), {}, item, props)} 158 | 159 | ); 160 | }) 161 | : iteratorProp.map((item: any, index: number) => { 162 | return ( 163 | 172 | {changeValuePropsChildren(cloneDeep(children), {}, item, props)} 173 | 174 | ); 175 | }); 176 | }; 177 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | export { Conditional } from "./Conditional"; 2 | export { If } from "./If"; 3 | export { Else } from "./Else"; 4 | export { Repeat } from "./Repeat"; 5 | export { Cases, Case } from "./Cases"; 6 | -------------------------------------------------------------------------------- /src/interfaces/Component.ts: -------------------------------------------------------------------------------- 1 | import { IObject } from "./Object"; 2 | 3 | export interface IProps extends IObject { 4 | children?: any; 5 | tag?: string; 6 | className?: string; 7 | useFragment?: boolean; 8 | } 9 | -------------------------------------------------------------------------------- /src/interfaces/Object.ts: -------------------------------------------------------------------------------- 1 | export interface IObject { 2 | [key: string]: any; 3 | } 4 | -------------------------------------------------------------------------------- /src/js/Cases.jsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | export const Case = ({ children }) => children; 5 | 6 | Case.propTypes = { 7 | expressionValue: PropTypes.any.isRequired, 8 | }; 9 | 10 | export const Cases = ({ 11 | children, 12 | condition, 13 | expression, 14 | tag: Component = "div", 15 | className = "", 16 | useFragment = false, 17 | }) => { 18 | let CaseItem = null; 19 | 20 | if (React.Children.count(children) < 2) { 21 | throw new SyntaxError( 22 | "You must include at least two cases with one marked as default" 23 | ); 24 | } 25 | 26 | if (React.Children.count(children) > 2) { 27 | let filteredArrayElement = children.filter((childItem) => { 28 | return childItem.props.expressionValue === expression; 29 | }); 30 | 31 | if (filteredArrayElement.length === 0) { 32 | CaseItem = children[children.length - 1]; 33 | } else if (filteredArrayElement.length > 1) { 34 | throw new SyntaxError( 35 | "You most probably have set the same expressionValue in your Case components" 36 | ); 37 | } else { 38 | CaseItem = filteredArrayElement; 39 | } 40 | } 41 | 42 | return useFragment ? ( 43 | 44 | {React.Children.count(children) > 2 45 | ? CaseItem 46 | : condition 47 | ? children[0] 48 | : children[1]} 49 | 50 | ) : ( 51 | 52 | {React.Children.count(children) > 2 53 | ? CaseItem 54 | : condition 55 | ? children[0] 56 | : children[1]} 57 | 58 | ); 59 | }; 60 | 61 | Cases.propTypes = { 62 | expression: PropTypes.any.isRequired, 63 | tag: PropTypes.string, 64 | className: PropTypes.string, 65 | useFragment: PropTypes.bool, 66 | }; 67 | -------------------------------------------------------------------------------- /src/js/Conditional.jsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | export const Conditional = ({ 5 | children, 6 | condition = true, 7 | tag: Component = "div", 8 | className = "", 9 | useFragment = false, 10 | }) => { 11 | if (React.Children.count(children) < 2) { 12 | throw new SyntaxError( 13 | "You must include an If component and an Else component" 14 | ); 15 | } 16 | 17 | return useFragment ? ( 18 | {condition ? children[0] : children[1]} 19 | ) : ( 20 | 21 | {condition ? children[0] : children[1]} 22 | 23 | ); 24 | }; 25 | 26 | Conditional.propTypes = { 27 | condition: PropTypes.bool.isRequired, 28 | tag: PropTypes.string, 29 | className: PropTypes.string, 30 | useFragment: PropTypes.bool, 31 | }; 32 | -------------------------------------------------------------------------------- /src/js/Else.jsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | export const Else = ({ 5 | children, 6 | show = true, 7 | tag: Component = "div", 8 | className = "", 9 | useFragment = false, 10 | }) => { 11 | return ( 12 | show && 13 | (useFragment ? ( 14 | {children} 15 | ) : ( 16 | {children} 17 | )) 18 | ); 19 | }; 20 | 21 | Else.propTypes = { 22 | show: PropTypes.bool, 23 | tag: PropTypes.string, 24 | className: PropTypes.string, 25 | useFragment: PropTypes.bool, 26 | }; 27 | -------------------------------------------------------------------------------- /src/js/If.jsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | export const If = ({ 5 | children, 6 | show = true, 7 | tag: Component = "div", 8 | className = "", 9 | useFragment = false, 10 | }) => { 11 | return ( 12 | show && 13 | (useFragment ? ( 14 | {children} 15 | ) : ( 16 | {children} 17 | )) 18 | ); 19 | }; 20 | 21 | If.propTypes = { 22 | show: PropTypes.bool, 23 | tag: PropTypes.string, 24 | className: PropTypes.string, 25 | useFragment: PropTypes.bool, 26 | }; 27 | -------------------------------------------------------------------------------- /src/js/Repeat.jsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import PropTypes from "prop-types"; 3 | import cloneDeep from "lodash.clonedeep"; 4 | 5 | const getNestedObjectValue = (obj, path) => { 6 | if (path.split(".").length > 1) { 7 | const deep = path.split("."); 8 | for (let value of deep) { 9 | obj = obj[value]; 10 | } 11 | return obj; 12 | } else { 13 | return obj[path]; 14 | } 15 | }; 16 | 17 | const changeValuePropsChildren = ( 18 | source, 19 | reactElement = {}, 20 | iteratorItem, 21 | props 22 | ) => { 23 | if (typeof source === "undefined") { 24 | throw new SyntaxError("Please add children inside Repeat"); 25 | } 26 | 27 | const { stringInterpolationIdentifier } = props; 28 | 29 | if (source instanceof Array) { 30 | for (let item of source) { 31 | if (item instanceof Array) { 32 | changeValuePropsChildren(item, {}, iteratorItem, props); 33 | } else if (item instanceof Object) { 34 | const element = item; 35 | 36 | for (let prop in element.props) { 37 | if (element.props.hasOwnProperty(prop) && prop !== "children") { 38 | if ( 39 | new RegExp("(" + stringInterpolationIdentifier + ")", "g").test( 40 | element.props[prop] 41 | ) 42 | ) { 43 | if ( 44 | element.props[prop].split(stringInterpolationIdentifier + ".") 45 | .length > 1 46 | ) { 47 | element.props[prop] = getNestedObjectValue( 48 | iteratorItem, 49 | element.props[prop].split( 50 | stringInterpolationIdentifier + "." 51 | )[1] 52 | ); 53 | } else { 54 | element.props[prop] = iteratorItem; 55 | } 56 | } 57 | } 58 | } 59 | 60 | changeValuePropsChildren( 61 | element.props.children, 62 | item, 63 | iteratorItem, 64 | props 65 | ); 66 | } else if (typeof item === "string") { 67 | if ( 68 | new RegExp("(" + stringInterpolationIdentifier + ")", "g").test(item) 69 | ) { 70 | if (item.split(stringInterpolationIdentifier + ".").length > 1) { 71 | source[source.indexOf(item)] = getNestedObjectValue( 72 | iteratorItem, 73 | item.split(stringInterpolationIdentifier + ".")[1] 74 | ); 75 | } else { 76 | source[source.indexOf(item)] = iteratorItem; 77 | } 78 | } 79 | } 80 | } 81 | } else { 82 | if ( 83 | new RegExp("(" + stringInterpolationIdentifier + ")", "g").test(source) 84 | ) { 85 | if (source.split(stringInterpolationIdentifier + ".").length > 1) { 86 | source = getNestedObjectValue( 87 | iteratorItem, 88 | source.split(stringInterpolationIdentifier + ".")[1] 89 | ); 90 | } else { 91 | source = iteratorItem; 92 | } 93 | } 94 | } 95 | return source; 96 | }; 97 | 98 | const calculateKey = (setKey, item, stringInterpolationIdentifier, index) => { 99 | let key; 100 | 101 | if (setKey === "index") { 102 | key = index; 103 | } else if (setKey === stringInterpolationIdentifier) { 104 | key = item; 105 | } else { 106 | key = getNestedObjectValue( 107 | item, 108 | setKey.split(stringInterpolationIdentifier + ".")[1] 109 | ); 110 | } 111 | 112 | return key; 113 | }; 114 | 115 | export const Repeat = ({ 116 | iterator, 117 | children, 118 | tag: Component = "div", 119 | className = "", 120 | useFragment = false, 121 | setKey = "index", 122 | stringInterpolationIdentifier = "@iterator", 123 | }) => { 124 | if (typeof iterator === "undefined") { 125 | throw new SyntaxError("The iterator prop is mandatory"); 126 | } 127 | 128 | let iteratorProp = 129 | typeof iterator === "number" ? [...new Array(iterator)] : iterator; 130 | 131 | return useFragment 132 | ? iteratorProp.map((item, index) => { 133 | return ( 134 | 142 | {changeValuePropsChildren(cloneDeep(children), {}, item, props)} 143 | 144 | ); 145 | }) 146 | : iteratorProp.map((item, index) => { 147 | return ( 148 | 157 | {changeValuePropsChildren(cloneDeep(children), {}, item, props)} 158 | 159 | ); 160 | }); 161 | }; 162 | 163 | Repeat.propTypes = { 164 | iterator: PropTypes.oneOfType([PropTypes.array, PropTypes.number]).isRequired, 165 | tag: PropTypes.string, 166 | className: PropTypes.string, 167 | stringInterpolationIdentifier: PropTypes.string, 168 | useRandomKeyForIteration: PropTypes.bool, 169 | useFragment: PropTypes.bool, 170 | }; 171 | -------------------------------------------------------------------------------- /src/js/index.jsx: -------------------------------------------------------------------------------- 1 | export { Conditional } from "./Conditional"; 2 | export { If } from "./If"; 3 | export { Else } from "./Else"; 4 | export { Repeat } from "./Repeat"; 5 | export { Cases, Case } from "./Cases"; 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "target": "es5", 5 | "module": "commonjs", 6 | "strict": true, 7 | "jsx": "react", 8 | "importHelpers": true, 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": false, 14 | "removeComments": true, 15 | "baseUrl": ".", 16 | "lib": [ 17 | "esnext", 18 | "dom", 19 | "dom.iterable", 20 | "scripthost" 21 | ] 22 | }, 23 | "include": [ 24 | "./src/**/*" 25 | ], 26 | "exclude": [ 27 | "node_modules" 28 | ] 29 | } 30 | --------------------------------------------------------------------------------