[增添]添加了datasource的setting数据库以及默认值
This commit is contained in:
32
vendor/filament/widgets/composer.json
vendored
Normal file
32
vendor/filament/widgets/composer.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "filament/widgets",
|
||||
"description": "Easily add beautiful dashboard widgets to any Livewire component.",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/filamentphp/filament",
|
||||
"support": {
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"filament/support": "self.version",
|
||||
"spatie/laravel-package-tools": "^1.9"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Filament\\Widgets\\": "src"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Filament\\Widgets\\WidgetsServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
37
vendor/filament/widgets/dist/components/chart.js
vendored
Normal file
37
vendor/filament/widgets/dist/components/chart.js
vendored
Normal file
File diff suppressed because one or more lines are too long
29
vendor/filament/widgets/dist/components/stats-overview/stat/chart.js
vendored
Normal file
29
vendor/filament/widgets/dist/components/stats-overview/stat/chart.js
vendored
Normal file
File diff suppressed because one or more lines are too long
174
vendor/filament/widgets/docs/01-installation.md
vendored
Normal file
174
vendor/filament/widgets/docs/01-installation.md
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: Installation
|
||||
---
|
||||
|
||||
**The Widgets package is pre-installed with the [Panel Builder](/docs/panels).** This guide is for using the Widgets package in a custom TALL Stack application (Tailwind, Alpine, Livewire, Laravel).
|
||||
|
||||
## Requirements
|
||||
|
||||
Filament requires the following to run:
|
||||
|
||||
- PHP 8.1+
|
||||
- Laravel v10.0+
|
||||
- Livewire v3.0+
|
||||
|
||||
## Installation
|
||||
|
||||
Require the Widgets package using Composer:
|
||||
|
||||
```bash
|
||||
composer require filament/widgets:"^3.2" -W
|
||||
```
|
||||
|
||||
## New Laravel projects
|
||||
|
||||
To quickly get started with Filament in a new Laravel project, run the following commands to install [Livewire](https://livewire.laravel.com), [Alpine.js](https://alpinejs.dev), and [Tailwind CSS](https://tailwindcss.com):
|
||||
|
||||
> Since these commands will overwrite existing files in your application, only run this in a new Laravel project!
|
||||
|
||||
```bash
|
||||
php artisan filament:install --scaffold --widgets
|
||||
|
||||
npm install
|
||||
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Existing Laravel projects
|
||||
|
||||
Run the following command to install the Widgets package assets:
|
||||
|
||||
```bash
|
||||
php artisan filament:install --widgets
|
||||
```
|
||||
|
||||
### Installing Tailwind CSS
|
||||
|
||||
Run the following command to install Tailwind CSS with the Tailwind Forms and Typography plugins:
|
||||
|
||||
```bash
|
||||
npm install tailwindcss @tailwindcss/forms @tailwindcss/typography postcss postcss-nesting autoprefixer --save-dev
|
||||
```
|
||||
|
||||
Create a new `tailwind.config.js` file and add the Filament `preset` *(includes the Filament color scheme and the required Tailwind plugins)*:
|
||||
|
||||
```js
|
||||
import preset from './vendor/filament/support/tailwind.config.preset'
|
||||
|
||||
export default {
|
||||
presets: [preset],
|
||||
content: [
|
||||
'./app/Filament/**/*.php',
|
||||
'./resources/views/filament/**/*.blade.php',
|
||||
'./vendor/filament/**/*.blade.php',
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### Configuring styles
|
||||
|
||||
Add Tailwind's CSS layers to your `resources/css/app.css`:
|
||||
|
||||
```css
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
```
|
||||
|
||||
Create a `postcss.config.js` file in the root of your project and register Tailwind CSS, PostCSS Nesting and Autoprefixer as plugins:
|
||||
|
||||
```js
|
||||
export default {
|
||||
plugins: {
|
||||
'tailwindcss/nesting': 'postcss-nesting',
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Automatically refreshing the browser
|
||||
You may also want to update your `vite.config.js` file to refresh the page automatically when Livewire components are updated:
|
||||
|
||||
```js
|
||||
import { defineConfig } from 'vite'
|
||||
import laravel, { refreshPaths } from 'laravel-vite-plugin'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
laravel({
|
||||
input: ['resources/css/app.css', 'resources/js/app.js'],
|
||||
refresh: [
|
||||
...refreshPaths,
|
||||
'app/Livewire/**',
|
||||
],
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
### Compiling assets
|
||||
|
||||
Compile your new CSS and Javascript assets using `npm run dev`.
|
||||
|
||||
### Configuring your layout
|
||||
|
||||
Create a new `resources/views/components/layouts/app.blade.php` layout file for Livewire components:
|
||||
|
||||
```blade
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<meta name="application-name" content="{{ config('app.name') }}">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>{{ config('app.name') }}</title>
|
||||
|
||||
<style>
|
||||
[x-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@filamentStyles
|
||||
@vite('resources/css/app.css')
|
||||
</head>
|
||||
|
||||
<body class="antialiased">
|
||||
{{ $slot }}
|
||||
|
||||
@filamentScripts
|
||||
@vite('resources/js/app.js')
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Publishing configuration
|
||||
|
||||
You can publish the package configuration using the following command (optional):
|
||||
|
||||
```bash
|
||||
php artisan vendor:publish --tag=filament-config
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
Filament automatically upgrades to the latest non-breaking version when you run `composer update`. After any updates, all Laravel caches need to be cleared, and frontend assets need to be republished. You can do this all at once using the `filament:upgrade` command, which should have been added to your `composer.json` file when you ran `filament:install` the first time:
|
||||
|
||||
```json
|
||||
"post-autoload-dump": [
|
||||
// ...
|
||||
"@php artisan filament:upgrade"
|
||||
],
|
||||
```
|
||||
|
||||
Please note that `filament:upgrade` does not actually handle the update process, as Composer does that already. If you're upgrading manually without a `post-autoload-dump` hook, you can run the command yourself:
|
||||
|
||||
```bash
|
||||
composer update
|
||||
|
||||
php artisan filament:upgrade
|
||||
```
|
||||
167
vendor/filament/widgets/docs/02-stats-overview.md
vendored
Normal file
167
vendor/filament/widgets/docs/02-stats-overview.md
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
---
|
||||
title: Stats overview widgets
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Filament comes with a "stats overview" widget template, which you can use to display a number of different stats in a single widget, without needing to write a custom view.
|
||||
|
||||
Start by creating a widget with the command:
|
||||
|
||||
```bash
|
||||
php artisan make:filament-widget StatsOverview --stats-overview
|
||||
```
|
||||
|
||||
This command will create a new `StatsOverview.php` file. Open it, and return `Stat` instances from the `getStats()` method:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
class StatsOverview extends BaseWidget
|
||||
{
|
||||
protected function getStats(): array
|
||||
{
|
||||
return [
|
||||
Stat::make('Unique views', '192.1k'),
|
||||
Stat::make('Bounce rate', '21%'),
|
||||
Stat::make('Average time on page', '3:12'),
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, check out your widget in the dashboard.
|
||||
|
||||
## Adding a description and icon to a stat
|
||||
|
||||
You may add a `description()` to provide additional information, along with a `descriptionIcon()`:
|
||||
|
||||
```php
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
protected function getStats(): array
|
||||
{
|
||||
return [
|
||||
Stat::make('Unique views', '192.1k')
|
||||
->description('32k increase')
|
||||
->descriptionIcon('heroicon-m-arrow-trending-up'),
|
||||
Stat::make('Bounce rate', '21%')
|
||||
->description('7% decrease')
|
||||
->descriptionIcon('heroicon-m-arrow-trending-down'),
|
||||
Stat::make('Average time on page', '3:12')
|
||||
->description('3% increase')
|
||||
->descriptionIcon('heroicon-m-arrow-trending-up'),
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
The `descriptionIcon()` method also accepts a second parameter to put the icon before the description instead of after it:
|
||||
|
||||
```php
|
||||
use Filament\Support\Enums\IconPosition;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
Stat::make('Unique views', '192.1k')
|
||||
->description('32k increase')
|
||||
->descriptionIcon('heroicon-m-arrow-trending-up', IconPosition::Before)
|
||||
```
|
||||
|
||||
## Changing the color of the stat
|
||||
|
||||
You may also give stats a `color()` (`danger`, `gray`, `info`, `primary`, `success` or `warning`):
|
||||
|
||||
```php
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
protected function getStats(): array
|
||||
{
|
||||
return [
|
||||
Stat::make('Unique views', '192.1k')
|
||||
->description('32k increase')
|
||||
->descriptionIcon('heroicon-m-arrow-trending-up')
|
||||
->color('success'),
|
||||
Stat::make('Bounce rate', '21%')
|
||||
->description('7% increase')
|
||||
->descriptionIcon('heroicon-m-arrow-trending-down')
|
||||
->color('danger'),
|
||||
Stat::make('Average time on page', '3:12')
|
||||
->description('3% increase')
|
||||
->descriptionIcon('heroicon-m-arrow-trending-up')
|
||||
->color('success'),
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
## Adding extra HTML attributes to a stat
|
||||
|
||||
You may also pass extra HTML attributes to stats using `extraAttributes()`:
|
||||
|
||||
```php
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
protected function getStats(): array
|
||||
{
|
||||
return [
|
||||
Stat::make('Processed', '192.1k')
|
||||
->color('success')
|
||||
->extraAttributes([
|
||||
'class' => 'cursor-pointer',
|
||||
'wire:click' => "\$dispatch('setStatusFilter', { filter: 'processed' })",
|
||||
]),
|
||||
// ...
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
In this example, we are deliberately escaping the `$` in `$dispatch()` since this needs to be passed directly to the HTML, it is not a PHP variable.
|
||||
|
||||
## Adding a chart to a stat
|
||||
|
||||
You may also add or chain a `chart()` to each stat to provide historical data. The `chart()` method accepts an array of data points to plot:
|
||||
|
||||
```php
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
protected function getStats(): array
|
||||
{
|
||||
return [
|
||||
Stat::make('Unique views', '192.1k')
|
||||
->description('32k increase')
|
||||
->descriptionIcon('heroicon-m-arrow-trending-up')
|
||||
->chart([7, 2, 10, 3, 15, 4, 17])
|
||||
->color('success'),
|
||||
// ...
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
## Live updating stats (polling)
|
||||
|
||||
By default, stats overview widgets refresh their data every 5 seconds.
|
||||
|
||||
To customize this, you may override the `$pollingInterval` property on the class to a new interval:
|
||||
|
||||
```php
|
||||
protected static ?string $pollingInterval = '10s';
|
||||
```
|
||||
|
||||
Alternatively, you may disable polling altogether:
|
||||
|
||||
```php
|
||||
protected static ?string $pollingInterval = null;
|
||||
```
|
||||
|
||||
## Disabling lazy loading
|
||||
|
||||
By default, widgets are lazy-loaded. This means that they will only be loaded when they are visible on the page.
|
||||
|
||||
To disable this behavior, you may override the `$isLazy` property on the widget class:
|
||||
|
||||
```php
|
||||
protected static bool $isLazy = false;
|
||||
```
|
||||
321
vendor/filament/widgets/docs/03-charts.md
vendored
Normal file
321
vendor/filament/widgets/docs/03-charts.md
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
---
|
||||
title: Chart widgets
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Filament comes with many "chart" widget templates, which you can use to display real-time, interactive charts.
|
||||
|
||||
Start by creating a widget with the command:
|
||||
|
||||
```bash
|
||||
php artisan make:filament-widget BlogPostsChart --chart
|
||||
```
|
||||
|
||||
There is a single `ChartWidget` class that is used for all charts. The type of chart is set by the `getType()` method. In this example, that method returns the string `'line'`.
|
||||
|
||||
The `protected static ?string $heading` variable is used to set the heading that describes the chart. If you need to set the heading dynamically, you can override the `getHeading()` method.
|
||||
|
||||
The `getData()` method is used to return an array of datasets and labels. Each dataset is a labeled array of points to plot on the chart, and each label is a string. This structure is identical to the [Chart.js](https://www.chartjs.org/docs) library, which Filament uses to render charts. You may use the [Chart.js documentation](https://www.chartjs.org/docs) to fully understand the possibilities to return from `getData()`, based on the chart type.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use Filament\Widgets\ChartWidget;
|
||||
|
||||
class BlogPostsChart extends ChartWidget
|
||||
{
|
||||
protected static ?string $heading = 'Blog Posts';
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Blog posts created',
|
||||
'data' => [0, 10, 5, 2, 21, 32, 45, 74, 65, 45, 77, 89],
|
||||
],
|
||||
],
|
||||
'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'line';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, check out your widget in the dashboard.
|
||||
|
||||
## Available chart types
|
||||
|
||||
Below is a list of available chart widget classes which you may extend, and their corresponding [Chart.js](https://www.chartjs.org/docs) documentation page, for inspiration on what to return from `getData()`:
|
||||
|
||||
- Bar chart - [Chart.js documentation](https://www.chartjs.org/docs/latest/charts/bar)
|
||||
- Bubble chart - [Chart.js documentation](https://www.chartjs.org/docs/latest/charts/bubble)
|
||||
- Doughnut chart - [Chart.js documentation](https://www.chartjs.org/docs/latest/charts/doughnut)
|
||||
- Line chart - [Chart.js documentation](https://www.chartjs.org/docs/latest/charts/line)
|
||||
- Pie chart - [Chart.js documentation](https://www.chartjs.org/docs/latest/charts/doughnut)
|
||||
- Polar area chart - [Chart.js documentation](https://www.chartjs.org/docs/latest/charts/polar)
|
||||
- Radar chart - [Chart.js documentation](https://www.chartjs.org/docs/latest/charts/radar)
|
||||
- Scatter chart - [Chart.js documentation](https://www.chartjs.org/docs/latest/charts/scatter)
|
||||
|
||||
## Customizing the chart color
|
||||
|
||||
You can customize the color of the chart data by setting the `$color` property to either `danger`, `gray`, `info`, `primary`, `success` or `warning`:
|
||||
|
||||
```php
|
||||
protected static string $color = 'info';
|
||||
```
|
||||
|
||||
If you're looking to customize the color further, or use multiple colors across multiple datasets, you can still make use of Chart.js's [color options](https://www.chartjs.org/docs/latest/general/colors.html) in the data:
|
||||
|
||||
```php
|
||||
protected function getData(): array
|
||||
{
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Blog posts created',
|
||||
'data' => [0, 10, 5, 2, 21, 32, 45, 74, 65, 45, 77, 89],
|
||||
'backgroundColor' => '#36A2EB',
|
||||
'borderColor' => '#9BD0F5',
|
||||
],
|
||||
],
|
||||
'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
## Generating chart data from an Eloquent model
|
||||
|
||||
To generate chart data from an Eloquent model, Filament recommends that you install the `flowframe/laravel-trend` package. You can view the [documentation](https://github.com/Flowframe/laravel-trend).
|
||||
|
||||
Here is an example of generating chart data from a model using the `laravel-trend` package:
|
||||
|
||||
```php
|
||||
use Flowframe\Trend\Trend;
|
||||
use Flowframe\Trend\TrendValue;
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$data = Trend::model(BlogPost::class)
|
||||
->between(
|
||||
start: now()->startOfYear(),
|
||||
end: now()->endOfYear(),
|
||||
)
|
||||
->perMonth()
|
||||
->count();
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Blog posts',
|
||||
'data' => $data->map(fn (TrendValue $value) => $value->aggregate),
|
||||
],
|
||||
],
|
||||
'labels' => $data->map(fn (TrendValue $value) => $value->date),
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
## Filtering chart data
|
||||
|
||||
You can set up chart filters to change the data shown on chart. Commonly, this is used to change the time period that chart data is rendered for.
|
||||
|
||||
To set a default filter value, set the `$filter` property:
|
||||
|
||||
```php
|
||||
public ?string $filter = 'today';
|
||||
```
|
||||
|
||||
Then, define the `getFilters()` method to return an array of values and labels for your filter:
|
||||
|
||||
```php
|
||||
protected function getFilters(): ?array
|
||||
{
|
||||
return [
|
||||
'today' => 'Today',
|
||||
'week' => 'Last week',
|
||||
'month' => 'Last month',
|
||||
'year' => 'This year',
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
You can use the active filter value within your `getData()` method:
|
||||
|
||||
```php
|
||||
protected function getData(): array
|
||||
{
|
||||
$activeFilter = $this->filter;
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Live updating chart data (polling)
|
||||
|
||||
By default, chart widgets refresh their data every 5 seconds.
|
||||
|
||||
To customize this, you may override the `$pollingInterval` property on the class to a new interval:
|
||||
|
||||
```php
|
||||
protected static ?string $pollingInterval = '10s';
|
||||
```
|
||||
|
||||
Alternatively, you may disable polling altogether:
|
||||
|
||||
```php
|
||||
protected static ?string $pollingInterval = null;
|
||||
```
|
||||
|
||||
## Setting a maximum chart height
|
||||
|
||||
You may place a maximum height on the chart to ensure that it doesn't get too big, using the `$maxHeight` property:
|
||||
|
||||
```php
|
||||
protected static ?string $maxHeight = '300px';
|
||||
```
|
||||
|
||||
## Setting chart configuration options
|
||||
|
||||
You may specify an `$options` variable on the chart class to control the many configuration options that the Chart.js library provides. For instance, you could turn off the [legend](https://www.chartjs.org/docs/latest/configuration/legend.html) for a line chart:
|
||||
|
||||
```php
|
||||
protected static ?array $options = [
|
||||
'plugins' => [
|
||||
'legend' => [
|
||||
'display' => false,
|
||||
],
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
Alternatively, you can override the `getOptions()` method to return a dynamic array of options:
|
||||
|
||||
```php
|
||||
protected function getOptions(): array
|
||||
{
|
||||
return [
|
||||
'plugins' => [
|
||||
'legend' => [
|
||||
'display' => false,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
These PHP arrays will get transformed into JSON objects when the chart is rendered. If you want to return raw JavaScript from this method instead, you can return a `RawJs` object. This is useful if you want to use a JavaScript callback function, for example:
|
||||
|
||||
```php
|
||||
use Filament\Support\RawJs;
|
||||
|
||||
protected function getOptions(): RawJs
|
||||
{
|
||||
return RawJs::make(<<<JS
|
||||
{
|
||||
scales: {
|
||||
y: {
|
||||
ticks: {
|
||||
callback: (value) => '€' + value,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
JS);
|
||||
}
|
||||
```
|
||||
|
||||
## Adding a description
|
||||
|
||||
You may add a description, below the heading of the chart, using the `getDescription()` method:
|
||||
|
||||
```php
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return 'The number of blog posts published per month.';
|
||||
}
|
||||
```
|
||||
|
||||
## Disabling lazy loading
|
||||
|
||||
By default, widgets are lazy-loaded. This means that they will only be loaded when they are visible on the page.
|
||||
|
||||
To disable this behavior, you may override the `$isLazy` property on the widget class:
|
||||
|
||||
```php
|
||||
protected static bool $isLazy = true;
|
||||
```
|
||||
|
||||
## Using custom Chart.js plugins
|
||||
|
||||
Chart.js offers a powerful plugin system that allows you to extend its functionality and create custom chart behaviors. This guide details how to use them in a chart widget.
|
||||
|
||||
### Step 1: Install the plugin with NPM
|
||||
|
||||
To start with, install the plugin using NPM into your project. In this guide, we will install [`chartjs-plugin-datalabels`](https://chartjs-plugin-datalabels.netlify.app/guide/getting-started.html#installation):
|
||||
|
||||
```bash
|
||||
npm install chartjs-plugin-datalabels --save-dev
|
||||
```
|
||||
|
||||
### Step 2: Create a JavaScript file importing the plugin
|
||||
|
||||
Create a new JavaScript file where you will define your custom plugin. In this guide, we'll call it `filament-chart-js-plugins.js`. Import the plugin, and add it to the `window.filamentChartJsPlugins` array:
|
||||
|
||||
```javascript
|
||||
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
||||
|
||||
window.filamentChartJsPlugins ??= []
|
||||
window.filamentChartJsPlugins.push(ChartDataLabels)
|
||||
```
|
||||
|
||||
It's important to initialise the array if it has not been already, before pushing onto it. This ensures that mutliple JavaScript files (especially those from Filament plugins) that register Chart.js plugins do not overwrite each other, regardless of the order they are booted in.
|
||||
|
||||
You can push as many plugins to the `filamentChartJsPlugins` array as you would like to install, you do not need a separate file to import each plugin.
|
||||
|
||||
### Step 3: Compile the JavaScript file with Vite
|
||||
|
||||
Now, you need to build the JavaScript file with Vite, or your bundler of choice. Include the file in your Vite configuration (usually `vite.config.js`). For example:
|
||||
|
||||
```javascript
|
||||
import { defineConfig } from 'vite';
|
||||
import laravel from 'laravel-vite-plugin';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
laravel({
|
||||
input: [
|
||||
'resources/css/app.css',
|
||||
'resources/js/app.js',
|
||||
'resources/css/filament/admin/theme.css',
|
||||
'resources/js/filament-chart-js-plugins.js', // Include the new file in the `input` array so it is built
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
Build the file with `npm run build`.
|
||||
|
||||
### Step 4: Register the JavaScript file in Filament
|
||||
|
||||
Filament needs to know to include this JavaScript file when rendering chart widgets. You can do this in the `boot()` method of a service provider like `AppServiceProvider`:
|
||||
|
||||
```php
|
||||
use Filament\Support\Assets\Js;
|
||||
use Filament\Support\Facades\FilamentAsset;
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
|
||||
FilamentAsset::register([
|
||||
Js::make('chart-js-plugins', Vite::asset('resources/js/filament-chart-js-plugins.js'))->module(),
|
||||
]);
|
||||
```
|
||||
|
||||
You can find out more about [asset registration](../support/assets), and even [register assets for a specific panel](../panels/configuration#registering-assets-for-a-panel).
|
||||
7
vendor/filament/widgets/docs/04-tables.md
vendored
Normal file
7
vendor/filament/widgets/docs/04-tables.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Table widgets
|
||||
---
|
||||
|
||||
When using the [Panel Builder](../panels), you can use table widgets. These use the [table builder](../tables). You can find out how to create them [here](../panels/dashboard#table-widgets).
|
||||
|
||||
If you're not using the Panel Builder, there's no need to use a "widget" to render a table. You can simply [add a table to a Livewire component](../tables/adding-a-table-to-a-livewire-component), which does not provide any specific benefits over a widget.
|
||||
13
vendor/filament/widgets/docs/05-adding-a-widget-to-a-blade-view.md
vendored
Normal file
13
vendor/filament/widgets/docs/05-adding-a-widget-to-a-blade-view.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: Adding a widget to a Blade view
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Since widgets are Livewire components, you can easily render a widget in any Blade view using the `@livewire` directive:
|
||||
|
||||
```blade
|
||||
<div>
|
||||
@livewire(\App\Livewire\Dashboard\PostsChart::class)
|
||||
</div>
|
||||
```
|
||||
96
vendor/filament/widgets/resources/js/components/chart.js
vendored
Normal file
96
vendor/filament/widgets/resources/js/components/chart.js
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
import Chart from 'chart.js/auto'
|
||||
import 'chartjs-adapter-luxon'
|
||||
|
||||
export default function chart({ cachedData, options, type }) {
|
||||
return {
|
||||
init: function () {
|
||||
this.initChart()
|
||||
|
||||
this.$wire.$on('updateChartData', ({ data }) => {
|
||||
chart = this.getChart()
|
||||
chart.data = data
|
||||
chart.update('resize')
|
||||
})
|
||||
|
||||
Alpine.effect(() => {
|
||||
Alpine.store('theme')
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (!this.getChart()) {
|
||||
return
|
||||
}
|
||||
|
||||
this.getChart().destroy()
|
||||
this.initChart()
|
||||
})
|
||||
})
|
||||
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addEventListener('change', () => {
|
||||
if (Alpine.store('theme') !== 'system') {
|
||||
return
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.getChart().destroy()
|
||||
this.initChart()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
initChart: function (data = null) {
|
||||
Chart.defaults.animation.duration = 0
|
||||
|
||||
Chart.defaults.backgroundColor = getComputedStyle(
|
||||
this.$refs.backgroundColorElement,
|
||||
).color
|
||||
|
||||
const borderColor = getComputedStyle(
|
||||
this.$refs.borderColorElement,
|
||||
).color
|
||||
|
||||
Chart.defaults.borderColor = borderColor
|
||||
|
||||
Chart.defaults.color = getComputedStyle(
|
||||
this.$refs.textColorElement,
|
||||
).color
|
||||
|
||||
Chart.defaults.font.family = getComputedStyle(this.$el).fontFamily
|
||||
|
||||
Chart.defaults.plugins.legend.labels.boxWidth = 12
|
||||
Chart.defaults.plugins.legend.position = 'bottom'
|
||||
|
||||
const gridColor = getComputedStyle(
|
||||
this.$refs.gridColorElement,
|
||||
).color
|
||||
|
||||
options ??= {}
|
||||
options.borderWidth ??= 2
|
||||
options.pointBackgroundColor ??= borderColor
|
||||
options.pointHitRadius ??= 4
|
||||
options.pointRadius ??= 2
|
||||
options.scales ??= {}
|
||||
options.scales.x ??= {}
|
||||
options.scales.x.grid ??= {}
|
||||
options.scales.x.grid.color = gridColor
|
||||
options.scales.x.grid.display ??= false
|
||||
options.scales.x.grid.drawBorder ??= false
|
||||
options.scales.y ??= {}
|
||||
options.scales.y.grid ??= {}
|
||||
options.scales.y.grid.color = gridColor
|
||||
options.scales.y.grid.drawBorder ??= false
|
||||
|
||||
return new Chart(this.$refs.canvas, {
|
||||
type: type,
|
||||
data: data ?? cachedData,
|
||||
options: options,
|
||||
plugins: window.filamentChartJsPlugins ?? [],
|
||||
})
|
||||
},
|
||||
|
||||
getChart: function () {
|
||||
return Chart.getChart(this.$refs.canvas)
|
||||
},
|
||||
}
|
||||
}
|
||||
99
vendor/filament/widgets/resources/js/components/stats-overview/stat/chart.js
vendored
Normal file
99
vendor/filament/widgets/resources/js/components/stats-overview/stat/chart.js
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
import Chart from 'chart.js/auto'
|
||||
|
||||
export default function statsOverviewStatChart({
|
||||
dataChecksum,
|
||||
labels,
|
||||
values,
|
||||
}) {
|
||||
return {
|
||||
dataChecksum,
|
||||
|
||||
init: function () {
|
||||
Alpine.effect(() => {
|
||||
Alpine.store('theme')
|
||||
|
||||
const chart = this.getChart()
|
||||
|
||||
if (chart) {
|
||||
chart.destroy()
|
||||
}
|
||||
|
||||
this.initChart()
|
||||
})
|
||||
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addEventListener('change', () => {
|
||||
if (Alpine.store('theme') !== 'system') {
|
||||
return
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
const chart = this.getChart()
|
||||
|
||||
if (chart) {
|
||||
chart.destroy()
|
||||
}
|
||||
|
||||
this.initChart()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
initChart: function () {
|
||||
Chart.defaults.backgroundColor = getComputedStyle(
|
||||
this.$refs.backgroundColorElement,
|
||||
).color
|
||||
|
||||
Chart.defaults.borderColor = getComputedStyle(
|
||||
this.$refs.borderColorElement,
|
||||
).color
|
||||
|
||||
return new Chart(this.$refs.canvas, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [
|
||||
{
|
||||
data: values,
|
||||
borderWidth: 2,
|
||||
fill: 'start',
|
||||
tension: 0.5,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
animation: {
|
||||
duration: 0,
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0,
|
||||
},
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: false,
|
||||
},
|
||||
y: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
tooltips: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
getChart: function () {
|
||||
return Chart.getChart(this.$refs.canvas)
|
||||
},
|
||||
}
|
||||
}
|
||||
113
vendor/filament/widgets/resources/views/chart-widget.blade.php
vendored
Normal file
113
vendor/filament/widgets/resources/views/chart-widget.blade.php
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
@php
|
||||
use Filament\Support\Facades\FilamentView;
|
||||
|
||||
$color = $this->getColor();
|
||||
$heading = $this->getHeading();
|
||||
$description = $this->getDescription();
|
||||
$filters = $this->getFilters();
|
||||
@endphp
|
||||
|
||||
<x-filament-widgets::widget class="fi-wi-chart">
|
||||
<x-filament::section :description="$description" :heading="$heading">
|
||||
@if ($filters)
|
||||
<x-slot name="headerEnd">
|
||||
<x-filament::input.wrapper
|
||||
inline-prefix
|
||||
wire:target="filter"
|
||||
class="w-max sm:-my-2"
|
||||
>
|
||||
<x-filament::input.select
|
||||
inline-prefix
|
||||
wire:model.live="filter"
|
||||
>
|
||||
@foreach ($filters as $value => $label)
|
||||
<option value="{{ $value }}">
|
||||
{{ $label }}
|
||||
</option>
|
||||
@endforeach
|
||||
</x-filament::input.select>
|
||||
</x-filament::input.wrapper>
|
||||
</x-slot>
|
||||
@endif
|
||||
|
||||
<div
|
||||
@if ($pollingInterval = $this->getPollingInterval())
|
||||
wire:poll.{{ $pollingInterval }}="updateChartData"
|
||||
@endif
|
||||
>
|
||||
<div
|
||||
@if (FilamentView::hasSpaMode())
|
||||
ax-load="visible"
|
||||
@else
|
||||
ax-load
|
||||
@endif
|
||||
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('chart', 'filament/widgets') }}"
|
||||
wire:ignore
|
||||
x-data="chart({
|
||||
cachedData: @js($this->getCachedData()),
|
||||
options: @js($this->getOptions()),
|
||||
type: @js($this->getType()),
|
||||
})"
|
||||
x-ignore
|
||||
@class([
|
||||
match ($color) {
|
||||
'gray' => null,
|
||||
default => 'fi-color-custom',
|
||||
},
|
||||
is_string($color) ? "fi-color-{$color}" : null,
|
||||
])
|
||||
>
|
||||
<canvas
|
||||
x-ref="canvas"
|
||||
@if ($maxHeight = $this->getMaxHeight())
|
||||
style="max-height: {{ $maxHeight }}"
|
||||
@endif
|
||||
></canvas>
|
||||
|
||||
<span
|
||||
x-ref="backgroundColorElement"
|
||||
@class([
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-100 dark:text-gray-800',
|
||||
default => 'text-custom-50 dark:text-custom-400/10',
|
||||
},
|
||||
])
|
||||
@style([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [50, 400],
|
||||
alias: 'widgets::chart-widget.background',
|
||||
) => $color !== 'gray',
|
||||
])
|
||||
></span>
|
||||
|
||||
<span
|
||||
x-ref="borderColorElement"
|
||||
@class([
|
||||
match ($color) {
|
||||
'gray' => 'text-gray-400',
|
||||
default => 'text-custom-500 dark:text-custom-400',
|
||||
},
|
||||
])
|
||||
@style([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$color,
|
||||
shades: [400, 500],
|
||||
alias: 'widgets::chart-widget.border',
|
||||
) => $color !== 'gray',
|
||||
])
|
||||
></span>
|
||||
|
||||
<span
|
||||
x-ref="gridColorElement"
|
||||
class="text-gray-200 dark:text-gray-800"
|
||||
></span>
|
||||
|
||||
<span
|
||||
x-ref="textColorElement"
|
||||
class="text-gray-500 dark:text-gray-400"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
</x-filament::section>
|
||||
</x-filament-widgets::widget>
|
||||
35
vendor/filament/widgets/resources/views/components/widget.blade.php
vendored
Normal file
35
vendor/filament/widgets/resources/views/components/widget.blade.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
@php
|
||||
$columnSpan = $this->getColumnSpan();
|
||||
|
||||
if (! is_array($columnSpan)) {
|
||||
$columnSpan = [
|
||||
'default' => $columnSpan,
|
||||
];
|
||||
}
|
||||
|
||||
$columnStart = $this->getColumnStart();
|
||||
|
||||
if (! is_array($columnStart)) {
|
||||
$columnStart = [
|
||||
'default' => $columnStart,
|
||||
];
|
||||
}
|
||||
@endphp
|
||||
|
||||
<x-filament::grid.column
|
||||
:default="$columnSpan['default'] ?? 1"
|
||||
:sm="$columnSpan['sm'] ?? null"
|
||||
:md="$columnSpan['md'] ?? null"
|
||||
:lg="$columnSpan['lg'] ?? null"
|
||||
:xl="$columnSpan['xl'] ?? null"
|
||||
:twoXl="$columnSpan['2xl'] ?? null"
|
||||
:defaultStart="$columnStart['default'] ?? null"
|
||||
:smStart="$columnStart['sm'] ?? null"
|
||||
:mdStart="$columnStart['md'] ?? null"
|
||||
:lgStart="$columnStart['lg'] ?? null"
|
||||
:xlStart="$columnStart['xl'] ?? null"
|
||||
:twoXlStart="$columnStart['2xl'] ?? null"
|
||||
:attributes="\Filament\Support\prepare_inherited_attributes($attributes)->class('fi-wi-widget')"
|
||||
>
|
||||
{{ $slot }}
|
||||
</x-filament::grid.column>
|
||||
39
vendor/filament/widgets/resources/views/components/widgets.blade.php
vendored
Normal file
39
vendor/filament/widgets/resources/views/components/widgets.blade.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
@props([
|
||||
'columns' => [
|
||||
'lg' => 2,
|
||||
],
|
||||
'data' => [],
|
||||
'widgets' => [],
|
||||
])
|
||||
|
||||
<x-filament::grid
|
||||
:default="$columns['default'] ?? 1"
|
||||
:sm="$columns['sm'] ?? null"
|
||||
:md="$columns['md'] ?? null"
|
||||
:lg="$columns['lg'] ?? ($columns ? (is_array($columns) ? null : $columns) : 2)"
|
||||
:xl="$columns['xl'] ?? null"
|
||||
:two-xl="$columns['2xl'] ?? null"
|
||||
:attributes="\Filament\Support\prepare_inherited_attributes($attributes)->class('fi-wi gap-6')"
|
||||
>
|
||||
@php
|
||||
$normalizeWidgetClass = function (string | Filament\Widgets\WidgetConfiguration $widget): string {
|
||||
if ($widget instanceof \Filament\Widgets\WidgetConfiguration) {
|
||||
return $widget->widget;
|
||||
}
|
||||
|
||||
return $widget;
|
||||
};
|
||||
@endphp
|
||||
|
||||
@foreach ($widgets as $widgetKey => $widget)
|
||||
@php
|
||||
$widgetClass = $normalizeWidgetClass($widget);
|
||||
@endphp
|
||||
|
||||
@livewire(
|
||||
$widgetClass,
|
||||
[...(($widget instanceof \Filament\Widgets\WidgetConfiguration) ? [...$widget->widget::getDefaultProperties(), ...$widget->getProperties()] : $widget::getDefaultProperties()), ...$data],
|
||||
key("{$widgetClass}-{$widgetKey}"),
|
||||
)
|
||||
@endforeach
|
||||
</x-filament::grid>
|
||||
22
vendor/filament/widgets/resources/views/stats-overview-widget.blade.php
vendored
Normal file
22
vendor/filament/widgets/resources/views/stats-overview-widget.blade.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
@php
|
||||
$columns = $this->getColumns();
|
||||
@endphp
|
||||
|
||||
<x-filament-widgets::widget class="fi-wi-stats-overview">
|
||||
<div
|
||||
@if ($pollingInterval = $this->getPollingInterval())
|
||||
wire:poll.{{ $pollingInterval }}
|
||||
@endif
|
||||
@class([
|
||||
'fi-wi-stats-overview-stats-ctn grid gap-6',
|
||||
'md:grid-cols-1' => $columns === 1,
|
||||
'md:grid-cols-2' => $columns === 2,
|
||||
'md:grid-cols-3' => $columns === 3,
|
||||
'md:grid-cols-2 xl:grid-cols-4' => $columns === 4,
|
||||
])
|
||||
>
|
||||
@foreach ($this->getCachedStats() as $stat)
|
||||
{{ $stat }}
|
||||
@endforeach
|
||||
</div>
|
||||
</x-filament-widgets::widget>
|
||||
159
vendor/filament/widgets/resources/views/stats-overview-widget/stat.blade.php
vendored
Normal file
159
vendor/filament/widgets/resources/views/stats-overview-widget/stat.blade.php
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
@php
|
||||
use Filament\Support\Enums\IconPosition;
|
||||
use Filament\Support\Facades\FilamentView;
|
||||
|
||||
$chartColor = $getChartColor() ?? 'gray';
|
||||
$descriptionColor = $getDescriptionColor() ?? 'gray';
|
||||
$descriptionIcon = $getDescriptionIcon();
|
||||
$descriptionIconPosition = $getDescriptionIconPosition();
|
||||
$url = $getUrl();
|
||||
$tag = $url ? 'a' : 'div';
|
||||
$dataChecksum = $generateDataChecksum();
|
||||
|
||||
$descriptionIconClasses = \Illuminate\Support\Arr::toCssClasses([
|
||||
'fi-wi-stats-overview-stat-description-icon h-5 w-5',
|
||||
match ($descriptionColor) {
|
||||
'gray' => 'text-gray-400 dark:text-gray-500',
|
||||
default => 'text-custom-500',
|
||||
},
|
||||
]);
|
||||
|
||||
$descriptionIconStyles = \Illuminate\Support\Arr::toCssStyles([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$descriptionColor,
|
||||
shades: [500],
|
||||
alias: 'widgets::stats-overview-widget.stat.description.icon',
|
||||
) => $descriptionColor !== 'gray',
|
||||
]);
|
||||
@endphp
|
||||
|
||||
<{!! $tag !!}
|
||||
@if ($url)
|
||||
{{ \Filament\Support\generate_href_html($url, $shouldOpenUrlInNewTab()) }}
|
||||
@endif
|
||||
{{
|
||||
$getExtraAttributeBag()
|
||||
->class([
|
||||
'fi-wi-stats-overview-stat relative rounded-xl bg-white p-6 shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10',
|
||||
])
|
||||
}}
|
||||
>
|
||||
<div class="grid gap-y-2">
|
||||
<div class="flex items-center gap-x-2">
|
||||
@if ($icon = $getIcon())
|
||||
<x-filament::icon
|
||||
:icon="$icon"
|
||||
class="fi-wi-stats-overview-stat-icon h-5 w-5 text-gray-400 dark:text-gray-500"
|
||||
/>
|
||||
@endif
|
||||
|
||||
<span
|
||||
class="fi-wi-stats-overview-stat-label text-sm font-medium text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ $getLabel() }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="fi-wi-stats-overview-stat-value text-3xl font-semibold tracking-tight text-gray-950 dark:text-white"
|
||||
>
|
||||
{{ $getValue() }}
|
||||
</div>
|
||||
|
||||
@if ($description = $getDescription())
|
||||
<div class="flex items-center gap-x-1">
|
||||
@if ($descriptionIcon && in_array($descriptionIconPosition, [IconPosition::Before, 'before']))
|
||||
<x-filament::icon
|
||||
:icon="$descriptionIcon"
|
||||
:class="$descriptionIconClasses"
|
||||
:style="$descriptionIconStyles"
|
||||
/>
|
||||
@endif
|
||||
|
||||
<span
|
||||
@class([
|
||||
'fi-wi-stats-overview-stat-description text-sm',
|
||||
match ($descriptionColor) {
|
||||
'gray' => 'text-gray-500 dark:text-gray-400',
|
||||
default => 'fi-color-custom text-custom-600 dark:text-custom-400',
|
||||
},
|
||||
is_string($descriptionColor) ? "fi-color-{$descriptionColor}" : null,
|
||||
])
|
||||
@style([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$descriptionColor,
|
||||
shades: [400, 600],
|
||||
alias: 'widgets::stats-overview-widget.stat.description',
|
||||
) => $descriptionColor !== 'gray',
|
||||
])
|
||||
>
|
||||
{{ $description }}
|
||||
</span>
|
||||
|
||||
@if ($descriptionIcon && in_array($descriptionIconPosition, [IconPosition::After, 'after']))
|
||||
<x-filament::icon
|
||||
:icon="$descriptionIcon"
|
||||
:class="$descriptionIconClasses"
|
||||
:style="$descriptionIconStyles"
|
||||
/>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($chart = $getChart())
|
||||
{{-- An empty function to initialize the Alpine component with until it's loaded with `ax-load`. This removes the need for `x-ignore`, allowing the chart to be updated via Livewire polling. --}}
|
||||
<div x-data="{ statsOverviewStatChart: function () {} }">
|
||||
<div
|
||||
@if (FilamentView::hasSpaMode())
|
||||
ax-load="visible"
|
||||
@else
|
||||
ax-load
|
||||
@endif
|
||||
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('stats-overview/stat/chart', 'filament/widgets') }}"
|
||||
x-data="statsOverviewStatChart({
|
||||
dataChecksum: @js($dataChecksum),
|
||||
labels: @js(array_keys($chart)),
|
||||
values: @js(array_values($chart)),
|
||||
})"
|
||||
@class([
|
||||
'fi-wi-stats-overview-stat-chart absolute inset-x-0 bottom-0 overflow-hidden rounded-b-xl',
|
||||
match ($chartColor) {
|
||||
'gray' => null,
|
||||
default => 'fi-color-custom',
|
||||
},
|
||||
is_string($chartColor) ? "fi-color-{$chartColor}" : null,
|
||||
])
|
||||
@style([
|
||||
\Filament\Support\get_color_css_variables(
|
||||
$chartColor,
|
||||
shades: [50, 400, 500],
|
||||
alias: 'widgets::stats-overview-widget.stat.chart',
|
||||
) => $chartColor !== 'gray',
|
||||
])
|
||||
>
|
||||
<canvas x-ref="canvas" class="h-6"></canvas>
|
||||
|
||||
<span
|
||||
x-ref="backgroundColorElement"
|
||||
@class([
|
||||
match ($chartColor) {
|
||||
'gray' => 'text-gray-100 dark:text-gray-800',
|
||||
default => 'text-custom-50 dark:text-custom-400/10',
|
||||
},
|
||||
])
|
||||
></span>
|
||||
|
||||
<span
|
||||
x-ref="borderColorElement"
|
||||
@class([
|
||||
match ($chartColor) {
|
||||
'gray' => 'text-gray-400',
|
||||
default => 'text-custom-500 dark:text-custom-400',
|
||||
},
|
||||
])
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</{!! $tag !!}>
|
||||
7
vendor/filament/widgets/resources/views/table-widget.blade.php
vendored
Normal file
7
vendor/filament/widgets/resources/views/table-widget.blade.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<x-filament-widgets::widget class="fi-wi-table">
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\Widgets\View\WidgetsRenderHook::TABLE_WIDGET_START, scopes: static::class) }}
|
||||
|
||||
{{ $this->table }}
|
||||
|
||||
{{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\Widgets\View\WidgetsRenderHook::TABLE_WIDGET_END, scopes: static::class) }}
|
||||
</x-filament-widgets::widget>
|
||||
14
vendor/filament/widgets/src/BarChartWidget.php
vendored
Normal file
14
vendor/filament/widgets/src/BarChartWidget.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
/**
|
||||
* @deprecated Extend `ChartWidget` instead and define the `getType()` method.
|
||||
*/
|
||||
class BarChartWidget extends ChartWidget
|
||||
{
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'bar';
|
||||
}
|
||||
}
|
||||
14
vendor/filament/widgets/src/BubbleChartWidget.php
vendored
Normal file
14
vendor/filament/widgets/src/BubbleChartWidget.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
/**
|
||||
* @deprecated Extend `ChartWidget` instead and define the `getType()` method.
|
||||
*/
|
||||
class BubbleChartWidget extends ChartWidget
|
||||
{
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'bubble';
|
||||
}
|
||||
}
|
||||
120
vendor/filament/widgets/src/ChartWidget.php
vendored
Normal file
120
vendor/filament/widgets/src/ChartWidget.php
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
use Filament\Support\RawJs;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Livewire\Attributes\Locked;
|
||||
|
||||
abstract class ChartWidget extends Widget
|
||||
{
|
||||
use Concerns\CanPoll;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed> | null
|
||||
*/
|
||||
protected ?array $cachedData = null;
|
||||
|
||||
#[Locked]
|
||||
public ?string $dataChecksum = null;
|
||||
|
||||
public ?string $filter = null;
|
||||
|
||||
protected static string $color = 'primary';
|
||||
|
||||
protected static ?string $heading = null;
|
||||
|
||||
protected static ?string $description = null;
|
||||
|
||||
protected static ?string $maxHeight = null;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed> | null
|
||||
*/
|
||||
protected static ?array $options = null;
|
||||
|
||||
/**
|
||||
* @var view-string
|
||||
*/
|
||||
protected static string $view = 'filament-widgets::chart-widget';
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->dataChecksum = $this->generateDataChecksum();
|
||||
}
|
||||
|
||||
abstract protected function getType(): string;
|
||||
|
||||
protected function generateDataChecksum(): string
|
||||
{
|
||||
return md5(json_encode($this->getCachedData()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function getCachedData(): array
|
||||
{
|
||||
return $this->cachedData ??= $this->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function getData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<scalar, scalar> | null
|
||||
*/
|
||||
protected function getFilters(): ?array
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getHeading(): string | Htmlable | null
|
||||
{
|
||||
return static::$heading;
|
||||
}
|
||||
|
||||
public function getDescription(): string | Htmlable | null
|
||||
{
|
||||
return static::$description;
|
||||
}
|
||||
|
||||
protected function getMaxHeight(): ?string
|
||||
{
|
||||
return static::$maxHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed> | RawJs | null
|
||||
*/
|
||||
protected function getOptions(): array | RawJs | null
|
||||
{
|
||||
return static::$options;
|
||||
}
|
||||
|
||||
public function updateChartData(): void
|
||||
{
|
||||
$newDataChecksum = $this->generateDataChecksum();
|
||||
|
||||
if ($newDataChecksum !== $this->dataChecksum) {
|
||||
$this->dataChecksum = $newDataChecksum;
|
||||
|
||||
$this->dispatch('updateChartData', data: $this->getCachedData());
|
||||
}
|
||||
}
|
||||
|
||||
public function rendering(): void
|
||||
{
|
||||
$this->updateChartData();
|
||||
}
|
||||
|
||||
public function getColor(): string
|
||||
{
|
||||
return static::$color;
|
||||
}
|
||||
}
|
||||
14
vendor/filament/widgets/src/Commands/Aliases/MakeWidgetCommand.php
vendored
Normal file
14
vendor/filament/widgets/src/Commands/Aliases/MakeWidgetCommand.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets\Commands\Aliases;
|
||||
|
||||
use Filament\Widgets\Commands;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
#[AsCommand(name: 'filament:widget')]
|
||||
class MakeWidgetCommand extends Commands\MakeWidgetCommand
|
||||
{
|
||||
protected $hidden = true;
|
||||
|
||||
protected $signature = 'filament:widget {name?} {--R|resource=} {--C|chart} {--T|table} {--S|stats-overview} {--panel=} {--F|force}';
|
||||
}
|
||||
228
vendor/filament/widgets/src/Commands/MakeWidgetCommand.php
vendored
Normal file
228
vendor/filament/widgets/src/Commands/MakeWidgetCommand.php
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets\Commands;
|
||||
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Panel;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Support\Commands\Concerns\CanManipulateFiles;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
||||
use function Laravel\Prompts\select;
|
||||
use function Laravel\Prompts\text;
|
||||
|
||||
#[AsCommand(name: 'make:filament-widget')]
|
||||
class MakeWidgetCommand extends Command
|
||||
{
|
||||
use CanManipulateFiles;
|
||||
|
||||
protected $description = 'Create a new Filament widget class';
|
||||
|
||||
protected $signature = 'make:filament-widget {name?} {--R|resource=} {--C|chart} {--T|table} {--S|stats-overview} {--panel=} {--F|force}';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$widget = (string) str($this->argument('name') ?? text(
|
||||
label: 'What is the widget name?',
|
||||
placeholder: 'BlogPostsChart',
|
||||
required: true,
|
||||
))
|
||||
->trim('/')
|
||||
->trim('\\')
|
||||
->trim(' ')
|
||||
->replace('/', '\\');
|
||||
$widgetClass = (string) str($widget)->afterLast('\\');
|
||||
$widgetNamespace = str($widget)->contains('\\') ?
|
||||
(string) str($widget)->beforeLast('\\') :
|
||||
'';
|
||||
|
||||
$resource = null;
|
||||
$resourceClass = null;
|
||||
|
||||
$type = match (true) {
|
||||
$this->option('chart') => 'Chart',
|
||||
$this->option('stats-overview') => 'Stats overview',
|
||||
$this->option('table') => 'Table',
|
||||
default => select(
|
||||
label: 'What type of widget do you want to create?',
|
||||
options: ['Chart', 'Stats overview', 'Table', 'Custom'],
|
||||
),
|
||||
};
|
||||
|
||||
if (class_exists(Resource::class)) {
|
||||
$resourceInput = $this->option('resource') ?? text(
|
||||
label: 'What is the resource you would like to create this in?',
|
||||
placeholder: '[Optional] BlogPostResource',
|
||||
);
|
||||
|
||||
if (filled($resourceInput)) {
|
||||
$resource = (string) str($resourceInput)
|
||||
->studly()
|
||||
->trim('/')
|
||||
->trim('\\')
|
||||
->trim(' ')
|
||||
->replace('/', '\\');
|
||||
|
||||
if (! str($resource)->endsWith('Resource')) {
|
||||
$resource .= 'Resource';
|
||||
}
|
||||
|
||||
$resourceClass = (string) str($resource)
|
||||
->afterLast('\\');
|
||||
}
|
||||
}
|
||||
|
||||
$panel = null;
|
||||
|
||||
if (class_exists(Panel::class)) {
|
||||
$panel = $this->option('panel');
|
||||
|
||||
if ($panel) {
|
||||
$panel = Filament::getPanel($panel, isStrict: false);
|
||||
}
|
||||
|
||||
if (! $panel) {
|
||||
$panels = Filament::getPanels();
|
||||
$namespace = config('livewire.class_namespace');
|
||||
|
||||
/** @var ?Panel $panel */
|
||||
$panel = $panels[select(
|
||||
label: 'Where would you like to create this?',
|
||||
options: array_unique([
|
||||
...array_map(
|
||||
fn (Panel $panel): string => "The [{$panel->getId()}] panel",
|
||||
$panels,
|
||||
),
|
||||
$namespace => "[{$namespace}] alongside other Livewire components",
|
||||
])
|
||||
)] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
$path = null;
|
||||
$namespace = null;
|
||||
$resourcePath = null;
|
||||
$resourceNamespace = null;
|
||||
|
||||
if (! $panel) {
|
||||
$namespace = config('livewire.class_namespace');
|
||||
$path = app_path((string) str($namespace)->after('App\\')->replace('\\', '/'));
|
||||
} elseif ($resource === null) {
|
||||
$widgetDirectories = $panel->getWidgetDirectories();
|
||||
$widgetNamespaces = $panel->getWidgetNamespaces();
|
||||
|
||||
$namespace = (count($widgetNamespaces) > 1) ?
|
||||
select(
|
||||
label: 'Which namespace would you like to create this in?',
|
||||
options: $widgetNamespaces,
|
||||
) :
|
||||
(Arr::first($widgetNamespaces) ?? 'App\\Filament\\Widgets');
|
||||
$path = (count($widgetDirectories) > 1) ?
|
||||
$widgetDirectories[array_search($namespace, $widgetNamespaces)] :
|
||||
(Arr::first($widgetDirectories) ?? app_path('Filament/Widgets/'));
|
||||
} else {
|
||||
$resourceDirectories = $panel->getResourceDirectories();
|
||||
$resourceNamespaces = $panel->getResourceNamespaces();
|
||||
|
||||
$resourceNamespace = (count($resourceNamespaces) > 1) ?
|
||||
select(
|
||||
label: 'Which namespace would you like to create this in?',
|
||||
options: $resourceNamespaces,
|
||||
) :
|
||||
(Arr::first($resourceNamespaces) ?? 'App\\Filament\\Resources');
|
||||
$resourcePath = (count($resourceDirectories) > 1) ?
|
||||
$resourceDirectories[array_search($resourceNamespace, $resourceNamespaces)] :
|
||||
(Arr::first($resourceDirectories) ?? app_path('Filament/Resources/'));
|
||||
}
|
||||
|
||||
$view = str($widget)->prepend(
|
||||
(string) str($resource === null ? ($panel ? "{$namespace}\\" : 'livewire\\') : "{$resourceNamespace}\\{$resource}\\widgets\\")
|
||||
->replaceFirst('App\\', '')
|
||||
)
|
||||
->replace('\\', '/')
|
||||
->explode('/')
|
||||
->map(fn ($segment) => Str::lower(Str::kebab($segment)))
|
||||
->implode('.');
|
||||
|
||||
$path = (string) str($widget)
|
||||
->prepend('/')
|
||||
->prepend($resource === null ? $path : "{$resourcePath}\\{$resource}\\Widgets\\")
|
||||
->replace('\\', '/')
|
||||
->replace('//', '/')
|
||||
->append('.php');
|
||||
|
||||
$viewPath = resource_path(
|
||||
(string) str($view)
|
||||
->replace('.', '/')
|
||||
->prepend('views/')
|
||||
->append('.blade.php'),
|
||||
);
|
||||
|
||||
if (! $this->option('force') && $this->checkForCollision([
|
||||
$path,
|
||||
...($this->option('stats-overview') || $this->option('chart')) ? [] : [$viewPath],
|
||||
])) {
|
||||
return static::INVALID;
|
||||
}
|
||||
|
||||
if ($type === 'Chart') {
|
||||
$chartType = select(
|
||||
label: 'Which type of chart would you like to create?',
|
||||
options: [
|
||||
'Bar chart',
|
||||
'Bubble chart',
|
||||
'Doughnut chart',
|
||||
'Line chart',
|
||||
'Pie chart',
|
||||
'Polar area chart',
|
||||
'Radar chart',
|
||||
'Scatter chart',
|
||||
],
|
||||
);
|
||||
|
||||
$this->copyStubToApp('ChartWidget', $path, [
|
||||
'class' => $widgetClass,
|
||||
'namespace' => filled($resource) ? "{$resourceNamespace}\\{$resource}\\Widgets" . ($widgetNamespace !== '' ? "\\{$widgetNamespace}" : '') : $namespace . ($widgetNamespace !== '' ? "\\{$widgetNamespace}" : ''),
|
||||
'type' => match ($chartType) {
|
||||
'Bar chart' => 'bar',
|
||||
'Bubble chart' => 'bubble',
|
||||
'Doughnut chart' => 'doughnut',
|
||||
'Pie chart' => 'pie',
|
||||
'Polar area chart' => 'polarArea',
|
||||
'Radar chart' => 'radar',
|
||||
'Scatter chart' => 'scatter',
|
||||
default => 'line',
|
||||
},
|
||||
]);
|
||||
} elseif ($type === 'Stats overview') {
|
||||
$this->copyStubToApp('StatsOverviewWidget', $path, [
|
||||
'class' => $widgetClass,
|
||||
'namespace' => filled($resource) ? "{$resourceNamespace}\\{$resource}\\Widgets" . ($widgetNamespace !== '' ? "\\{$widgetNamespace}" : '') : $namespace . ($widgetNamespace !== '' ? "\\{$widgetNamespace}" : ''),
|
||||
]);
|
||||
} elseif ($type === 'Table') {
|
||||
$this->copyStubToApp('TableWidget', $path, [
|
||||
'class' => $widgetClass,
|
||||
'namespace' => filled($resource) ? "{$resourceNamespace}\\{$resource}\\Widgets" . ($widgetNamespace !== '' ? "\\{$widgetNamespace}" : '') : $namespace . ($widgetNamespace !== '' ? "\\{$widgetNamespace}" : ''),
|
||||
]);
|
||||
} else {
|
||||
$this->copyStubToApp('Widget', $path, [
|
||||
'class' => $widgetClass,
|
||||
'namespace' => filled($resource) ? "{$resourceNamespace}\\{$resource}\\Widgets" . ($widgetNamespace !== '' ? "\\{$widgetNamespace}" : '') : $namespace . ($widgetNamespace !== '' ? "\\{$widgetNamespace}" : ''),
|
||||
'view' => $view,
|
||||
]);
|
||||
|
||||
$this->copyStubToApp('WidgetView', $viewPath);
|
||||
}
|
||||
|
||||
$this->components->info("Filament widget [{$path}] created successfully.");
|
||||
|
||||
if ($resource !== null) {
|
||||
$this->components->info("Make sure to register the widget in `{$resourceClass}::getWidgets()`, and then again in `getHeaderWidgets()` or `getFooterWidgets()` of any `{$resourceClass}` page.");
|
||||
}
|
||||
|
||||
return static::SUCCESS;
|
||||
}
|
||||
}
|
||||
13
vendor/filament/widgets/src/Concerns/CanPoll.php
vendored
Normal file
13
vendor/filament/widgets/src/Concerns/CanPoll.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets\Concerns;
|
||||
|
||||
trait CanPoll
|
||||
{
|
||||
protected static ?string $pollingInterval = '5s';
|
||||
|
||||
protected function getPollingInterval(): ?string
|
||||
{
|
||||
return static::$pollingInterval;
|
||||
}
|
||||
}
|
||||
14
vendor/filament/widgets/src/DoughnutChartWidget.php
vendored
Normal file
14
vendor/filament/widgets/src/DoughnutChartWidget.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
/**
|
||||
* @deprecated Extend `ChartWidget` instead and define the `getType()` method.
|
||||
*/
|
||||
class DoughnutChartWidget extends ChartWidget
|
||||
{
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'doughnut';
|
||||
}
|
||||
}
|
||||
14
vendor/filament/widgets/src/LineChartWidget.php
vendored
Normal file
14
vendor/filament/widgets/src/LineChartWidget.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
/**
|
||||
* @deprecated Extend `ChartWidget` instead and define the `getType()` method.
|
||||
*/
|
||||
class LineChartWidget extends ChartWidget
|
||||
{
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'line';
|
||||
}
|
||||
}
|
||||
14
vendor/filament/widgets/src/PieChartWidget.php
vendored
Normal file
14
vendor/filament/widgets/src/PieChartWidget.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
/**
|
||||
* @deprecated Extend `ChartWidget` instead and define the `getType()` method.
|
||||
*/
|
||||
class PieChartWidget extends ChartWidget
|
||||
{
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'pie';
|
||||
}
|
||||
}
|
||||
14
vendor/filament/widgets/src/PolarAreaChartWidget.php
vendored
Normal file
14
vendor/filament/widgets/src/PolarAreaChartWidget.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
/**
|
||||
* @deprecated Extend `ChartWidget` instead and define the `getType()` method.
|
||||
*/
|
||||
class PolarAreaChartWidget extends ChartWidget
|
||||
{
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'polarArea';
|
||||
}
|
||||
}
|
||||
14
vendor/filament/widgets/src/RadarChartWidget.php
vendored
Normal file
14
vendor/filament/widgets/src/RadarChartWidget.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
/**
|
||||
* @deprecated Extend `ChartWidget` instead and define the `getType()` method.
|
||||
*/
|
||||
class RadarChartWidget extends ChartWidget
|
||||
{
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'radar';
|
||||
}
|
||||
}
|
||||
14
vendor/filament/widgets/src/ScatterChartWidget.php
vendored
Normal file
14
vendor/filament/widgets/src/ScatterChartWidget.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
/**
|
||||
* @deprecated Extend `ChartWidget` instead and define the `getType()` method.
|
||||
*/
|
||||
class ScatterChartWidget extends ChartWidget
|
||||
{
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'scatter';
|
||||
}
|
||||
}
|
||||
63
vendor/filament/widgets/src/StatsOverviewWidget.php
vendored
Normal file
63
vendor/filament/widgets/src/StatsOverviewWidget.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
class StatsOverviewWidget extends Widget
|
||||
{
|
||||
use Concerns\CanPoll;
|
||||
|
||||
/**
|
||||
* @var array<Stat> | null
|
||||
*/
|
||||
protected ?array $cachedStats = null;
|
||||
|
||||
protected int | string | array $columnSpan = 'full';
|
||||
|
||||
/**
|
||||
* @var view-string
|
||||
*/
|
||||
protected static string $view = 'filament-widgets::stats-overview-widget';
|
||||
|
||||
protected function getColumns(): int
|
||||
{
|
||||
$count = count($this->getCachedStats());
|
||||
|
||||
if ($count < 3) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (($count % 3) !== 1) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Stat>
|
||||
*/
|
||||
protected function getCachedStats(): array
|
||||
{
|
||||
return $this->cachedStats ??= $this->getStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `getStats()` instead.
|
||||
*
|
||||
* @return array<Stat>
|
||||
*/
|
||||
protected function getCards(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Stat>
|
||||
*/
|
||||
protected function getStats(): array
|
||||
{
|
||||
return $this->getCards();
|
||||
}
|
||||
}
|
||||
10
vendor/filament/widgets/src/StatsOverviewWidget/Card.php
vendored
Normal file
10
vendor/filament/widgets/src/StatsOverviewWidget/Card.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets\StatsOverviewWidget;
|
||||
|
||||
/**
|
||||
* @deprecated Use `Stat` instead.
|
||||
*/
|
||||
class Card extends Stat
|
||||
{
|
||||
}
|
||||
296
vendor/filament/widgets/src/StatsOverviewWidget/Stat.php
vendored
Normal file
296
vendor/filament/widgets/src/StatsOverviewWidget/Stat.php
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets\StatsOverviewWidget;
|
||||
|
||||
use Closure;
|
||||
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
|
||||
{
|
||||
/**
|
||||
* @var array<float> | null
|
||||
*/
|
||||
protected ?array $chart = 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 $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;
|
||||
|
||||
/**
|
||||
* @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 $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
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @param scalar | Htmlable | Closure $value
|
||||
*/
|
||||
final public function __construct(string | Htmlable $label, $value)
|
||||
{
|
||||
$this->label($label);
|
||||
$this->value($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scalar | Htmlable | Closure $value
|
||||
*/
|
||||
public static function make(string | Htmlable $label, $value): static
|
||||
{
|
||||
return app(static::class, ['label' => $label, 'value' => $value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 chartColor(string | array | null $color): static
|
||||
{
|
||||
$this->chartColor = $color;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
*/
|
||||
public function descriptionColor(string | array | null $color): static
|
||||
{
|
||||
$this->descriptionColor = $color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function descriptionIcon(?string $icon, IconPosition | string | null $position = null): static
|
||||
{
|
||||
$this->descriptionIcon = $icon;
|
||||
$this->descriptionIconPosition = $position;
|
||||
|
||||
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
|
||||
*/
|
||||
public function chart(?array $chart): static
|
||||
{
|
||||
$this->chart = $chart;
|
||||
|
||||
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
|
||||
*/
|
||||
public function value($value): static
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<float> | null
|
||||
*/
|
||||
public function getChart(): ?array
|
||||
{
|
||||
return $this->chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 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;
|
||||
}
|
||||
|
||||
public function getIcon(): ?string
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
public function getDescriptionIcon(): ?string
|
||||
{
|
||||
return $this->descriptionIcon;
|
||||
}
|
||||
|
||||
public function getDescriptionIconPosition(): IconPosition | string
|
||||
{
|
||||
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()
|
||||
{
|
||||
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
|
||||
{
|
||||
return md5(json_encode($this->getChart()) . now());
|
||||
}
|
||||
}
|
||||
58
vendor/filament/widgets/src/TableWidget.php
vendored
Normal file
58
vendor/filament/widgets/src/TableWidget.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
use Filament\Actions;
|
||||
use Filament\Forms;
|
||||
use Filament\Infolists;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Contracts\Pagination\CursorPaginator;
|
||||
use Illuminate\Contracts\Pagination\Paginator;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class TableWidget extends Widget implements Actions\Contracts\HasActions, Forms\Contracts\HasForms, Infolists\Contracts\HasInfolists, Tables\Contracts\HasTable
|
||||
{
|
||||
use Actions\Concerns\InteractsWithActions;
|
||||
use Forms\Concerns\InteractsWithForms;
|
||||
use Infolists\Concerns\InteractsWithInfolists;
|
||||
use Tables\Concerns\InteractsWithTable {
|
||||
makeTable as makeBaseTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var view-string
|
||||
*/
|
||||
protected static string $view = 'filament-widgets::table-widget';
|
||||
|
||||
/**
|
||||
* @deprecated Override the `table()` method to configure the table.
|
||||
*/
|
||||
protected static ?string $heading = null;
|
||||
|
||||
protected function paginateTableQuery(Builder $query): Paginator | CursorPaginator
|
||||
{
|
||||
return $query->simplePaginate(($this->getTableRecordsPerPage() === 'all') ? $query->count() : $this->getTableRecordsPerPage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Override the `table()` method to configure the table.
|
||||
*/
|
||||
protected function getTableHeading(): string | Htmlable | null
|
||||
{
|
||||
return static::$heading;
|
||||
}
|
||||
|
||||
protected function makeTable(): Table
|
||||
{
|
||||
return $this->makeBaseTable()
|
||||
->heading(
|
||||
$this->getTableHeading() ?? (string) str(class_basename(static::class))
|
||||
->beforeLast('Widget')
|
||||
->kebab()
|
||||
->replace('-', ' ')
|
||||
->title(),
|
||||
);
|
||||
}
|
||||
}
|
||||
10
vendor/filament/widgets/src/View/WidgetsRenderHook.php
vendored
Normal file
10
vendor/filament/widgets/src/View/WidgetsRenderHook.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets\View;
|
||||
|
||||
class WidgetsRenderHook
|
||||
{
|
||||
const TABLE_WIDGET_END = 'widgets::table-widget.end';
|
||||
|
||||
const TABLE_WIDGET_START = 'widgets::table-widget.start';
|
||||
}
|
||||
108
vendor/filament/widgets/src/Widget.php
vendored
Normal file
108
vendor/filament/widgets/src/Widget.php
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
use Filament\Support\Concerns\CanBeLazy;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
|
||||
abstract class Widget extends Component
|
||||
{
|
||||
use CanBeLazy;
|
||||
|
||||
protected static bool $isDiscovered = true;
|
||||
|
||||
protected static ?int $sort = null;
|
||||
|
||||
/**
|
||||
* @var view-string
|
||||
*/
|
||||
protected static string $view;
|
||||
|
||||
/**
|
||||
* @var int | string | array<string, int | null>
|
||||
*/
|
||||
protected int | string | array $columnSpan = 1;
|
||||
|
||||
/**
|
||||
* @var int | string | array<string, int | null>
|
||||
*/
|
||||
protected int | string | array $columnStart = [];
|
||||
|
||||
public static function canView(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function getSort(): int
|
||||
{
|
||||
return static::$sort ?? -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int | string | array<string, int | null>
|
||||
*/
|
||||
public function getColumnSpan(): int | string | array
|
||||
{
|
||||
return $this->columnSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int | string | array<string, int | null>
|
||||
*/
|
||||
public function getColumnStart(): int | string | array
|
||||
{
|
||||
return $this->columnStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function getViewData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function isDiscovered(): bool
|
||||
{
|
||||
return static::$isDiscovered;
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view(static::$view, $this->getViewData());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $properties
|
||||
*/
|
||||
public static function make(array $properties = []): WidgetConfiguration
|
||||
{
|
||||
return app(WidgetConfiguration::class, ['widget' => static::class, 'properties' => $properties]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getPlaceholderData(): array
|
||||
{
|
||||
return [
|
||||
'columnSpan' => $this->getColumnSpan(),
|
||||
'columnStart' => $this->getColumnStart(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function getDefaultProperties(): array
|
||||
{
|
||||
$properties = [];
|
||||
|
||||
if (static::isLazy()) {
|
||||
$properties['lazy'] = true;
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
}
|
||||
24
vendor/filament/widgets/src/WidgetConfiguration.php
vendored
Normal file
24
vendor/filament/widgets/src/WidgetConfiguration.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
class WidgetConfiguration
|
||||
{
|
||||
/**
|
||||
* @param class-string<Widget> $widget
|
||||
* @param array<string, mixed> $properties
|
||||
*/
|
||||
public function __construct(
|
||||
readonly public string $widget,
|
||||
protected array $properties = [],
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getProperties(): array
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
}
|
||||
54
vendor/filament/widgets/src/WidgetsServiceProvider.php
vendored
Normal file
54
vendor/filament/widgets/src/WidgetsServiceProvider.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Filament\Widgets;
|
||||
|
||||
use Filament\Support\Assets\AlpineComponent;
|
||||
use Filament\Support\Facades\FilamentAsset;
|
||||
use Spatie\LaravelPackageTools\Package;
|
||||
use Spatie\LaravelPackageTools\PackageServiceProvider;
|
||||
|
||||
class WidgetsServiceProvider extends PackageServiceProvider
|
||||
{
|
||||
public function configurePackage(Package $package): void
|
||||
{
|
||||
$package
|
||||
->name('filament-widgets')
|
||||
->hasCommands($this->getCommands())
|
||||
->hasViews();
|
||||
}
|
||||
|
||||
public function packageBooted(): void
|
||||
{
|
||||
FilamentAsset::register([
|
||||
AlpineComponent::make('chart', __DIR__ . '/../dist/components/chart.js'),
|
||||
AlpineComponent::make('stats-overview/stat/chart', __DIR__ . '/../dist/components/stats-overview/stat/chart.js'),
|
||||
], 'filament/widgets');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string>
|
||||
*/
|
||||
protected function getCommands(): array
|
||||
{
|
||||
$commands = [
|
||||
Commands\MakeWidgetCommand::class,
|
||||
];
|
||||
|
||||
$aliases = [];
|
||||
|
||||
foreach ($commands as $command) {
|
||||
$class = 'Filament\\Widgets\\Commands\\Aliases\\' . class_basename($command);
|
||||
|
||||
if (! class_exists($class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aliases[] = $class;
|
||||
}
|
||||
|
||||
return [
|
||||
...$commands,
|
||||
...$aliases,
|
||||
];
|
||||
}
|
||||
}
|
||||
22
vendor/filament/widgets/stubs/ChartWidget.stub
vendored
Normal file
22
vendor/filament/widgets/stubs/ChartWidget.stub
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
use Filament\Widgets\ChartWidget;
|
||||
|
||||
class {{ class }} extends ChartWidget
|
||||
{
|
||||
protected static ?string $heading = 'Chart';
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return '{{ type }}';
|
||||
}
|
||||
}
|
||||
16
vendor/filament/widgets/stubs/StatsOverviewWidget.stub
vendored
Normal file
16
vendor/filament/widgets/stubs/StatsOverviewWidget.stub
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
|
||||
class {{ class }} extends BaseWidget
|
||||
{
|
||||
protected function getStats(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
21
vendor/filament/widgets/stubs/TableWidget.stub
vendored
Normal file
21
vendor/filament/widgets/stubs/TableWidget.stub
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as BaseWidget;
|
||||
|
||||
class {{ class }} extends BaseWidget
|
||||
{
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(
|
||||
// ...
|
||||
)
|
||||
->columns([
|
||||
// ...
|
||||
]);
|
||||
}
|
||||
}
|
||||
10
vendor/filament/widgets/stubs/Widget.stub
vendored
Normal file
10
vendor/filament/widgets/stubs/Widget.stub
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace {{ namespace }};
|
||||
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class {{ class }} extends Widget
|
||||
{
|
||||
protected static string $view = '{{ view }}';
|
||||
}
|
||||
5
vendor/filament/widgets/stubs/WidgetView.stub
vendored
Normal file
5
vendor/filament/widgets/stubs/WidgetView.stub
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<x-filament-widgets::widget>
|
||||
<x-filament::section>
|
||||
{{-- Widget content --}}
|
||||
</x-filament::section>
|
||||
</x-filament-widgets::widget>
|
||||
Reference in New Issue
Block a user