Livewire + Alpine.jsでdraggableなモーダルを作る
calendar_today
2022-11-03
insights
views: 1250
thumbnail images

経緯

Alpine.jsでドラッグして移動できるモーダルを作成しようと思ったが、誰も作っていないので自分で作成した。Alpine流行ってくれ...
ただ、Alpine.jsは基本的にプレーンなjavascriptが書ければ1から作成するのもそれほど難しくない。(難しいのはLivewireとの同期)

環境

  • Laravel 9
  • Livewire 2
  • Alpine.js

ソースコード

フロントエンド

<div class="modal-position-wrap fixed"
    x-data="{
        mouse: { x: 0, y: 0 },
        modalStyle: $wire.entangle('modal_window'),
        isModalShow: $wire.entangle('is_modal_show')
    }"
    x-show="isModalShow" x-cloak x-transition>
    <div class="modal"
        x-ref="modalEl"
        x-bind:style="modalStyle">
        <div class="modal-header" draggable="true"
            x-on:dragstart="
                mouse.x = $refs.modalEl.offsetLeft - event.pageX;
                mouse.y = $refs.modalEl.offsetTop - event.pageY;
                event.dataTransfer.setDragImage(document.createElement('div'), 0, 0);
            "
            x-on:drag="
                if (!event.x && !event.y) return;
                $refs.modalEl.style.setProperty('left', event.pageX + mouse.x + 'px');
                $refs.modalEl.style.setProperty('top', event.pageY + mouse.y + 'px');
            "
            x-on:dragend="
                $wire.set('modal_window.left', event.pageX + mouse.x + 'px');
                $wire.set('modal_window.top', event.pageY + mouse.y + 'px');
                mouse.x = mouse.y = 0;">

            <span class="modal-title">
                Modal Title
            </span>
            <button type="button" class="modal-close" x-on:click="isModalShow = !isModalShow">
                x
            </button>
        </div>
        <div class="modal-content">
            this is modal content area.
            <input type="text" wire:model="input_model" class="modal-input">
        </div>
        <div class="modal-footer">
            <button type="button" class="modal-action" x-on:click="isModalShow = !isModalShow">
                Save(Close)
            </button>
        </div>
    </div>
</div>

重要部分の解説

LivewireとAlpine.jsでの状態共有

基本的にはモーダルの中にinputが無いとか、モーダル表示中にバックエンド側と通信するようなことがなければLivewireにはモーダルの位置情報の共有はしなくて良いが、このソースのようにmodalが開いた状態でLivewireのモデルと通信するような場合は必須。(Livewireはbladeの静的ファイルを参照して差分更新するから)
コード中では、変数 modalStylemodalShow でモーダルの位置情報と開閉状態を $wire.entangle で逐一双方向通信している。

ちなみに変数 modalStyle はデータセットしていないので $wire.entangle でなくても良いんじゃないの?と思ったが、 $wire.modal_window だとモーダル位置がリセットされてしまう。 要検証

モーダルの位置情報のセット

モーダルの位置を操作するに当たり、element objectを取得する必要があるが、Alpineでは x-ref="hoge" で取得することができる。
elementを取得できたところで、コード中の x-on:dragstart , x-on:drag , x-on:dragend でそれぞれやることを記述している。

  1. x-ref="modalEl" でモーダルを取得
  2. x-on:dragstart でモーダルの初期位置を取得してマウス位置情報として保存、モーダルの残像も消す。
  3. x-on:drag でドラッグ中のマウス位置を逐一モーダルに反映させる
  4. x-on:drag でドラッグ終了直後のモーダル位置をLivewireに共有

バックエンド

class Modal extends Component
{
    public $modal_window;
    public $is_modal_show;
    public $input_model;

    public function mount()
    {
        $this->is_modal_show = true;
        $this->modal_window['left'] = '0px';
        $this->modal_window['top'] = '0px';
    }
}

重要部分

mount() でマウントされた際の初期データを入れておこう。

calendar_today
2022-11-03
insights
views: 1250