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

Vertical stepper's animation #3881

Open
StarDustEins opened this issue Oct 16, 2024 · 7 comments
Open

Vertical stepper's animation #3881

StarDustEins opened this issue Oct 16, 2024 · 7 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@StarDustEins
Copy link

StarDustEins commented Oct 16, 2024

Description

Vertical stepper's animation seems not good, in Quasor's official demo it works smoothly. I use the latest version of Nicegui 2.3.0

2024-10-16.13.28.09.mov
@falkoschindler
Copy link
Contributor

Thanks for reporting this issue, @StarDustEins!

Minimum reproducible example:

with ui.stepper().props('vertical animated') as stepper:
    with ui.step('A'):
        with ui.stepper_navigation():
            ui.button('Next', on_click=stepper.next)
    with ui.step('B'):
        with ui.stepper_navigation():
            ui.button('Next', on_click=stepper.next)
            ui.button('Back', on_click=stepper.previous)
    with ui.step('C'):
        with ui.stepper_navigation():
            ui.button('Back', on_click=stepper.previous)

@falkoschindler falkoschindler added the bug Something isn't working label Oct 16, 2024
@falkoschindler
Copy link
Contributor

So far I failed to reproduce it with plain Quasar/Vue:

<html>
  <head>
    <link href="https://fonts.googleapis.com/css?family=Roboto:400|Material+Icons" rel="stylesheet" type="text/css" />
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/quasar.prod.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <div id="q-app">
      <q-stepper ref="stepper" :model-value="step" @update:model-value="onStepChange" vertical animated>
        <q-step :name="1" title="Step 1">
          <q-stepper-navigation>
            <q-btn @click="next" label="Next"></q-btn>
          </q-stepper-navigation>
        </q-step>
        <q-step :name="2" title="Step 2">
          <q-stepper-navigation>
            <q-btn @click="next" label="Next"></q-btn>
            <q-btn @click="previous" label="Previous"></q-btn>
          </q-stepper-navigation>
        </q-step>
        <q-step :name="3" title="Step 3">
          <q-stepper-navigation>
            <q-btn @click="previous" label="Previous"></q-btn>
          </q-stepper-navigation>
        </q-step>
      </q-stepper>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/quasar.umd.prod.js"></script>
    <script>
      const app = Vue.createApp({
        setup() {
          const step = Vue.ref(1);
          const stepper = Vue.ref(null);
          return {
            step,
            stepper,
            onStepChange: (newStep) => (step.value = newStep),
            next: () => stepper.value.next(),
            previous: () => stepper.value.previous(),
          };
        },
      });
      app.use(Quasar);
      app.mount("#q-app");
    </script>
  </body>
</html>

This animates as expected. But what's the difference to a NiceGUI app? Does anyone have an idea?

@falkoschindler falkoschindler added the help wanted Extra attention is needed label Oct 18, 2024
@StarDustEins
Copy link
Author

I have no idea about Vue and js

@CrystalWindSnake
Copy link
Contributor

@falkoschindler I suspect the reason for the issue is that the stepper rebuilds all its child elements every time it updates

@falkoschindler
Copy link
Contributor

I looked into this issue once again, but still don't understand why NiceGUI behaves differently than plain Quasar. @CrystalWindSnake might be right and Vue is recreating the QStep elements. But I don't see why, because NiceGUI is only calling next() and previous(), just like in the Quasar snippet I posted above.

@CrystalWindSnake
Copy link
Contributor

I looked into this issue once again, but still don't understand why NiceGUI behaves differently than plain Quasar. @CrystalWindSnake might be right and Vue is recreating the QStep elements. But I don't see why, because NiceGUI is only calling next() and previous(), just like in the Quasar snippet I posted above.

NiceGUI not only calls next(), but also calls the update method of the stepper itself. This is causing the entire component to refresh, including all its child elements.

In the following example, when you click next, the backend will print updating stepper.

from nicegui import ui


class MyStepper(ui.stepper):
    def update(self) -> None:
        print("updating stepper")
        return super().update()


with MyStepper().props("vertical animated") as stepper:
    with ui.step("A"):
        with ui.stepper_navigation():
            ui.button("Next", on_click=stepper.next)
    with ui.step("B"):
        with ui.stepper_navigation():
            ui.button("Next", on_click=stepper.next)
            ui.button("Back", on_click=stepper.previous)
    with ui.step("C"):
        with ui.stepper_navigation():
            ui.button("Back", on_click=stepper.previous)


ui.run()

@falkoschindler
Copy link
Contributor

Ah, I wasn't aware of update() being called. This is because ui.stepper.LOOPBACK = True which causes update being called when the value changes. So I'd expect the problem being fixed by setting ui.stepper.LOOPBACK = False (the value is updated by setting the "model-value" directly on the client) or ui.stepper.LOOPBACK = None (the value is updated automatically by the Vue element). But none of them work.

It seems like calling next is not enough to change the step:

with ui.element('q-stepper').props('model-value=A vertical animated') as stepper:
    with ui.element('q-step').props('name=A title=A'):
        ui.button('next', on_click=lambda: stepper.run_method('next'))
    with ui.element('q-step').props('name=B title=B'):
        ui.button('back', on_click=lambda: stepper.run_method('previous'))

Only when subscribing to the update:model-value event and setting the new model-value prop, the step changes:

with ui.element('q-stepper').props('model-value=A vertical animated') \
        .on('update:model-value', lambda e: stepper.props(f'model-value={e.args}')) as stepper:
    with ui.element('q-step').props('name=A title=A'):
        ui.button('next', on_click=lambda: stepper.run_method('next'))
    with ui.element('q-step').props('name=B title=B'):
        ui.button('back', on_click=lambda: stepper.run_method('previous'))

Now the question remains: What does ui.stepper do differently? Calling .props() also leads to an update(), so this can't be the problem. And ui.stepper overwrites _handle_value_change, but removing this code doesn't help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants