Created
February 17, 2021 02:24
-
-
Save catmando/01fe5c31eb95ef949702ed953a6a8c05 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/hyperstack/libs/throttle.rb | |
class Throttle | |
# call the provided block (using the run method) no faster than the | |
# specified update frequency. | |
def initialize(update_frequency = 0.5, &block) | |
@update_frequency = update_frequency | |
@block = block | |
@last_start_time = Time.now - update_frequency | |
end | |
attr_reader :update_frequency | |
# To do this we wait for the block operation to complete or resolve | |
# and then wait if necessary, and then if there is another set of args | |
# to send repeat the process. | |
def throttle(args) | |
# wait until the end of the period then run again if there are new args | |
after([@update_frequency - (Time.now - @last_start_time), 0].max) do | |
@last_start_time = Time.now | |
@promise = nil | |
run(*@args) if @args && @args != args | |
end | |
end | |
def run(*args) | |
# copy the args and return if we already have a promise in play | |
return (@args = args) if @promise | |
# otherwise clear the @args variable, | |
# use load to get a promise that will resolve the block completes, | |
# and then throttle, and if there is a new @args value start the process | |
# over again. | |
@args = nil | |
@promise = Hyperstack::Model.load { @block.call(*args) }.then { throttle(args) } | |
end | |
end | |
# app/hyperstack/components/throttled_slider.rb | |
class ThrottledSlider < HyperComponent | |
# Generalized component that displays a range input. | |
# While the user is moving the slider the component will | |
# fire the update event at the specified frequency. | |
# The current value of the input is provided as the value param. | |
# Any other params such as the min, max, classes and styles can also | |
# be specified. | |
param :value | |
param frequency: 0.5 | |
others :opts | |
fires :update | |
before_mount do | |
@throttle = Throttle.new(frequency) do |new_value| | |
update!(new_value) | |
end | |
end | |
# While the user is not interacting with the component we want the | |
# component to reflect any value changes (say from other | |
# sessions.) Once the user begins moving the slider we want the | |
# slider to act purely as an uncontrolled component. This prevents | |
# any jitters as we send and receive data from the IOT hub and entity. | |
def ignore_external_changes | |
# lock the value to the current param value | |
# note that even though locked, because its an uncontrolled component | |
# the user will be able to move change the value. But by keeping the | |
# component uncontrolled and locking the initial value we get a smooth | |
# UI response. | |
mutate @locked_value = value | |
end | |
def follow_external_changes | |
# Begin tracking changes external changes, but wait a bit for any | |
# messages to make the round trip, so the control doesn't | |
# jitter when the mouse is released. We assume 3 times the update frequency | |
# is sufficient. | |
after(@throttle.update_frequency * 3) { mutate @locked_value = nil } | |
end | |
def current_value | |
# use either the locked value or the value param. | |
@locked_value || value | |
end | |
render do | |
DIV(key: current_value) do | |
INPUT(opts, type: :range, defaultValue: current_value) | |
.on(:mouse_down) { ignore_external_changes } | |
.on(:mouse_up) { follow_external_changes } | |
.on(:change) { |e| @throttle.run(e.target.value) } | |
end | |
end | |
end | |
# example useage: | |
ThrottledSlider(min: 1, max: 254, style: { appearance: 'slider-vertical' }, value: entity.current_brightness) | |
.on(:update) do |brightness| | |
Sample.first.set_brightness(brightness) | |
Sample.first.clear_cached_values(:set_brightness) | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment