Replies: 4 comments 4 replies
-
@tabuna any chance you could point me in the right direction for drag/drop sort order? Is this possible with Layout::table or TD::make? Alternatively I was thinking I could create a new popup in the Screen with a new custom Field/Layout of some type. |
Beta Was this translation helpful? Give feedback.
-
It would be great to have this functionality right away. Right now, the only way seems to be adding component. It's inconvenient. |
Beta Was this translation helpful? Give feedback.
-
What do you think about this package, https://github.com/asantibanez/laravel-blade-sortable, do you think that this package could be in Orchid platform? |
Beta Was this translation helpful? Give feedback.
-
If you need this component right now you may create your own layout. For example
// resources/js/admin/sortable_controller.js
import Sortable from 'sortablejs'
export default class extends window.Controller {
connect() {
Sortable.create(this.element.querySelector('.sortable-body'), {
handle: '.handle',
animation: 200,
sort: true,
onEnd: (evt) => {
if (evt.oldIndex === evt.newIndex) {
return
}
// this route needs to be registered
window.axios.post(`orchid-sortable/on-end/${evt.item.dataset.id}`, {
// pass any data you need. I found useful to get only old/new index or difference between them
old: evt.oldIndex,
new: evt.newIndex,
})
.then(() => {
this.alert('Hooray', 'Updated', 'success')
})
.catch(() => {
this.alert('Congratulations!', 'You\'ve broke something', 'danger')
})
},
})
}
} I'm registering onEnd callback passing old and new index to Laravel route handler via POST request plus an ID of a row stored as HTML // resources/js/admin/index.js
import SortableTableController from './sortable_controller'
application.register('sortable-table', SortableTableController)
// vite.config.js
// Add
input: [
// ... other app assets,
'resources/js/admin/index.js', // <-- add this into Laravel Vite plugin input
], // config/platform.php
'vite' => [
'resources/js/admin/index.js',
],
// routes/web.php
Route::prefix(config('platform.prefix'))
->group(function () {
Route::post('orchid-sortable/on-end/{id}', function (int $id, Request $request) {
dd(
$request->input() // ['old' => 0, 'new' => 4] -> meaning element moved from first position (index 0) to 5th
);
});
});
// app/Orchid/Layouts/SortableTable.php
namespace App\Orchid\Layouts;
use Orchid\Screen\Layouts\Table;
abstract class SortableTable extends Table
{
/**
* @var string
*/
protected $template = 'admin.layouts.sortable'; // points to view
/**
* @return array
*/
abstract protected function columns(): iterable;
}
// app/Orchid/Layouts/User/UserListLayout.php
namespace App\Orchid\Layouts\User;
use App\Orchid\Layouts\SortableTable;
class UserListLayout extends SortableTable
{
// ... logic within stands the same
}
<div data-controller="sortable-table">
@empty(!$title)
<fieldset>
<div class="col p-0 px-3">
<legend class="text-black mt-2 mx-2">
{{ $title }}
</legend>
</div>
</fieldset>
@endempty
<div class="bg-white rounded shadow-sm mb-3"
data-controller="table"
data-table-slug="{{$slug}}"
>
<div class="table-responsive">
<table @class([
'table',
'table-compact' => $compact,
'table-striped' => $striped,
'table-bordered' => $bordered,
'table-hover' => $hoverable,
])>
@if($showHeader)
<thead>
<tr>
<th></th>
@foreach($columns as $column)
{!! $column->buildTh() !!}
@endforeach
</tr>
</thead>
@endif
<tbody class="sortable-body">
@foreach($rows as $source)
<tr data-id="{{ $source->id }}">
<td style="width: 50px; cursor: pointer;">
<div class="handle" >
<x-orchid-icon path="bs.arrows-move" class="block" />
</div>
</td>
@foreach($columns as $column)
{!! $column->buildTd($source, $loop->parent) !!}
@endforeach
</tr>
@endforeach
@if($total->isNotEmpty())
<tr>
@foreach($total as $column)
{!! $column->buildTd($repository, $loop) !!}
@endforeach
</tr>
@endif
</tbody>
</table>
</div>
@if($rows->isEmpty())
<div class="d-md-flex align-items-center px-md-0 px-2 pt-4 pb-5 w-100 text-md-start text-center">
@isset($iconNotFound)
<div class="col-auto mx-md-4 mb-3 mb-md-0">
<x-orchid-icon :path="$iconNotFound" class="block h1"/>
</div>
@endisset
<div>
<h3 class="fw-light">
{!! $textNotFound !!}
</h3>
{!! $subNotFound !!}
</div>
</div>
@else
<footer class="pb-3 w-100 v-md-center px-4 d-flex flex-wrap">
<div class="col-auto me-auto">
@if(isset($columns) && \Orchid\Screen\TD::isShowVisibleColumns($columns))
<div class="btn-group dropup d-inline-block">
<button type="button"
class="btn btn-sm btn-link dropdown-toggle p-0 m-0"
data-bs-toggle="dropdown"
aria-haspopup="true"
data-bs-boundary="viewport"
aria-expanded="false">
{{ __('Configure columns') }}
</button>
<div class="dropdown-menu dropdown-column-menu dropdown-scrollable">
@foreach($columns as $column)
{!! $column->buildItemMenu() !!}
@endforeach
</div>
</div>
@endif
@if($rows instanceof \Illuminate\Contracts\Pagination\LengthAwarePaginator)
<small class="text-muted d-block">
{{ __('Displayed records: :from-:to of :total',[
'from' => ($rows->currentPage() -1 ) * $rows->perPage() + 1,
'to' => ($rows->currentPage() -1 ) * $rows->perPage() + count($rows->items()),
'total' => $rows->total(),
]) }}
</small>
@endif
</div>
<div class="col-auto overflow-auto flex-shrink-1 mt-3 mt-sm-0">
@if($rows instanceof \Illuminate\Contracts\Pagination\CursorPaginator)
{!!
$rows->appends(request()
->except(['page','_token']))
->links('platform::partials.pagination')
!!}
@elseif($rows instanceof \Illuminate\Contracts\Pagination\Paginator)
{!!
$rows->appends(request()
->except(['page','_token']))
->onEachSide($onEachSide ?? 3)
->links('platform::partials.pagination')
!!}
@endif
</div>
</footer>
@endif
</div>
</div> What I've done is actually create controller wrapper and just copy-paste default Orchid view within with some minor changes <div data-controller="sortable-table">
<!-- Content of Orchid table view -->
</div>
<td style="width: 50px; cursor: pointer;">
<div class="handle">
<x-orchid-icon path="bs.arrows-move" class="block" />
</div>
</td>
Sorry for the GIF quality Now route may accept id, old and new index. You may typecast an id within route as a model off course Note 1: You may pass also a model class, table name or something to identify which Model is actually should be updated if you need more than one sortable table - change JS controller and view, pass new data with request. Keep in mind that update position logic is up to you within provided route - I recommend to create an Observer for Note 2: Imagine you have 15 rows. You need to swap position 15 and 16. For now you cannot unless you pass some kind of dynamic value to list pagination. Plus you need to change default sorting off course to your custom sorting column public function query(): iterable
{
return [
'users' => User::with('roles')
->filters(UserFiltersLayout::class)
->defaultSort('sorting_order_column', 'desc')
->paginate($request->query('per_page')), // or something like this, add ?per_page=16 to URL
];
} P.S I personally don't like this solution - it is just temporary if you need this type of sorting right now and it still not present in Orchid - it is a lot of copy from original table, it's not universal as component should be. There is a lot of work to do to make it "register and enjoy". Plus I don't like implementation - I think it would be better to create some SortableColumnAction for Orchid Table and pass it in array of columns like public function columns(): array
{
return [
Sortable::make()
->width(50)
->icon('bs.arrows-move')
];
} but I didn't go this way to check how to make it this way |
Beta Was this translation helpful? Give feedback.
-
Possible to get something similar to this working for Orchid?
https://github.com/optimistdigital/nova-sortable
Beta Was this translation helpful? Give feedback.
All reactions