Skip to content

Instantly share code, notes, and snippets.

@jgaskins
Created October 5, 2015 03:51
Show Gist options
  • Save jgaskins/5bfe94ba62eb6ba4c52b to your computer and use it in GitHub Desktop.
Save jgaskins/5bfe94ba62eb6ba4c52b to your computer and use it in GitHub Desktop.
Widgets with Clearwater
require 'clearwater/component'
require 'google_map_container'
class Foo
include Clearwater::Component
def render
GoogleMapContainer.new(
height: 500,
width: 500,
# Baltimore
latitude: 39.287858,
longitude: -76.613702,
zoom: 13,
)
end
end
class GoogleMapContainer < Widget
attr_reader :map
def initialize(height:, width:, latitude: 0, longitude: 0, zoom: 13)
@height = height
@width = width
@latitude = latitude
@longitude = longitude
@zoom = zoom
end
def mount
@map = %x{
new google.maps.Map(#{element}, {
center: new google.maps.LatLng(#@latitude, #@longitude),
zoom: #@zoom,
})
}
end
# Copy the map from the previous instance. This is important if we want to do anything
# with it.
def update prev, dom_node
@map = prev.map
end
def unmount
@map = nil
end
# Override the node being provided. We want to ensure it has a height and width because
# Google Maps won't set it.
def node
VirtualDOM.node('div', {
style: {
height: "#{@height}px",
width: "#{@width}px",
},
})
end
end
class Widget
attr_reader :element
def self.new(*args, &block)
widget = super
# virtual-dom handles "Widgets" in a special way
widget.instance_exec { @type = 'Widget' }
widget
end
# The mount method is called when the widget is inserted into the DOM. You'll
# want to run any setup here, such as attaching a Google Map
def mount
end
# The update method is called when we render a new widget in place of another.
# Any state initialized in the mount method will need to be migrated to this
# instance of the widget.
#
# previous: the widget this one will be replacing
# dom_node: the DOM node this object is being mounted into
def update previous, dom_node
end
# The unmount method is called when the widget is removed from the DOM.
# We'll need to get rid of the Google Map or chart or whatever we're working with.
def unmount
end
def new_element
# I had to add this method.
VirtualDOM.create_element(node)
end
def node
VirtualDOM.node('div')
end
# These are the JS-native virtual-dom Widget callbacks. We delegate to the Ruby versions
# so they can be overridden easily.
%x{
Opal.defn(self, 'init', function() {
var self = this;
self.element = self.$new_element();
#{mount};
return self.element;
});
Opal.defn(self, 'update', function(prev, dom_node) {
var self = this;
self.element = dom_node;
#{update(`prev`, Opal::Browser::DOM::Element.new(`dom_node`))};
});
Opal.defn(self, 'destroy', function() {
var self = this;
delete self.element;
#{unmount};
});
}
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment