11require 'react/ext/string'
22
33module React
4+ #
5+ # Wraps the React Native element class
6+ #
7+ # adds the #on method to add event handlers to the element
8+ #
9+ # adds the #render method to place elements in the DOM and
10+ # #delete (alias/deprecated #as_node) method to remove elements from the DOM
11+ #
12+ # handles the haml style class notation so that
13+ # div.bar.blat becomes div(class: "bar blat")
14+ # by using method missing
15+ #
416 class Element
517 include Native
618
@@ -20,58 +32,124 @@ def initialize(native_element, type, properties, block)
2032 @native = native_element
2133 end
2234
23- def on ( event_name )
24- name = event_name . to_s . event_camelize
25- props = if React ::Event ::BUILT_IN_EVENTS . include? ( "on#{ name } " )
26- { "on#{ name } " => %x{
27- function(event){
28- #{ yield React ::Event . new ( `event` ) }
29- }
30- } }
31- else
32- { "_on#{ name } " => %x{
33- function(){
34- #{ yield *Array ( `arguments` ) }
35- }
36- } }
37- end
38- @native = `React.cloneElement(#{ self . to_n } , #{ props . to_n } )`
39- @properties . merge! props
35+ # Attach event handlers.
36+
37+ def on ( *event_names , &block )
38+ event_names . each { |event_name | merge_event_prop! ( event_name , &block ) }
39+ @native = `React.cloneElement(#{ to_n } , #{ properties . to_n } )`
4040 self
4141 end
4242
43- def render ( props = { } ) # for rendering children
43+ # Render element into DOM in the current rendering context.
44+ # Used for elements that are not yet in DOM, i.e. they are provided as children
45+ # or they have been explicitly removed from the rendering context using the delete method.
46+
47+ def render ( props = { } )
4448 if props . empty?
4549 React ::RenderingContext . render ( self )
4650 else
4751 React ::RenderingContext . render (
4852 Element . new (
49- `React.cloneElement(#{ self . to_n } , #{ API . convert_props ( props ) } )` ,
50- type ,
51- properties . merge ( props ) ,
52- block
53+ `React.cloneElement(#{ to_n } , #{ API . convert_props ( props ) } )` ,
54+ type , properties . merge ( props ) , block
5355 )
5456 )
5557 end
5658 end
5759
60+ # Delete (remove) element from rendering context, the element may later be added back in
61+ # using the render method.
62+
63+ def delete
64+ React ::RenderingContext . delete ( self )
65+ end
66+
67+ # Deprecated version of delete method
68+
69+ def as_node
70+ React ::RenderingContext . as_node ( self )
71+ end
72+
73+ # Any other method applied to an element will be treated as class name (haml style) thus
74+ # div.foo.bar(id: :fred) is the same as saying div(class: "foo bar", id: :fred)
75+ #
76+ # single underscores become dashes, and double underscores become a single underscore
77+ #
78+ # params may be provide to each class (but typically only to the last for easy reading.)
79+
5880 def method_missing ( class_name , args = { } , &new_block )
59- class_name = class_name . split ( "__" ) . collect { | s | s . gsub ( "_" , "-" ) } . join ( "_" )
81+ class_name = class_name . gsub ( /__|_/ , '__' => '_' , '_' => '-' )
6082 new_props = properties . dup
61- new_props [ "class" ] = "#{ new_props [ 'class' ] } #{ class_name } #{ args . delete ( "class" ) } #{ args . delete ( 'className' ) } " . split ( " " ) . uniq . join ( " " )
83+ new_props [ :class ] = "\
84+ #{ class_name } #{ new_props [ :class ] } #{ args . delete ( :class ) } #{ args . delete ( :className ) } \
85+ ". split ( ' ' ) . uniq . join ( ' ' )
6286 new_props . merge! args
6387 React ::RenderingContext . replace (
6488 self ,
65- React :: RenderingContext . build { React :: RenderingContext . render ( type , new_props , &new_block ) }
89+ RenderingContext . build { RenderingContext . render ( type , new_props , &new_block ) }
6690 )
6791 end
6892
69- def as_node
70- React ::RenderingContext . as_node ( self )
93+ private
94+
95+ # built in events, events going to native components, and events going to reactrb
96+
97+ # built in events will have their event param translated to the Event wrapper
98+ # and the name will camelcased and have on prefixed, so :click becomes onClick.
99+ #
100+ # events emitting from native components are assumed to have the same camel case and
101+ # on prefixed.
102+ #
103+ # events emitting from reactrb components will just have on_ prefixed. So
104+ # :play_button_pushed attaches to the :on_play_button_pushed param
105+ #
106+ # in all cases the default name convention can be overriden by wrapping in <...> brackets.
107+ # So on("<MyEvent>") will attach to the "MyEvent" param.
108+
109+ def merge_event_prop! ( event_name , &block )
110+ if event_name =~ /^<(.+)>$/
111+ merge_component_event_prop! event_name . gsub ( /^<(.+)>$/ , '\1' ) , &block
112+ elsif React ::Event ::BUILT_IN_EVENTS . include? ( name = "on#{ event_name . event_camelize } " )
113+ merge_built_in_event_prop! name , &block
114+ elsif @type . instance_variable_get ( '@native_import' )
115+ merge_component_event_prop! name , &block
116+ else
117+ merge_deprecated_component_event_prop! event_name , &block
118+ merge_component_event_prop! "on_#{ event_name } " , &block
119+ end
71120 end
72121
73- def delete
74- React ::RenderingContext . delete ( self )
122+ def merge_built_in_event_prop! ( prop_name )
123+ @properties . merge! (
124+ prop_name => %x{
125+ function(event){
126+ return #{ yield ( React ::Event . new ( `event` ) ) }
127+ }
128+ }
129+ )
130+ end
131+
132+ def merge_component_event_prop! ( prop_name )
133+ @properties . merge! (
134+ prop_name => %x{
135+ function(){
136+ return #{ yield ( *Array ( `arguments` ) ) }
137+ }
138+ }
139+ )
140+ end
141+
142+ def merge_deprecated_component_event_prop! ( event_name )
143+ prop_name = "_on#{ event_name . event_camelize } "
144+ fn = %x{function(){#{
145+ React ::Component . deprecation_warning (
146+ "In future releases React::Element#on('#{ event_name } ') will no longer respond " \
147+ "to the '#{ prop_name } ' emitter.\n " \
148+ "Rename your emitter param to 'on_#{ event_name } ' or use .on('<#{ prop_name } >')"
149+ ) }
150+ return #{ yield ( *Array ( `arguments` ) ) }
151+ }}
152+ @properties . merge! ( prop_name => fn )
75153 end
76154 end
77155end
0 commit comments