Playing Next Lesson In
seconds

EdgeJS Components #4.2

Adding Conditional Icon, Headline, and Message Content

In This Lesson

We'll make our alert's icon, headline, and message completely optional. We'll also allow our icon to be provided via slot and our headline and message via prop or slot.

Created by
@tomgobich
Published

Now that we have our variants out of the way, let’s focus on adding the content options for our alert which will enable us to provide our own:

  • Icon

  • Headline

  • Message

Icon Content

We kind of already have our icon conditionally added via our showIcon variable, but we can make this a lot better by instead relying on the existence of a slot.

We can conditionally check whether a slot is provided by checking for its existence on the $slots property of our component’s state. For example, we’ll be calling our icon slot icon, so we can check for its existence using $slots.icon.

--@set('showIcon', showIcon ?? true)

@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`,
})

<div 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' : '' }}">
<div 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] {{ showIcon ? 'pl-11' : '' }}">

    @if ($slots.icon)
    @if (showIcon)
      {{{ await $slots.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>
    @endif
    <h5 class="mb-1 font-medium leading-none tracking-tight">Alert Message Headline</h5>
    <div class="text-sm opacity-70">This is the subtext for your alert message, providing important information or instructions.</div>
</div>
Copied!
  • resources
  • views
  • components
  • alert
  • index.edge

Here we’re replacing showIcon in both our class ternary and if statement with $slots.icon, then we’re removing the showIcon variable as it’s no longer needed. Then, we’re replacing the svg icon with a slot called icon.

Now in our demonstration, if we don’t specify an icon in our alert component, it’ll render a-okay without one. Or, we can add an icon via the icon slot and it’ll render it accordingly.

@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()
      @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

      Default, Base Alert
    @end
  </div>

  {{-- variants --}}
@end
Copied!
  • resources
  • views
  • pages
  • components
  • alert.edge

Headline Content

Next, let’s take care of our H5 headline. We’ll want to accept either a prop or a slot for the content, and we’ll also want to make it completely optional. We’ll call both the prop and slot title, and we’ll want to check to ensure one or the other is provided before rendering out our H5 element so that it doesn’t get added to our DOM if we don’t have a headline to show.

@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`,
})

<div 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">
        Alert Message Headline
      </h5>
    @endif

    <div class="text-sm opacity-70">This is the subtext for your alert message, providing important information or instructions.</div>
</div>
Copied!
  • resources
  • views
  • components
  • alert
  • index.edge

For our H5’s content, we’ll then want to use an if/else check to determine which of the two should be displayed.

@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`,
})

<div 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

    <div class="text-sm opacity-70">This is the subtext for your alert message, providing important information or instructions.</div>
</div>
Copied!
  • resources
  • views
  • components
  • alert
  • index.edge

We can now go ahead and add a title prop or slot to our base alert demonstration

@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({ title: 'Alert Title' })
    @alert()
      @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

      Default, Base Alert
    @end
  </div>

  {{-- variants --}}
@end
Copied!
  • resources
  • views
  • pages
  • components
  • alert.edge

Message Content

Lastly, we have our message content. Like our title, we’ll want to allow our message to be specified as either a prop or slot, however, we’ll use the main slot for this one and call the prop message.

@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`,
})

<div 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

    <div class="text-sm opacity-70">
      @if (message)
        {{ message }}
      @else
        {{{ await $slots.main() }}}
      @endif
      This is the subtext for your alert message, providing important information or instructions.
    </div>
</div>
Copied!
  • resources
  • views
  • components
  • alert
  • index.edge

Great, we’ve got that sorted out, however, like the headline, we also want the message to be completely optional. So, we’ll need a way to determine if we have a message prop or if our main slot has content. This can be easily done with the message prop, however, we’ll need to know our main slot’s content to determine if it has content.

To remedy this, we can get the content by returning the result of the $slots.main() method into a variable.

@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`,
})

<div 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 }}}
        {{{ await $slots.main() }}}
      @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 awaiting the result of our $slots.main() call and storing the return value in a variable called mainSlot. This will contain the string content of the slot. If slot content isn’t provided, this will be an empty value, and fail the if check.

Then, since we already have the slot value, if we need to render it out, we can just plop the stored mainSlot value directly into where the slot content should be displayed.

Join the Discussion 0 comments

Create a free account to join in on the discussion
robot comment bubble

Be the first to comment!