-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
160 lines (139 loc) · 4.18 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
'use strict';
import React from 'react';
import ReactDOM from 'react-dom';
/**
* Add event listeners to find all elements with [data-react-class] attribute,
* get the react class name from the attribute and render the component using
* ReactDOM.
* Props can be passed in using the [data-react-props] attribute as a
* stringified json object.
*
* Based on ReactRails' ReactUJS: https://github.com/reactjs/react-rails/blob/master/react_ujs/index.js
*
* Usage
*
* ```javascript
* // src/js/app.js
* import LuckyReact from "lucky-react";
* import { Component } from './components';
*
* LuckyReact.register({ Component });
* ```
*
* And in your Lucky Page.
*
* ```crystal
* class Home::IndexPage < GuestLayout
* def content
* h1 "React Component"
* div "data-react-class": "Component",
* "data-react-props": { message: "Message" }.to_json
*
* # or if you have the lucky_react shard installed
* react "Component", { message: "Message" }
* end
* end
* ```
*/
var LuckyReact = {
CLASS_NAME_ATTR: 'data-react-class',
PROPS_ATTR: 'data-react-props',
components: {},
/**
* Get all nodes with the [data-react-class] attribute
*/
getNodes() {
return document.querySelectorAll('[' + LuckyReact.CLASS_NAME_ATTR + ']');
},
/**
* Iterate through the nodes and call mountComponent
*/
mountComponents() {
var nodes = LuckyReact.getNodes();
for (var i = 0; i < nodes.length; ++i) {
LuckyReact.mountComponent(nodes[i]);
}
},
/**
* Find nodes with [data-react-class] attribute and find the react component
* with that class name in the registery. Get the props from
* [data-react-props], set the children and render the component.
*/
mountComponent(node) {
var className = node.getAttribute(LuckyReact.CLASS_NAME_ATTR);
var constructor = LuckyReact.getConstructor(className);
var propsJson = node.getAttribute(LuckyReact.PROPS_ATTR);
var props = propsJson && JSON.parse(propsJson);
if (!constructor) {
var message = "Cannot find component: '" + className + "'"
if (console && console.log) {
console.log("%c[react-lucky] %c" + message + " for element", "font-weight: bold", "", node)
}
throw new Error(message + ". Make sure you've registered your component, for example: LuckyReact.register({ Component }).")
} else {
var children = LuckyReact.nodeChildren(node);
ReactDOM.render(
React.createElement(constructor, { ...props, children }),
node
);
}
},
/**
* Render the children of the directly with innerHTML
*/
nodeChildren(node) {
if (node.childNodes.length > 0) {
return <div dangerouslySetInnerHTML={{ __html: node.innerHTML }} />
} else {
return null;
}
},
/**
* Unmount all react components on nodes with the [data-react-class] attribute
*/
unmountComponents() {
var nodes = LuckyReact.getNodes();
for (var i = 0; i < nodes.length; ++i) {
ReactDOM.unmountComponentAtNode(nodes[i]);
}
},
/**
* Add event listeners on turbolinks load anad before-render
*/
setup() {
document.addEventListener('turbolinks:load', LuckyReact.mountComponents);
document.addEventListener('turbolinks:before-render', LuckyReact.unmountComponents);
},
/**
* Remove turbolinks event listeners
*/
teardown() {
document.removeEventListener('turbolinks:load', LuckyReact.mountComponents);
document.removeEventListener('turbolinks:before-render',LuckyReact.unmountComponents);
},
/**
* Try to find the React component's constructor function in the registry,
* then globally then finally try eval
*/
getConstructor(className) {
return this.components[className] || window[className] || eval(className);
},
/**
* Remove then add event listeners
*/
start() {
LuckyReact.teardown();
LuckyReact.setup();
},
/**
* Store references to the app's React components in a hash for later access,
* then call start()
*/
register(componentHash) {
Object.keys(componentHash).forEach(key => {
this.components[key] = componentHash[key]
});
LuckyReact.start();
},
}
module.exports = LuckyReact;