GithubHelp home page GithubHelp logo

Comments (8)

alecdotninja avatar alecdotninja commented on May 26, 2024 1

Thanks for the positive feedback, @sosodev ! I'm glad that you were able to get Motion up and running 🎉

Currently, there is not any built-in way to debounce motions; however, if you're willing to write some JavaScript, the client does expose the ability to trigger motions programatically via motion.getComponent(element).processMotion(motion, event). Here is how that might look with Stimulus:

<input class="input" type="text" placeholder="Search" data-controller="example" data-action="input->search">
// app/javascript/controllers/example_controller.js
import { Controller } from "stimulus"
import motion from "../motion"

export default class extends Controller {
  search(event) {
    if (this.timeout) {
      clearTimeout(this.timeout)
    }

    this.timeout = setTimeout(() => {
      this.timeout = null

      motion.getComponent(event.target).processMotion("search", event)
    }, 1000)
  }
}

This isn't really an answer to your question, but it is worth noting that if you used the change event instead of input, the motion would only be triggered once when the input looses focus instead of on each key press.

from motion.

sosodev avatar sosodev commented on May 26, 2024 1

Thanks for the info. :) Manually triggering the event seems like a good solution. I tend to avoid change events though since I think the idea of a focused input doesn't make any sense to the average user.

I'll go ahead and close this as solved. Hope you have a great weekend.

from motion.

sosodev avatar sosodev commented on May 26, 2024

Hi @alecdotninja, I found a slight problem while implementing this. Currently Motion attempts to serialize the currentTarget for the event but currentTarget is null by the time the debounced query fires and it causes an error.

Is there any particular reason why Motion chooses to provide both the target and currentTarget? I'm testing a naive patch that serializes the target as the currentTarget and it seems to work fine without requiring the API to change at all. Alternatively, allowing currentTarget to be null would work too but might be a little confusing for new users who expect that value to always be set on the server side.

from motion.

alecdotninja avatar alecdotninja commented on May 26, 2024

Good catch, @sosodev . Thank you for being the trailblazer on this. 😄

The reason that we send currentTarget (and indeed bless it as the default choice by aliasing it to Motion::Event#element) is that this is the element to which the user added the data-motion attribute, and it is likely where they have placed any other data attributes they want to use as "arguments" to their motion.

When writing that code, I didn't realize that currentTarget could be null (I thought it was always equivalent to this inside of the event handler 😅).

I would gladly accept a patch to gracefully allow currentTarget to be null. That said, I think the ideal solution is actually to ditch currentTarget completely, start sending the element to which the motion was bound, and expose that as Motion::Event#element.

from motion.

sosodev avatar sosodev commented on May 26, 2024

Oh, I see what you mean. Determining the actual target element is a bit tricky. 😅

Maybe processMotion could be modified to accept a motionElement parameter, for custom stuff like debouncing, and query the component itself for the element when the motionElement is null.

I'm imagining it looking something like this...

// motion/javascript/Component.js

  processMotion (name, event = null, motionElement = null) {
    if (!motionElement) {
      motionElement = this.element.querySelector(`[data-motion=${name}]`)
    }

    if (!this._subscription) {
      this.client.log('Dropped motion', name, 'on', this.element)
      return false
    }

    this.client.log('Processing motion', name, 'on', this.element)

    const extraDataForEvent = event && this.client.getExtraDataForEvent(event)

    this._subscription.perform(
      'process_motion',
      {
        name,
        event: event && serializeEvent(event, extraDataForEvent, motionElement)
      }
    )

    return true
  }

Modifying serializeEvent to emit our intended element like you mentioned

// motion/javascript/serializeElement.js

export default function serializeEvent (event, extraData = null, element = null) {
  const { type } = event
  const details = serializeEventDetails(event)
  const target = serializeElement(event.target)

  return {
    type,
    details,
    extraData,
    target,
    element: element && serializeElement(element)
  }
};

and simply doing away with current_target and the alias

# motion/lib/motion/event.rb

    def element
        return @element if defined?(@element)

        @element = Motion::Element.from_raw(raw["element"])
    end

This does seem like a cleaner solution. 😄 What do you think?

from motion.

alecdotninja avatar alecdotninja commented on May 26, 2024

Sorry for the delay, @sosodev 🙈 .

I like where this is going, but can we change the default in the case the element is not passed to something like this:

event.currentTarget || event.target.closest(`[data-motion=${name}]`)

I actually think the event.target.closest approach will always give the right answer since (AFAIK) events only bubble up the DOM.

from motion.

alecdotninja avatar alecdotninja commented on May 26, 2024

In the next release of Motion, processMotion will accept an optional 3rd argument for the Motion::Event#element. 🎉 That said, my thinking on this issue has changed.

I think the ideal way to solve this problem is with something like debounced rather than hand-rolling the debouncing JavaScript. It turns out this approach works with or without #43.

from motion.

alecdotninja avatar alecdotninja commented on May 26, 2024

I'm going to go ahead and close this for now. Please feel free to reopen, @sosodev, if you have any questions or think something more should be done here. 😄

from motion.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.