nuxt / ui

A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.
https://ui.nuxt.com
MIT License
3.76k stars 458 forks source link

Add auto loading state for `UButton`/`UForm` #1893

Open maximepvrt opened 2 months ago

maximepvrt commented 2 months ago

Description

Currently, there is no built-in way to set a auto loading state for buttons in nuxt/ui when they are linked to a @click event or are the submit button of a UForm.

Proposal

Add a feature to nuxt/ui to enable a loading state for buttons. This should be configurable for:

  1. Buttons with @click events.
  2. Submit buttons of a UForm with a reference to the form.

This enhancement will improve user experience by preventing multiple submissions and indicating that an action is in progress.

Example

<UButton promise-loading @click="handleClick">
  Submit
</UButton>

<UForm ref="myForm" @submit="handleSubmit">
  <UButton :loading="myForm.promiseLoading" type="submit">
    Submit
  </UButton>
</UForm>

Benefits

Additional context

No response

romhml commented 2 months ago

I like the idea, but i think it would be a bit tricky since we don't have access to any information about event handlers using emits and can't really wait for it to resolve. (For example here: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/Form.vue#L136).

The only solution I can think of is to replace the @submit / @click events by a prop with a callback function, but it feels a bit odd.

A dedicated composable maybe? Something like useAsyncData except in only returns a handle to execute the function and the loading state?

const { loading, execute: submit } = useLoading(async () => { /* ... */ })
romhml commented 2 months ago

This can be achieved using VueUse's useAsyncState (https://vueuse.org/core/useAsyncState/) to wrap your event handlers:

const { isReady, execute } = useAsyncState(async () => { 
    // ...
  },
  null,
  { immediate: false },
)
romhml commented 2 months ago

I found out that we can declare emits using props while exploring ways to implement this, so it's actually possible to do it without changing the way components are used 😄

Here's a sample implementation for v3: https://github.com/benjamincanac/ui3/pull/135/files

maximepvrt commented 2 months ago

So cool 🤩