quasarframework / quasar

Quasar Framework - Build high-performance VueJS user interfaces in record time
https://quasar.dev
MIT License
25.95k stars 3.52k forks source link

Feature Request: Scrolling Tabs #7404

Open smakinson opened 4 years ago

smakinson commented 4 years ago

Is your feature request related to a problem? Please describe. Currently the Quasar scrolling tabs shows arrows for moving through them and the arrows are difficult to see.

Describe the solution you'd like The more native approach is something like what is shown here on this Flutter example:

https://gallery.flutter.dev/#/demo/tabs

With the ability to drag the content area and see visual feedback on the tabs movement as well. I am not certain, but I think Quasar would be the only one to provide this component feature.

pdanpdan commented 4 years ago

Hello.

On mobile you have that functionality, and on desktop you can choose between overlay arrows or external arrows. In my opinion the tab drag is not exactly what I would expect on desktop.

smakinson commented 4 years ago

Can the content area (QTabPanel?) Be made to be draggable (not only swipable) and control the tabs? Maybe I just missed it.

pdanpdan commented 4 years ago

You can swipe the panels, yes, and the selected tab will change.

smakinson commented 4 years ago

I see the arrows go away and its swipable, but is it possible to be able to pan/drag the panel content and have it affect the tabs scrolling the way it does in the flutter example?

https://codepen.io/smakinson/full/WNrypxG

pdanpdan commented 4 years ago

It could be done, but not with QTabPanels, because they are optimized for efficiency and only render the current tab.

smakinson commented 4 years ago

Ah I see, so would require a new component of some type?

matejmohar commented 4 years ago

On desktop, maybe there should be option to scroll with mouse wheel? Just like in visual studio code?

danielfaust commented 4 years ago

I'm also interested in this issue.

I often accidentally swipe a tab without wanting to do that, because the sensitivity of the swipe is too high. When that happens, I lose the state of the panel I was in.

Ideally a component would exist which replicates the functionality of the Flutter component mentioned initially. These swipeable panes are a very common UI element across many platforms, where the swipe matches the finger movement until it is released. When the finger is released, depending on the amount of drag or the swipe impulse, the pane will either animate back to the initial position or switch to the new panel.

The way this component here (QTabPanels) works is unusable for that, this one is at most good for either tabbed navigation or something like an image carousel.

Would it be too hard to create a new component for this?

pdanpdan commented 4 years ago

One of the main reasons is that (excepting the indicator line) it's trivial to do a scrolling strip of panels with all of them active and to pan them.

danielfaust commented 4 years ago

Hi Dan, thanks for your reply. Could you point me a bit more in the right direction? Also the panes should be lazy loaded, on the first time they enter the screen. I would appreciate any hints you could give me.

pdanpdan commented 4 years ago

Make a container with class row no-wrap scroll and with a v-touch-pan.mouse.horizontal that moves the panel. Also you can look at the CSS properties related to scroll snap. Inside put components with the col-xx class you want (if you need lazy loading use the intersection directive or component.

pdanpdan commented 3 years ago

Cross reference: https://github.com/quasarframework/quasar/issues/4714 https://github.com/quasarframework/quasar/issues/4406 https://github.com/quasarframework/quasar/issues/7404 https://github.com/quasarframework/quasar/issues/7795 https://github.com/quasarframework/quasar/issues/8266

namespace-github commented 9 months ago

Hello folks,

here is my solution for moving the qTabs left/right using the mouse wheel or dragging the content area:


  <q-tabs
    ref="tabs"
   ...
    v-touch-pan.mouse.horizontal="handleMousePan"
    @mousedown="mousedown"
    @mousewheel.prevent="mousewheel"
  >
  data()
  {
    return {
      initialScrollLeft: 0,
    };
  },
    mousewheel(event)
    {
      const scrollContainer = this.$refs.tabs.$el.querySelector('.q-tabs__content');

      let scrollLeft;

      if (event.deltaY > 0)
      {
        scrollLeft = scrollContainer.scrollLeft + Math.abs(event.wheelDelta);
      }
      else
      {
        scrollLeft = scrollContainer.scrollLeft - Math.abs(event.wheelDelta);
      }

      scrollContainer.scrollLeft = scrollLeft;
    },

    handleMousePan(event)
    {
      const scrollContainer = this.$refs.tabs.$el.querySelector('.q-tabs__content');

      let scrollLeft;

      if (event.direction === 'left')
      {
        scrollLeft = this.initialScrollLeft + event.distance.x;
      }
      else
      {
        scrollLeft = this.initialScrollLeft - event.distance.x;
      }

      scrollContainer.scrollLeft = scrollLeft;
    },

    mousedown()
    {
      this.initialScrollLeft = this.$refs.tabs.$el.querySelector('.q-tabs__content').scrollLeft;
    },

    mouseup()
    {
      this.initialScrollLeft = 0;
    },
mlrcbsousa commented 2 months ago

Hello folks,

here is my solution for moving the qTabs left/right using the mouse wheel or dragging the content area:

  <q-tabs
    ref="tabs"
   ...
    v-touch-pan.mouse.horizontal="handleMousePan"
    @mousedown="mousedown"
    @mousewheel.prevent="mousewheel"
  >
  data()
  {
    return {
      initialScrollLeft: 0,
    };
  },
    mousewheel(event)
    {
      const scrollContainer = this.$refs.tabs.$el.querySelector('.q-tabs__content');

      let scrollLeft;

      if (event.deltaY > 0)
      {
        scrollLeft = scrollContainer.scrollLeft + Math.abs(event.wheelDelta);
      }
      else
      {
        scrollLeft = scrollContainer.scrollLeft - Math.abs(event.wheelDelta);
      }

      scrollContainer.scrollLeft = scrollLeft;
    },

    handleMousePan(event)
    {
      const scrollContainer = this.$refs.tabs.$el.querySelector('.q-tabs__content');

      let scrollLeft;

      if (event.direction === 'left')
      {
        scrollLeft = this.initialScrollLeft + event.distance.x;
      }
      else
      {
        scrollLeft = this.initialScrollLeft - event.distance.x;
      }

      scrollContainer.scrollLeft = scrollLeft;
    },

    mousedown()
    {
      this.initialScrollLeft = this.$refs.tabs.$el.querySelector('.q-tabs__content').scrollLeft;
    },

    mouseup()
    {
      this.initialScrollLeft = 0;
    },

Thanks! great potential solution

a version of this worked for me for the vertical tabs

const onScrollTabs = (event: WheelEvent & { wheelDelta: number }) => {
  const scrollContainer = tabsRef.value?.$el?.querySelector('.q-tabs__content') as HTMLElement
  if (!scrollContainer) {
    return
  }

  let scrollTop: number

  if (event.deltaY > 0) {
    scrollTop = scrollContainer.scrollTop + Math.abs(event.wheelDelta)
  } else {
    scrollTop = scrollContainer.scrollTop - Math.abs(event.wheelDelta)
  }

  scrollContainer.scrollTop = scrollTop
}