Lastly, for our alerts, let’s add in a little interactivity. In this lesson, we’re going to make our alerts:
Dismissible, meaning that if we click a close button the alert should go away.
Self-destructible, meaning that after a defined time the alert should remove itself.
Making Our Alerts Dismissible
First, let’s conditionally make our alerts dismissible. Since our AlpineJS state for this is going to be pretty simple, and we’ll want to derive the default value from our component’s props, I’m going to define the x-data within EdgeJS and pass it into AlpineJS as a string.
@set('variant', variant ?? 'light') @set('variants', variants ?? { light: `bg-white text-neutral-900`, dark: `bg-black text-neutral-100`, blue: `border-transparent bg-blue-600 text-white`, red: `border-transparent bg-red-600 text-white`, green: `border-transparent bg-green-500 text-white`, yellow: `border-transparent bg-yellow-500 text-white`, }) @set('slots', { ...$slots, ...$props.$slots }) @set('data', { show: show ?? true, dismissible: dismissible ?? false, }) <div x-data="{{ stringify(data) }}" x-show="show" class="{{ variants[variant] }} relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] {{ slots.icon ? 'pl-11' : '' }}"> @if (slots.icon) {{{ await slots.icon() }}} @endif @if (title || slots.title) <h5 class="mb-1 font-medium leading-none tracking-tight"> @if (title) {{ title }} @else {{{ await slots.title() }}} @endif Alert Message Headline </h5> @endif @set('mainSlot', await slots.main()) @if (message || mainSlot) <div class="text-sm opacity-70"> @if (message) {{ message }} @else {{{ mainSlot }}} @endif This is the subtext for your alert message, providing important information or instructions. </div> @endif </div>Copied!
- resources
- views
- components
- alert
- index.edge
Here we’re defining a variable called data within EdgeJS that has the properties show and dismissible. Both of these properties will attempt to get their default value from the component props and use a fallback value if a prop value isn’t provided.
Then, we’re using the stringify utility to convert data into an HTML-safe string that we’re passing directly into AlpineJS as our state.
Lastly, we’re using the show value from our AlpineJS state to determine whether the alert should be displayed.
Adding Our Dismiss Button
Now we need to add our dismiss button when the alert is dismissible.
@set('variant', variant ?? 'light') @set('variants', variants ?? { light: `bg-white text-neutral-900`, dark: `bg-black text-neutral-100`, blue: `border-transparent bg-blue-600 text-white`, red: `border-transparent bg-red-600 text-white`, green: `border-transparent bg-green-500 text-white`, yellow: `border-transparent bg-yellow-500 text-white`, }) @set('slots', { ...$slots, ...$props.$slots }) @set('data', { show: show ?? true, dismissible: dismissible ?? false, }) <div x-data="{{ stringify(data) }}" x-show="show" class="{{ variants[variant] }} relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] {{ slots.icon ? 'pl-11' : '' }}"> <button x-show="dismissible" class="absolute top-4 right-4" @click="show = false"> <svg xmlns="<http://www.w3.org/2000/svg>" class="icon icon-tabler icon-tabler-x" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M18 6l-12 12"></path> <path d="M6 6l12 12"></path> </svg> </button> @if (slots.icon) {{{ await slots.icon() }}} @endif @if (title || slots.title) <h5 class="mb-1 font-medium leading-none tracking-tight"> @if (title) {{ title }} @else {{{ await slots.title() }}} @endif Alert Message Headline </h5> @endif @set('mainSlot', await slots.main()) @if (message || mainSlot) <div class="text-sm opacity-70"> @if (message) {{ message }} @else {{{ mainSlot }}} @endif This is the subtext for your alert message, providing important information or instructions. </div> @endif </div>Copied!
- resources
- views
- components
- alert
- index.edge
Here we’ve added a button, absolutely positioned to the top-right corner of our alert. The button is only displayed when the AlpineJS variable dismissible is truthy. Then, when the button is clicked, we’re setting the show AlpineJS value to false so that the alert itself disappears.
Making Our Alert Self Destructible
Next, let’s make our alert able to conditionally auto-remove itself after a defined portion of time. For this, we’ll add two new variables to our data state.
selfDestruct- Controls whether the alert will self-destructselfDestructTimeout- Controls how long the alert will remain visible before self-destructing
@set('variant', variant ?? 'light') @set('variants', variants ?? { light: `bg-white text-neutral-900`, dark: `bg-black text-neutral-100`, blue: `border-transparent bg-blue-600 text-white`, red: `border-transparent bg-red-600 text-white`, green: `border-transparent bg-green-500 text-white`, yellow: `border-transparent bg-yellow-500 text-white`, }) @set('slots', { ...$slots, ...$props.$slots }) @set('data', { show: show ?? true, dismissible: dismissible ?? false, selfDestruct: selfDestruct ?? false, selfDestructTimeout: selfDestructTimeout ?? 5000 }) {{-- ... --}}Copied!
- resources
- views
- components
- alert
- index.edge
Then, when selfDestruct is truthy, we need to initialize our timeout, set to the time of our selfDestructTimeout. For this, we can make use of AlpineJS’ x-init attribute. This is called whenever AlpineJS initializes the element the x-init resides on.
@set('variant', variant ?? 'light') @set('variants', variants ?? { light: `bg-white text-neutral-900`, dark: `bg-black text-neutral-100`, blue: `border-transparent bg-blue-600 text-white`, red: `border-transparent bg-red-600 text-white`, green: `border-transparent bg-green-500 text-white`, yellow: `border-transparent bg-yellow-500 text-white`, }) @set('slots', { ...$slots, ...$props.$slots }) @set('data', { show: show ?? true, dismissible: dismissible ?? false, selfDestruct: selfDestruct ?? false, selfDestructTimeout: selfDestructTimeout ?? 5000 }) <div x-data="{{ stringify(data) }}" x-show="show" class="{{ variants[variant] }} relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] {{ slots.icon ? 'pl-11' : '' }}"> <button x-show="dismissible" x-init="selfDestruct && setTimeout(() => show = false, selfDestructTimeout)" class="absolute top-4 right-4" @click="show = false"> <svg xmlns="<http://www.w3.org/2000/svg>" class="icon icon-tabler icon-tabler-x" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M18 6l-12 12"></path> <path d="M6 6l12 12"></path> </svg> </button> @if (slots.icon) {{{ await slots.icon() }}} @endif @if (title || slots.title) <h5 class="mb-1 font-medium leading-none tracking-tight"> @if (title) {{ title }} @else {{{ await slots.title() }}} @endif Alert Message Headline </h5> @endif @set('mainSlot', await slots.main()) @if (message || mainSlot) <div class="text-sm opacity-70"> @if (message) {{ message }} @else {{{ mainSlot }}} @endif This is the subtext for your alert message, providing important information or instructions. </div> @endif </div>Copied!
- resources
- views
- components
- alert
- index.edge
Now, we can successfully tell our alert to be dismissible, self-destructible, or both!
@layout.app({ title: request.params().name }) <h3 class="font-bold">Base Alert</h3> <div x-data class="mb-8 space-x-3 space-y-3"> @alert({ dismissable: true, selfDestruct: true }) @slot('icon') <svg class="w-4 h-4" xmlns="<http://www.w3.org/2000/svg>" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"></polyline><line x1="12" x2="20" y1="19" y2="19"></line></svg> @endslot @slot('title') Default, Base Alert @endslot This is our message! @end </div> {{-- ... --}} @endCopied!
- resources
- views
- pages
- components
- alert.edge