Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Sticky Save Bar Laravel Package

cocosmos/filament-sticky-save-bar

Filament v5 plugin that shows a sticky save bar when a form has unsaved changes, keeping actions visible on long pages. Auto-hides after save or undo, works with all field types, respects sidebar, supports dark mode, translations, and per-page opt-out.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:
    composer require cocosmos/filament-sticky-save-bar
    
  2. Register the plugin in your PanelServiceProvider:
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->plugin(StickySaveBarPlugin::make());
    }
    
  3. Test immediately: Edit a long form in your Filament admin panel. The sticky bar will appear automatically when you make unsaved changes, provided the native save button is out of view.

First Use Case

Scenario: You have a multi-tabbed edit form (e.g., EditUser) where users frequently forget to save after scrolling through sections. The sticky bar ensures visibility of save actions without requiring users to scroll back to the top.

Steps:

  1. Navigate to a long edit form (e.g., users/1/edit).
  2. Modify a field (e.g., change the user’s name).
  3. Scroll down to a lower section (e.g., "Address").
  4. Observe the sticky bar appear at the bottom of the viewport with "Save" and "Cancel" buttons.

Implementation Patterns

Core Workflow

  1. Automatic Detection:

    • The plugin listens to Filament’s form events (wire:model changes, wire:submit).
    • Dirty state is tracked per-form (scoped to the current page’s Livewire component).
    • The bar appears when:
      • The form is dirty (ShowOn::Dirty or ShowOn::DirtyAlways).
      • The native save button is scrolled out of view (ShowOn::Dirty or ShowOn::Always).
  2. User Interaction:

    • Save: Triggers the form’s wire:submit handler.
    • Cancel: Navigates back to the previous page (e.g., users index).
    • Discard: Reloads the page to revert changes (requires explicit configuration).
    • Save & Close: Saves and then navigates back (requires explicit configuration).
  3. State Management:

    • The bar hides automatically after:
      • A successful save (wire:submit.success).
      • The user undoes all changes (e.g., via browser back/forward or manual reset).
      • A modal is opened (detected via Filament’s modal state).

Integration Tips

For Existing Forms

  • No changes required for most Filament EditRecord or Form pages. The plugin works out of the box with standard Livewire forms.
  • Custom Forms: Ensure your form uses wire:submit for submission. The plugin relies on Filament’s form lifecycle events.

Conditional Logic

  • Disable per page: Use the HasStickySaveBarDisabled trait on specific pages:
    class EditShortForm extends EditRecord
    {
        use HasStickySaveBarDisabled;
    }
    
  • Dynamic enabling: Control visibility via a closure:
    StickySaveBarPlugin::make()
        ->enabled(fn () => auth()->user()->isAdmin());
    

Button Customization

  • Add/Remove Buttons:
    StickySaveBarPlugin::make()
        ->withDiscard()          // Add "Discard changes"
        ->withSaveAndClose()     // Add "Save & Close"
        ->withCancel(false);     // Remove "Cancel"
    
  • Custom Actions: Extend the plugin by publishing its assets and overriding the Blade view (resources/views/vendor/filament-sticky-save-bar.blade.php).

Positioning

  • Top vs. Bottom:
    use Cocosmos\FilamentStickySaveBar\Enums\Position;
    
    StickySaveBarPlugin::make()
        ->position(Position::Top); // Pins to the top
    
  • Respecting Sidebar: The plugin automatically avoids overlapping Filament’s sidebar by using fixed positioning with inset-inline-end: 0 (Tailwind).

Translations

  • Publish translations for customization:
    php artisan vendor:publish --tag=sticky-save-bar-translations
    
  • Override labels globally:
    StickySaveBarPlugin::make()
        ->label('Guardar cambios') // Spanish
        ->saveLabel('Guardar');
    

Advanced: Extending Functionality

  • Add Custom Buttons: Override the Blade view and inject additional buttons:
    @extends('filament::components.action-group')
    @section('actions')
        {{ $this->getSaveButton() }}
        {{ $this->getCancelButton() }}
        <x-filament::button
            wire:click="customAction"
            color="gray"
        >
            Custom Action
        </x-filament::button>
    @endsection
    
  • Hook into Events: Listen to the plugin’s events (e.g., StickySaveBarWillShow, StickySaveBarWillHide) by extending the plugin class.

