Merge pull request #14968 from filamentphp/feature/stats-overview-schema-refactor

feature: Stats overview schema refactor
This commit is contained in:
Dan Harrin
2024-12-02 11:39:19 +00:00
committed by GitHub
15 changed files with 122 additions and 279 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -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,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;
+1 -1
View File
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,
+1
View File
@@ -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)),
})"
+33 -12
View File
@@ -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>
*/
+14 -143
View File
@@ -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());
}