Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[invokers] invoke on focus but not hover #1030

Open
flackr opened this issue Apr 5, 2024 · 3 comments
Open

[invokers] invoke on focus but not hover #1030

flackr opened this issue Apr 5, 2024 · 3 comments

Comments

@flackr
Copy link

flackr commented Apr 5, 2024

The idea of using invokers came up for how to explain the behaviors of some of the carousel primitives. For scroll markers (flackr/carousel#6) in particular, there are some complex requirements which I'm trying to navigate whether we could properly explain through some collection of the UI's here. The intended behavior of scroll markers is almost identical to radio buttons. On activation (which can be applied through arrow key presses in a manner similar to focus group focus), they scroll to their targeted element. The interest invokers proposal is almost this, except includes hover, which is very unlike radio-button interaction.

TLDR; I feel like there are a class of applications where we want some activation behavior on focus, but not hover. Would it make sense to say that for some elements, we apply the invoke behavior on focus? E.g. this may be the case for radio buttons.

@lukewarlow
Copy link
Collaborator

This is something that's come up for something before. We currently don't expose the type of interest (hover, focus, long press). Maybe we should do that so you could at least with a bit of JS decide whether to prevent default the event. But JS might not be the ideal here.

Pointer events while designed to be input agnostic do expose the type of input. So maybe this follows in the same theme?

@keithamus
Copy link
Collaborator

Interest invokers very specifically abstract away focus/hover to unify cross HID behaviours. I think if you want "interest invokers but just focus" then using the focus event is probably what you want. I'm not convinced we should extend interest invokers to facilitate exclusion or different semantics for a particular HID.

To more directly talk about your pattern though, a carousel is kind of a spicy tabpanel; it has a list of buttons that can be immediately activated and they will bring the matching panel to the front. They should also activate on click though. You could map the focus to a click, then use invokers to activate a custom element to switch the tab-panel. Given that, you might be better implementing a Carousel as tabpanels, and focusgroup might be a better fit for the tablist. In lieu of an actual tablist built-in, here's what I'd do:

<my-carousel id=carousel">
  <div role=tablist focusgroup slot=tablist>
    <button role=tab value=1 tabindex=-1 onfocus="this.onclick()" invoketarget="carousel">1</button>
    <button role=tab value=2 tabindex=-1 onfocus="this.onclick()" invoketarget="carousel">2</button>
    <button role=tab value=3 tabindex=-1 onfocus="this.onclick()" invoketarget="carousel">3</button>
  </div>
  <div role=tabpanel>1</div>
  <div role=tabpanel>2</div>
  <div role=tabpanel>3</div>
</my-carousel>
<script>
  customElements.define('my-carousel', class extends HTMLElement {
    connectedCallback() { this.addEventListener('invoke', this) }
    handleEvent(event) {
      const idx = event.target.closest('[role=tab]')?.value
      if (value) this.switchTo(idx)
    }
    switchTo(idx) {
      let i = 0;
      for(const panel of this.querySelectorAll('[role=tabpanel]')) {
        i += 1;
        panel.hidden = i != idx
      }
    }
  });
</script>

@flackr
Copy link
Author

flackr commented Apr 11, 2024

Yes, totally agree that clicking should invoke the action as well, and that mapping focus to the invoke action is a way to solve this. I'd like to have a way to do this declaratively, otherwise it doesn't really explain the thing that the pseudo-element scroll markers create. I think this could well be some attribute value for focusgroup which implies invoking on focus.

E.g.

<my-carousel id=carousel">
  <div role=tablist focusgroup="invoke" slot=tablist>
    <button role=tab value=1 tabindex=-1 invoketarget="tab1" invokeaction="scrollTo">1</button>
    <button role=tab value=2 tabindex=-1 invoketarget="tab2" invokeaction="scrollTo">2</button>
    <button role=tab value=3 tabindex=-1 invoketarget="tab3" invokeaction="scrollTo">3</button>
  </div>
  <div role=tabpanel id=tab1>1</div>
  <div role=tabpanel id=tab2>2</div>
  <div role=tabpanel id=tab3>3</div>
</my-carousel>

As an example of the scrolling interaction you can see the demo at https://flackr.github.io/carousel/examples/carousel/image/ . The advantage of using scrolling is that it automatically supports swiping to advance.

As described in #1031 this alone wouldn't explain how the active button also needs to be updated on scroll.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants