1+ import os
2+ import hashlib
3+ import json
4+ from django .contrib .staticfiles import finders
5+ from django .utils .safestring import mark_safe
6+ from .exceptions import ReactComponentSourceFileNotFound
7+ from .settings import STATIC_URL , STATIC_ROOT
8+ from .utils import render , bundle
9+
10+
11+ class ReactComponent (object ):
12+ source = None
13+ # TODO
14+ # exported_as = None
15+ props = None
16+ serialised_props = None
17+ path_to_bundled_source = None
18+
19+ def __init__ (self , ** kwargs ):
20+ self .props = kwargs
21+
22+ def render (self ):
23+ return self .render_container (
24+ content = render (self )
25+ )
26+
27+ def render_static (self ):
28+ return self .render_container (
29+ content = render (self , to_static_markup = True )
30+ )
31+
32+ def render_js (self ):
33+ rendered_component = '\n ' .join ((
34+ self .render_props (),
35+ self .render_source (),
36+ self .render_init ()
37+ ))
38+ return mark_safe (rendered_component )
39+
40+ def render_container (self , content = None ):
41+ if content is None :
42+ content = ''
43+ rendered_mount_element = '<div id="{container_id}" class="{container_class_name}">{content}</div>' .format (
44+ container_id = self .get_container_id (),
45+ container_class_name = self .get_container_class_name (),
46+ content = content ,
47+ )
48+ return mark_safe (rendered_mount_element )
49+
50+ def render_props (self ):
51+ rendered_props = '<script>{props_variable} = {serialised_props};</script>' .format (
52+ props_variable = self .get_props_variable (),
53+ serialised_props = self .get_serialised_props (),
54+ )
55+ return mark_safe (rendered_props )
56+
57+ def render_source (self ):
58+ rendered_bundle = '<script src="{url_to_mount_bundle}"></script>' .format (
59+ url_to_mount_bundle = self .get_url_to_source ()
60+ )
61+ return mark_safe (rendered_bundle )
62+
63+ def render_init (self ):
64+ props_check = ''
65+ if self .has_props ():
66+ props_check = '''
67+ if (!window.{props_variable}) {{
68+ throw new Error('Cannot find props `{props_variable}` for component `{exported_variable}`');
69+ }}
70+ ''' .format (
71+ props_variable = self .get_props_variable (),
72+ exported_variable = self .get_component_variable (),
73+ )
74+ rendered_mount_script = '''
75+ <script>
76+ if (!window.{exported_variable}) {{
77+ throw new Error('Cannot find component `{exported_variable}`');
78+ }}
79+ {props_check}
80+ (function(React, component, props, container_id) {{
81+ var react_element = React.createElement(component, props);
82+ var mount_container = document.getElementById(container_id);
83+ if (!mount_container) {{
84+ throw new Error('Cannot find the container element `#' + container_id + '` for component `{exported_variable}`');
85+ }}
86+ React.render(react_element, mount_container);
87+ }})({react_variable}, {exported_variable}, window.{props_variable}, '{container_id}');
88+ </script>
89+ ''' .format (
90+ react_variable = self .get_react_variable (),
91+ exported_variable = self .get_component_variable (),
92+ props_variable = self .get_props_variable (),
93+ container_id = self .get_container_id (),
94+ props_check = props_check ,
95+ )
96+ return mark_safe (rendered_mount_script )
97+
98+ def get_source (self ):
99+ return self .source
100+
101+ def get_path_to_source (self ):
102+ source = self .get_source ()
103+ path_to_source = finders .find (source )
104+ if not os .path .exists (path_to_source ):
105+ raise ReactComponentSourceFileNotFound (path_to_source )
106+ return path_to_source
107+
108+ def get_props (self ):
109+ return self .props
110+
111+ def has_props (self ):
112+ if self .get_props ():
113+ return True
114+ return False
115+
116+ # TODO
117+ # def get_exported_as(self):
118+ # return self.exported_as
119+
120+ def get_component_name (self ):
121+ return self .__class__ .__name__
122+
123+ def get_component_variable (self ):
124+ return self .get_component_name ()
125+
126+ def get_react_variable (self ):
127+ return 'React'
128+
129+ def get_component_id (self ):
130+ return unicode (id (self ))
131+
132+ def get_container_id (self ):
133+ return 'reactComponentContainer-{component_id}' .format (
134+ component_id = self .get_component_id ()
135+ )
136+
137+ def get_container_class_name (self ):
138+ return 'reactComponentContainer reactComponentContainer--{component_name}' .format (
139+ component_name = self .get_component_name ()
140+ )
141+
142+ def get_serialised_props (self ):
143+ if not self .serialised_props :
144+ self .serialised_props = json .dumps (self .get_props ())
145+ return self .serialised_props
146+
147+ def get_serialised_props_hash (self ):
148+ serialised_props = self .get_serialised_props ()
149+ md5 = hashlib .md5 ()
150+ md5 .update (serialised_props )
151+ return md5 .hexdigest ()
152+
153+ def get_props_variable (self ):
154+ return '__propsFor{component_name}_{serialised_props_hash}__' .format (
155+ component_name = self .get_component_name (),
156+ serialised_props_hash = self .get_serialised_props_hash (),
157+ )
158+
159+ def get_path_to_bundled_source (self ):
160+ if not self .path_to_bundled_source :
161+ self .path_to_bundled_source = bundle (self )
162+ return self .path_to_bundled_source
163+
164+ def get_url_to_source (self ):
165+ _ , rel_path_to_bundled_source = self .get_path_to_bundled_source ().split (STATIC_ROOT )
166+ if rel_path_to_bundled_source [0 ] in ['/' , '\\ ' ]:
167+ rel_path_to_bundled_source = rel_path_to_bundled_source [1 :]
168+ return os .path .join (STATIC_URL , rel_path_to_bundled_source )
0 commit comments