├── rector.php ├── src ├── ShoutServiceProvider.php └── Components │ ├── Concerns │ ├── HasContent.php │ ├── HasType.php │ ├── HasActions.php │ └── HasIcon.php │ └── Shout.php ├── LICENSE.md ├── pint.json ├── composer.json ├── resources └── views │ └── components │ └── shout.blade.php └── README.md /rector.php: -------------------------------------------------------------------------------- 1 | withPaths([ 9 | __DIR__.'/src', 10 | ]) 11 | ->withPreparedSets( 12 | deadCode: true, 13 | codeQuality: true, 14 | typeDeclarations: true, 15 | privatization: true, 16 | earlyReturn: true, 17 | strictBooleans: true, 18 | ) 19 | ->withPhpSets(); 20 | -------------------------------------------------------------------------------- /src/ShoutServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('shout') 15 | ->hasAssets() 16 | ->hasViews(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Components/Concerns/HasContent.php: -------------------------------------------------------------------------------- 1 | content = $content; 14 | 15 | return $this; 16 | } 17 | 18 | public function getContent(): mixed 19 | { 20 | return $this->evaluate($this->content); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Components/Concerns/HasType.php: -------------------------------------------------------------------------------- 1 | evaluate($this->type) ?? 'info'; 17 | 18 | if (! in_array($type, ['info', 'success', 'warning', 'danger'])) { 19 | throw new InvalidArgumentException("Invalid Shout type [{$type}]."); 20 | } 21 | 22 | return $type; 23 | } 24 | 25 | public function type(string|Closure $type): static 26 | { 27 | $this->type = $type; 28 | 29 | return $this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Components/Concerns/HasActions.php: -------------------------------------------------------------------------------- 1 | actions = $this->evaluate($actions); 16 | $this->registerActions($this->actions); 17 | 18 | return $this; 19 | } 20 | 21 | public function inlineActions(bool|Closure $position = true): static 22 | { 23 | $this->inlineActions = $position; 24 | 25 | return $this; 26 | } 27 | 28 | public function hasInlineActions(): bool 29 | { 30 | return $this->evaluate($this->inlineActions) ?? false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Components/Concerns/HasIcon.php: -------------------------------------------------------------------------------- 1 | icon = $icon; 20 | 21 | return $this; 22 | } 23 | 24 | public function iconSize(string|Closure|IconSize $size): static 25 | { 26 | $this->iconSize = $size; 27 | 28 | return $this; 29 | } 30 | 31 | public function getIcon(): string|Heroicon|null 32 | { 33 | return $this->evaluate($this->icon); 34 | } 35 | 36 | public function getIconSize(): string|IconSize|null 37 | { 38 | return $this->evaluate($this->iconSize) ?? 'md'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) awcodes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /pint.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "laravel", 3 | "rules": { 4 | "array_push": true, 5 | "backtick_to_shell_exec": true, 6 | "date_time_immutable": true, 7 | "declare_strict_types": true, 8 | "lowercase_keywords": true, 9 | "lowercase_static_reference": true, 10 | "fully_qualified_strict_types": true, 11 | "global_namespace_import": { 12 | "import_classes": true, 13 | "import_constants": true, 14 | "import_functions": true 15 | }, 16 | "mb_str_functions": true, 17 | "modernize_types_casting": true, 18 | "new_with_parentheses": false, 19 | "no_superfluous_elseif": true, 20 | "no_useless_else": true, 21 | "no_multiple_statements_per_line": true, 22 | "ordered_class_elements": { 23 | "order": [ 24 | "use_trait", 25 | "case", 26 | "constant", 27 | "constant_public", 28 | "constant_protected", 29 | "constant_private", 30 | "property_public", 31 | "property_protected", 32 | "property_private", 33 | "construct", 34 | "destruct", 35 | "magic", 36 | "phpunit", 37 | "method_abstract", 38 | "method_public_static", 39 | "method_public", 40 | "method_protected_static", 41 | "method_protected", 42 | "method_private_static", 43 | "method_private" 44 | ], 45 | "sort_algorithm": "none" 46 | }, 47 | "ordered_interfaces": true, 48 | "ordered_traits": true, 49 | "protected_to_private": true, 50 | "self_accessor": true, 51 | "self_static_accessor": true, 52 | "strict_comparison": true, 53 | "visibility_required": true 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awcodes/shout", 3 | "description": "A simple inline contextual notice for Filament forms, basically just a fancy placeholder.", 4 | "keywords": [ 5 | "awcodes", 6 | "filament", 7 | "shout" 8 | ], 9 | "homepage": "https://github.com/awcodes/shout", 10 | "support": { 11 | "issues": "https://github.com/awcodes/shout/issues", 12 | "source": "https://github.com/awcodes/shout" 13 | }, 14 | "license": "MIT", 15 | "authors": [ 16 | { 17 | "name": "Adam Weston", 18 | "email": "awcodes1@gmail.com", 19 | "role": "Developer" 20 | } 21 | ], 22 | "require": { 23 | "php": "^8.2", 24 | "filament/filament": "^4.0", 25 | "spatie/laravel-package-tools": "^1.15" 26 | }, 27 | "require-dev": { 28 | "laravel/pint": "^1.0", 29 | "orchestra/testbench": "^9.0|^10.0", 30 | "pestphp/pest": "^3.7", 31 | "pestphp/pest-plugin-laravel": "^3.0", 32 | "pestphp/pest-plugin-livewire": "^3.0", 33 | "rector/rector": "^2.0", 34 | "spatie/laravel-ray": "^1.29" 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "Awcodes\\Shout\\": "src" 39 | } 40 | }, 41 | "autoload-dev": { 42 | "psr-4": { 43 | "Awcodes\\Shout\\Tests\\": "tests/src" 44 | } 45 | }, 46 | "scripts": { 47 | "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", 48 | "lint": "pint", 49 | "refactor": "rector", 50 | "test:lint": "pint --test", 51 | "test:refactor": "rector --dry-run", 52 | "test:unit": "pest", 53 | "test": [ 54 | "@test:refactor", 55 | "@test:lint", 56 | "@test:unit" 57 | ] 58 | }, 59 | "config": { 60 | "sort-packages": true, 61 | "allow-plugins": { 62 | "composer/package-versions-deprecated": true, 63 | "pestphp/pest-plugin": true, 64 | "phpstan/extension-installer": true 65 | } 66 | }, 67 | "extra": { 68 | "laravel": { 69 | "providers": [ 70 | "Awcodes\\Shout\\ShoutServiceProvider" 71 | ] 72 | } 73 | }, 74 | "minimum-stability": "stable", 75 | "prefer-stable": true 76 | } 77 | -------------------------------------------------------------------------------- /resources/views/components/shout.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | use Filament\Support\Enums\IconSize; 3 | use Illuminate\Support\Arr; 4 | use Illuminate\Support\HtmlString; 5 | use function Filament\Support\generate_icon_html; 6 | 7 | $iconSize = $getIconSize(); 8 | 9 | if (filled($iconSize) && (! $iconSize instanceof IconSize)) { 10 | $iconSize = IconSize::tryFrom($iconSize) ?? $iconSize; 11 | } 12 | 13 | $iconAlias = 'shout::icon.{{ $type }}'; 14 | 15 | $panelStyles = Arr::toCssStyles([ 16 | Filament\Support\get_color_css_variables($getColor(), shades: [100, 300, 600, 900]) => $getColor() !== 'gray', 17 | ]); 18 | 19 | $actions = $getActions(); 20 | $heading = $getHeading(); 21 | $hasInlineActions = $hasInlineActions(); 22 | @endphp 23 | 24 |
merge($getExtraAttributes()) 29 | ->class([ 30 | 'shout-component border rounded-lg p-4 bg-custom-100 border-custom-300 text-custom-900 dark:border-custom-300 dark:bg-custom-100 dark:text-custom-900', 31 | ]) 32 | }} 33 | style="{{ $panelStyles }}" 34 | > 35 |
36 | @if ($icon) 37 |
$heading, 41 | ]) 42 | > 43 | {{ generate_icon_html(icon: $getIcon(), alias: $iconAlias, size: $iconSize ?? IconSize::Small)}} 44 |
45 | @endif 46 | 47 |
$hasInlineActions, 51 | 'flex-col' => ! $hasInlineActions, 52 | ]) 53 | > 54 |
55 | @if ($heading instanceof HtmlString) 56 | {!! $heading !!} 57 | @else 58 |

