Problem 💡
Jeste li se ikada susreli sa situacijom gdje trebate implementirati loading state za gumb, ali nakon klika gumb mijenja veličinu?
Slika: Gumb koji mijenja velicinu u loading state-u
Ovo ponašanje može izgledati nespretno, neprofesionalno i neprijateljski prema korisniku. Također uvodi pomake u izgledu, što negativno utječe na korisničko iskustvo vaše web stranice i SEO izvedbu. Kako to spriječiti? Kako pristupiti ovom problemu?
Postoje dva glavna pristupa za rješavanje ovog problema, ali jedan se ističe kao bolji. Istražimo oba.
- Absolute positioning pristup
- Grid stacking pristup
Početni pristup
U početku biste mogli pomisliti prikazati tekst prema zadanim postavkama i, kada je loading state aktivan, zamijeniti tekst spinnerom. Evo kako možete kreirati osnovnu komponentu gumba:
1import Spinner from './spinner';
2
3type TProps = {
4 text: string;
5 isLoading?: boolean;
6 onClick: () => void;
7};
8const Button = ({ text, isLoading, onClick }: TProps) => {
9 return (
10 <button
11 onClick={onClick}
12 className='px-4 py-2 bg-purple-500 text-white flex justify-between items-center'
13 >
14 {!isLoading ? <span>{text}</span> : <Spinner />}
15 </button>
16 );
17};
18
19export default Button;
20
Kao što vidite, ovo je jednostavna komponenta gumba s isLoading
svojstvom koje određuje hoće li biti prikazan tekst ili spinner.
1'use client';
2
3import Button from './components/button';
4import { useState } from 'react';
5
6export default function Home() {
7 const [isLoading, setIsLoading] = useState(false);
8
9 const handleLoading = () => {
10 setIsLoading(true);
11 setTimeout(() => {
12 setIsLoading(false);
13 }, 3000);
14 };
15
16 return (
17 <main className='grid place-items-center h-screen'>
18 <Button
19 text='Submit this form'
20 onClick={() => handleLoading()}
21 isLoading={isLoading}
22 />
23 </main>
24 );
25}
26
Evo kako biste prikazali komponentu gumba.
Međutim, ovaj pristup dovodi do problema s promjenom veličine: kada se prikazuje tekst, gumb ima veću veličinu, a kada se aktivira loading state, gumb postaje uži. Ovdje trebamo ponovno razmotriti rješenje.
1. Absolute Positioning pristup 📊
Kako biste riješili problem promjene veličine, možete koristiti apsolutno pozicioniranje. Ovo zahtijeva nekoliko prilagodbi unutar komponenti gumba i spinnera.
1import Spinner from './spinner';
2
3type TProps = {
4 text: string;
5 isLoading?: boolean;
6 onClick: () => void;
7};
8const Button = ({ text, isLoading, onClick }: TProps) => {
9 return (
10 <button
11 onClick={onClick}
12 className='px-4 py-2 bg-purple-500 text-white flex justify-between items-center relative'
13 >
14 <span className={`${isLoading ? 'invisible' : 'visible'}`}>{text}</span>
15 {isLoading && (
16 <div className='absolute inset-0 w-full flex items-center justify-center'>
17 <Spinner />
18 </div>
19 )}
20 </button>
21 );
22};
23
24export default Button;
25
Promjene uključuju dodavanje klase relative
gumbu, izmjenu načina prikazivanja teksta gumba tijekom loading state i omatanje komponente Spinner
dodatnim spremnikom.
Ali u čemu je nedostatak ovog pristupa? Iako rješava problem promjene veličine, uvodi novi problem - preljev teksta.
Slika: Problem sa absolute pristupom
Kao što je ovdje prikazano, ako je poruka loading state dulja od početnog teksta gumba, dolazi do prelijevanja teksta. Za rješavanje ovog problema potrebno je bolje rješenje.
2. Grid Stacking pristup 🌐
Grid stacking pruža elegantan i robustan način rješavanja ovog problema. Kod ovog pristupa sve promjene potrebne su unutar same komponente gumba.
1import Spinner from './spinner';
2
3type TProps = {
4 text: string;
5 isLoading?: boolean;
6 onClick: () => void;
7};
8const Button = ({ text, isLoading, onClick }: TProps) => {
9 return (
10 <button
11 onClick={onClick}
12 className={`px-4 py-2 bg-purple-500 text-white grid [grid-template-areas:stack]`}
13 >
14 <span
15 className={`[grid-area:stack] ${!isLoading ? 'visible' : 'invisible'}`}
16 >
17 {text}
18 </span>
19 <Spinner
20 className={`[grid-area:stack] place-self-center ${
21 isLoading ? 'visible' : 'invisible'
22 }`}
23 />
24 </button>
25 );
26};
27
28export default Button;
29
Prvo, trebate dodati grid
kao className gumbu, čime omogućavate CSS Grid izgled. Ključni className ovdje je [grid-template-areas:stack]
, koji definira jedno grid područje nazvano stack
. Ovo osigurava da se svi child elementi slažu jedan na drugi.
Kako bi grid-područja radila, primijenite [grid-area:stack]
na tekst gumba i komponentu Spinner
. Dodatno, koristite className poput visible
i invisible
za upravljanje vidljivošću elemenata prema potrebi.
Slika: Grid stacking pristup output
Izlaz spinnera ostaje identičan pristupu s apsolutnim pozicioniranjem. Međutim, grid stacking nudi značajnu prednost.
Slika: Grid stacking approach sa dužom loading porukom
Kao što je prikazano, grid pristup uzima u obzir najveći child element prilikom određivanja veličine gumba. To znači da početna veličina gumba već uključuje dulju poruku loading state, sprječavajući pomake u izgledu.
Zaključak 🔧
Ovaj blog post detaljno opisuje dva pristupa za rješavanje problema promjene veličine gumba tijekom loading state: apsolutno pozicioniranje i grid stacking. Iako apsolutno pozicioniranje nudi brzo rješenje, grid stacking se pokazuje kao superiornije rješenje jer izbjegava preljev teksta i pomake u izgledu, čime poboljšava korisničko iskustvo i održava SEO izvedbu.
U AS Agency, mi pažljivo pristupamo ovim nijansama kako bismo osigurali da web stranice naših klijenata budu dotjerane, prilagođene korisnicima i optimizirane za najbolju izvedbu. Ako tražite tim koji prioritet daje svakom detalju kako bi isporučio izvanredne rezultate, ne tražite dalje. Stvorimo nešto nevjerojatno zajedno! ✨