Skip to content

Commit aae89a3

Browse files
committed
Add HasCardAction trait and integrate card actions into Board and KanbanBoard components
1 parent 20cb275 commit aae89a3

File tree

5 files changed

+120
-5
lines changed

5 files changed

+120
-5
lines changed

resources/css/flowforge.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,25 @@
3030
@apply cursor-pointer;
3131
}
3232

33+
&.ff-card--clickable {
34+
@apply cursor-pointer transition-all duration-200 ease-in-out;
35+
36+
&:hover {
37+
@apply transform -translate-y-0.5 shadow-lg ring-1 ring-primary-200 dark:ring-primary-800/30;
38+
}
39+
40+
&:active {
41+
@apply transform translate-y-0 shadow-md;
42+
}
43+
}
44+
3345
&.ff-card--non-interactive {
3446
@apply cursor-default;
3547
}
48+
49+
.ff-card__actions {
50+
@apply relative z-10;
51+
}
3652
}
3753

3854
/**

resources/views/livewire/card.blade.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,31 @@
33
@php
44
$processedRecordActions = $this->getCardActionsForRecord($record);
55
$hasActions = !empty($processedRecordActions);
6+
$cardAction = $this->getCardActionForRecord($record);
7+
$hasCardAction = $this->hasCardAction($record);
68
@endphp
79

810
<div
911
@class([
1012
'ff-card kanban-card',
11-
'ff-card--interactive' => $hasActions,
12-
'ff-card--non-interactive' => !$hasActions
13+
'ff-card--interactive' => $hasActions || $hasCardAction,
14+
'ff-card--clickable' => $hasCardAction,
15+
'ff-card--non-interactive' => !$hasActions && !$hasCardAction
1316
])
1417
x-sortable-handle
1518
x-sortable-item="{{ $record['id'] }}"
19+
@if($hasCardAction && $cardAction)
20+
wire:click="mountAction('{{ $cardAction }}', { recordKey: '{{ $record['id'] }}' })"
21+
style="cursor: pointer;"
22+
@endif
1623
>
1724
<div class="ff-card__content">
1825
<div class="ff-card__header">
1926
<h4 class="ff-card__title">{{ $record['title'] }}</h4>
2027

2128
{{-- Render record actions --}}
2229
@if($hasActions)
23-
<div class="ff-card__actions">
30+
<div class="ff-card__actions" @if($hasCardAction) @click.stop @endif>
2431
<x-filament-actions::group :actions="$processedRecordActions" />
2532
</div>
2633
@endif

src/Board.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66

77
use Filament\Support\Components\ViewComponent;
88
use Relaticle\Flowforge\Concerns\HasActions;
9+
use Relaticle\Flowforge\Concerns\HasCardAction;
910
use Relaticle\Flowforge\Concerns\HasColumns;
1011
use Relaticle\Flowforge\Concerns\HasProperties;
1112
use Relaticle\Flowforge\Concerns\InteractsWithKanbanQuery;
1213

1314
class Board extends ViewComponent
1415
{
1516
use HasActions;
17+
use HasCardAction;
1618
use HasColumns;
1719
use HasProperties;
1820
use InteractsWithKanbanQuery;

src/Concerns/HasCardAction.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Relaticle\Flowforge\Concerns;
6+
7+
use Closure;
8+
use Filament\Actions\Action;
9+
use Illuminate\Database\Eloquent\Model;
10+
11+
trait HasCardAction
12+
{
13+
protected string | Closure | null $cardAction = null;
14+
15+
public function cardAction(string | Closure | null $action): static
16+
{
17+
$this->cardAction = $action;
18+
19+
return $this;
20+
}
21+
22+
/**
23+
* @param Model | array<string, mixed> $record
24+
*/
25+
public function getCardAction(Model | array $record): ?string
26+
{
27+
$action = $this->evaluate(
28+
$this->cardAction,
29+
namedInjections: [
30+
'record' => $record,
31+
],
32+
typedInjections: $record instanceof Model ? [
33+
Model::class => $record,
34+
$record::class => $record,
35+
] : [],
36+
);
37+
38+
if (! $action) {
39+
return null;
40+
}
41+
42+
if (! class_exists($action)) {
43+
return $action;
44+
}
45+
46+
if (! is_subclass_of($action, Action::class)) {
47+
return $action;
48+
}
49+
50+
return $action::getDefaultName() ?? $action;
51+
}
52+
}

src/Livewire/KanbanBoard.php

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,44 @@ public function getCardActionsForRecord(array $recordData): array
400400
return $processedActions;
401401
}
402402

403+
/**
404+
* Get the card action for a specific record.
405+
* This method follows Filament's pattern for record actions.
406+
*/
407+
public function getCardActionForRecord(array $recordData): ?string
408+
{
409+
$boardPage = $this->getBoardPage();
410+
if (! $boardPage) {
411+
return null;
412+
}
413+
414+
$board = $boardPage->getBoard();
415+
416+
if (! method_exists($board, 'getCardAction')) {
417+
return null;
418+
}
419+
420+
try {
421+
// Get the record model
422+
$recordModel = $this->getAdapter()->getModelById($recordData['id']);
423+
if (! $recordModel) {
424+
return null;
425+
}
426+
427+
return $board->getCardAction($recordModel);
428+
} catch (\Exception) {
429+
return null;
430+
}
431+
}
432+
433+
/**
434+
* Check if a card has an action defined.
435+
*/
436+
public function hasCardAction(array $recordData): bool
437+
{
438+
return $this->getCardActionForRecord($recordData) !== null;
439+
}
440+
403441
/**
404442
* Refresh all board data.
405443
*/
@@ -513,8 +551,8 @@ public function getDefaultActionRecord(\Filament\Actions\Action $action): ?\Illu
513551
// Get the last (current) mounted action
514552
$currentMountedAction = end($mountedActions);
515553

516-
// Extract recordKey from the context
517-
$recordKey = $currentMountedAction['context']['recordKey'] ?? null;
554+
// Extract recordKey from the arguments (second parameter of mountAction)
555+
$recordKey = $currentMountedAction['arguments']['recordKey'] ?? null;
518556

519557
if (! $recordKey) {
520558
return null;

0 commit comments

Comments
 (0)