1
1
import Adapt from 'core/js/adapt' ;
2
2
import TemplateRenderEvent from './templateRenderEvent' ;
3
3
import HTMLReactParser from 'html-react-parser' ;
4
-
5
- /**
6
- * Finds a node in a react node hierarchy
7
- * Return true from the iterator to stop traversal
8
- * @param {object } hierarchy
9
- * @param {function } iterator
10
- */
11
- export function find ( hierarchy , iterator ) {
12
- if ( iterator ( hierarchy ) ) {
13
- return true ;
14
- }
15
- if ( ! hierarchy . props || ! hierarchy . props . children ) return ;
16
- if ( Array . isArray ( hierarchy . props . children ) ) {
17
- return hierarchy . props . children . find ( child => {
18
- if ( ! child ) return ;
19
- return find ( child , iterator ) ;
20
- } ) ;
21
- }
22
- return find ( hierarchy . props . children , iterator ) ;
23
- } ;
24
-
25
- /**
26
- * Allows clone and modification of a react node hierarchy
27
- * @param {* } value
28
- * @param {boolean } isDeep=false
29
- * @param {function } modifier
30
- * @returns {* }
31
- */
32
- export function clone ( value , isDeep = false , modifier = null ) {
33
- if ( typeof value !== 'object' || value === null ) {
34
- return value ;
35
- }
36
- const cloned = Array . isArray ( value ) ? [ ] : { } ;
37
- const descriptors = Object . getOwnPropertyDescriptors ( value ) ;
38
- for ( let name in descriptors ) {
39
- const descriptor = descriptors [ name ] ;
40
- if ( ! descriptor . hasOwnProperty ( 'value' ) ) {
41
- Object . defineProperty ( cloned , name , descriptor ) ;
42
- continue ;
43
- }
44
- let value = descriptor . value ;
45
- if ( typeof value === 'object' && value !== null ) {
46
- if ( isDeep ) {
47
- value = descriptor . value = clone ( value , isDeep , modifier ) ;
48
- }
49
- if ( modifier && typeof value . $$typeof === 'symbol' ) {
50
- modifier ( value ) ;
51
- }
52
- }
53
- descriptor . writable = true ;
54
- Object . defineProperty ( cloned , name , descriptor ) ;
55
- }
56
- if ( modifier && typeof cloned . $$typeof === 'symbol' ) {
57
- modifier ( cloned ) ;
58
- }
59
- return cloned ;
60
- } ;
4
+ import React from 'react' ;
61
5
62
6
/**
63
7
* Used by babel plugin babel-plugin-transform-react-templates to inject react templates
@@ -79,6 +23,29 @@ export default function register(name, component) {
79
23
} ;
80
24
} ;
81
25
26
+ /**
27
+ * Override React.createElement to allow trapping and modification of react
28
+ * template elements.
29
+ */
30
+ ( function ( ) {
31
+ const original = React . createElement ;
32
+ React . createElement = ( ...args ) => {
33
+ const name = args [ 0 ] ;
34
+ // Trap render calls to emit preRender and postRender events
35
+ const mode = 'reactElement' ;
36
+ // Send preRender event to allow modification of args
37
+ const preRenderEvent = new TemplateRenderEvent ( `${ mode } :preRender` , name , mode , null , args ) ;
38
+ Adapt . trigger ( preRenderEvent . type , preRenderEvent ) ;
39
+ // Execute element creation
40
+ const value = original ( ...preRenderEvent . args ) ;
41
+ // Send postRender event to allow modification of rendered element
42
+ const postRenderEvent = new TemplateRenderEvent ( `${ mode } :postRender` , name , mode , value , preRenderEvent . args ) ;
43
+ Adapt . trigger ( postRenderEvent . type , postRenderEvent ) ;
44
+ // Return rendered, modified element
45
+ return postRenderEvent . value ;
46
+ } ;
47
+ } ) ( ) ;
48
+
82
49
/**
83
50
* Storage for react templates
84
51
*/
@@ -92,24 +59,12 @@ export function html(html, ref = null) {
92
59
if ( ! html ) return ;
93
60
let node = html ? HTMLReactParser ( html ) : '' ;
94
61
if ( typeof node === 'object' && ref ) {
95
- // Strip object freeze and write locks by cloning
96
- node = clone ( node ) ;
97
- node . ref = ref ;
62
+ node = Array . isArray ( node ) ? node [ 0 ] : node ;
63
+ node = React . cloneElement ( node , { ref } ) ;
98
64
}
99
65
return node ;
100
66
}
101
67
102
- /**
103
- * Render the named react component
104
- * @param {string } name React template name
105
- * @param {...any } args React template arguments
106
- */
107
- export function render ( name , ...args ) {
108
- const template = templates [ name ] ;
109
- const component = template ( ...args ) ;
110
- return component ;
111
- } ;
112
-
113
68
/**
114
69
* Handlebars compile integration
115
70
* @param {string } name Handlebars template
@@ -141,9 +96,20 @@ export function helper(name, ...args) {
141
96
} ;
142
97
143
98
/**
144
- * Helper for a list of classes, filtering out falsies and joining with spaces
99
+ * Helper for a list of classes, filtering out falsies and duplicates, and joining with spaces
145
100
* @param {...any } args List or arrays of classes
146
101
*/
147
102
export function classes ( ...args ) {
148
- return _ . flatten ( args ) . filter ( Boolean ) . join ( ' ' ) ;
103
+ return _ . uniq ( _ . flatten ( args ) . filter ( Boolean ) . join ( ' ' ) . split ( ' ' ) ) . join ( ' ' ) ;
104
+ } ;
105
+
106
+ /**
107
+ * Helper for prefixing a list of classes, filtering out falsies and duplicates and joining with spaces
108
+ * @param {[...string] } prefixes Array of class prefixes
109
+ * @param {...any } args List or arrays of classes
110
+ */
111
+ export function prefixClasses ( prefixes , ...args ) {
112
+ const classes = _ . flatten ( args ) . filter ( Boolean ) ;
113
+ const prefixed = _ . flatten ( prefixes . map ( prefix => classes . map ( className => `${ prefix } ${ className } ` ) ) ) ;
114
+ return _ . uniq ( prefixed . join ( ' ' ) . split ( ' ' ) ) . join ( ' ' ) ;
149
115
} ;
0 commit comments