mirror of
https://github.com/filamentphp/filament.git
synced 2026-10-25 10:04:54 +08:00
Merge branch '3.x' into 4.x
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -19,4 +19,21 @@ trait BelongsToGroup
|
||||
{
|
||||
return $this->group;
|
||||
}
|
||||
|
||||
public function getRootGroup(): ?ActionGroup
|
||||
{
|
||||
$group = $this->getGroup();
|
||||
|
||||
while ($group) {
|
||||
$parentGroup = $group->getGroup();
|
||||
|
||||
if (! $parentGroup) {
|
||||
break;
|
||||
}
|
||||
|
||||
$group = $parentGroup;
|
||||
}
|
||||
|
||||
return $group;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ abstract class Exporter
|
||||
protected Export $export,
|
||||
protected array $columnMap,
|
||||
protected array $options,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return array<mixed>
|
||||
|
||||
@@ -47,6 +47,7 @@ class Export extends Model
|
||||
$authenticatable = app(Authenticatable::class);
|
||||
|
||||
if ($authenticatable) {
|
||||
/** @phpstan-ignore-next-line */
|
||||
return $this->belongsTo($authenticatable::class);
|
||||
}
|
||||
|
||||
@@ -54,6 +55,7 @@ class Export extends Model
|
||||
throw new Exception('No [App\\Models\\User] model found. Please bind an authenticatable model to the [Illuminate\\Contracts\\Auth\\Authenticatable] interface in a service provider\'s [register()] method.');
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,4 @@ namespace Filament\Actions\Imports\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class RowImportFailedException extends Exception
|
||||
{
|
||||
}
|
||||
class RowImportFailedException extends Exception {}
|
||||
|
||||
@@ -37,8 +37,7 @@ abstract class Importer
|
||||
protected Import $import,
|
||||
protected array $columnMap,
|
||||
protected array $options,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
|
||||
@@ -53,6 +53,7 @@ class Import extends Model
|
||||
$authenticatable = app(Authenticatable::class);
|
||||
|
||||
if ($authenticatable) {
|
||||
/** @phpstan-ignore-next-line */
|
||||
return $this->belongsTo($authenticatable::class);
|
||||
}
|
||||
|
||||
@@ -60,6 +61,7 @@ class Import extends Model
|
||||
throw new Exception('No [App\\Models\\User] model found. Please bind an authenticatable model to the [Illuminate\\Contracts\\Auth\\Authenticatable] interface in a service provider\'s [register()] method.');
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
|
||||
namespace Filament\Forms\Components;
|
||||
|
||||
class ViewField extends Field
|
||||
{
|
||||
}
|
||||
class ViewField extends Field {}
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
|
||||
namespace Filament\Infolists\Components;
|
||||
|
||||
class ViewEntry extends Entry
|
||||
{
|
||||
}
|
||||
class ViewEntry extends Entry {}
|
||||
|
||||
@@ -425,3 +425,55 @@ import { Notification, NotificationAction } from '../../vendor/filament/notifica
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
## Closing a notification with JavaScript
|
||||
|
||||
Once a notification has been sent, you can close it on demand by dispatching a browser event on the window called `close-notification`.
|
||||
|
||||
The event needs to contain the ID of the notification you sent. To get the ID, you can use the `getId()` method on the `Notification` object:
|
||||
|
||||
```php
|
||||
use Filament\Notifications\Notification;
|
||||
|
||||
$notification = Notification::make()
|
||||
->title('Hello')
|
||||
->persistent()
|
||||
->send()
|
||||
|
||||
$notificationId = $notification->getId()
|
||||
```
|
||||
|
||||
To close the notification, you can dispatch the event from Livewire:
|
||||
|
||||
```php
|
||||
$this->dispatch('close-notification', id: $notificationId);
|
||||
```
|
||||
|
||||
Or from JavaScript, in this case Alpine.js:
|
||||
|
||||
```blade
|
||||
<button x-on:click="$dispatch('close-notification', { id: notificationId })" type="button">
|
||||
Close Notification
|
||||
</button>
|
||||
```
|
||||
|
||||
If you are able to retrieve the notification ID, persist it, and then use it to close the notification, that is the recommended approach, as IDs are generated uniquely, and you will not risk closing the wrong notification. However, if it is not possible to persist the random ID, you can pass in a custom ID when sending the notification:
|
||||
|
||||
```php
|
||||
use Filament\Notifications\Notification;
|
||||
|
||||
Notification::make('greeting')
|
||||
->title('Hello')
|
||||
->persistent()
|
||||
->send()
|
||||
```
|
||||
|
||||
In this case, you can close the notification by dispatching the event with the custom ID:
|
||||
|
||||
```blade
|
||||
<button x-on:click="$dispatch('close-notification', { id: 'greeting' })" type="button">
|
||||
Close Notification
|
||||
</button>
|
||||
```
|
||||
|
||||
Please be aware that if you send multiple notifications with the same ID, you may experience unexpected side effects, so random IDs are recommended.
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
$attributes
|
||||
->merge([
|
||||
'wire:key' => "{$this->getId()}.notifications.{$notification->getId()}",
|
||||
'x-on:close-notification.window' => "if (\$event.detail.id == '{$notification->getId()}') close()",
|
||||
], escape: false)
|
||||
->class(['pointer-events-auto invisible'])
|
||||
}}
|
||||
|
||||
@@ -17,8 +17,7 @@ class BroadcastNotification extends BaseNotification implements ShouldQueue
|
||||
*/
|
||||
public function __construct(
|
||||
public array $data,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param Model $notifiable
|
||||
|
||||
@@ -17,8 +17,7 @@ class DatabaseNotification extends BaseNotification implements Arrayable, Should
|
||||
*/
|
||||
public function __construct(
|
||||
public array $data,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param Model $notifiable
|
||||
|
||||
@@ -29,9 +29,7 @@ class DatabaseNotifications extends Component
|
||||
public static ?string $authGuard = null;
|
||||
|
||||
#[On('databaseNotificationsSent')]
|
||||
public function refresh(): void
|
||||
{
|
||||
}
|
||||
public function refresh(): void {}
|
||||
|
||||
#[On('notificationClosed')]
|
||||
public function removeNotification(string $id): void
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<li
|
||||
x-data="{ label: @js($subNavigation ? "sub_navigation_{$label}" : $label) }"
|
||||
data-group-label="{{ $label }}"
|
||||
data-group-label="{{ $subNavigation ? "sub_navigation_{$label}" : $label }}"
|
||||
{{
|
||||
$attributes->class([
|
||||
'fi-sidebar-group flex flex-col gap-y-1',
|
||||
|
||||
@@ -11,8 +11,7 @@ class Registered
|
||||
|
||||
public function __construct(
|
||||
protected Authenticatable $user,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
public function getUser(): Authenticatable
|
||||
{
|
||||
|
||||
@@ -14,8 +14,7 @@ class TenantSet
|
||||
public function __construct(
|
||||
protected Model $tenant,
|
||||
protected Model | Authenticatable | HasTenants $user,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
public function getTenant(): Model
|
||||
{
|
||||
|
||||
@@ -4,6 +4,4 @@ namespace Filament\Http\Responses\Auth\Contracts;
|
||||
|
||||
use Illuminate\Contracts\Support\Responsable;
|
||||
|
||||
interface EmailVerificationResponse extends Responsable
|
||||
{
|
||||
}
|
||||
interface EmailVerificationResponse extends Responsable {}
|
||||
|
||||
@@ -4,6 +4,4 @@ namespace Filament\Http\Responses\Auth\Contracts;
|
||||
|
||||
use Illuminate\Contracts\Support\Responsable;
|
||||
|
||||
interface LoginResponse extends Responsable
|
||||
{
|
||||
}
|
||||
interface LoginResponse extends Responsable {}
|
||||
|
||||
@@ -4,6 +4,4 @@ namespace Filament\Http\Responses\Auth\Contracts;
|
||||
|
||||
use Illuminate\Contracts\Support\Responsable;
|
||||
|
||||
interface LogoutResponse extends Responsable
|
||||
{
|
||||
}
|
||||
interface LogoutResponse extends Responsable {}
|
||||
|
||||
@@ -4,6 +4,4 @@ namespace Filament\Http\Responses\Auth\Contracts;
|
||||
|
||||
use Illuminate\Contracts\Support\Responsable;
|
||||
|
||||
interface PasswordResetResponse extends Responsable
|
||||
{
|
||||
}
|
||||
interface PasswordResetResponse extends Responsable {}
|
||||
|
||||
@@ -4,6 +4,4 @@ namespace Filament\Http\Responses\Auth\Contracts;
|
||||
|
||||
use Illuminate\Contracts\Support\Responsable;
|
||||
|
||||
interface RegistrationResponse extends Responsable
|
||||
{
|
||||
}
|
||||
interface RegistrationResponse extends Responsable {}
|
||||
|
||||
@@ -34,9 +34,7 @@ class MenuItem extends Component
|
||||
|
||||
protected bool | Closure $isVisible = true;
|
||||
|
||||
final public function __construct()
|
||||
{
|
||||
}
|
||||
final public function __construct() {}
|
||||
|
||||
public static function make(): static
|
||||
{
|
||||
|
||||
@@ -2,33 +2,35 @@
|
||||
|
||||
namespace Filament\Panel\Concerns;
|
||||
|
||||
use Closure;
|
||||
|
||||
trait HasSidebar
|
||||
{
|
||||
protected string $sidebarWidth = '20rem';
|
||||
|
||||
protected string $collapsedSidebarWidth = '4.5rem';
|
||||
|
||||
protected bool $isSidebarCollapsibleOnDesktop = false;
|
||||
protected bool | Closure $isSidebarCollapsibleOnDesktop = false;
|
||||
|
||||
protected bool $isSidebarFullyCollapsibleOnDesktop = false;
|
||||
protected bool | Closure $isSidebarFullyCollapsibleOnDesktop = false;
|
||||
|
||||
protected bool $hasCollapsibleNavigationGroups = true;
|
||||
protected bool | Closure $hasCollapsibleNavigationGroups = true;
|
||||
|
||||
public function sidebarCollapsibleOnDesktop(bool $condition = true): static
|
||||
public function sidebarCollapsibleOnDesktop(bool | Closure $condition = true): static
|
||||
{
|
||||
$this->isSidebarCollapsibleOnDesktop = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function sidebarFullyCollapsibleOnDesktop(bool $condition = true): static
|
||||
public function sidebarFullyCollapsibleOnDesktop(bool | Closure $condition = true): static
|
||||
{
|
||||
$this->isSidebarFullyCollapsibleOnDesktop = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function collapsibleNavigationGroups(bool $condition = true): static
|
||||
public function collapsibleNavigationGroups(bool | Closure $condition = true): static
|
||||
{
|
||||
$this->hasCollapsibleNavigationGroups = $condition;
|
||||
|
||||
@@ -61,16 +63,16 @@ trait HasSidebar
|
||||
|
||||
public function isSidebarCollapsibleOnDesktop(): bool
|
||||
{
|
||||
return $this->isSidebarCollapsibleOnDesktop;
|
||||
return (bool) $this->evaluate($this->isSidebarCollapsibleOnDesktop);
|
||||
}
|
||||
|
||||
public function isSidebarFullyCollapsibleOnDesktop(): bool
|
||||
{
|
||||
return $this->isSidebarFullyCollapsibleOnDesktop;
|
||||
return (bool) $this->evaluate($this->isSidebarFullyCollapsibleOnDesktop);
|
||||
}
|
||||
|
||||
public function hasCollapsibleNavigationGroups(): bool
|
||||
{
|
||||
return $this->hasCollapsibleNavigationGroups;
|
||||
return (bool) $this->evaluate($this->hasCollapsibleNavigationGroups);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,9 +77,7 @@ class ListRecords extends Page implements Tables\Contracts\HasTable
|
||||
$this->loadDefaultActiveTab();
|
||||
}
|
||||
|
||||
protected function authorizeAccess(): void
|
||||
{
|
||||
}
|
||||
protected function authorizeAccess(): void {}
|
||||
|
||||
public function getBreadcrumb(): ?string
|
||||
{
|
||||
|
||||
@@ -11,8 +11,7 @@ class PageRegistration
|
||||
public function __construct(
|
||||
protected string $page,
|
||||
protected Closure $route,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
public function registerRoute(Panel $panel): ?Route
|
||||
{
|
||||
|
||||
@@ -27,8 +27,7 @@ class RelationGroup extends Component
|
||||
public function __construct(
|
||||
protected string | Closure $label,
|
||||
protected array | Closure $managers,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param array<class-string<RelationManager> | RelationManagerConfiguration> $managers
|
||||
|
||||
@@ -11,8 +11,7 @@ class RelationManagerConfiguration
|
||||
public function __construct(
|
||||
readonly public string $relationManager,
|
||||
protected array $properties = [],
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
|
||||
@@ -7,6 +7,4 @@ use Filament\Actions\LocaleSwitcher as BaseLocaleSwitcher;
|
||||
/**
|
||||
* @deprecated Use `\Filament\Actions\LocaleSwitcher` instead.
|
||||
*/
|
||||
class LocaleSwitcher extends BaseLocaleSwitcher
|
||||
{
|
||||
}
|
||||
class LocaleSwitcher extends BaseLocaleSwitcher {}
|
||||
|
||||
+1
-3
@@ -12,9 +12,7 @@ use function Filament\Support\generate_search_column_expression;
|
||||
|
||||
class SpatieLaravelTranslatableContentDriver implements TranslatableContentDriver
|
||||
{
|
||||
public function __construct(protected string $activeLocale)
|
||||
{
|
||||
}
|
||||
public function __construct(protected string $activeLocale) {}
|
||||
|
||||
public function isAttributeTranslatable(string $model, string $attribute): bool
|
||||
{
|
||||
|
||||
@@ -14,9 +14,7 @@ class SpatieLaravelTranslatablePlugin implements Plugin
|
||||
|
||||
protected ?Closure $getLocaleLabelUsing = null;
|
||||
|
||||
final public function __construct()
|
||||
{
|
||||
}
|
||||
final public function __construct() {}
|
||||
|
||||
public static function make(): static
|
||||
{
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
|
||||
namespace Filament\Support\Assets;
|
||||
|
||||
class Theme extends Css
|
||||
{
|
||||
}
|
||||
class Theme extends Css {}
|
||||
|
||||
@@ -10,8 +10,7 @@ class ChunkIterator
|
||||
public function __construct(
|
||||
protected Iterator $iterator,
|
||||
protected int $chunkSize,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
public function get(): Generator
|
||||
{
|
||||
|
||||
@@ -24,9 +24,7 @@ class ComponentManager implements ScopedComponentManager
|
||||
*/
|
||||
protected array $methodCache = [];
|
||||
|
||||
final public function __construct()
|
||||
{
|
||||
}
|
||||
final public function __construct() {}
|
||||
|
||||
public static function resolve(): ScopedComponentManager
|
||||
{
|
||||
|
||||
@@ -27,7 +27,5 @@ trait Configurable
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
}
|
||||
protected function setUp(): void {}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ class Markdown implements Htmlable, Stringable
|
||||
final public function __construct(
|
||||
protected string $text,
|
||||
protected bool $isInline = false,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
public static function inline(string $text): static
|
||||
{
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
|
||||
namespace Filament\Tables\Columns;
|
||||
|
||||
class ViewColumn extends Column
|
||||
{
|
||||
}
|
||||
class ViewColumn extends Column {}
|
||||
|
||||
@@ -21,9 +21,7 @@ trait HasBulkActions
|
||||
|
||||
protected Collection $cachedSelectedTableRecords;
|
||||
|
||||
protected function configureTableBulkAction(BulkAction $action): void
|
||||
{
|
||||
}
|
||||
protected function configureTableBulkAction(BulkAction $action): void {}
|
||||
|
||||
/**
|
||||
* @deprecated Use the `callMountedAction()` method instead.
|
||||
|
||||
@@ -11,8 +11,7 @@ class WidgetConfiguration
|
||||
public function __construct(
|
||||
readonly public string $widget,
|
||||
protected array $properties = [],
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
|
||||
@@ -7,6 +7,8 @@ parameters:
|
||||
excludePaths:
|
||||
- packages/upgrade/*
|
||||
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
|
||||
ignoreErrors:
|
||||
- '#Unable to resolve the template type TKey in call to function collect#'
|
||||
- '#Unable to resolve the template type TValue in call to function collect#'
|
||||
|
||||
@@ -173,9 +173,5 @@ test('components can be visible based on Livewire component', function () {
|
||||
->toHaveLength(1);
|
||||
});
|
||||
|
||||
class Foo extends Livewire
|
||||
{
|
||||
}
|
||||
class Bar extends Livewire
|
||||
{
|
||||
}
|
||||
class Foo extends Livewire {}
|
||||
class Bar extends Livewire {}
|
||||
|
||||
@@ -278,13 +278,17 @@ it('can call an action and halt', function () {
|
||||
});
|
||||
|
||||
it('can hide an action', function () {
|
||||
$post = Post::factory()->create();
|
||||
|
||||
livewire(PostsTable::class)
|
||||
->assertActionVisible(TestAction::make('visible')->table())
|
||||
->assertActionHidden(TestAction::make('hidden')->table());
|
||||
|
||||
livewire(PostsTable::class)
|
||||
->assertTableActionVisible('visible')
|
||||
->assertTableActionHidden('hidden');
|
||||
->assertTableActionHidden('hidden')
|
||||
->assertTableActionVisible('groupedWithVisibleGroupCondition', $post)
|
||||
->assertTableActionHidden('groupedWithHiddenGroupCondition', $post);
|
||||
});
|
||||
|
||||
it('can disable an action', function () {
|
||||
|
||||
Reference in New Issue
Block a user