zauberzeug / nicegui

Create web-based user interfaces with Python. The nice way.
https://nicegui.io
MIT License
8.78k stars 534 forks source link

Tooltip doesn't disappear when changing tabs #1949

Open falkoschindler opened 10 months ago

falkoschindler commented 10 months ago

Discussed in https://github.com/zauberzeug/nicegui/discussions/1942

Originally posted by **snowbollaanm** November 2, 2023 Hi I have encountered an issue with tooltips and tabs. I have made a button that when pressed switches to the next tab panel. However, I gave that button a tooltip and discovered a bug that when the tab changes and you leave your cursor in the position of the button for about half a second the tooltip becomes persistent and no longer goes away until you put your cursor back over the button on the original tab. ```py from nicegui import ui def switchTab(): tabs = [tabOne, tabTwo] currentTab = testTabPanels.value if currentTab in tabs: changeTab = 1 - tabs.index(currentTab) testTabPanels.set_value(tabs[changeTab]) with ui.tabs() as testTabs: tabOne = ui.tab(1) tabTwo = ui.tab(2) with ui.tab_panels(testTabs, value=tabOne) as testTabPanels: with ui.tab_panel(tabOne): ui.label('Tab One') with ui.button('switch tab', on_click=lambda: switchTab()): ui.tooltip('This is a tooltip').classes('text-body2') with ui.tab_panel(tabTwo): ui.label('Tab Two') ui.button('This button has NO tooltip', on_click=lambda: switchTab()) ui.run() ``` ![Screenshot 2023-11-02 134521](https://github.com/zauberzeug/nicegui/assets/139186074/dc39919b-b6d8-46f7-9973-2289b3ec6c94) ![tooltipBug screenshot1](https://github.com/zauberzeug/nicegui/assets/139186074/c2d55819-b678-4e90-848b-608df9eacf17)
falkoschindler commented 10 months ago

Here is a minimal reproduction:

with ui.tab_panels(value='1') as tabs:
    with ui.tab_panel('1'):
        ui.label('Tab 1')
        ui.button('Go to 2', on_click=lambda: tabs.set_value('2')).tooltip('Tooltip')
    with ui.tab_panel('2'):
        ui.label('Tab 2')
        ui.button('Back to 1', on_click=lambda: tabs.set_value('1'))

First I thought it might be a bug in Quasar, but a plain Quasar app works correctly:

<!DOCTYPE html>
<html>
  <head>
    <link href="https://cdn.jsdelivr.net/npm/quasar@2.13.0/dist/quasar.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <div id="q-app">
      <q-tab-panels v-model="tab">
        <q-tab-panel name="1">
          <div>Tab 1 content</div>
          <q-btn label="Go to 2" @click="tab = '2'"><q-tooltip>Go to Tab 2</q-tooltip></q-btn>
        </q-tab-panel>
        <q-tab-panel name="2">
          <div>Tab 2 content</div>
          <q-btn label="Back to 1" @click="tab = '1'" />
        </q-tab-panel>
      </q-tab-panels>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@2.13.0/dist/quasar.umd.js"></script>
    <script>
      const app = Vue.createApp({
        data() {
          return { tab: "1" };
        },
        setup() {
          return {};
        },
      });
      app.use(Quasar);
      app.mount("#q-app");
    </script>
  </body>
</html>
python-and-fiction commented 3 weeks ago

@falkoschindler It seems to be related to tab_panels' keep_alive property. When I disable keep_alive,it works as expected.

with ui.tab_panels(keep_alive=False,value='1') as tabs:
    with ui.tab_panel('1'):
        ui.label('Tab 1')
        ui.button('Go to 2', on_click=lambda: tabs.set_value('2')).tooltip('Tooltip')
    with ui.tab_panel('2'):
        ui.label('Tab 2')
        ui.button('Back to 1', on_click=lambda: tabs.set_value('1'))

Add keep-alive to q-tab-panels,it works as nicegui app.

<!DOCTYPE html>
<html>
  <head>
    <link href="https://cdn.jsdelivr.net/npm/quasar@2.13.0/dist/quasar.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <div id="q-app">
      <q-tab-panels keep-alive v-model="tab">
        <q-tab-panel name="1">
          <div>Tab 1 content</div>
          <q-btn label="Go to 2" @click="tab = '2'"><q-tooltip>Go to Tab 2</q-tooltip></q-btn>
        </q-tab-panel>
        <q-tab-panel name="2">
          <div>Tab 2 content</div>
          <q-btn label="Back to 1" @click="tab = '1'" />
        </q-tab-panel>
      </q-tab-panels>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@2.13.0/dist/quasar.umd.js"></script>
    <script>
      const app = Vue.createApp({
        data() {
          return { tab: "1" };
        },
        setup() {
          return {};
        },
      });
      app.use(Quasar);
      app.mount("#q-app");
    </script>
  </body>
</html>

I think it is a feature of vue? I guess. Maybe we should set default keep_alive to False in TabPanels's defination? Or note it in document?

python-and-fiction commented 3 weeks ago

@falkoschindler Finally,I get it. This bug or feature only occurs when transition-prev/next is slide-* and transition-duration <= 500 and keep-alive is enable. This is demo code:

from nicegui import ui
with ui.card():
    ui.label('animate options:')
    inp = ui.select(['fade','slide-left','slide-up','slide-down'],value='slide-left',label='transition-prev/next'').classes('w-64')
    inp2 = ui.number(min=100,max=600,step=100,value=500,label='transition-duration').classes('w-64')

@ui.refreshable
def bug_show():
    ui.label('tabs:')
    with ui.tab_panels(animated=True,value='1').props(f'height=180px keep-alive transition-prev="{inp.value}" transition-next="{inp.value}" transition-duration={inp2.value}').classes('border w-64') as tabs:
        with ui.tab_panel('1'):
            ui.label('Tab 1')
            ui.button('Go to 2', on_click=lambda: tabs.set_value('2')).tooltip('Tooltip')
        with ui.tab_panel('2'):
            ui.label('Tab 2')
            ui.button('Back to 1', on_click=lambda: tabs.set_value('1'))
    #for carousel,it will keep tooltip when animated is False
    ui.label('carousel:')
    with ui.carousel(animated=True).props(f'height=100px keep-alive transition-prev="{inp.value}" transition-next="{inp.value}" transition-duration={inp2.value}').classes('border w-64') as card:
        with ui.carousel_slide().classes('p-0'):
            ui.label('Card 1')
            ui.button('Go to 2', on_click=lambda:card.next()).tooltip('Tooltip')
        with ui.carousel_slide().classes('p-0'):
            ui.label('Card 2')
            ui.button('Back to 1', on_click=lambda:card.previous())
bug_show()
inp.on_value_change(bug_show.refresh)
inp2.on_value_change(bug_show.refresh)
ui.run()

动画

falkoschindler commented 1 week ago

Awesome, @python-and-fiction!

To conclude:

Can we do anything about it? Or does it have to be solved upstream?