Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ php artisan vendor:publish --tag=rapidez-blade-components-views

The [readmore component](https://github.com/rapidez/blade-components/blob/master/resources/views/components/readmore/readmore.blade.php) includes some Javascript, we're using a [Blade Stack](https://laravel.com/docs/master/blade#stacks) named `foot` for that. Make sure you've an `@stack('foot')` before your closing `</body>` tag. Within Rapidez this is already present within the [`layouts/app.blade.php`](https://github.com/rapidez/core/blob/master/resources/views/layouts/app.blade.php).

### Slideover

The slideover component uses a dialog with commandfor, with browser support since 2025. It also has a secondary variant using a popover, which only has browser support since mid-2024.

If you want to use a polyfill for the dialog variant, you should add the `invokers-polyfill` npm package to your project:

```
yarn add -D invokers-polyfill
```

If you also wish to use the popover variant, we provide an extension to this polyfill that essentially just turns all popover variant slideovers into the dialog variant on unsupported browsers. To use this polyfill, include the `.../js/polyfill.js` file that comes with this package.

## Usage

Just like any other Blade component, check out the [Laravel Blade docs](https://laravel.com/docs/master/blade) and the examples within the components code linked above. All components are prefixed with `x-rapidez::` to avoid any conflicts with existing Blade components within your project.
Expand Down
136 changes: 45 additions & 91 deletions demo/components.html

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions resources/js/polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import "invokers-polyfill"

if (!(typeof HTMLElement !== 'undefined' && typeof HTMLElement.prototype === 'object' && 'popover' in HTMLElement.prototype))
{
function replacePopovers(baseElement) {
function replacePopover(el, newCommand) {
let target = el.getAttribute('popovertarget') ?? el.getAttribute('commandfor')
el.removeAttribute('popovertargetaction')
el.removeAttribute('popovertarget')
el.setAttribute('commandfor', target)
el.setAttribute('command', newCommand)
}

baseElement.querySelectorAll('[command=show-popover]').forEach(el => el.command = 'show-modal')
baseElement.querySelectorAll('[command=toggle-popover]').forEach(el => el.command = 'show-modal')
baseElement.querySelectorAll('[command=hide-popover]').forEach(el => el.command = 'close')

baseElement.querySelectorAll('[popovertargetaction=show]').forEach(el => replacePopover(el, 'show-modal'))
baseElement.querySelectorAll('[popovertargetaction=toggle]').forEach(el => replacePopover(el, 'show-modal'))
baseElement.querySelectorAll('[popovertargetaction=hide]').forEach(el => replacePopover(el, 'close'))
}

replacePopovers(document)

const observer = new MutationObserver((mutations) => {
if (mutations.some((mutation) =>
mutation.target.hasAttribute('popovertargetaction')
|| mutation.target.hasAttribute('popovertarget')
|| mutation.target.hasAttribute('commandfor')
)) {
replacePopovers(mutation.target.parentElement ?? document)
}
})

observer.observe(document, {
attributes: true,
childList: true,
subtree: true,
attributeFilter: ['popovertargetaction', 'popovertarget', 'command']
})
}
62 changes: 41 additions & 21 deletions resources/views/components-preview.blade.php

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this is missing an example of the popover, these are all dialog slideovers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Visually it's exactly the same (same component same styling), within the <x-slideover component i've added simple instructions on how you can use the attributes on your button to trigger it as a popover or dialog.

If others also agree I'll add it :)

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!doctype html>
<html>
<html class="has-[:is([popover]:popover-open,dialog[open])]:overflow-clip">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
Expand All @@ -10,7 +10,7 @@

<title>Rapidez Blade Components Preview</title>
</head>
<body class="has-[.prevent-scroll:checked]:overflow-clip">
<body>
<div class="bg mb-6">
<div class="container mx-auto px-5 py-10">
<h1 class="font-bold text-2xl">Rapidez Blade Components preview</h1>
Expand Down Expand Up @@ -207,44 +207,64 @@
<div class="flex flex-col gap-3">
<h3 class="text-md font-bold">Default</h3>
<div>
<x-rapidez::button.primary for="default-slideover">
<x-rapidez::button.primary command="show-modal" commandfor="default-slideover">
Open Slideover
</x-rapidez::button.primary>
<x-rapidez::slideover id="default-slideover" title="Example Slideover">
<div class="p-4">
<p class="mb-4">This is an example of the slideover component.</p>
<p>You can put any content here!</p>
</div>

<x-rapidez::slideover id="default-slideover" closedby="any">
<x-rapidez::slideover.header>
Title
<x-rapidez::slideover.close command="close" commandfor="default-slideover" />

@JimmyHoenderdaal JimmyHoenderdaal Jul 29, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make command="close" the default for the close and back components and inherit the parent slideover’s id for commandfor, for example using @aware? That way, userss could simply use <x-rapidez::slideover.close /> without repeating these attributes for every slideover, while still being able to override them when needed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some ways but since you can choose between a dialog and a popover those attributes differ from eachother. We can make some magic that that can detect what type you use and add those attributes but that feels wrong in my opinion.

Also when you nest popovers (slideover inside a slideover) you can target the parent popover to close them both if you navigate a layer deeper. By keeping it simple like this it does require some default attributes to specify what you want it to do.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried the following, added aware that checks if parent component has the id attribute, and uses that automatically for the close commandfor and popovertarget. I've added both attributes for dialog and popover by default so that you don't have to explicitly add them each time.

The component looks like the following
image

Issue is that i would have another back.blade.php file that looks like the close.blade.php component doing the exact same thing.

image

</x-rapidez::slideover.header>
<x-rapidez::slideover.content>
Content
</x-rapidez::slideover.content>
<x-rapidez::slideover.footer>
Footer
</x-rapidez::slideover.footer>
</x-rapidez::slideover>
</div>
</div>

<div class="flex flex-col gap-3">
<h3 class="text-md font-bold">Right-positioned</h3>
<div>
<x-rapidez::button.secondary for="right-slideover">
<x-rapidez::button.secondary command="show-modal" commandfor="right-slideover">
Open Right Slideover
</x-rapidez::button.secondary>
<x-rapidez::slideover id="right-slideover" position="right" title="Right Slideover">
<div class="p-4">
<p class="mb-4">This slideover appears from the right side.</p>
<p>It demonstrates the position property.</p>
</div>

<x-rapidez::slideover id="right-slideover" position="right" closedby="any">
<x-rapidez::slideover.header>
Title
<x-rapidez::slideover.close command="close" commandfor="right-slideover" />
</x-rapidez::slideover.header>
<x-rapidez::slideover.content>
Content
</x-rapidez::slideover.content>
<x-rapidez::slideover.footer>
Footer
</x-rapidez::slideover.footer>
</x-rapidez::slideover>
</div>
</div>
<div class="flex flex-col gap-3">
<h3 class="text-md font-bold">Mobile only</h3>
<div>
<x-rapidez::button.outline for="mobile-slideover" class="lg:hidden">
<x-rapidez::button.outline command="show-modal" commandfor="example" class="lg:hidden">
Open Mobile Slideover
</x-rapidez::button.outline>
<x-rapidez::slideover.mobile id="mobile-slideover" title="Mobile Slideover">
<div class="max-lg:p-4">
<p class="mb-4">This is a mobile-specific slideover that transforms on desktop.</p>
<p>On mobile devices, it appears as a slideover.</p>
<p class="mt-4">On desktop screens (lg breakpoint and above), this content is directly embedded in the page instead of being in a slideover.</p>
</div>

<x-rapidez::slideover.mobile id="example" closedby="any">
<x-rapidez::slideover.mobile.header>
Title
<x-rapidez::slideover.close command="close" commandfor="example" />
</x-rapidez::slideover.mobile.header>
<x-rapidez::slideover.mobile.content>
This content will be visible on desktop, however if you scale to mobile resolution this will be hidden inside a slideover.
</x-rapidez::slideover.mobile.content>
<x-rapidez::slideover.mobile.footer>
Footer
</x-rapidez::slideover.mobile.footer>
</x-rapidez::slideover.mobile>
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions resources/views/components/slideover/back.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<button {{ $attributes->twMerge('shrink-0 cursor-pointer hover:opacity-75') }}>
<x-heroicon-o-arrow-left class="size-6" />
</button>
3 changes: 3 additions & 0 deletions resources/views/components/slideover/close.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<button {{ $attributes->twMerge('shrink-0 cursor-pointer hover:opacity-75 absolute right-5 top-1/2 -translate-y-1/2') }}>
<x-heroicon-o-x-mark class="size-7" />
</button>
3 changes: 3 additions & 0 deletions resources/views/components/slideover/content.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div {{ $attributes->twMerge('p-5 overflow-y-auto max-h-full') }}>
{{ $slot }}
</div>
3 changes: 3 additions & 0 deletions resources/views/components/slideover/footer.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div {{ $attributes->twMerge('p-5 flex gap-x-4')}}>
{{ $slot }}
</div>
3 changes: 3 additions & 0 deletions resources/views/components/slideover/header.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div {{ $attributes->twMerge('p-5 bg-primary text-primary-text flex gap-x-4 items-center text-center justify-center text-lg relative') }}>
{{ $slot }}
</div>
19 changes: 0 additions & 19 deletions resources/views/components/slideover/mobile.blade.php

This file was deleted.

3 changes: 3 additions & 0 deletions resources/views/components/slideover/mobile/content.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<x-rapidez::slideover.content :attributes="$attributes->twMerge('lg:contents')">
{{ $slot }}
</x-rapidez::slideover.content>
3 changes: 3 additions & 0 deletions resources/views/components/slideover/mobile/footer.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<x-rapidez::slideover.footer :attributes="$attributes->twMerge('lg:hidden')">
{{ $slot }}
</x-rapidez::slideover.footer>
3 changes: 3 additions & 0 deletions resources/views/components/slideover/mobile/header.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<x-rapidez::slideover.header :attributes="$attributes->twMerge('lg:hidden')">
{{ $slot }}
</x-rapidez::slideover.header>
32 changes: 32 additions & 0 deletions resources/views/components/slideover/mobile/mobile.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{{--
This mobile version shows content on desktop and hides it within a slideover on mobile resolution.

## Behavior
- On mobile: Functions as a regular slideover
- On desktop: Content is displayed directly on the page
- Button should be hidden on desktop using `lg:hidden`

## Example
```blade
<button commandfor="example" command="show-modal" class="lg:hidden">
Open Mobile Slideover
</button>

<x-rapidez::slideover.mobile id="example" closedby="any">
<x-rapidez::slideover.mobile.header>
Title
<x-rapidez::slideover.close commandfor="example" command="show-modal" />
</x-rapidez::slideover.mobile.header>
<x-rapidez::slideover.mobile.content>
Content
</x-rapidez::slideover.mobile.content>
<x-rapidez::slideover.mobile.footer>
Footer
</x-rapidez::slideover.mobile.footer>
</x-rapidez::slideover.mobile>
```
--}}

<x-rapidez::slideover :attributes="$attributes->twMerge('lg:contents')">
{{ $slot }}
</x-rapidez::slideover>
21 changes: 0 additions & 21 deletions resources/views/components/slideover/partials/header.blade.php

This file was deleted.

Loading
Loading