Genanvendelige popovers for at tilføje lidt pop

En popover er en forbigående visning, der vises oven på et indhold på skærmen, når en bruger klikker på en kontrolknap eller inden for et defineret område. For eksempel ved at klikke på et info-ikon på et bestemt listeelement for at få elementdetaljerne. Typisk indeholder en popover en pil, der peger på det sted, hvorfra den dukkede op.

Popovers er gode til situationer, hvor vi ønsker at vise en midlertidig kontekst for at få brugerens opmærksomhed, når vi interagerer med et bestemt element på skærmen. De giver ekstra kontekst og instruktion til brugere uden at skulle rode op på en skærm. Brugere kan blot lukke dem ved at klikke på samme måde som de blev åbnet eller uden for popover-vinduet.

Vi skal se på et bibliotek kaldet popper.js, der giver os mulighed for at oprette genanvendelige popover-komponenter i Vue-rammerne. Popovers er den perfekte type komponent til et komponentbaseret system som Vue, fordi de kan være indeholdte, indkapslede komponenter, der vedligeholdes alene, men bruges overalt i en app.

Lad os grave ind og komme i gang.

Men først:Hvad er forskellen mellem en popover og værktøjstip?

Var navnet "popover" smidt på dig? Sandheden er, at popovers er meget som værktøjstip, som er et andet almindeligt UI-mønster til at vise yderligere kontekst i et indeholdt element. Der er dog forskelle mellem dem, så lad os kort beskrive dem, så vi har et solidt greb om, hvad vi bygger.

Værktøjstip Popovers
Værktøjstip er beregnet til at være præcis det, et hint eller tip til, hvad et værktøj eller anden interaktion gør. De er beregnet til at tydeliggøre eller hjælpe dig med at bruge det indhold, som de svæver over, ikke tilføje yderligere indhold. Popovers , på den anden side kan være meget mere udførligt, de kan indeholde en overskrift og mange tekstlinjer i brødteksten.
Værktøjstip er typisk kun synlige ved hover, af den grund, hvis du har brug for at kunne læse indholdet, mens du interagerer med andre dele af siden, så vil et værktøjstip ikke fungere. Popovers er typisk afviselige, hvad enten det er ved at klikke på andre dele af siden eller andet klik på popover-målet (afhængigt af implementeringen), af den grund kan du konfigurere en popover, så du kan interagere med andre elementer på siden, mens du stadig er i stand til at læs dets indhold.

Popovers er mest passende på større skærme, og vi støder højst sandsynligt på dem i tilfælde af brug som:

  • rullemenuer (navigationsmenu, brugerdefineret valg)
  • bruger onboarding
  • midlertidige formularer
  • interaktionsmenuer for listeelementer

Når vi ser på disse use cases, kan vi samle nogle krav, der gør en god popover:

  1. Genbrugbarhed: En popover skal give mulighed for at videregive et tilpasset indhold til popover.
  2. Afvisning: En popover bør kunne afvises ved at klikke uden for popover- og escape-knappen.
  3. Placering: En popover skal flytte sig selv, når skærmkanten er nået.
  4. Interaktion: En popover skal gøre det muligt at interagere med indholdet i popover.

Jeg har lavet et eksempel, som vi skal referere til, mens vi går gennem processen med at skabe en komponent.

Se demo

OK, nu hvor vi har en grundlæggende forståelse af popovers og hvad vi bygger, lad os komme ind på de trinvise detaljer for at oprette dem ved hjælp af popper.js.

Trin 1:Opret BasePopover-komponenten

