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
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
"mime": "^4.0.4",
"nouislider": "^15.8.1",
"npm-run-all": "^4.1.5",
"playwright": "^1.57.0",
"playwright": "^1.58.0",
"prettier": "^3.1.1",
"prettier-plugin-blade": "v2.0.0",
"prettier-plugin-tailwindcss": "^0.6.10",
Expand Down
2 changes: 1 addition & 1 deletion packages/panels/dist/theme.css

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions packages/support/resources/css/components/section.css
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,15 @@
}

& > .fi-section-header-after-ctn {
& .fi-sc-text,
& .fi-link {
&
.fi-sc-text:not(
.fi-section-header-after-ctn .fi-dropdown-panel *
),
& .fi-link:not(.fi-section-header-after-ctn .fi-dropdown-panel *) {
@apply leading-6;
}

& .fi-btn {
& .fi-btn:not(.fi-section-header-after-ctn .fi-dropdown-panel *) {
&.fi-size-xs {
@apply -my-0.5;
}
Expand Down
58 changes: 58 additions & 0 deletions packages/widgets/docs/03-charts.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,64 @@ The `$this->filters` array will always reflect the current form data. Please not
If you want to add filters that apply to multiple widgets at once, see [filtering widget data](overview#filtering-widget-data) in the dashboard.
</Aside>

#### Deferring filter updates

By default, filters using the `filtersSchema()` method update the chart data immediately as they are changed. However, for complex queries or better user experience, you may want to **defer** filter updates until the user clicks an "Apply" button.

When deferred, filter changes are only applied when the user clicks the "Apply" button. This ensures that the chart only re-renders when the user has finished adjusting all of their filters.

The chart will display data using the default filter values when the page first loads, ensuring users see meaningful data immediately without needing to take action.

To enable deferred filters, set the `$hasDeferredFilters` property to `true`:

```php
use Filament\Widgets\ChartWidget\Concerns\HasFiltersSchema;

class BlogPostsChart extends ChartWidget
{
use HasFiltersSchema;

protected bool $hasDeferredFilters = true;

// ...
}
```

If you need dynamic control over whether filters are deferred, you may override the `hasDeferredFilters()` method:

```php
public function hasDeferredFilters(): bool
{
return auth()->user()->prefersDeferredFilters();
}
```

#### Resetting filters to defaults

When using deferred filters, a "Reset" link appears in the filter dropdown footer alongside the "Apply" button. Clicking this link restores all filters to their default values as defined in the `filtersSchema()` method. For example, if you set `->default(now()->subDays(30))` on a `DatePicker`, the reset action will restore that default date, not an empty value.

#### Customizing filter actions

You may customize the apply and reset actions that appear when using deferred filters. All methods that are available to [customize action trigger buttons](../actions/overview) can be used:

```php
use Filament\Actions\Action;

public function filtersApplyAction(Action $action): Action
{
return $action
->label('Update Chart')
->color('success');
}

public function filtersResetAction(Action $action): Action
{
return $action
->label('Clear Filters')
->color('danger');
}
```

## Live updating chart data (polling)

By default, chart widgets refresh their data every 5 seconds.
Expand Down
6 changes: 5 additions & 1 deletion packages/widgets/resources/css/chart-widget.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@

&.fi-dropdown {
& .fi-wi-chart-filter-content {
@apply p-6;
@apply grid gap-y-4 p-6;
}

& .fi-wi-chart-filter-content-actions-ctn {
@apply flex gap-3;
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions packages/widgets/resources/lang/en/chart.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,20 @@

],

'filters' => [

'actions' => [

'apply' => [
'label' => 'Apply',
],

'reset' => [
'label' => 'Reset',
],

],

],

];
10 changes: 10 additions & 0 deletions packages/widgets/resources/views/chart-widget.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ class="fi-wi-chart-filter"

<div class="fi-wi-chart-filter-content">
{{ $this->getFiltersSchema() }}

@if (method_exists($this, 'hasDeferredFilters') && $this->hasDeferredFilters())
<div
class="fi-wi-chart-filter-content-actions-ctn"
>
{{ $this->getFiltersApplyAction() }}

{{ $this->getFiltersResetAction() }}
</div>
@endif
</div>
</x-filament::dropdown>
@endif
Expand Down
4 changes: 0 additions & 4 deletions packages/widgets/src/ChartWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ abstract class ChartWidget extends Widget implements HasSchemas

public function mount(): void
{
if (method_exists($this, 'getFiltersSchema')) {
$this->getFiltersSchema()->fill();
}

$this->dataChecksum = $this->generateDataChecksum();
}

Expand Down
88 changes: 85 additions & 3 deletions packages/widgets/src/ChartWidget/Concerns/HasFiltersSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,35 @@

trait HasFiltersSchema /** @phpstan-ignore trait.unused */
{
/**
* @var array<string, mixed> | null
*/
public ?array $filters = [];

/**
* @var array<string, mixed> | null
*/
public ?array $deferredFilters = null;

public function filtersSchema(Schema $schema): Schema
{
return $schema;
}

public function hasDeferredFilters(): bool
{
return property_exists($this, 'hasDeferredFilters') && $this->hasDeferredFilters;
}

public function mountHasFiltersSchema(): void
{
$this->getFiltersSchema()->fill();

if ($this->hasDeferredFilters()) {
$this->filters = $this->deferredFilters;
}
}

public function getFiltersTriggerAction(): Action
{
return Action::make('filter')
Expand All @@ -33,8 +55,68 @@ public function getFiltersSchema(): Schema
return $this->getSchema('filtersSchema');
}

return $this->filtersSchema($this->makeSchema()
->statePath('filters')
->live());
return $this->filtersSchema($this->makeSchema())
->when(
$this->hasDeferredFilters(),
fn (Schema $schema) => $schema
->statePath('deferredFilters')
->partiallyRender(),
fn (Schema $schema) => $schema
->statePath('filters')
->live(),
);
}

public function updatedFilters(): void
{
$this->cachedData = null;
}

public function applyFilters(): void
{
$this->filters = $this->deferredFilters;
$this->cachedData = null;
}

public function resetFiltersForm(): void
{
$this->getFiltersSchema()->fill();

if ($this->hasDeferredFilters()) {
$this->applyFilters();

return;
}

$this->cachedData = null;
}

public function getFiltersApplyAction(): Action
{
$action = Action::make('applyFilters')
->label(__('filament-widgets::chart.filters.actions.apply.label'))
->action('applyFilters')
->button();

if (method_exists($this, 'filtersApplyAction')) {
$action = $this->filtersApplyAction($action);
}

return $action;
}

public function getFiltersResetAction(): Action
{
$action = Action::make('resetFilters')
->label(__('filament-widgets::chart.filters.actions.reset.label'))
->action('resetFiltersForm')
->color('danger')
->button();

if (method_exists($this, 'filtersResetAction')) {
$action = $this->filtersResetAction($action);
}

return $action;
}
}
Loading
Loading