Wire Elements Modal

repository·main·Indexed 22 days ago

https://github.com/wire-elements/modal

A Livewire component providing a robust modal system that supports multiple stacked child modals while maintaining state. It allows for programmatic opening via the openModal event, custom width configurations, and control over closing behaviors such as Escape key presses or clicking away. The package includes tools for managing modal stacks, including methods to skip or destroy previous modals, and provides a configuration file for global defaults.

Tokens
3.4K
Snippets
10
Records
16
Agent score
29%

What's inside wire-elements-modal

  1. Install Wire Elements Modal

    main

    To install the package, require it via Composer:

    composer require wire-elements/modal

    After installation, you must add the Livewire directive to your main layout template (e.g., app.blade.php) to render the modal container:

    <html>
    <body>
        <!-- your content -->
    
        @livewire('wire-elements-modal')
    </body>
    </html>
  2. Publish and configure the modal settings

    main

    You can customize global modal behavior using the wire-elements-modal.php configuration file. To publish this file to your application, run:

    php artisan vendor:publish --tag=wire-elements-modal-config

    Key configuration options include:

    • include_css: Set to true if your application does not use TailwindCSS. This includes modern-normalize CSS.
    • include_js: If true, the required Javascript is injected into your Blade templates. If false, you must manually bundle the JS using require('vendor/wire-elements/modal/resources/js/modal');.
    • component_defaults: An array of default properties for all modal components, such as modal_max_width, close_modal_on_click_away, and close_modal_on_escape.
  3. Upgrade from v2 to v3

    main

    If you are upgrading from version 2, you can automate part of the process using the following command:

    php artisan livewire:upgrade --run-only wire-elements-modal-upgrade

    Manual Changes Required:

    1. Event Dispatching: Replace $emit with $dispatch.

      • Old: wire:click="$emit('openModal', 'users')"
      • New: wire:click="$dispatch('openModal', {component: 'users'})"
      • For arguments: wire:click="$dispatch('openModal', {component: 'edit-user', arguments: {user: 5}})"
    2. Component Name: Replace @livewire('livewire-ui-modal') with @livewire('wire-elements-modal').

    3. Configuration: The config file has been renamed. Re-publish it using:

      php artisan vendor:publish --tag=wire-elements-modal-config
    4. Cache: Clear your view cache after upgrading:

      php artisan view:clear
  4. Create a Modal Component

    main

    To create a modal, generate a standard Livewire component and ensure the class extends LivewireUI\Modal\ModalComponent instead of the default Component class.

    <?php
    
    namespace App\Http\Livewire;
    
    use LivewireUI\Modal\ModalComponent;
    
    class EditUser extends ModalComponent
    {
        public function render()
        {
            return view('livewire.edit-user');
        }
    }
  5. Prevent Modal Closing on Unsaved Changes

    main

    You can intercept the closing process by listening to closingModalOnEscape or closingModalOnClickAway events. This allows you to prevent the modal from closing if the user has unsaved changes (e.g., a isDirty property).

    @script
    <script>
        $wire.on('closingModalOnEscape', data => {
            if ($wire.isDirty && !confirm('You have unsaved changes. Are you sure?')) {
                data.closing = false;
            }
        });
        $wire.on('closingModalOnClickAway', data => {
            if ($wire.isDirty && !confirm('You have unsaved changes. Are you sure?')) {
                data.closing = false;
            }
        });
    </script>
    @endscript
    @script
    <script>
        $wire.on('closingModalOnEscape', data => {
            if ($wire.isDirty && !confirm('{{ __('You have unsaved changes. Are you sure you want to close this dialog?') }}')) {
                data.closing = false;
            }
        });
        $wire.on('closingModalOnClickAway', data => {
            if ($wire.isDirty && !confirm('{{ __('You have unsaved changes. Are you sure you want to close this dialog?') }}')) {
                data.closing = false;
            }
        });
    </script>
    @endscript
  6. Configure Tailwind CSS for production

    main

    To ensure Tailwind CSS purges the classes used by the package correctly, you must add the package's Blade views and Laravel's compiled views to your content (or purge) array.

    Because some classes (like max-width utilities) are generated dynamically, you should also add a pattern to your safelist to prevent them from being removed.

    // For Tailwind CSS 3.x
    export default {
      content: [
        './vendor/wire-elements/modal/resources/views/*.blade.php',
        './storage/framework/views/*.php',
        './resources/views/**/*.blade.php',
      ],
      safelist: [
        {
          pattern: /max-w-(sm|md|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl)/,
          variants: ['sm', 'md', 'lg', 'xl', '2xl']
        }
      ],
      // other options
    }
  7. Skip previous modals in the stack

    main

    When navigating through a sequence of modals (e.g., Overview -> Edit -> Delete), you may want to skip the intermediate steps when a final action is completed. Use the skipPreviousModal() method to remove the immediate previous modal from the stack.

    To skip multiple modals, pass an integer to skipPreviousModals(n).

    Additionally, you can call destroySkippedModals() to ensure that the skipped modals are destroyed, resetting their state if they are opened again later.

    public function delete()
    {
        // ... logic to delete item
    
        $this->skipPreviousModal()->closeModalWithEvents([
            TeamOverview::class => 'teamDeleted'
        ]);
    }
  8. Open a Modal and Pass Parameters

    main

    Modals are opened by dispatching the openModal event. You can pass a component name and an arguments object.

    From a Livewire component:

    <button wire:click="$dispatch('openModal', { component: 'edit-user', arguments: { user: {{ $user->id }} }})">Edit User</button>

    From plain JavaScript/HTML:

    <button onclick="Livewire.dispatch('openModal', { component: 'edit-user', arguments: { user: {{ $user->id }} }})">Edit User</button>

    Handling Parameters in the Component:

    Arguments passed in the arguments object are automatically injected into the component's properties or the mount method. If a type-hinted model is used, the package will automatically fetch the model from the database using the provided ID.

    class EditUser extends ModalComponent
    {
        public User $user;
    
        public function mount()
        {
            Gate::authorize('update', $this->user);
        }
    }
  9. Close Modals

    main

    Closing via Events

    To close the current modal (or return to a previous child modal if one exists), dispatch the closeModal event:

    <button wire:click="$dispatch('closeModal')">Close</button>

    Closing via Component Class

    Inside your ModalComponent class, you can use several methods:

    • $this->closeModal(): Closes the current modal. If a child modal was open, it returns to the parent.
    • $this->forceClose()->closeModal(): Closes the entire modal stack, preventing return to a parent modal.
    • $this->closeModalWithEvents([...]): Closes the modal and dispatches specific events to other components. This is useful for refreshing data in the background.
    // Example: Close and notify UserOverview component
    $this->closeModalWithEvents([
        UserOverview::class => ['userModified', [$this->user->id]],
    ]);
    $this->closeModalWithEvents([
        UserOverview::class => ['userModified', [$this->user->id]],
    ]);
  10. Reference: wire-elements-modal.php configuration keys

    main

    The following keys are available in the wire-elements-modal.php configuration file:

    • include_css (bool): Whether to include CSS if not using Tailwind.
    • include_js (bool): Whether to inject required Javascript automatically.
    • component_defaults (array): Default properties for modal components.
      • modal_max_width (string): Default width. Options: 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl'.
      • close_modal_on_click_away (bool): Whether clicking outside the modal closes it.
      • close_modal_on_escape (bool): Whether pressing Escape closes the modal.
      • close_modal_on_escape_is_forceful (bool): Whether the Escape key close is forceful.
      • dispatch_close_event (bool): Whether to dispatch a close event.
      • destroy_on_close (bool): Whether to destroy the component on close.
    return [
        'include_css' => false,
        'include_js' => true,
        'component_defaults' => [
            'modal_max_width' => '2xl',
            'close_modal_on_click_away' => true,
            'close_modal_on_escape' => true,
            'close_modal_on_escape_is_forceful' => true,
            'dispatch_close_event' => false,
            'destroy_on_close' => false,
        ],
    ];
  11. Configure Modal Properties

    main

    You can customize the behavior of your modal by overriding static methods in your ModalComponent class.

    MethodReturn TypeDescription
    modalMaxWidth()stringSets width. Supported: 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl'.
    closeModalOnEscape()boolWhether the modal closes when the Esc key is pressed.
    closeModalOnClickAway()boolWhether the modal closes when clicking outside the modal.
    closeModalOnEscapeIsForceful()boolIf true, Esc closes all modals. If false, it only closes the top-most modal.
    dispatchCloseEvent()boolIf true, fires a modalClosed event when the modal is closed.
    destroyOnClose()boolIf true, the component state is destroyed when closed. If false, state is preserved for next time.

    Example Configuration

    class EditUser extends ModalComponent
    {
        public static function modalMaxWidth(): string
        {
            return 'xl';
        }
    
        public static function closeModalOnEscape(): bool
        {
            return false;
        }
    }
  12. Open a modal using openModal()

    main

    The openModal method is used to programmatically trigger the opening of a modal component. It accepts the component name, an array of arguments to pass to the component, and an optional array of modal attributes to override default behaviors.

    Requirements:

    • The target component must implement the LivewireUI\Modal\Contracts\ModalComponent interface.

    Arguments:

    • $component (string): The name or class of the Livewire component to display.
    • $arguments (array): Data to be passed to the component's public properties. The method automatically resolves these based on property types (e.g., resolving Eloquent models via route binding or converting values to Enums).
    • $modalAttributes (array): Configuration to override the component's default modal settings (see Modal Component Configuration).

    When a modal is opened, the activeModalComponentChanged event is dispatched with the unique ID of the modal.