diff --git a/README.md b/README.md index 89141f50..e8c9485e 100644 --- a/README.md +++ b/README.md @@ -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 `` 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. diff --git a/demo/components.html b/demo/components.html index e4b882de..83c9034d 100644 --- a/demo/components.html +++ b/demo/components.html @@ -1,17 +1,17 @@ - + Rapidez Blade Components Preview - +

Rapidez Blade Components preview

@@ -250,109 +250,63 @@

Slideover Component

Default

-
-
-
-
-

This is an example of the slideover component.

-

You can put any content here!

-
-
-
-
+ +
+ Content +
+ Footer +
+

Right-positioned

-
-
-
-
-

This slideover appears from the right side.

-

It demonstrates the position property.

-
-
- - + +
+ Content +
+ Footer +
+

Mobile only

-
-
-
-
-

This is a mobile-specific slideover that transforms on desktop.

-

On mobile devices, it appears as a slideover.

-

On desktop screens (lg breakpoint and above), this content is directly embedded in the page instead of being in a slideover.

-
-
- - + +
+ This content will be visible on desktop, however if you scale to mobile resolution this will be hidden inside a slideover. +
+ Footer +
+ diff --git a/resources/js/polyfill.js b/resources/js/polyfill.js new file mode 100644 index 00000000..e1b6de7a --- /dev/null +++ b/resources/js/polyfill.js @@ -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'] + }) +} diff --git a/resources/views/components-preview.blade.php b/resources/views/components-preview.blade.php index 6f47dec9..e00714c0 100644 --- a/resources/views/components-preview.blade.php +++ b/resources/views/components-preview.blade.php @@ -1,5 +1,5 @@ - + @@ -10,7 +10,7 @@ Rapidez Blade Components Preview - +

Rapidez Blade Components preview

@@ -207,14 +207,21 @@

Default

- + Open Slideover - -
-

This is an example of the slideover component.

-

You can put any content here!

-
+ + + + Title + + + + Content + + + Footer +
@@ -222,29 +229,42 @@

Right-positioned

- + Open Right Slideover - -
-

This slideover appears from the right side.

-

It demonstrates the position property.

-
+ + + + Title + + + + Content + + + Footer +

Mobile only

- + Open Mobile Slideover - -
-

This is a mobile-specific slideover that transforms on desktop.

-

On mobile devices, it appears as a slideover.

-

On desktop screens (lg breakpoint and above), this content is directly embedded in the page instead of being in a slideover.