59 | {{ $heading }} 60 |

61 | @endif 62 | 63 |
64 | {{ $getContent() }} 65 |
66 |
67 | 68 | @if($actions) 69 |
$hasInlineActions, 73 | ]) 74 | > 75 | @foreach ($actions as $action) 76 | @if ($action->isVisible()) 77 | {{ $action }} 78 | @endif 79 | @endforeach 80 |
81 | @endif 82 |
83 |
84 |
85 | -------------------------------------------------------------------------------- /src/Components/Shout.php: -------------------------------------------------------------------------------- 1 | name($name); 33 | $this->statePath($name); 34 | } 35 | 36 | protected function setUp(): void 37 | { 38 | parent::setUp(); 39 | 40 | $this->dehydrated(false); 41 | } 42 | 43 | /** @throws Exception */ 44 | public static function make(?string $name = null): static 45 | { 46 | $class = static::class; 47 | 48 | $name ??= static::getDefaultName(); 49 | 50 | if (blank($name)) { 51 | throw new Exception("Shout of class [$class] must have a unique name, passed to the [make()] method."); 52 | } 53 | 54 | $static = app(static::class, ['name' => $name]); 55 | $static->configure(); 56 | 57 | return $static; 58 | } 59 | 60 | public static function getDefaultName(): ?string 61 | { 62 | return null; 63 | } 64 | 65 | public function getColor(): string|array|null 66 | { 67 | $color = $this->evaluate($this->color); 68 | 69 | if (! $color) { 70 | return match ($this->getType()) { 71 | 'success' => 'success', 72 | 'warning' => 'warning', 73 | 'danger' => 'danger', 74 | default => 'info', 75 | }; 76 | } 77 | 78 | return $color; 79 | } 80 | 81 | public function getIcon(): string|Heroicon|null 82 | { 83 | $icon = $this->evaluate($this->icon); 84 | 85 | if ($icon === false) { 86 | return null; 87 | } 88 | 89 | if (! $icon && $icon !== '') { 90 | return match ($this->getType()) { 91 | 'success' => 'heroicon-o-check-circle', 92 | 'warning' => 'heroicon-o-exclamation-triangle', 93 | 'danger' => 'heroicon-o-x-circle', 94 | default => 'heroicon-o-information-circle', 95 | }; 96 | } 97 | 98 | return $icon; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![shout-og](https://res.cloudinary.com/aw-codes/image/upload/w_1200,f_auto,q_auto/plugins/shout/awcodes-shout.jpg) 2 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/awcodes/shout.svg?style=flat-square)](https://packagist.org/packages/awcodes/shout) 3 | [![Total Downloads](https://img.shields.io/packagist/dt/awcodes/shout.svg?style=flat-square)](https://packagist.org/packages/awcodes/shout) 4 | 5 | # Shout 6 | 7 | A simple inline contextual notice for Filament forms and infolist, basically just a fancy placeholder. 8 | 9 | ## Compatibility 10 | 11 | | Package Version | Filament Version | 12 | |-----------------|------------------| 13 | | 1.x | 2.x | 14 | | 2.x | 3.x | 15 | | 3.x | 4.x | 16 | 17 | ## Upgrading from v2 to v3 18 | 19 | If you are upgrading from version 2 to version 3, the `ShoutEntry` component is no longer needed and has been removed. You can simply use the `Shout` component directly in infolists. 20 | 21 | ## Installation 22 | 23 | You can install the package via composer: 24 | 25 | ```bash 26 | composer require awcodes/shout 27 | ``` 28 | 29 | > [!IMPORTANT] 30 | > If you have not set up a custom theme and are using Filament Panels follow the instructions in the [Filament Docs](https://filamentphp.com/docs/4.x/styling/overview#creating-a-custom-theme) first. 31 | 32 | After setting up a custom theme add the plugin's views to your theme css file or your app's css file if using the standalone packages. 33 | 34 | ```css 35 | @source '../../../../vendor/awcodes/shout/resources/**/*.blade.php'; 36 | ``` 37 | 38 | ## Usage 39 | 40 | Simply include the component in any of your form or infolists `schema()` methods. 41 | 42 | ```php 43 | use Awcodes\Shout\Components\Shout; 44 | 45 | Shout::make('so-important') 46 | ->content('This is a test') 47 | ``` 48 | 49 | ## Custom Colors 50 | 51 | You can use the `color()` method to set a custom color using Filament's Color Object. 52 | 53 | ```php 54 | use Awcodes\Shout\Components\Shout; 55 | use Filament\Support\Colors\Color; 56 | 57 | Shout::make('so-important') 58 | ->content('This is a test') 59 | ->color(Color::Lime) 60 | 61 | Shout::make('so-important') 62 | ->content('This is a test') 63 | ->color(Color::hex('#badA55')) 64 | ``` 65 | 66 | ## Icons 67 | 68 | ### Changing the icon 69 | 70 | ```php 71 | use Awcodes\Shout\Components\Shout; 72 | 73 | Shout::make('so-important') 74 | ->content('This is a test') 75 | ->icon('heroicon-s-circle-check') 76 | ``` 77 | 78 | ### Icon Size 79 | 80 | ```php 81 | use Awcodes\Shout\Components\Shout; 82 | 83 | Shout::make('so-important') 84 | ->content('This is a test') 85 | ->iconSize('sm|md|lg|xl') 86 | ``` 87 | 88 | ### Disabling the icon 89 | 90 | ```php 91 | use Awcodes\Shout\Components\Shout; 92 | 93 | Shout::make('so-important') 94 | ->content('This is a test') 95 | ->icon(false) 96 | ``` 97 | 98 | ## Headings 99 | 100 | You can add a heading to your shout using the `heading()` method. By default , the heading will be a h2 element, but you can override this by using an `HtmlString` object. 101 | 102 | ```php 103 | use Awcodes\Shout\Components\Shout; 104 | 105 | Shout::make('so-important') 106 | ->heading('Important Notice') 107 | ->content('This is a test') 108 | ``` 109 | 110 | ## Actions 111 | 112 | You can add actions to your shout using the `actions()` method. This accepts an array of Filament Action objects. 113 | 114 | ```php 115 | use Awcodes\Shout\Components\Shout; 116 | use Filament\Forms\Components\Actions\Action; 117 | 118 | Shout::make('so-important') 119 | ->content('This is a test') 120 | ->actions([ 121 | Action::make('action1') 122 | ->label('Action 1') 123 | ->url('https://example.com'), 124 | Action::make('action2') 125 | ->label('Action 2') 126 | ->url('https://example.com'), 127 | ]) 128 | ``` 129 | 130 | ## Changelog 131 | 132 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 133 | 134 | ## Contributing 135 | 136 | Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. 137 | 138 | ## Security Vulnerabilities 139 | 140 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 141 | 142 | ## Credits 143 | 144 | - [Adam Weston](https://github.com/awcodes) 145 | - [All Contributors](../../contributors) 146 | 147 | ## License 148 | 149 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 150 | --------------------------------------------------------------------------------