1<?php2 3Route::get('/third-party', function () {4 return view('third-party');5});
1<x-app-layout>2 <x-slot name="title">Third Party - Larva Interactions</x-slot>3 <a href="/" class="underline text-blue-500">Back</a>4 <h1 class="text-2xl mb-4">Third Party</h1>+ <livewire:third-party.alert /> 6 <livewire:third-party.select/>7 <livewire:third-party.text-editor/>8 <livewire:third-party.nested/>9</x-app-layout>
1<?php 2 3use Livewire\Attributes\On; 4use Livewire\Volt\Component; 5 6new class extends Component { 7 public $visible = true; 8 9 #[On('destroy')]10 function destroy()11 {12 // Mock delete process in server with sleep function13 sleep(3);14 $this->dispatch('alert:ok', data: [15 'message' => 'Item has been deleted!'16 ]);17 $this->visible = false;18 }19}; ?>20 21@push('scripts')22<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js" crossorigin="anonymous"></script>23<script>24 function confirmDestroy(el) {25 swal({26 title: 'Warning!',27 text: el.getAttribute('data-message'),28 icon: 'warning',29 buttons: true,30 dangerMode: true,31 })32 .then((willDelete) => {33 if (willDelete) {34 swal('Data is being deleted. Please wait...', {35 icon: 'info',36 closeOnClickOutside: false,37 button: false38 });39 window.Livewire.dispatch('destroy');40 } else {41 swal('Your data still saved!')42 }43 })44 }45 46 window.addEventListener('alert:ok', function (e) {47 swal({48 icon: 'success',49 text: e.detail.data.message50 });51 })52</script>53@endpush54 55<div>56 <h2 class="text-xl mb-4">Alert with Sweet Alert</h2>57 @if ($visible)58 <div class="flex items-center gap-2 mb-4">59 <p>Delete me and I will say good bye!</p>60 <button onclick="confirmDestroy(this);" data-message="Are you sure to delete this item?" class="bg-red-500 p-1 rounded text-white">Delete</button>61 </div>62 @else63 <p class="mb-4">Good bye! 👋🏼</p>64 @endif65</div>
Delete me and I will say good bye!
1<?php2 3Route::get('/third-party', function () {4 return view('third-party');5});
1<x-app-layout>2 <x-slot name="title">Third Party - Larva Interactions</x-slot>3 <a href="/" class="underline text-blue-500">Back</a>4 <h1 class="text-2xl mb-4">Third Party</h1>5 <livewire:third-party.alert />+ <livewire:third-party.select/> 7 <livewire:third-party.text-editor/>8 <livewire:third-party.nested/>9</x-app-layout>
1<?php 2 3use Livewire\Volt\Component; 4 5new class extends Component { 6 7 public $selectedOption = '1'; 8 public $selectedOptions = ['3', '4']; 9 10 public function with(): array 11 { 12 return [ 13 'md_content' => markdown_convert(resource_path('docs/third-party/select.md')), 14 'options' => [ 15 [ 16 'id' => '1', 17 'name' => 'Jujutsu Kaisen' 18 ], 19 [ 20 'id' => '2', 21 'name' => 'One Piece' 22 ], 23 [ 24 'id' => '3', 25 'name' => 'Elusive Samurai' 26 ], 27 [ 28 'id' => '4', 29 'name' => 'Black Clover' 30 ] 31 ] 32 ]; 33 } 34 35 function clearSelectedOption() 36 { 37 $this->selectedOption = ''; 38 } 39 40 function clearSelectedOptions() 41 { 42 $this->selectedOptions = []; 43 } 44}; ?> 45 46@push('styles') 47<link href="https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/css/tom-select.css" rel="stylesheet"> 48<style> 49 [x-cloak] { 50 display: none !important; 51 } 52</style> 53@endpush 54 55@push('scripts') 56<script src="https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/js/tom-select.complete.min.js"></script> 57@endpush 58 59<div> 60 <h2 class="text-xl mb-4">Select with Tom Select</h2> 61 {!! $md_content !!} 62 <h3 class="text-lg">Single option</h3> 63 <div wire:ignore> 64 <select x-data="{ 65 tomSelectInstance: null, 66 options: {{ collect($options) }}, 67 items: $wire.entangle('selectedOption').live 68 }" 69 x-init="tomSelectInstance = new TomSelect($refs.select, { 70 valueField: 'id', 71 labelField: 'name', 72 searchField: 'name', 73 options: options, 74 items: items, 75 onItemAdd: function () { 76 this.setTextboxValue(''); 77 } 78 }); $watch('items', (value, oldValue) => { 79 const result = JSON.parse(JSON.stringify(value)); 80 if (result.length === 0) { 81 tomSelectInstance.clear(); 82 } 83 });" 84 x-ref="select" 85 x-cloak 86 id="selectedOption" 87 wire:model.live="selectedOption" 88 autocomplete="off"> 89 90 </select> 91 </div> 92 <p class="mb-4">Selected option: {{ @json_encode($selectedOption) }}</p> 93 94 <h3 class="text-lg">Multiple options</h3> 95 <div wire:ignore> 96 <select x-data="{ 97 tomSelectInstance: null, 98 options: {{ collect($options) }}, 99 items: $wire.entangle('selectedOptions').live100 }"101 x-init="tomSelectInstance = new TomSelect($refs.select, {102 valueField: 'id',103 labelField: 'name',104 searchField: 'name',105 options: options,106 items: items,107 maxItems: 2,108 onItemAdd: function () {109 this.setTextboxValue('');110 this.refreshOptions();111 }112 }); $watch('items', (value, oldValue) => {113 const result = JSON.parse(JSON.stringify(value));114 if (result.length === 0) {115 tomSelectInstance.clear();116 }117 })"118 x-ref="select"119 x-cloak120 id="selectedOptions"121 wire:model.live="selectedOptions"122 multiple123 autocomplete="off">124 125 </select>126 </div>127 <p class="mb-4">Selected options: {{ @json_encode($selectedOptions) }}</p>128</div>
Selected option: "1"
Selected options: ["3","4"]
1<?php2 3Route::get('/third-party', function () {4 return view('third-party');5});
1<x-app-layout>2 <x-slot name="title">Third Party - Larva Interactions</x-slot>3 <a href="/" class="underline text-blue-500">Back</a>4 <h1 class="text-2xl mb-4">Third Party</h1>5 <livewire:third-party.alert />6 <livewire:third-party.select/>+ <livewire:third-party.text-editor/> 8 <livewire:third-party.nested/>9</x-app-layout>
1<?php 2 3use Livewire\Volt\Component; 4 5new class extends Component { 6 public $content = ''; 7 8 public function with(): array 9 {10 return [11 'md_content' => markdown_convert(resource_path('docs/third-party/text-editor.md')),12 ];13 }14 15 public function submitEditor()16 {17 // May be add some validation here!18 }19 20 public function clear()21 {22 $this->content = '';23 }24}; ?>25 26@push('styles')27<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/trix/dist/trix.min.css" crossorigin="anonymous">28@endpush29 30@push('scripts')31<script src="https://cdn.jsdelivr.net/npm/trix/dist/trix.umd.min.js" crossorigin="anonymous"></script>32@endpush33 34<div35 x-data="{36 content: $wire.entangle('content'),37 upload(event) {38 const data = new FormData();39 data.append('file', event.attachment.file);40 41 fetch('/upload-file', {42 headers: {43 'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').getAttribute('content')44 },45 method: 'POST',46 credentials: 'same-origin',47 body: data48 })49 .then(response => response.json())50 .then(data => {51 event.attachment.setAttributes({52 url: data.image_url53 });54 });55 }56 }">57 <h2 class="text-xl mb-4">Text Area with Trix Editor</h2>58 {!! $md_content !!}59 <form class="mb-4" wire:submit="submitEditor">60 <div wire:ignore61 x-on:trix-change="content = $event.target.value"62 x-on:trix-initialize="$refs.text.editor.loadHTML(content)">63 64 <input type="hidden" id="x" x-model="content">65 66 <trix-editor67 x-on:trix-file-accept="event.preventDefault()"68 x-on:trix-attachment-add="upload"69 x-ref="text"70 input="x" class="trix-content"></trix-editor>71 </div>72 <button class="bg-blue-500 text-white p-2 rounded my-4">Submit</button>73 <button type="button" x-on:click="$wire.clear(); $refs.text.editor.loadHTML('');" class="bg-gray-200 p-2 rounded my-4">Clear</button>74 {!! $content !!}75 </form>76</div>
Usually, I use nested sortable to display tree menu then drag and drop menu position. Imagine you build dynamic menus for your site.
1<?php2 3Route::get('/third-party', function () {4 return view('third-party');5});
1<x-app-layout>2 <x-slot name="title">Third Party - Larva Interactions</x-slot>3 <a href="/" class="underline text-blue-500">Back</a>4 <h1 class="text-2xl mb-4">Third Party</h1>5 <livewire:third-party.alert />6 <livewire:third-party.select/>7 <livewire:third-party.text-editor/>+ <livewire:third-party.nested/> 9</x-app-layout>
1<?php 2 3use Livewire\Attributes\On; 4use Livewire\Volt\Component; 5 6new class extends Component { 7 public $sortableData; 8 public $sortableItem; 9 10 #[On('set-data')] 11 function setSortableData($data) 12 { 13 $this->sortableData = $data; 14 } 15 16 public function submitSortable() 17 { 18 if (!empty($this->sortableItem)) { 19 $this->dispatch('nested-sortable-store-item', data: [ 20 'item' => $this->sortableItem 21 ]); 22 $this->sortableItem = ''; 23 } 24 } 25}; ?> 26 27@push('styles') 28<style> 29 /* Nested sortable style */ 30 ul { 31 list-style-type: none; 32 margin: 0; 33 } 34 35 .list-group-item { 36 border: 1px solid rgba(0, 0, 0, .125); 37 padding: .5rem; 38 cursor: move; 39 } 40 41 .list-group-item:last-child { 42 border-bottom-left-radius: .25rem; 43 border-bottom-right-radius: .25rem; 44 } 45 46 .list-group-item:hover { 47 z-index: 0; 48 } 49 50 .nested-sortable, 51 .nested-1, 52 .nested-2, 53 .nested-3 { 54 margin-top: 5px; 55 } 56 57 .nested-1 { 58 background-color: #e6e6e6; 59 } 60 61 .nested-2 { 62 background-color: #cccccc; 63 } 64 65 .nested-3 { 66 background-color: #b3b3b3; 67 } 68</style> 69@endpush 70 71@push('scripts') 72<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script> 73<script> 74 let nestedSortables = Array.from(document.querySelectorAll('.nested-sortable')); 75 for (let i = 0; i < nestedSortables.length; i++) { 76 new Sortable(nestedSortables[i], { 77 group: 'nested', 78 animation: 150, 79 fallbackOnBody: true, 80 swapThreshold: 0.65, 81 onEnd: function (evt) { 82 let newData = JSON.stringify(serialize(root)); 83 console.log(newData); 84 Livewire.dispatch('set-data', { data: newData }); 85 } 86 }); 87 } 88 89 const nestedQuery = '.nested-sortable'; 90 const identifier = 'id'; 91 const root = document.getElementById('wrapper-nested-sortable'); 92 function serialize(sortable) { 93 const serialized = []; 94 const children = [].slice.call(sortable.children); 95 for (const i in children) { 96 const nested = children[i].querySelector(nestedQuery); 97 const parentId = children[i].closest(nestedQuery).getAttribute('data-parent-id'); 98 serialized.push({ 99 id: children[i].dataset[identifier],100 parent_id: parentId,101 children: nested ? serialize(nested) : [],102 order: (parseInt(i) + 1).toString(),103 });104 }105 return serialized;106 }107 108 window.addEventListener('nested-sortable-store-item', (e) => {109 document.getElementById('wrapper-nested-sortable').appendChild(110 document111 .createRange()112 .createContextualFragment(`<li data-id="${e.detail.data.item}" data-parent="0"113 class="list-group-item nested-1">${e.detail.data.item}114 </li>`));115 });116</script>117@endpush118 119<div>120<h2 class="text-xl mb-4">Nested Sortable</h2>121 <p class="mb-4">Usually, I use nested sortable to display tree menu then drag and drop menu position. Imagine you build dynamic menus for your site.</p>122 <form class="flex gap-2" wire:submit.prevent="submitSortable">123 <input type="text" wire:model="sortableItem" placeholder="Insert an item" class="flex-1 rounded">124 <button type="submit" class="bg-blue-500 p-2 rounded text-white">Submit</button>125 </form>126 <div wire:ignore>127 {!! build_sortable_list(get_menus()) !!}128 </div>129 @if (!empty($sortableData))130 <pre style="overflow-x: scroll;">131 <code>132 {{ @json_encode($sortableData) }}133 </code>134 </pre>135 @else136 <p>Please drag and drop the list above to see updated list.</p>137 @endif138</div>
Please drag and drop the list above to see updated list.