mirror of
https://github.com/filamentphp/filament.git
synced 2026-09-24 15:42:09 +08:00
Merge pull request #14968 from filamentphp/feature/stats-overview-schema-refactor
feature: Stats overview schema refactor
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -14,11 +14,11 @@ use Illuminate\Contracts\Support\Htmlable;
|
||||
|
||||
class Entry extends Component
|
||||
{
|
||||
use Concerns\CanOpenUrl;
|
||||
use Concerns\HasExtraEntryWrapperAttributes;
|
||||
use Concerns\HasHelperText;
|
||||
use Concerns\HasHint;
|
||||
use Concerns\HasName;
|
||||
use \Filament\Schemas\Components\Concerns\CanOpenUrl;
|
||||
use HasAlignment;
|
||||
use HasPlaceholder;
|
||||
use HasTooltip;
|
||||
|
||||
@@ -12,7 +12,7 @@ trait InteractsWithPageFilters
|
||||
#[Reactive]
|
||||
public ?array $pageFilters = null;
|
||||
|
||||
public function __get($property)
|
||||
public function __get($property): mixed
|
||||
{
|
||||
// Backwards compatibility for the `$this->filters` property before it was renamed.
|
||||
if ($property === 'filters') {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
:collapsed="$isCollapsed()"
|
||||
:collapsible="$isCollapsible() && (! $isAside)"
|
||||
:compact="$isCompact()"
|
||||
:contained="$isContained()"
|
||||
:content-before="$isFormBefore()"
|
||||
:description="$getDescription()"
|
||||
:footer="$getDecorations($schemaComponent::FOOTER_DECORATIONS)"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Infolists\Components\Concerns;
|
||||
namespace Filament\Schemas\Components\Concerns;
|
||||
|
||||
use Closure;
|
||||
|
||||
@@ -15,6 +15,7 @@ use Filament\Schemas\Components\Contracts\CanConcealComponents;
|
||||
use Filament\Schemas\Components\Contracts\CanEntangleWithSingularRelationships;
|
||||
use Filament\Schemas\Components\Decorations\Layouts\AlignDecorations;
|
||||
use Filament\Schemas\Components\Decorations\Layouts\DecorationsLayout;
|
||||
use Filament\Support\Concerns\CanBeContained;
|
||||
use Filament\Support\Concerns\HasExtraAlpineAttributes;
|
||||
use Filament\Support\Concerns\HasIcon;
|
||||
use Filament\Support\Concerns\HasIconColor;
|
||||
@@ -25,6 +26,7 @@ class Section extends Component implements CanConcealComponents, CanEntangleWith
|
||||
{
|
||||
use CanBeCollapsed;
|
||||
use CanBeCompacted;
|
||||
use CanBeContained;
|
||||
use EntanglesStateWithSingularRelationship;
|
||||
use HasDescription;
|
||||
use HasExtraAlpineAttributes;
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,6 +1,64 @@
|
||||
.fi-section {
|
||||
&:not(.fi-aside) {
|
||||
@apply rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10;
|
||||
&:not(.fi-section-not-contained) {
|
||||
& .fi-section-content {
|
||||
@apply p-6;
|
||||
}
|
||||
|
||||
& .fi-section-footer {
|
||||
@apply border-t border-gray-200 px-6 py-4 dark:border-white/10;
|
||||
}
|
||||
|
||||
&:not(.fi-aside) {
|
||||
@apply rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10;
|
||||
|
||||
& .fi-section-header {
|
||||
@apply px-6 py-4;
|
||||
}
|
||||
|
||||
&.fi-section-has-header {
|
||||
& .fi-section-content-ctn {
|
||||
@apply border-t border-gray-200 dark:border-white/10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.fi-aside {
|
||||
& .fi-section-content-ctn {
|
||||
@apply rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10 md:col-span-2;
|
||||
}
|
||||
}
|
||||
|
||||
&.fi-compact {
|
||||
&:not(.fi-aside) {
|
||||
& .fi-section-header {
|
||||
@apply px-4 py-2.5;
|
||||
}
|
||||
}
|
||||
|
||||
& .fi-section-content {
|
||||
@apply p-4;
|
||||
}
|
||||
|
||||
& .fi-section-footer {
|
||||
@apply px-4 py-2.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.fi-section-not-contained:not(.fi-aside) {
|
||||
@apply grid gap-y-4;
|
||||
|
||||
& .fi-section-content-ctn {
|
||||
@apply grid gap-y-4;
|
||||
}
|
||||
|
||||
&.fi-compact {
|
||||
@apply gap-y-2.5;
|
||||
|
||||
& .fi-section-content-ctn {
|
||||
@apply gap-y-2.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.fi-aside {
|
||||
@@ -23,56 +81,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.fi-aside {
|
||||
& .fi-section-content-ctn {
|
||||
@apply rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10 md:col-span-2;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.fi-aside) {
|
||||
& .fi-section-header {
|
||||
@apply px-6 py-4;
|
||||
}
|
||||
}
|
||||
|
||||
&.fi-compact {
|
||||
&:not(.fi-aside) {
|
||||
& .fi-section-header {
|
||||
@apply px-4 py-2.5;
|
||||
}
|
||||
}
|
||||
|
||||
& .fi-section-content {
|
||||
@apply p-4;
|
||||
}
|
||||
|
||||
&.fi-section-footer {
|
||||
@apply px-4 py-2.5;
|
||||
}
|
||||
}
|
||||
|
||||
&.fi-section-has-header {
|
||||
&:not(.fi-aside) {
|
||||
& .fi-section-content-ctn {
|
||||
@apply border-t border-gray-200 dark:border-white/10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.fi-section-has-content-before {
|
||||
& .fi-section-content-ctn {
|
||||
@apply md:order-first;
|
||||
}
|
||||
}
|
||||
|
||||
& .fi-section-content {
|
||||
@apply p-6;
|
||||
}
|
||||
|
||||
& .fi-section-footer {
|
||||
@apply border-t border-gray-200 px-6 py-4 dark:border-white/10;
|
||||
}
|
||||
|
||||
& .fi-section-header {
|
||||
@apply flex items-center gap-3;
|
||||
}
|
||||
@@ -97,14 +111,6 @@
|
||||
@apply grid flex-1 gap-y-1;
|
||||
}
|
||||
|
||||
& .fi-section-content {
|
||||
@apply p-6;
|
||||
}
|
||||
|
||||
& .fi-section-footer {
|
||||
@apply border-t border-gray-200 px-6 py-4 dark:border-white/10;
|
||||
}
|
||||
|
||||
& .fi-section-header-heading {
|
||||
@apply text-base font-semibold leading-6 text-gray-950 dark:text-white;
|
||||
}
|
||||
|
||||
@@ -1,39 +1,3 @@
|
||||
.fi-wi-stats-overview {
|
||||
@apply grid gap-y-4;
|
||||
|
||||
& .fi-wi-stats-overview-header {
|
||||
@apply grid gap-y-1;
|
||||
|
||||
& .fi-wi-stats-overview-header-heading {
|
||||
@apply text-base font-semibold leading-6 text-gray-950 dark:text-white;
|
||||
}
|
||||
|
||||
& .fi-wi-stats-overview-header-description {
|
||||
@apply overflow-hidden break-words text-sm text-gray-500 dark:text-gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
& .fi-wi-stats-overview-stats-grid {
|
||||
@apply grid gap-6;
|
||||
|
||||
&.fi-grid-cols-1 {
|
||||
@apply md:grid-cols-1;
|
||||
}
|
||||
|
||||
&.fi-grid-cols-2 {
|
||||
@apply md:grid-cols-2;
|
||||
}
|
||||
|
||||
&.fi-grid-cols-3 {
|
||||
@apply md:grid-cols-3;
|
||||
}
|
||||
|
||||
&.fi-grid-cols-4 {
|
||||
@apply md:grid-cols-2 xl:grid-cols-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fi-wi-stats-overview-stat {
|
||||
@apply relative rounded-xl bg-white p-6 shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
'collapsed' => false,
|
||||
'collapsible' => false,
|
||||
'compact' => false,
|
||||
'contained' => true,
|
||||
'contentBefore' => false,
|
||||
'description' => null,
|
||||
'footer' => null,
|
||||
@@ -43,7 +44,8 @@
|
||||
{{
|
||||
$attributes->class([
|
||||
'fi-section',
|
||||
'fi-section-has-content-before' => filled($contentBefore),
|
||||
'fi-section-not-contained' => ! $contained,
|
||||
'fi-section-has-content-before' => $contentBefore,
|
||||
'fi-section-has-header' => $hasHeader,
|
||||
'fi-aside' => $aside,
|
||||
'fi-compact' => $compact,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"filament/schemas": "self.version",
|
||||
"filament/support": "self.version",
|
||||
"spatie/laravel-package-tools": "^1.9"
|
||||
},
|
||||
|
||||
@@ -19,30 +19,5 @@
|
||||
])
|
||||
"
|
||||
>
|
||||
@if ($hasHeading || $hasDescription)
|
||||
<div class="fi-wi-stats-overview-header">
|
||||
@if ($hasHeading)
|
||||
<h3 class="fi-wi-stats-overview-header-heading">
|
||||
{{ $heading }}
|
||||
</h3>
|
||||
@endif
|
||||
|
||||
@if ($hasDescription)
|
||||
<p class="fi-wi-stats-overview-header-description">
|
||||
{{ $description }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div
|
||||
@class([
|
||||
'fi-wi-stats-overview-stats-grid',
|
||||
'fi-grid-cols-' . $columns,
|
||||
])
|
||||
>
|
||||
@foreach ($this->getCachedStats() as $stat)
|
||||
{{ $stat }}
|
||||
@endforeach
|
||||
</div>
|
||||
{{ $this->content }}
|
||||
</x-filament-widgets::widget>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
$descriptionIconPosition = $getDescriptionIconPosition();
|
||||
$url = $getUrl();
|
||||
$tag = $url ? 'a' : 'div';
|
||||
$dataChecksum = $generateDataChecksum();
|
||||
$chartDataChecksum = $generateChartDataChecksum();
|
||||
|
||||
$descriptionIconStyles = \Illuminate\Support\Arr::toCssStyles([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
@@ -87,7 +87,7 @@
|
||||
@endif
|
||||
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('stats-overview/stat/chart', 'filament/widgets') }}"
|
||||
x-data="statsOverviewStatChart({
|
||||
dataChecksum: @js($dataChecksum),
|
||||
dataChecksum: @js($chartDataChecksum),
|
||||
labels: @js(array_keys($chart)),
|
||||
values: @js(array_values($chart)),
|
||||
})"
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Concerns\InteractsWithSchemas;
|
||||
use Filament\Schemas\Contracts\HasSchemas;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
class StatsOverviewWidget extends Widget
|
||||
class StatsOverviewWidget extends Widget implements HasSchemas
|
||||
{
|
||||
use Concerns\CanPoll;
|
||||
use InteractsWithSchemas;
|
||||
|
||||
/**
|
||||
* @var array<Stat> | null
|
||||
@@ -24,7 +29,23 @@ class StatsOverviewWidget extends Widget
|
||||
*/
|
||||
protected static string $view = 'filament-widgets::stats-overview-widget';
|
||||
|
||||
protected function getColumns(): int
|
||||
public function content(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make()
|
||||
->heading($this->getHeading())
|
||||
->description($this->getDescription())
|
||||
->schema($this->getCachedStats())
|
||||
->columns($this->getColumns())
|
||||
->contained(false),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, int | string | null> | int | string | null
|
||||
*/
|
||||
protected function getColumns(): array | int | string | null
|
||||
{
|
||||
$count = count($this->getCachedStats());
|
||||
|
||||
@@ -39,6 +60,16 @@ class StatsOverviewWidget extends Widget
|
||||
return 4;
|
||||
}
|
||||
|
||||
protected function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
protected function getHeading(): ?string
|
||||
{
|
||||
return $this->heading;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Stat>
|
||||
*/
|
||||
@@ -57,16 +88,6 @@ class StatsOverviewWidget extends Widget
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
protected function getHeading(): ?string
|
||||
{
|
||||
return $this->heading;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Stat>
|
||||
*/
|
||||
|
||||
@@ -3,17 +3,20 @@
|
||||
namespace Filament\Widgets\StatsOverviewWidget;
|
||||
|
||||
use Closure;
|
||||
use Filament\Support\Concerns\Macroable;
|
||||
use Filament\Schemas\Components\Component;
|
||||
use Filament\Schemas\Components\Concerns\CanOpenUrl;
|
||||
use Filament\Schemas\Components\Concerns\HasDescription;
|
||||
use Filament\Support\Concerns\HasColor;
|
||||
use Filament\Support\Enums\IconPosition;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
class Stat extends Component implements Htmlable
|
||||
class Stat extends Component
|
||||
{
|
||||
use Macroable;
|
||||
use CanOpenUrl;
|
||||
use HasColor;
|
||||
use HasDescription;
|
||||
|
||||
protected string $view = 'filament-widgets::stats-overview-widget.stat';
|
||||
|
||||
/**
|
||||
* @var array<float> | null
|
||||
@@ -25,15 +28,8 @@ class Stat extends Component implements Htmlable
|
||||
*/
|
||||
protected string | array | null $chartColor = null;
|
||||
|
||||
/**
|
||||
* @var string | array{50: string, 100: string, 200: string, 300: string, 400: string, 500: string, 600: string, 700: string, 800: string, 900: string, 950: string} | null
|
||||
*/
|
||||
protected string | array | null $color = null;
|
||||
|
||||
protected ?string $icon = null;
|
||||
|
||||
protected string | Htmlable | null $description = null;
|
||||
|
||||
protected ?string $descriptionIcon = null;
|
||||
|
||||
protected IconPosition | string | null $descriptionIconPosition = null;
|
||||
@@ -43,19 +39,6 @@ class Stat extends Component implements Htmlable
|
||||
*/
|
||||
protected string | array | null $descriptionColor = null;
|
||||
|
||||
/**
|
||||
* @var array<string, scalar>
|
||||
*/
|
||||
protected array $extraAttributes = [];
|
||||
|
||||
protected bool $shouldOpenUrlInNewTab = false;
|
||||
|
||||
protected ?string $url = null;
|
||||
|
||||
protected ?string $id = null;
|
||||
|
||||
protected string | Htmlable $label;
|
||||
|
||||
/**
|
||||
* @var scalar | Htmlable | Closure
|
||||
*/
|
||||
@@ -88,16 +71,6 @@ class Stat extends Component implements Htmlable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string | array{50: string, 100: string, 200: string, 300: string, 400: string, 500: string, 600: string, 700: string, 800: string, 900: string, 950: string} | null $color
|
||||
*/
|
||||
public function color(string | array | null $color): static
|
||||
{
|
||||
$this->color = $color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function icon(?string $icon): static
|
||||
{
|
||||
$this->icon = $icon;
|
||||
@@ -105,13 +78,6 @@ class Stat extends Component implements Htmlable
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function description(string | Htmlable | null $description): static
|
||||
{
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string | array{50: string, 100: string, 200: string, 300: string, 400: string, 500: string, 600: string, 700: string, 800: string, 900: string, 950: string} | null $color
|
||||
*/
|
||||
@@ -130,31 +96,6 @@ class Stat extends Component implements Htmlable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, scalar> $attributes
|
||||
*/
|
||||
public function extraAttributes(array $attributes): static
|
||||
{
|
||||
$this->extraAttributes = $attributes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function openUrlInNewTab(bool $condition = true): static
|
||||
{
|
||||
$this->shouldOpenUrlInNewTab = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function url(?string $url, bool $shouldOpenInNewTab = false): static
|
||||
{
|
||||
$this->openUrlInNewTab($shouldOpenInNewTab);
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<float> | null $chart
|
||||
*/
|
||||
@@ -165,20 +106,6 @@ class Stat extends Component implements Htmlable
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function label(string | Htmlable $label): static
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function id(string $id): static
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scalar | Htmlable | Closure $value
|
||||
*/
|
||||
@@ -202,15 +129,7 @@ class Stat extends Component implements Htmlable
|
||||
*/
|
||||
public function getChartColor(): string | array | null
|
||||
{
|
||||
return $this->chartColor ?? $this->color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string | array{50: string, 100: string, 200: string, 300: string, 400: string, 500: string, 600: string, 700: string, 800: string, 900: string, 950: string} | null
|
||||
*/
|
||||
public function getColor(): string | array | null
|
||||
{
|
||||
return $this->color;
|
||||
return $this->chartColor ?? $this->getColor();
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
@@ -218,17 +137,12 @@ class Stat extends Component implements Htmlable
|
||||
return $this->icon;
|
||||
}
|
||||
|
||||
public function getDescription(): string | Htmlable | null
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string | array{50: string, 100: string, 200: string, 300: string, 400: string, 500: string, 600: string, 700: string, 800: string, 900: string, 950: string} | null
|
||||
*/
|
||||
public function getDescriptionColor(): string | array | null
|
||||
{
|
||||
return $this->descriptionColor ?? $this->color;
|
||||
return $this->descriptionColor ?? $this->getColor();
|
||||
}
|
||||
|
||||
public function getDescriptionIcon(): ?string
|
||||
@@ -241,58 +155,15 @@ class Stat extends Component implements Htmlable
|
||||
return $this->descriptionIconPosition ?? IconPosition::After;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, scalar>
|
||||
*/
|
||||
public function getExtraAttributes(): array
|
||||
{
|
||||
return $this->extraAttributes;
|
||||
}
|
||||
|
||||
public function getExtraAttributeBag(): ComponentAttributeBag
|
||||
{
|
||||
return new ComponentAttributeBag($this->getExtraAttributes());
|
||||
}
|
||||
|
||||
public function getUrl(): ?string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function shouldOpenUrlInNewTab(): bool
|
||||
{
|
||||
return $this->shouldOpenUrlInNewTab;
|
||||
}
|
||||
|
||||
public function getLabel(): string | Htmlable
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id ?? Str::slug($this->getLabel());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return scalar | Htmlable | Closure
|
||||
*/
|
||||
public function getValue()
|
||||
public function getValue(): mixed
|
||||
{
|
||||
return value($this->value);
|
||||
}
|
||||
|
||||
public function toHtml(): string
|
||||
{
|
||||
return $this->render()->render();
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('filament-widgets::stats-overview-widget.stat', $this->data());
|
||||
}
|
||||
|
||||
public function generateDataChecksum(): string
|
||||
public function generateChartDataChecksum(): string
|
||||
{
|
||||
return md5(json_encode($this->getChart()) . now());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user