Gotchas and Tips

Pitfalls

  1. Dirty State Scope:

    • Issue: The bar may incorrectly detect changes if multiple forms exist on the same page (e.g., nested Livewire components).
    • Fix: Ensure the form using the plugin is the only wire:submit-enabled form on the page. If you have nested forms, disable the plugin for the parent form using HasStickySaveBarDisabled.
  2. Modal Conflicts:

    • Issue: The bar may remain visible when a Filament modal is open, even if configured to hide.
    • Fix: Verify that your modals use Filament’s Modal component (not custom JS modals). The plugin detects Filament modals via its internal state.
  3. Sidebar Overlap:

    • Issue: On wide screens, the bar might overlap with the Filament sidebar.
    • Fix: The plugin is designed to avoid this by using inset-inline-end: 0 (Tailwind). If issues persist, check for custom CSS overriding Filament’s default spacing.
  4. Form Reset Behavior:

    • Issue: The bar doesn’t hide if the form is reset programmatically (e.g., via wire:ignore or custom JS).
    • Fix: Manually trigger the reset event or use the wire:model binding to reset fields. The plugin listens to Filament’s reset event.
  5. Custom Form Fields:

    • Issue: Complex custom fields (e.g., dynamic tables, file uploads) may not trigger dirty state.
    • Fix: Ensure your custom fields dispatch input events or update wire:model bindings. Example:
      <x-your-custom-field
          wire:model="field.name"
          @change="dispatch('input')"
      />
      

Debugging Tips

  1. Check Dirty State:

    • Inspect the network tab for wire:model updates. Dirty state is tracked via these events.
    • Add temporary logging to your form’s mount() or updated() methods to verify state changes:
      public function mount()
      {
          $this->dispatch('log', ['StickySaveBar: Form mounted']);
      }
      
  2. Verify Plugin Registration:

    • Ensure the plugin is registered after Filament’s core plugins in your PanelServiceProvider:
      public function panel(Panel $panel): Panel
      {
          return $panel
              ->plugins([
                  // Other plugins...
              ])
              ->plugin(StickySaveBarPlugin::make());
      }
      
  3. Inspect CSS:

    • Use browser dev tools to check if the sticky bar’s fixed positioning is being overridden. Look for !important rules or custom styles targeting .filament-sticky-save-bar.
  4. Test Edge Cases:

    • Rapid Changes: Edit a field, then immediately revert it. The bar should hide.
    • Modal Flow: Open a modal, then close it. The bar should reappear if the form is still dirty.
    • Page Refresh: Discard changes and reload the page. The form should reset.

Configuration Quirks

  1. ShowOn::Dirty vs. ShowOn::DirtyAlways:

    • ShowOn::Dirty (default): Bar appears only if the native save button is scrolled out of view. Use this for most cases to avoid clutter.
    • ShowOn::DirtyAlways: Bar appears as soon as the form is dirty, regardless of scroll position. Useful for very long forms where users might forget to save.
  2. Button Visibility:

    • The Cancel button is enabled by default and navigates back using Filament’s redirect() helper. If your app uses custom navigation (e.g., SPA-like routing), disable it and add your own logic.
    • The Discard button reloads the page. For SPAs, replace this with a custom action that resets the form state.
  3. Dark Mode:

    • The plugin respects Filament’s dark mode via Tailwind’s dark: variants. If issues arise, ensure your Filament theme supports dark mode and that no custom CSS overrides the bar’s background/color.
  4. Translations:

    • Published translations are stored in `lang/vendor/sticky-save-bar/{locale
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
briefley/filament-workflow-builder
gsferro/filament-odometer-easy
gsferro/filament-stat-plus-easy
jeffersongoncalves/filament-barcode-field
martin6363/filament-sidebar-resize
graystackit/laravel-dymo-printer
phpinnacle/money
lastdragon-ru/lara-asp-documentator
tumutech/laravel-qr-code
babenkoivan/elastic-scout-driver-plus
socialiteproviders/apple
phpshko/laravel-livewire-depdrop
larasell-dev/larasell
calliostro/spotify-bundle
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle