Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion public/assets/app.css

Large diffs are not rendered by default.

339 changes: 167 additions & 172 deletions public/assets/app.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/assets/app.rtl.css

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
{
"resources/js/app.js": {
"file": "assets/app.js?v=9a0ca443",
"file": "assets/app.js?v=6e0472f3",
"name": "app",
"src": "resources/js/app.js",
"isEntry": true,
"integrity": "sha384-0g2/8lmkC15eCqxDKzwRpg03UsJuQWFXSQxv0M0cmamLXN35/olQITcbjfrREWLz"
"integrity": "sha384-cZ/7iZPxx23EqO7AcrkKB9TjDFMJrCReSjXPQQwbL18Ejg2jU/lxa9p3GBo1d+S7"
},
"resources/sass/app.scss": {
"file": "assets/app.css?v=1a2da261",
"src": "resources/sass/app.scss",
"isEntry": true,
"file": "assets/app.css?v=319ce13b",
"name": "app",
"names": [
"app.css"
],
"integrity": "sha384-UhZlAcQieMUrdzbGq+dtJNJu0CjrMsKiUSYTMoRMNEuePoxH1kkP0a+eZNwZ0m7i"
"src": "resources/sass/app.scss",
"isEntry": true,
"integrity": "sha384-Vd91EAf60UDxhQ0Ox8GgGM3nBKilTSgBu5dLLnzcIVhsvKwLRKKcurU609VrVEgA"
}
}
69 changes: 69 additions & 0 deletions resources/js/controllers/sortable_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export default class extends ApplicationController {
handle: this.selectorValue,
onEnd: () => this.reorderElements(),
});

this.loadOpenDetails();
}

/**
Expand Down Expand Up @@ -53,4 +55,71 @@ export default class extends ApplicationController {
this.toast(this.failureMessageValue, "danger");
});
}

toggleDetail(event) {
const button = event.currentTarget;
const target = document.getElementById(button.dataset.detailTargetId);
const row = target?.closest("[data-row-detail-row]");

if (!target || !row) {
return;
}

const isExpanded = button.getAttribute("aria-expanded") === "true";

button.setAttribute("aria-expanded", String(!isExpanded));
row.classList.toggle("d-none", isExpanded);

if (!isExpanded && button.dataset.detailLoaded !== "true") {
this.loadDetail(button, target);
}
}

loadOpenDetails() {
this.element
.querySelectorAll('[data-detail-loaded="false"][aria-expanded="true"]')
.forEach(button => {
const target = document.getElementById(
button.dataset.detailTargetId
);

if (target) {
this.loadDetail(button, target);
}
});
}

loadDetail(button, target) {
const body = this.detailData(button.dataset.detailBody);
const query = this.detailData(button.dataset.detailQuery);
const queryString = new URLSearchParams(query).toString();
const url = queryString
? `${button.dataset.detailUrl}?${queryString}`
: button.dataset.detailUrl;

this.loadStream(url, {
...body,
_state: document.getElementById("screen-state")?.value || null,
})
.then(() => {
button.dataset.detailLoaded = "true";
})
.catch(error => {
console.error(error);
target.innerHTML = `<div class="text-danger small">${error.message}</div>`;
});
}

detailData(value) {
if (!value) {
return {};
}

try {
return JSON.parse(value);
} catch (error) {
console.error(error);
return {};
}
}
}
69 changes: 69 additions & 0 deletions resources/js/controllers/table_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export default class extends ApplicationController {
e.stopPropagation();
});
}

this.loadOpenDetails();
}

/**
Expand Down Expand Up @@ -109,6 +111,73 @@ export default class extends ApplicationController {
});
}

toggleDetail(event) {
const button = event.currentTarget;
const target = document.getElementById(button.dataset.detailTargetId);
const row = target?.closest("[data-row-detail-row]");

if (!target || !row) {
return;
}

const isExpanded = button.getAttribute("aria-expanded") === "true";

button.setAttribute("aria-expanded", String(!isExpanded));
row.classList.toggle("d-none", isExpanded);

if (!isExpanded && button.dataset.detailLoaded !== "true") {
this.loadDetail(button, target);
}
}

loadOpenDetails() {
this.element
.querySelectorAll('[data-detail-loaded="false"][aria-expanded="true"]')
.forEach(button => {
const target = document.getElementById(
button.dataset.detailTargetId
);

if (target) {
this.loadDetail(button, target);
}
});
}

loadDetail(button, target) {
const body = this.detailData(button.dataset.detailBody);
const query = this.detailData(button.dataset.detailQuery);
const queryString = new URLSearchParams(query).toString();
const url = queryString
? `${button.dataset.detailUrl}?${queryString}`
: button.dataset.detailUrl;

this.loadStream(url, {
...body,
_state: document.getElementById("screen-state")?.value || null,
})
.then(() => {
button.dataset.detailLoaded = "true";
})
.catch(error => {
console.error(error);
target.innerHTML = `<div class="text-danger small">${error.message}</div>`;
});
}

detailData(value) {
if (!value) {
return {};
}

try {
return JSON.parse(value);
} catch (error) {
console.error(error);
return {};
}
}

/**
*
* @returns {string}
Expand Down
69 changes: 56 additions & 13 deletions resources/views/layouts/sortable.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,67 @@
class="list-group">

@foreach($rows as $model)
@php
$rowLoop = $loop;
$detailTarget = "row-detail-{$slug}-{$loop->index}";
$detailPayload = $detail?->deferredPayload($slug, $detailTarget, $model, $rowLoop) ?? [];
$detailOpen = $detail?->isOpenByDefault() ?? false;
$detailDeferred = $detail?->isDeferred() ?? false;
@endphp
<li
data-model-id="{{ $model->getKey() }}"
class="reorder-handle list-group-item d-flex justify-content-between align-items-center px-4 py-3 list-group-item-action">
<div class="me-4">
<x-orchid-icon path="bs.arrow-down-up" class="cursor-move"/>
</div>
class="list-group-item px-4 py-3 list-group-item-action">
<div class="d-flex justify-content-between align-items-center">
<div class="me-4 reorder-handle" data-model-id="{{ $model->getKey() }}">
<x-orchid-icon path="bs.arrow-down-up" class="cursor-move"/>
</div>

@if($detail !== null)
<div class="me-4">
<button type="button"
class="btn btn-link icon-link p-0"
title="{{ $detail->buttonLabelValue() }}"
aria-label="{{ $detail->buttonLabelValue() }}"
aria-controls="{{ $detailTarget }}"
aria-expanded="{{ var_export($detailOpen) }}"
data-action="sortable#toggleDetail"
data-detail-target-id="{{ $detailTarget }}"
data-detail-url="{{ route('orchid.async.row-detail') }}"
data-detail-body='@json($detailPayload['body'] ?? [])'
data-detail-query='@json($detailPayload['query'] ?? [])'
data-detail-loaded="{{ var_export(!$detailDeferred) }}"
>
<x-orchid-icon :path="$detail->iconValue()" class="overflow-visible"/>
</button>
</div>
@endif

@foreach($columns as $column)
<div class="{{ $loop->first ? 'me-auto' : 'ms-3' }}">
@if($showBlockHeaders)
<div class="text-muted fw-normal">
{!! $column->buildDt($model) !!}
</div>
@endif
@foreach($columns as $column)
<div class="{{ $loop->first ? 'me-auto' : 'ms-3' }}">
@if($showBlockHeaders)
<div class="text-muted fw-normal">
{!! $column->buildDt($model) !!}
</div>
@endif

{!! $column->buildDd($model) !!}
</div>
@endforeach
</div>

{!! $column->buildDd($model) !!}
@if($detail !== null)
<div data-row-detail-row
@class(['d-none' => !$detailOpen])
>
<div id="{{ $detailTarget }}" class="mt-3 pt-3 border-top">
@if($detailDeferred)
<div class="text-muted small">{{ __('Loading...') }}</div>
@else
{!! $detail->build($model, $rowLoop) !!}
@endif
</div>
</div>
@endforeach
@endif
</li>
@endforeach
</ol>
Expand Down
49 changes: 48 additions & 1 deletion resources/views/layouts/table.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
@if($showHeader)
<thead>
<tr>
@if($detail !== null)
<th class="text-center" width="1"></th>
@endif
@foreach($columns as $column)
{!! $column->buildTh() !!}
@endforeach
Expand All @@ -35,15 +38,59 @@
<tbody>

@foreach($rows as $source)
@php
$rowLoop = $loop;
$detailTarget = "row-detail-{$slug}-{$loop->index}";
$detailPayload = $detail?->deferredPayload($slug, $detailTarget, $source, $rowLoop) ?? [];
$detailOpen = $detail?->isOpenByDefault() ?? false;
$detailDeferred = $detail?->isDeferred() ?? false;
@endphp
<tr>
@if($detail !== null)
<td class="text-center align-middle" width="1">
<button type="button"
class="btn btn-link icon-link p-0"
title="{{ $detail->buttonLabelValue() }}"
aria-label="{{ $detail->buttonLabelValue() }}"
aria-controls="{{ $detailTarget }}"
aria-expanded="{{ var_export($detailOpen) }}"
data-action="table#toggleDetail"
data-detail-target-id="{{ $detailTarget }}"
data-detail-url="{{ route('orchid.async.row-detail') }}"
data-detail-body='@json($detailPayload['body'] ?? [])'
data-detail-query='@json($detailPayload['query'] ?? [])'
data-detail-loaded="{{ var_export(!$detailDeferred) }}"
>
<x-orchid-icon :path="$detail->iconValue()" class="overflow-visible"/>
</button>
</td>
@endif
@foreach($columns as $column)
{!! $column->buildTd($source, $loop->parent) !!}
{!! $column->buildTd($source, $rowLoop) !!}
@endforeach
</tr>
@if($detail !== null)
<tr data-row-detail-row
@class(['d-none' => !$detailOpen])
>
<td colspan="{{ $columns->count() + 1 }}" class="p-0 border-top-0">
<div id="{{ $detailTarget }}" class="px-4 py-3 bg-light">
@if($detailDeferred)
<div class="text-muted small">{{ __('Loading...') }}</div>
@else
{!! $detail->build($source, $rowLoop, $repository) !!}
@endif
</div>
</td>
</tr>
@endif
@endforeach

@if($total->isNotEmpty() && $rows->isNotEmpty())
<tr>
@if($detail !== null)
<td></td>
@endif
@foreach($total as $column)
{!! $column->buildTd($repository, $loop) !!}
@endforeach
Expand Down
3 changes: 3 additions & 0 deletions resources/views/partials/layouts/row-detail-stream.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<x-orchid-stream :target="$target" action="update" rule="true">
{!! $content !!}
</x-orchid-stream>
3 changes: 3 additions & 0 deletions routes/orchid.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
Route::post('listener/{screen}/{layout}', [AsyncController::class, 'listener'])
->name('async.listener');

Route::post('row-detail', [AsyncController::class, 'rowDetail'])
->name('async.row-detail');

/*
|--------------------------------------------------------------------------
| File & Media Management
Expand Down
27 changes: 27 additions & 0 deletions src/Platform/Http/Controllers/AsyncController.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,31 @@ public function listener(Request $request, string $screen, string $layout)

return $screen->asyncPartialLayout($layout, $request);
}

/**
* @throws BindingResolutionException
* @throws \ReflectionException
*/
public function rowDetail(Request $request)
{
$request->validate([
'_call' => 'required|string',
'_layout' => 'required|string',
'_screen' => 'required|string',
'_target' => 'required|string',
]);

$screen = Crypt::decryptString(
$request->input('_screen')
);

/** @var Screen $screen */
$screen = app($screen);

return $screen->asyncRowDetail(
$request->input('_call'),
$request->input('_layout'),
$request->input('_target')
);
}
}
Loading
Loading