Lad os starte med at oprette en komponent, der vil være ansvarlig for initialisering og placering af popover. Vi kalder denne komponent BasePopover.vue og i komponentskabelonen gengiver vi to elementer:

  • Popover-indhold: Dette er det element, der vil være ansvarlig for at gengive indholdet i popoveren. Indtil videre bruger vi en slot, der giver os mulighed for at videregive indholdet fra den overordnede komponent, der er ansvarlig for gengivelsen af ​​vores popover (krav #1:Genanvendelighed).
  • Popover-overlejring: Dette er det element, der er ansvarligt for at dække indholdet under popoveren og forhindre brugeren i at interagere med elementerne uden for popoveren. Det giver os også mulighed for at lukke popover-vinduet, når der klikkes på (Krav #2:Afvisbarhed).
// BasePopover.vue
<template>
  <div>
    <div
      ref="basePopoverContent"
      class="base-popover"
    >
      <slot />
    </div>
    <div
      ref="basePopoverOverlay"
      class="base-popover__overlay"
    />
  </div>
</template>

I scriptsektionen af ​​komponenten:

  • vi importerer popper.js (biblioteket, der tager sig af popover-positioneringen), derefter
  • vi modtager popoverOptions rekvisitter, og til sidst
  • vi sætter initial popperInstance til null (fordi vi i første omgang ikke har nogen popover).

Lad os beskrive, hvad popoverOptions er objektet indeholder:

  • popoverReference :Dette er et objekt i forhold til hvilket popover'et vil blive placeret (normalt element, der udløser popover).
  • placement :Dette er en popper.js-placeringsindstilling, der angiver, hvor popover-billedet vises i forhold til popover-referenceelementet (det det er knyttet til)
  • offset :Dette er en popper.js offset-modifikator, der giver os mulighed for at justere popover-positionen ved at sende x- og y-koordinater.
import Popper from "popper.js"

export default {
  name: "BasePopover",

  props: {
    popoverOptions: {
      type: Object,
      required: true
    }
  },

  data() {
    return {
      popperInstance: null
    }
  }
}

Hvorfor har vi brug for det? Popper.js-biblioteket giver os mulighed for nemt at placere elementet i forhold til et andet element. Det gør også magien, når popoveren kommer til kanten af ​​skærmen og flytter den, så den altid er i brugerens visningsport (Krav #3:Positionering)

Trin 2:Initialiser popper.js

Nu hvor vi har en BasePopover komponentskelet, vil vi tilføje nogle få metoder, der vil være ansvarlige for at placere og vise popoveren.

I initPopper metode, vil vi starte med at oprette en modifiers objekt, der vil blive brugt til at oprette en Popper-instans. Vi indstiller de indstillinger, der modtages fra den overordnede komponent (placement og offset ) til de tilsvarende felter i modifiers objekt. Alle disse felter er valgfrie, og derfor skal vi først kontrollere, om de findes.

Derefter initialiserer vi en ny Popper instans ved at sende:

  • den popoverReference node (det element, som popoveren peger på:popoverReference ref)
  • popper-indholdsknuden (elementet, der indeholder popover-indholdet:basePopoverContent ref)
  • den options objekt

Vi indstiller også preventOverflow mulighed for at forhindre popover i at blive placeret uden for viewporten. Efter initialisering indstiller vi popper-forekomsten til vores popperInstance data-egenskab for at have adgang til metoder og egenskaber leveret af popper.js i fremtiden.

methods: {
...
  initPopper() {
    const modifiers = {}
    const { popoverReference, offset, placement } = this.popoverOptions
  
    if (offset) {
      modifiers.offset = {
        offset
      }
    }
  
    if (placement) {
      modifiers.placement = placement
    }
  
    this.popperInstance = new Popper(
      popoverReference,
      this.$refs.basePopoverContent,
      {
        placement,
        modifiers: {
          ...modifiers,
          preventOverflow: {
            boundariesElement: "viewport"
          }
        }
      }
    )
  }
...
}

Nu hvor vi har vores initPopper metode klar, vi har brug for et sted at påberåbe den. Det bedste sted til det er i den monterede livscykluskrog.

mounted() {
  this.initPopper()
  this.updateOverlayPosition()
}

Som du kan se, kalder vi endnu en metode i den monterede krog:updateOverlayPosition metode. Denne metode er en sikring, der bruges til at omplacere vores overlejring, hvis vi har andre elementer på siden, der har absolut positionering (f.eks. NavBar , SideBar ). Metoden sikrer, at overlejringen altid dækker hele skærmen og forhindrer brugeren i at interagere med ethvert element undtagen popover og selve overlejringen.

methods: {
...
  updateOverlayPosition() {
    const overlayElement = this.$refs.basePopoverOverlay;
    const overlayPosition = overlayElement.getBoundingClientRect();
  
    overlayElement.style.transform = <code>translate(-${overlayPosition.x}px, -${
      overlayPosition.y
    }px)`;
  }
...
}

Trin 3:Ødelæg Popper

Vi har vores popper initialiseret, men nu har vi brug for en måde at fjerne og bortskaffe den, når den bliver lukket. Der er ingen grund til at have det i DOM på det tidspunkt.

Vi ønsker at lukke popover-vinduet, når vi klikker et vilkårligt sted uden for det. Vi kan gøre det ved at tilføje en kliklytter til overlejringen, fordi vi sørgede for, at overlejringen altid dækker hele skærmen under vores popover

<template>
...
  <div
    ref="basePopoverOverlay"
    class="base-popover__overlay"
    @click.stop="destroyPopover"
  />
...
</template>

Lad os skabe en metode, der er ansvarlig for at ødelægge popover. I den metode tjekker vi først om popperInstance faktisk eksisterer, og hvis det gør, kalder vi popper destroy metode, der sikrer, at popper-forekomsten bliver ødelagt. Derefter renser vi vores popperInstance dataegenskab ved at indstille den til null og udsende en closePopover hændelse, der vil blive håndteret i den komponent, der er ansvarlig for gengivelse af popover.

methods: {
...
  destroyPopover() {
      if (this.popperInstance) {
        this.popperInstance.destroy();
        this.popperInstance = null;
        this.$emit("closePopover");
      }
    }
...
}

Trin 4:Gengiv BasePopover-komponent

OK, vi har vores popover klar til at blive gengivet. Det gør vi i vores overordnede komponent, som vil være ansvarlig for at administrere synligheden af ​​popover og videregive indholdet til det.

I skabelonen skal vi have et element, der er ansvarligt for at udløse vores popover (popoverReference ) og BasePopover komponent. BasePopover komponent modtager en popoverOptions egenskab, der vil fortælle komponenten, hvordan vi vil vise den og isPopoverVisible egenskab bundet til v-if direktiv, der vil være ansvarlig for at vise og skjule popover.

<template>
  <div>
    <img
      ref="popoverReference"
      width="25%"
      src="./assets/logo.png"
    >
    <BasePopover
      v-if="isPopoverVisible"
      :popover-options="popoverOptions"
    >
      <div class="custom-content">
        <img width="25%" src="./assets/logo.png">
        Vue is Awesome!
      </div>
    </BasePopover>
  </div>
</template>

I scriptsektionen af ​​komponenten importerer vi vores BasePopover komponent, skal du indstille isPopoverVisible flag indledningsvis til false og popoverOptions objekt, der vil blive brugt til at konfigurere popover på init.

data() {
  return {
    isPopoverVisible: false,
    popoverOptions: {
      popoverReference: null,
      placement: "top",
      offset: "0,0"
    }
  };
}

Vi indstiller popoverReference ejendom til null oprindeligt fordi det element, der vil være popover-udløseren, ikke eksisterer, når vores overordnede komponent oprettes. Vi får det rettet i den monterede livscykluskrog, når komponenten (og popover-referencen) bliver gengivet.

mounted() {
  this.popoverOptions.popoverReference = this.$refs.popoverReference;
}

Lad os nu oprette to metoder, openPopover og closePopover der vil være ansvarlig for at vise og skjule vores popover ved at indstille den korrekte værdi på isPopoverVisible ejendom.

methods: {
  closePopover() {
    this.isPopoverVisible = false;
  },
  openPopover() {
    this.isPopoverVisible = true;
  }
}

Den sidste ting, vi skal gøre i dette trin, er at vedhæfte disse metoder til passende elementer i vores skabelon. Vi vedhæfter openPopover metode til at klikke på hændelse på vores triggerelement og closePopover metode til closePopover hændelse udsendt fra BasePopover komponent, når popoveren bliver ødelagt ved at klikke på popover-overlejringen.

<template>
  <div>
    <img
      ...
      @click="openPopover"
    >
    <BasePopover
      ...
      @closePopover="closePopover"
    >
      ...
    </BasePopover>
  </div>
</template>

Når vi har dette på plads, får vi vores popover vist, når vi klikker på triggerelementet og forsvinder, når vi klikker uden for popoveren.