-
+ + + + Title + + + + This content will be visible on desktop, however if you scale to mobile resolution this will be hidden inside a slideover. + + + Footer +
diff --git a/resources/views/components/slideover/back.blade.php b/resources/views/components/slideover/back.blade.php new file mode 100644 index 00000000..98a20734 --- /dev/null +++ b/resources/views/components/slideover/back.blade.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/views/components/slideover/close.blade.php b/resources/views/components/slideover/close.blade.php new file mode 100644 index 00000000..688565da --- /dev/null +++ b/resources/views/components/slideover/close.blade.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/views/components/slideover/content.blade.php b/resources/views/components/slideover/content.blade.php new file mode 100644 index 00000000..145312bc --- /dev/null +++ b/resources/views/components/slideover/content.blade.php @@ -0,0 +1,3 @@ +
twMerge('p-5 overflow-y-auto max-h-full') }}> + {{ $slot }} +
\ No newline at end of file diff --git a/resources/views/components/slideover/footer.blade.php b/resources/views/components/slideover/footer.blade.php new file mode 100644 index 00000000..72aec06f --- /dev/null +++ b/resources/views/components/slideover/footer.blade.php @@ -0,0 +1,3 @@ +
twMerge('p-5 flex gap-x-4')}}> + {{ $slot }} +
\ No newline at end of file diff --git a/resources/views/components/slideover/header.blade.php b/resources/views/components/slideover/header.blade.php new file mode 100644 index 00000000..830a5e2a --- /dev/null +++ b/resources/views/components/slideover/header.blade.php @@ -0,0 +1,3 @@ +
twMerge('p-5 bg-primary text-primary-text flex gap-x-4 items-center text-center justify-center text-lg relative') }}> + {{ $slot }} +
\ No newline at end of file diff --git a/resources/views/components/slideover/mobile.blade.php b/resources/views/components/slideover/mobile.blade.php deleted file mode 100644 index 669c8400..00000000 --- a/resources/views/components/slideover/mobile.blade.php +++ /dev/null @@ -1,19 +0,0 @@ -{{-- -This mobile version transforms into inline content on desktop. The slideover behavior only applies on mobile screens. - -## Behavior -- On mobile: Functions as a regular slideover -- On desktop: Content is displayed directly on the page -- Label should be hidden on desktop using `lg:hidden` - -## Example -```blade - - - Mobile slideover - -``` ---}} -@include('rapidez::components.slideover.slideover', ['attributes' => $attributes->class('lg:contents [&>.slideover-wrapper]:lg:contents [&>.slideover-header]:lg:hidden mobile-slideover')]) diff --git a/resources/views/components/slideover/mobile/content.blade.php b/resources/views/components/slideover/mobile/content.blade.php new file mode 100644 index 00000000..998b728d --- /dev/null +++ b/resources/views/components/slideover/mobile/content.blade.php @@ -0,0 +1,3 @@ + + {{ $slot }} + \ No newline at end of file diff --git a/resources/views/components/slideover/mobile/footer.blade.php b/resources/views/components/slideover/mobile/footer.blade.php new file mode 100644 index 00000000..f81bdcfe --- /dev/null +++ b/resources/views/components/slideover/mobile/footer.blade.php @@ -0,0 +1,3 @@ + + {{ $slot }} + \ No newline at end of file diff --git a/resources/views/components/slideover/mobile/header.blade.php b/resources/views/components/slideover/mobile/header.blade.php new file mode 100644 index 00000000..ff368703 --- /dev/null +++ b/resources/views/components/slideover/mobile/header.blade.php @@ -0,0 +1,3 @@ + + {{ $slot }} + \ No newline at end of file diff --git a/resources/views/components/slideover/mobile/mobile.blade.php b/resources/views/components/slideover/mobile/mobile.blade.php new file mode 100644 index 00000000..65be37ab --- /dev/null +++ b/resources/views/components/slideover/mobile/mobile.blade.php @@ -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 + + + + + Title + + + + Content + + + Footer + + +``` +--}} + + + {{ $slot }} + \ No newline at end of file diff --git a/resources/views/components/slideover/partials/header.blade.php b/resources/views/components/slideover/partials/header.blade.php deleted file mode 100644 index 616726c3..00000000 --- a/resources/views/components/slideover/partials/header.blade.php +++ /dev/null @@ -1,21 +0,0 @@ -
-
-
- @if ($hasParent) - - @elseif ($headerbutton->isNotEmpty()) - {{ $headerbutton }} - @endif - @if ($title) - attributes->class('text-base max-w-full px-10 truncate font-semibold text-white antialiased') }}> - {{ $title }} - - @endif - -
-
-
diff --git a/resources/views/components/slideover/slideover.blade.php b/resources/views/components/slideover/slideover.blade.php index 63638959..34c9d6d5 100644 --- a/resources/views/components/slideover/slideover.blade.php +++ b/resources/views/components/slideover/slideover.blade.php @@ -1,98 +1,80 @@ {{-- -No-js slideover component by making use of checkboxes and form reset logic for nested slideovers. +No-js slideover component by making use of the amazing dialog or popover html functionality. ## Properties -- `has-parent` Used for nested slideovers. Set to `true` when this slideover is a child of another slideover -- `id` Unique identifier for the checkbox input. Required for the slideover toggle -- `open` Default `false`, set to `true` to have it open initially - `position` Position of the slideover. Can be `left` or `right`. Defaults to `left` -- `tag` Base tag of the slideover. Set to `form` for parent of nested slideover. Defaults to `div` -- `title` Title displayed in the header. Can be provided as a slot or property -## Slots -- `headerbutton` Custom button in the header instead of the default left arrow -- `title` Alternative way to set the header title -- Default slot Main content of the slideover - -## Body Class -Add this class to prevent scrolling when slideover is open: +## HTML Class +Add this class to prevent scrolling when popover is open: ```html - + ``` -## Nesting Slideovers -For nested slideovers: -1. Parent slideover must use `tag="form"` -2. Child slideovers must set `has-parent="true"` -3. Child slideovers should use default `tag="div"` +## Choose between a dialog or popover +```blade + -This setup enables form reset logic and ensures proper background backdrop behavior. + +``` ## Examples Basic usage: ```blade - - - Content goes here + + + + + Title + + + + Content + + + Footer + ``` Right-positioned slideover: ```blade - - - Settings content - + ``` -Nested slideovers: +Nested slideovers (inside ): ```blade - - - Parent content - - - Child content - + + + + + + Parent title + + + + Parent content + ``` --}} -@props(['id' => uniqid('slideover-'), 'title', 'hasParent' => false, 'position' => 'left', 'tag' => 'div', 'open' => false]) -@slots(['title', 'headerbutton']) - -@php - $isInForm = $tag === 'form' || $hasParent; - $closeId = $isInForm ? 'close-' . $id : $id; -@endphp +@props(['position' => 'left']) - - - @if (!$hasParent) - - - @else - - @endif -
class([ - 'fixed inset-y-0 transition-all bg-white z-slideover-sidebar flex flex-col max-w-md w-full', - '-right-full peer-checked:right-0' => $position === 'right', - '-left-full peer-checked:left-0' => $position === 'left', - ]) }}> - @include('rapidez::components.slideover.partials.header') -
- {{ $slot }} -
-
-
+merge(['popover' => '']) + ->twMerge('h-screen w-full max-w-lg shadow-xl z-slideover text open:flex flex-col overscroll-none max-h-full duration-500 backdrop:bg-backdrop backdrop:opacity-0 starting:open:backdrop:opacity-0 open:backdrop:opacity-100 backdrop:transition-opacity backdrop:transition-discrete backdrop:duration-700 transition-all transition-discrete transform-gpu') + ->class([ + '-translate-x-full starting:open:[transform:translateX(-100%)] open:translate-x-0' => $position === 'left', + 'translate-x-full starting:open:[transform:translateX(100%)] open:translate-x-0 ml-auto' => $position === 'right', + ]) +}}> + {{ $slot }} + \ No newline at end of file