ionic-team / ionic-framework

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
https://ionicframework.com
MIT License
50.65k stars 13.51k forks source link

feat: allow custom spinners for `ion-spinner`/`ion-loading` #23912

Open darrylnoakes opened 2 years ago

darrylnoakes commented 2 years ago

Prequisites

Describe the Feature Request

Allow an HTML element (such as one created with createElement()) to be given to the ion-spinner. This functionality should be able to come through to ion-loading, as it uses an ion-spinner under the hood.

Describe the Use Case

Sometimes I want a spinner that "just works", but want it to be slightly more "on brand" than a stock style. I created a Vue component that holds an animated SVG (for easy reuse), but have no way to use it with Ionic. Note: a Vue component gets compiled to standard HTML elements, so the root element of the Vue component can be passed to the ion-spinner.

Maybe using a custom spinner design violates someone's interface design guidelines/rules? (looking at you Apple)

Describe Preferred Solution

Allow an element to be given when creating a spinner using the spinner controller.

Not sure which way would be best. Maybe change the spinner property/param to allow HTMLElement as well or something? For the element version of ion-spinner, it could use a named slot: slot="spinner"?

Describe Alternatives

  1. Use a custom spinner instead of ion-spinner. This can actually work in many cases, but not when I want to use ion-loading.
  2. Set the spinner style to none and target the background of the spinner with CSS. e.g. background: url(#my-spinner-svg) Someone on the Ionic forum mentioned doing this as a workaround. I can find it again if needed. This unfortunately is more complex and only allows for a single SVG to be set.

Related Code

No response

Additional Information

As to implementing, I looked at Ionic's source, and I think this could get hairy. If it would be too complex, please say so and feel free to drop the request :).

willmartian commented 2 years ago

I'll discuss this with the team; thanks for the issue!

aparajita commented 4 months ago

@darrylnoakes You can use a custom spinner with ion-loading through clever use of css. I don't have time to document it now, but it is possible.

cyrilfr commented 2 weeks ago

I'd also love to have this possibility, without a hacky CSS workaround.

aparajita commented 2 weeks ago

I'm using tailwindcss with this css. Note that there are default values for each aspect of the spinner, but they can be overridden by the --loading-overlay-* variables:

  @keyframes loading-spin {
    0% {
      transform: rotate(0deg);
    }

    100% {
      transform: rotate(360deg);
    }
  }

  .loading-overlay {
    --backdrop-opacity: var(--loading-overlay-backdrop-opacity, 60%) !important;

    & .loading-wrapper {
      --background: var(
        --loading-overlay-background,
        theme('colors.neutral.900')
      );

      @apply rounded-xl py-5 pr-5 !important;
    }

    /* Customize the text of the loading message */

    & .loading-content {
      padding-left: var(--loading-overlay-size, 16px) !important;

      font-size: var(
        --loading-overlay-font-size,
        theme('fontSize.lg')
      ) !important;
      font-weight: var(
        --loading-overlay-font-weight,
        theme('fontWeight.normal')
      ) !important;
      line-height: var(--loading-overlay-size, 20px);
      color: var(--loading-overlay-text-color, white) !important;

      @apply ml-0 !important;

      /* Insert our custom spinner */

      &::before {
        content: '';

        position: absolute;
        left: var(--loading-overlay-left-margin, 18px);

        width: var(--loading-overlay-size, 22px);
        height: var(--loading-overlay-size, 22px);

        border: 3px solid rgb(255 255 255 / 15%);
        border-top: 3px solid rgb(255 255 255 / 80%);
        border-radius: 50%;

        animation: var(
          --loading-overlay-animation,
          loading-spin 1.5s linear infinite
        );
      }
    }
  }

  .md .loading-overlay .loading-content {
    /* On Android, move the spinner closer to the text */
    @apply ml-2 !important;

    &::before {
      left: var(--loading-overlay-left-margin, 16px);
    }
  }

Then create a spinner like this:

  const controller = await loadingController.create({
    message: toValue(message),
    spinner: null,
    cssClass: 'loading-overlay',
  })
crypt096 commented 1 week ago

Please add this option as soon as possible!