Livewire Sortable.js

repository·master·Indexed 43 days ago

https://github.com/wotzebra/livewire-sortablejs

A Laravel Livewire plugin and wrapper for Sortable.js that provides a drop-in replacement for the official Livewire sortable package. It offers improved compatibility with Alpine.js and better item height/width retention. Features include support for single lists, multiple draggable groups, and nested draggable items using directives like @sortable and @sortable-group.

Tokens
3.1K
Snippets
7
Records
8
Agent score
52%

What's inside Livewire Sortable.js

  1. Install Livewire Sortable.js via CDN or NPM

    master

    You can include Livewire Sortable.js in your project using a CDN or by installing it via NPM.

    CDN

    Add the following script tag to your HTML:

    NPM

    Install the package as a dev dependency:

    npm install @wotz/livewire-sortablejs --save-dev

    Then, import it into your JavaScript bundle:

    import '@wotz/livewire-sortablejs';
    // or
    require('@wotz/livewire-sortablejs');
    <script src="https://unpkg.com/@wotz/livewire-sortablejs@1.0.0/dist/livewire-sortable.js"></script>
  2. Use a single list of draggable items

    master

    To implement a single list (e.g., a to-do list), apply the following attributes to your HTML:

    • wire:sortable="methodName": Added to the container element. methodName is the Livewire method called when the order changes.
    • wire:sortable.options: (Optional) Pass Sortable.js options as a JSON object.
    • wire:sortable.item="itemIdentifier": Added to each draggable item. This identifier is sent to your Livewire method.
    • wire:sortable.handle: (Optional) Added to a specific element within the item to act as the drag handle.

    Data Format: Your Livewire method will receive an array of objects:

    [
        [
            'order' => 1,   // order of item (starts at 1)
            'value' => 20,  // item id
        ],
    ]
    <ul wire:sortable="updateTaskOrder" wire:sortable.options="{ animation: 100 }">
        @foreach ($tasks as $task)
            <li wire:sortable.item="{{ $task->id }}" wire:key="task-{{ $task->id }}">
                <h4>{{ $task->title }}</h4>
                <button wire:sortable.handle>drag</button>
            </li>
        @endforeach
    </ul>
  3. Use multiple lists with draggable items (Groups)

    master

    To allow moving items between different lists, use the wire:sortable-group attributes:

    • wire:sortable-group="methodName": Added to the wrapper containing all lists. This method handles item movement between groups.
    • wire:sortable-group.item-group="groupIdentifier": Added to the root element of each list. Identifies the group.
    • wire:sortable-group.options: (Optional) Pass Sortable.js options to the list.
    • wire:sortable-group.item="itemIdentifier": Added to each draggable item within the lists.
    • wire:sortable-group.handle: (Optional) Drag handle for the item.

    Data Format: Your Livewire method (the one assigned to wire:sortable-group) will receive an array representing the updated state of the groups:

    [
        [
            'order' => 1,            // order of group (starts at 1)
            'value' => 20,           // group id
            'items' => [
                [
                    'order' => 1,    // order of item within group (starts at 1)
                    'value' => 50,   // item id
                ]
            ]
        ]
    ]
    <div wire:sortable-group="updateTaskOrder">
        @foreach ($groups as $group)
            <div wire:key="group-{{ $group->id }}">
                <h4>{{ $group->label }}</h4>
    
                <ul wire:sortable-group.item-group="{{ $group->id }}" wire:sortable-group.options="{ animation: 100 }">
                    @foreach ($group->tasks()->orderBy('order')->get() as $task)
                        <li wire:sortable-group.item="{{ $task->id }}" wire:key="task-{{ $task->id }}">
                            <span>{{ $task->title }}</span>
                            <button wire:sortable-group.handle>drag</button>
                        </li>
                    @endforeach
                </ul>
            </div>
        @endforeach
    </div>
  4. Use draggable groups and draggable items together

    master

    If you need both the lists (groups) and the items within them to be draggable, combine wire:sortable and wire:sortable-group:

    1. For Group Reordering: Use wire:sortable="methodName" on the outer container and wire:sortable.item="groupIdentifier" on the group elements.
    2. For Item Reordering/Moving: Use wire:sortable-group="methodName" on the outer container and wire:sortable-group.item-group="groupIdentifier" on the list elements.

    Data Formats:

    • Moving an item: The wire:sortable-group method receives the group/item structure described in the 'Multiple lists' section.
    • Moving a group: The wire:sortable method receives the group order structure:
    [
        [
            'order' => 1,   // order of group (starts at 1)
            'value' => 20,  // group id
        ],
    ]
    <div wire:sortable="updateGroupOrder" wire:sortable-group="updateTaskOrder" wire:sortable.options="{ animation: 50 }">
        @foreach ($groups as $group)
            <div wire:sortable.item="{{ $group->id }}" wire:key="group-{{ $group->id }}">
                <h4>{{ $group->label }}</h4>
                <button wire:sortable.handle>drag group</button>
    
                <ul wire:sortable-group.item-group="{{ $group->id }}" wire:sortable-group.options="{ animation: 100 }">
                    @foreach ($group->tasks()->orderBy('order')->get() as $task)
                        <li wire:sortable-group.item="{{ $task->id }}" wire:key="task-{{ $task->id }}">
                            <span>{{ $task->title }}</span>
                            <button wire:sortable-group.handle>drag item</button>
                        </li>
                    @endforeach
                </ul>
            </div>
        @endforeach
    </div>
  5. Migrate from v0.3 to v0.4: Changes in update-method target

    master

    When upgrading from v0.3 to v0.4, note a change in how the update-method is triggered when using nested Livewire components.

    In v0.3, if your sortable items (elements with wire:sortable-group.item-group) were inside a nested Livewire component, but the group container (the element with wire:sortable-group) was in a parent Livewire component, the update-method was called on the nested component.

    In v0.4, the update-method is now called on the parent Livewire component that contains the element with the wire:sortable-group attribute. Ensure your backend logic for handling the sort order is correctly placed in the parent component.

  6. Configure Sortable.js options via attributes

    master

    Both @sortable and @sortable-group directives allow you to inject custom Sortable.js configuration objects directly from your HTML using specific attributes. The attribute value must be a valid JavaScript object string (JSON-like).

    Attributes:

    • For @sortable: Use wire:sortable.options.
    • For @sortable-group: Use wire:sortable-group.options.

    Example of passing a custom animation speed:

    <ul wire:sortable="reorder" wire:sortable.options="{ 'animation': 150 }">
        <li wire:sortable.item="1">Item 1</li>
    </ul>
  7. Use the @sortable-group Livewire directive

    master

    The @sortable-group directive is designed for complex layouts involving multiple nested groups of draggable items. It manages the state of multiple sub-groups within a single master container.

    Key Features:

    • Nested Group Management: It looks for elements with the wire:sortable-group.item-group modifier to manage sub-collections.
    • Data Structure: When a sort occurs, it calls the Livewire method defined in wire:sortable-group with a structured array representing all sub-groups and their respective items.
    • Item Identification: Items must be marked with wire:sortable-group.item.
    • Handles: Drag handles are identified by wire:sortable-group.handle.
    • Configuration: Custom options can be passed via wire:sortable-group.options as a JSON string.
    <!-- HTML Template -->
    <div wire:sortable-group="updateGroups" wire:sortable-group="my-complex-layout">
        <div wire:sortable-group.item-group="group-a">
            <div wire:sortable-group.item="a1">Item A1</div>
            <div wire:sortable-group.item="a2">Item A2</div>
        </div>
        <div wire:sortable-group.item-group="group-b">
            <div wire:sortable-group.item="b1">Item B1</div>
        </div>
    </div>
    
    <!-- Livewire Component Method -->
    public function updateGroups($groups)
    {
        // $groups is an array of:
        // [
        //    ['order' => 1, 'value' => 'group-a', 'items' => [['order' => 1, 'value' => 'a1'], ...]],
        //    ['order' => 2, 'value' => 'group-b', 'items' => [...]]
        // ]
    }
  8. Use the @sortable Livewire directive

    master

    The @sortable directive enables Sortable.js on a Livewire element. It automatically handles the synchronization of item order back to your Livewire component.

    Key Features:

    • Automatic Data Sync: When sorting finishes, it calls the Livewire method specified in the directive (e.g., @sortable('reorder')) with an array of objects containing order and value.
    • Item Identification: Items must be marked with the attribute wire:sortable.item to be draggable.
    • Handles: If an element inside an item has the attribute wire:sortable.handle, it will act as the drag handle.
    • Configuration: You can pass custom Sortable.js options via the wire:sortable.options attribute as a JSON string.
    • Grouping: Use the wire:sortable attribute on the container to define a group name, allowing items to be moved between containers with the same group name.
    <!-- HTML Template -->
    <ul wire:sortable="reorder" wire:sortable="my-group">
        <li wire:sortable.item="1">Item 1</li>
        <li wire:sortable.item="2">Item 2</li>
    </ul>
    
    <!-- Livewire Component Method -->
    public function reorder($items)
    {
        // $items is an array of ['order' => 1, 'value' => '1']
    }