Merge branch '3.x' into extract-theme

This commit is contained in:
Adam Weston
2022-11-20 09:12:08 -05:00
34 changed files with 294 additions and 83 deletions
@@ -484,7 +484,7 @@ public static function getNavigationLabel(): string
### Icons
The `$navigationIcon` property supports the name of any Blade component. By default, the [Blade Heroicons](https://github.com/blade-ui-kit/blade-heroicons) package is installed, so you may use the name of any [Heroicon](https://heroicons.com) out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
The `$navigationIcon` property supports the name of any Blade component. By default, the [Blade Heroicons v1](https://github.com/blade-ui-kit/blade-heroicons/tree/1.3.1) package is installed, so you may use the name of any [Heroicons v1](https://v1.heroicons.com) out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
```php
protected static ?string $navigationIcon = 'heroicon-o-user-group';
+1 -1
View File
@@ -80,7 +80,7 @@ protected function getHeaderActions(): array
}
```
Buttons may also have an `icon()`, which is the name of any Blade component. By default, the [Blade Heroicons](https://github.com/blade-ui-kit/blade-heroicons) package is installed, so you may use the name of any [Heroicon](https://heroicons.com) out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
Buttons may also have an `icon()`, which is the name of any Blade component. By default, the [Blade Heroicons v1](https://github.com/blade-ui-kit/blade-heroicons/tree/1.3.1) package is installed, so you may use the name of any [Heroicons v1](https://v1.heroicons.com) out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
```php
use Filament\Actions\Action;
+1 -1
View File
@@ -14,7 +14,7 @@ protected static ?string $navigationLabel = 'Custom Navigation Label';
protected static ?int $navigationSort = 3;
```
The `$navigationIcon` supports the name of any Blade component, and passes a set of formatting classes to it. By default, the [Blade Heroicons](https://github.com/blade-ui-kit/blade-heroicons) package is installed, so you may use the name of any [Heroicon](https://heroicons.com) out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
The `$navigationIcon` supports the name of any Blade component, and passes a set of formatting classes to it. By default, the [Blade Heroicons v1](https://github.com/blade-ui-kit/blade-heroicons/tree/1.3.1) package is installed, so you may use the name of any [Heroicons v1](https://v1.heroicons.com) out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
## Navigation item badges
+26 -26
View File
@@ -1,35 +1,35 @@
@import '../../../forms/dist/index.css';
@import 'theme.css';
@import 'tippy.js/dist/tippy.css';
@import 'tippy.js/themes/light.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
html {
-webkit-tap-highlight-color: transparent;
@layer components {
.filament-card-layout {
@apply relative bg-no-repeat;
background-image: radial-gradient(
circle at top,
theme('colors.primary.100'),
theme('colors.white') 50%
);
}
:root {
--sidebar-width: 20rem;
--collapsed-sidebar-width: 5.4rem;
--font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
--chart-font-family: var(--font-family);
.dark .filament-card-layout {
background-image: radial-gradient(
circle at top,
theme('colors.primary.800'),
theme('colors.gray.800'),
theme('colors.gray.900') 100%
);
}
body {
font-family: var(--font-family);
.filament-card-layout .filament-card-layout-card::before {
@apply absolute inset-x-0 mx-auto h-px w-2/3 bg-gradient-to-r from-gray-200 via-primary-400 to-gray-200 dark:from-gray-700 dark:via-primary-400 dark:to-gray-700;
content: '';
z-index: 1;
top: -1px;
}
:root.dark {
color-scheme: dark;
}
[dir='rtl'] select {
background-position: left 0.5rem center !important;
padding-left: 2.5rem;
padding-right: 0.75rem;
.filament-sidebar-header::before {
@apply pointer-events-none absolute inset-x-0 mx-auto h-px w-full bg-gradient-to-r from-black/5 via-primary-400 to-gray-200 dark:from-gray-700 dark:via-primary-400 dark:to-gray-700;
content: '';
z-index: 1;
bottom: -1px;
}
}
+29
View File
@@ -0,0 +1,29 @@
@import '../../../forms/dist/index.css';
@import 'tippy.js/dist/tippy.css';
@import 'tippy.js/themes/light.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
html {
-webkit-tap-highlight-color: transparent;
}
:root {
--sidebar-width: 20rem;
--collapsed-sidebar-width: 5.4rem;
}
:root.dark {
color-scheme: dark;
}
[dir='rtl'] select {
background-position: left 0.5rem center !important;
padding-left: 2.5rem;
padding-right: 0.75rem;
}
}
@@ -42,6 +42,8 @@
<link href="{{ $font['source'] }}" rel="stylesheet" />
@endif
{{ filament()->getTheme()->getHtml() }}
@filamentStyles
<style>
@@ -3,6 +3,8 @@
])
<div {{ $attributes->class(['filament-page']) }}>
{{ filament()->renderHook('page.start') }}
<div class="space-y-6">
@if ($header = $this->getHeader())
{{ $header }}
@@ -54,4 +56,6 @@
@if (! $this instanceof \Filament\Tables\Contracts\HasTable)
<x-filament-actions::modals />
@endif
{{ filament()->renderHook('page.end') }}
</div>
+38 -12
View File
@@ -21,7 +21,9 @@ use Filament\Pages\Auth\Register;
use Filament\Pages\Page;
use Filament\Resources\RelationManagers\RelationGroup;
use Filament\Resources\Resource;
use Filament\Support\Assets\Theme;
use Filament\Support\Color;
use Filament\Support\Facades\FilamentAsset;
use Filament\Widgets\Widget;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Auth\Authenticatable;
@@ -111,7 +113,7 @@ class Context
protected ?string $tenantSlugField = null;
protected string | Htmlable | null $theme = null;
protected string | Htmlable | Theme | null $theme = null;
protected array $tenantMenuItems = [];
@@ -336,7 +338,7 @@ class Context
return $this;
}
public function theme(string | Htmlable | null $theme): static
public function theme(string | Htmlable | Theme $theme): static
{
$this->theme = $theme;
@@ -923,9 +925,28 @@ class Context
return $this->path;
}
public function getTheme(): string | Htmlable | null
public function getTheme(): Theme
{
return $this->theme;
if (blank($this->theme)) {
return $this->getDefaultTheme();
}
if ($this->theme instanceof Theme) {
return $this->theme;
}
if ($this->theme instanceof Htmlable) {
return Theme::make('app')->html($this->theme);
}
$theme = FilamentAsset::getTheme($this->theme);
return $theme ?? Theme::make('app', path: $this->theme);
}
public function getDefaultTheme(): Theme
{
return FilamentAsset::getTheme('app');
}
public function getUrl(?Model $tenant = null): ?string
@@ -1219,21 +1240,26 @@ class Context
$this->queueLivewireComponentForRegistration(GlobalSearch::class);
$this->queueLivewireComponentForRegistration(Notifications::class);
if ($this->hasEmailVerification()) {
$this->queueLivewireComponentForRegistration($this->getEmailVerificationPromptRouteAction());
if ($this->hasEmailVerification() && is_subclass_of($emailVerificationRouteAction = $this->getEmailVerificationPromptRouteAction(), Component::class)) {
$this->queueLivewireComponentForRegistration($emailVerificationRouteAction);
}
if ($this->hasLogin()) {
$this->queueLivewireComponentForRegistration($this->getLoginRouteAction());
if ($this->hasLogin() && is_subclass_of($loginRouteAction = $this->getLoginRouteAction(), Component::class)) {
$this->queueLivewireComponentForRegistration($loginRouteAction);
}
if ($this->hasPasswordReset()) {
$this->queueLivewireComponentForRegistration($this->getRequestPasswordResetRouteAction());
$this->queueLivewireComponentForRegistration($this->getResetPasswordRouteAction());
if (is_subclass_of($requestPasswordResetRouteAction = $this->getRequestPasswordResetRouteAction(), Component::class)) {
$this->queueLivewireComponentForRegistration($requestPasswordResetRouteAction);
}
if (is_subclass_of($resetPasswordRouteAction = $this->getResetPasswordRouteAction(), Component::class)) {
$this->queueLivewireComponentForRegistration($resetPasswordRouteAction);
}
}
if ($this->hasRegistration()) {
$this->queueLivewireComponentForRegistration($this->getRegistrationRouteAction());
if ($this->hasRegistration() && is_subclass_of($registrationRouteAction = $this->getRegistrationRouteAction(), Component::class)) {
$this->queueLivewireComponentForRegistration($registrationRouteAction);
}
foreach ($this->getResources() as $resource) {
+2 -1
View File
@@ -9,6 +9,7 @@ use Filament\Contracts\Plugin;
use Filament\FilamentManager;
use Filament\GlobalSearch\Contracts\GlobalSearchProvider;
use Filament\Models\Contracts\HasTenants;
use Filament\Support\Assets\Theme;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\CanResetPassword;
use Illuminate\Contracts\Auth\MustVerifyEmail;
@@ -61,7 +62,7 @@ use Illuminate\Support\Facades\Facade;
* @method static string getTenantName(Model $tenant)
* @method static string | null getTenantRegistrationPage()
* @method static string | null getTenantRegistrationUrl()
* @method static string | Htmlable | null getTheme()
* @method static Theme getTheme()
* @method static string | null getUrl(Model | null $tenant = null)
* @method static string | null getUserAvatarUrl(Model | Authenticatable $user)
* @method static Model | null getUserDefaultTenant(HasTenants | Model | Authenticatable $user)
+2 -1
View File
@@ -12,6 +12,7 @@ use Filament\Models\Contracts\HasAvatar;
use Filament\Models\Contracts\HasDefaultTenant;
use Filament\Models\Contracts\HasName;
use Filament\Models\Contracts\HasTenants;
use Filament\Support\Assets\Theme;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\CanResetPassword;
use Illuminate\Contracts\Auth\Guard;
@@ -324,7 +325,7 @@ class FilamentManager
return $this->getCurrentContext()->getModelResource($model);
}
public function getTheme(): string | Htmlable | null
public function getTheme(): Theme
{
return $this->getCurrentContext()->getTheme();
}
+2 -4
View File
@@ -2,7 +2,6 @@
namespace Filament;
use Filament\Facades\Filament;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Http\Middleware\MirrorConfigToSubpackages;
@@ -17,10 +16,9 @@ use Filament\Http\Responses\Auth\LoginResponse;
use Filament\Http\Responses\Auth\LogoutResponse;
use Filament\Http\Responses\Auth\PasswordResetResponse;
use Filament\Http\Responses\Auth\RegistrationResponse;
use Filament\Support\Assets\Css;
use Filament\Support\Assets\Js;
use Filament\Support\Assets\Theme;
use Filament\Support\PluginServiceProvider;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Routing\Router;
use Illuminate\Support\Arr;
@@ -77,9 +75,9 @@ class FilamentServiceProvider extends PluginServiceProvider
protected function getAssets(): array
{
return [
Css::make('app', __DIR__ . '/../dist/index.css')->html(fn (): string | Htmlable | null => Filament::getTheme()),
Js::make('app', __DIR__ . '/../dist/index.js')->core(),
Js::make('echo', __DIR__ . '/../dist/echo.js')->core(),
Theme::make('app', __DIR__ . '/../dist/index.css'),
];
}
@@ -62,6 +62,7 @@
'min' => (! $isConcealed) ? $getMinValue() : null,
'minlength' => (! $isConcealed) ? $getMinLength() : null,
'placeholder' => $getPlaceholder(),
'readonly' => $isReadOnly(),
'required' => $isRequired() && (! $isConcealed),
'step' => $getStep(),
'type' => $hasMask ? 'text' : $getType(),
@@ -30,6 +30,7 @@
'maxlength' => (! $isConcealed) ? $getMaxLength() : null,
'minlength' => (! $isConcealed) ? $getMinLength() : null,
'placeholder' => $getPlaceholder(),
'readonly' => $isReadOnly(),
'required' => $isRequired() && (! $isConcealed),
'rows' => $getRows(),
$applyStateBindingModifiers('wire:model') => $statePath,
@@ -0,0 +1,39 @@
<?php
namespace Filament\Forms\Components\Concerns;
use Closure;
use Filament\Forms\Contracts\HasForms;
use Illuminate\Support\Arr;
trait CanBeReadOnly
{
protected bool | Closure $isReadOnly = false;
public function readOnly(bool | Closure $condition = true): static
{
$this->isReadOnly = $condition;
return $this;
}
public function readOnlyOn(string | array $contexts): static
{
$this->readOnly(static function (string $context, HasForms $livewire) use ($contexts): bool {
foreach (Arr::wrap($contexts) as $readOnlyContext) {
if ($readOnlyContext === $context || $livewire instanceof $readOnlyContext) {
return true;
}
}
return false;
});
return $this;
}
public function isReadOnly(): bool
{
return $this->evaluate($this->isReadOnly);
}
}
@@ -13,6 +13,7 @@ class TextInput extends Field implements Contracts\CanBeLengthConstrained, CanHa
use Concerns\CanBeAutocapitalized;
use Concerns\CanBeAutocompleted;
use Concerns\CanBeLengthConstrained;
use Concerns\CanBeReadOnly;
use Concerns\HasAffixes;
use Concerns\HasExtraInputAttributes;
use Concerns\HasInputMode;
@@ -10,6 +10,7 @@ class Textarea extends Field implements Contracts\CanBeLengthConstrained
use Concerns\CanBeAutocapitalized;
use Concerns\CanBeAutocompleted;
use Concerns\CanBeLengthConstrained;
use Concerns\CanBeReadOnly;
use Concerns\HasExtraInputAttributes;
use Concerns\HasPlaceholder;
use HasExtraAlpineAttributes;
+23
View File
@@ -204,6 +204,29 @@ class TestsForms
};
}
public function assertFormFieldIsReadOnly(): Closure
{
return function (string $fieldName, string $formName = 'form'): static {
/** @phpstan-ignore-next-line */
$this->assertFormFieldExists($fieldName, $formName);
/** @var ComponentContainer $form */
$form = $this->instance()->{$formName};
/** @var Field $field */
$field = $form->getFlatFields(withHidden: true)[$fieldName];
$livewireClass = $this->instance()::class;
Assert::assertTrue(
$field->isReadOnly(),
"Failed asserting that a field with the name [{$fieldName}] is read-only on the form named [{$formName}] on the [{$livewireClass}] component."
);
return $this;
};
}
public function assertFormFieldIsHidden(): Closure
{
return function (string $fieldName, string $formName = 'form'): static {
+4 -1
View File
@@ -22,7 +22,10 @@
"autoload": {
"psr-4": {
"Filament\\Notifications\\": "src"
}
},
"files": [
"src/Testing/Autoload.php"
]
},
"extra": {
"laravel": {
@@ -19,7 +19,7 @@ class EditPost extends Component
public function save(): void
{
// ...
Notification::make() // [tl! focus:start]
->title('Saved successfully')
->success()
@@ -54,7 +54,7 @@ Markdown text will automatically be rendered if passed to the title.
## Icon
Optionally, a notification can have an icon that's displayed in front of its content. You may also set a color for the icon, which defaults to the `secondary` color specified in your `tailwind.config.js` file. The icon can be the name of any Blade component. By default, the [Blade Heroicons](https://github.com/blade-ui-kit/blade-heroicons) package is installed, so you may use the name of any [Heroicon](https://heroicons.com) out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
Optionally, a notification can have an icon that's displayed in front of its content. You may also set a color for the icon, which defaults to the `secondary` color specified in your `tailwind.config.js` file. The icon can be the name of any Blade component. By default, the [Blade Heroicons v1](https://github.com/blade-ui-kit/blade-heroicons/tree/1.3.1) package is installed, so you may use the name of any [Heroicons v1](https://v1.heroicons.com) out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
```php
use Filament\Notifications\Notification;
+16
View File
@@ -17,6 +17,22 @@ it('sends a notification', function () {
});
```
```php
use Filament\Notifications\Notification
it('sends a notification', function () {
Notification::assertNotified();
});
```
```php
use function Filament\Notifications\Testing\assetNotified;
it('sends a notification', function () {
assertNotified();
});
```
You may optionally pass a notification title to test for:
```php
@@ -18,8 +18,10 @@ use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\DatabaseNotification as DatabaseNotificationModel;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use PHPUnit\Framework\Assert;
class Notification extends ViewComponent implements Arrayable
{
@@ -192,4 +194,27 @@ class Notification extends ViewComponent implements Arrayable
return $static;
}
public static function assertNotified(Notification | string $notification = null): void
{
$notifications = session()->get('filament.notifications');
Assert::assertIsArray($notifications);
$expectedNotification = Arr::last($notifications);
Assert::assertIsArray($expectedNotification);
if (blank($notification)) {
return;
}
if ($notification instanceof Notification) {
Assert::assertSame($expectedNotification, $notification->toArray());
return;
}
Assert::assertSame($expectedNotification['title'], $notification);
}
}
@@ -0,0 +1,15 @@
<?php
namespace Filament\Notifications\Testing;
use Filament\Notifications\Notification;
/**
* @return TestCall | TestCase | mixed
*/
function assertNotified(Notification | string $notification = null)
{
Notification::assertNotified($notification);
return test();
}
@@ -4,10 +4,8 @@ namespace Filament\Notifications\Testing;
use Closure;
use Filament\Notifications\Notification;
use Illuminate\Support\Arr;
use Livewire\Component;
use Livewire\Testing\TestableLivewire;
use PHPUnit\Framework\Assert;
/**
* @method Component instance()
@@ -19,19 +17,7 @@ class TestsNotifications
public function assertNotified(): Closure
{
return function (Notification | string $notification = null): static {
$notifications = session()->get('filament.notifications');
Assert::assertIsArray($notifications);
$expectedNotification = Arr::last($notifications);
Assert::assertIsArray($expectedNotification);
if ($notification instanceof Notification) {
Assert::assertSame($expectedNotification, $notification->toArray());
} elseif (filled($notification)) {
Assert::assertSame($expectedNotification['title'], $notification);
}
Notification::assertNotified($notification);
return $this;
};
@@ -17,7 +17,7 @@ class AlpineComponent extends Asset
$path .= 'components';
$path .= DIRECTORY_SEPARATOR;
$path .= "{$this->getName()}.js";
$path .= "{$this->getId()}.js";
return public_path("js/filament/{$path}");
}
+8 -8
View File
@@ -4,26 +4,26 @@ namespace Filament\Support\Assets;
abstract class Asset
{
protected string $name;
protected string $id;
protected string $path;
protected ?string $path = null;
protected ?string $package = null;
final public function __construct(string $name, string $path)
final public function __construct(string $id, ?string $path = null)
{
$this->name = $name;
$this->id = $id;
$this->path = $path;
}
public static function make(string $name, string $path): static
public static function make(string $id, ?string $path = null): static
{
return new static($name, $path);
return new static($id, $path);
}
public function getName(): string
public function getId(): string
{
return $this->name;
return $this->id;
}
public function package(?string $package): static
@@ -14,6 +14,8 @@ class AssetManager
protected array $styles = [];
protected array $themes = [];
public function register(array $assets, ?string $package = null): void
{
foreach ($assets as $asset) {
@@ -23,6 +25,7 @@ class AssetManager
AlpineComponent::class => $this->alpineComponents[$package][] = $asset,
Css::class => $this->styles[$package][] = $asset,
Js::class => $this->scripts[$package][] = $asset,
Theme::class => $this->themes[$asset->getId()] = $asset,
default => null,
};
}
@@ -100,6 +103,16 @@ class AssetManager
])->render();
}
public function getTheme(?string $id): ?Theme
{
return $this->themes[$id] ?? null;
}
public function getThemes(): array
{
return $this->themes;
}
protected function getAssets(array $assets, ?array $packages = null): array
{
if ($packages === null) {
+2 -2
View File
@@ -32,7 +32,7 @@ class Css extends Asset
$href .= "{$package}/";
}
$href .= "{$this->getName()}.css";
$href .= "{$this->getId()}.css";
return asset($href) . '?v=' . InstalledVersions::getVersion('filament/support');
}
@@ -61,7 +61,7 @@ class Css extends Asset
$path .= DIRECTORY_SEPARATOR;
}
$path .= "{$this->getName()}.css";
$path .= "{$this->getId()}.css";
return public_path("css/filament/{$path}");
}
+2 -2
View File
@@ -95,7 +95,7 @@ class Js extends Asset
$src .= "{$package}/";
}
$src .= "{$this->getName()}.js";
$src .= "{$this->getId()}.js";
return asset($src) . '?v=' . InstalledVersions::getVersion('filament/support');
}
@@ -111,7 +111,7 @@ class Js extends Asset
$path .= DIRECTORY_SEPARATOR;
}
$path .= "{$this->getName()}.js";
$path .= "{$this->getId()}.js";
return public_path("js/filament/{$path}");
}
+7
View File
@@ -0,0 +1,7 @@
<?php
namespace Filament\Support\Assets;
class Theme extends Css
{
}
@@ -41,6 +41,14 @@ class AssetsCommand extends Command
$this->copyAsset($asset->getPath(), $asset->getPublicPath());
}
foreach (FilamentAsset::getThemes() as $asset) {
if ($asset->isRemote()) {
continue;
}
$this->copyAsset($asset->getPath(), $asset->getPublicPath());
}
$this->components->info('Successfully published assets!');
return static::SUCCESS;
@@ -3,6 +3,7 @@
namespace Filament\Support\Facades;
use Filament\Support\Assets\AssetManager;
use Filament\Support\Assets\Theme;
use Illuminate\Support\Facades\Facade;
/**
@@ -10,6 +11,8 @@ use Illuminate\Support\Facades\Facade;
* @method static array getScriptData(array | null $packages = null)
* @method static array getScripts(array | null $packages = null, bool $withCore = true)
* @method static array getStyles(array | null $packages = null)
* @method static Theme | null getTheme(string $id)
* @method static array getThemes()
* @method static void register(array $assets, string | null $package = null)
* @method static void registerScriptData(array $data, string | null $package = null)
* @method static string renderScripts(array | null $packages = null, bool $withCore = true)
+1 -1
View File
@@ -82,7 +82,7 @@ IconColumn::make('is_featured')
### Customizing the boolean icons
You may customize the icon representing each state. Icons are the name of a Blade component present. By default, [Heroicons](https://heroicons.com) are installed:
You may customize the icon representing each state. Icons are the name of a Blade component present. By default, [Heroicons v1](https://v1.heroicons.com) are installed:
```php
use Filament\Tables\Columns\IconColumn;
@@ -13,13 +13,13 @@
x-tooltip="error"
x-bind:class="{
'border-gray-300 dark:border-gray-600': ! error,
'border-danger-600 ring-1 ring-inset ring-danger-600': error,
'border-danger-600 ring-1 ring-inset ring-danger-600 dark:border-danger-400 dark:ring-danger-400': error,
}"
{{
$attributes
->merge($getExtraAttributes(), escape: false)
->merge($getExtraInputAttributes(), escape: false)
->class(['ml-0.5 text-gray-900 block transition duration-75 rounded-lg shadow-sm focus:ring-primary-500 focus:ring-1 focus:ring-inset focus:border-primary-500 disabled:opacity-70 dark:bg-gray-700 dark:text-white dark:focus:border-primary-500'])
->class(['ml-0.5 text-gray-900 inline-block transition duration-75 rounded-lg shadow-sm focus:ring-primary-500 focus:ring-1 focus:ring-inset focus:border-primary-500 disabled:opacity-70 dark:bg-gray-700 dark:text-white dark:focus:border-primary-500'])
}}
>
@php
@@ -20,13 +20,21 @@
x-tooltip="error"
x-bind:class="{
'border-gray-300 dark:border-gray-600': ! error,
'border-danger-600 ring-1 ring-inset ring-danger-600': error,
'border-danger-600 ring-1 ring-inset ring-danger-600 dark:border-danger-400 dark:ring-danger-400': error,
}"
{{
$attributes
->merge($getExtraAttributes(), escape: false)
->merge($getExtraInputAttributes(), escape: false)
->class(['ml-0.5 text-gray-900 block transition duration-75 rounded-lg shadow-sm focus:ring-primary-500 focus:ring-1 focus:ring-inset focus:border-primary-500 disabled:opacity-70 dark:bg-gray-700 dark:text-white dark:focus:border-primary-500'])
->class([
'ml-0.5 text-gray-900 inline-block transition duration-75 rounded-lg shadow-sm focus:ring-primary-500 focus:ring-1 focus:ring-inset focus:border-primary-500 disabled:opacity-70 dark:bg-gray-700 dark:text-white dark:focus:border-primary-500',
match ($getAlignment()) {
'center' => 'text-center',
'right' => 'text-right',
default => 'text-left',
},
])
}}
wire:loading.attr="readonly"
/>
</div>