Merge branch '3.x' into fix-make-theme-command-to-work-with-tailwind-css-alias

This commit is contained in:
Zep Fietje
2025-04-30 10:16:49 +02:00
44 changed files with 274 additions and 105 deletions
+4 -3
View File
@@ -6455,10 +6455,11 @@
}
},
"node_modules/http-proxy-middleware": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz",
"integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==",
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
"integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/http-proxy": "^1.17.8",
"http-proxy": "^1.18.1",
@@ -4,7 +4,7 @@ return [
'single' => [
'label' => 'Buat',
'label' => 'Buat :label',
'modal' => [
@@ -4,16 +4,16 @@ return [
'single' => [
'label' => 'Kembalikan data',
'label' => 'Pulihkan data',
'modal' => [
'heading' => 'Kembalikan :label',
'heading' => 'Pulihkan :label',
'actions' => [
'restore' => [
'label' => 'Kembalikan',
'label' => 'Pulihkan',
],
],
@@ -23,7 +23,7 @@ return [
'notifications' => [
'restored' => [
'title' => 'Data berhasil dikembalikan',
'title' => 'Data berhasil dipulihkan',
],
],
@@ -32,16 +32,16 @@ return [
'multiple' => [
'label' => 'Kembalikan data yang dipilih',
'label' => 'Pulihkan data yang dipilih',
'modal' => [
'heading' => 'Kembalikan :label yang dipilih',
'heading' => 'Pulihkan :label yang dipilih',
'actions' => [
'restore' => [
'label' => 'Kembalikan',
'label' => 'Pulihkan',
],
],
@@ -51,7 +51,7 @@ return [
'notifications' => [
'restored' => [
'title' => 'Data berhasil dikembalikan',
'title' => 'Data berhasil dipulihkan',
],
],
@@ -255,7 +255,7 @@ trait CanExportRecords
}
});
$this->color('gray');
$this->defaultColor('gray');
$this->modalWidth('xl');
@@ -383,7 +383,7 @@ trait CanImportRecords
}),
]);
$this->color('gray');
$this->defaultColor('gray');
$this->modalWidth('xl');
+1 -1
View File
@@ -27,7 +27,7 @@ class DeleteAction extends Action
$this->successNotificationTitle(__('filament-actions::delete.single.notifications.deleted.title'));
$this->color('danger');
$this->defaultColor('danger');
$this->groupedIcon(FilamentIcon::resolve('actions::delete-action.grouped') ?? 'heroicon-m-trash');
+1 -1
View File
@@ -25,7 +25,7 @@ class ForceDeleteAction extends Action
$this->modalSubmitActionLabel(__('filament-actions::force-delete.single.modal.actions.delete.label'));
$this->color('danger');
$this->defaultColor('danger');
$this->groupedIcon(FilamentIcon::resolve('actions::force-delete-action.grouped') ?? 'heroicon-m-trash');
+1 -1
View File
@@ -27,7 +27,7 @@ class RestoreAction extends Action
$this->successNotificationTitle(__('filament-actions::restore.single.notifications.restored.title'));
$this->color('gray');
$this->defaultColor('gray');
$this->groupedIcon(FilamentIcon::resolve('actions::restore-action.grouped') ?? 'heroicon-m-arrow-uturn-left');
+1 -1
View File
@@ -27,7 +27,7 @@ class ViewAction extends Action
$this->modalSubmitAction(false);
$this->modalCancelAction(fn (StaticAction $action) => $action->label(__('filament-actions::view.single.modal.actions.close.label')));
$this->color('gray');
$this->defaultColor('gray');
$this->groupedIcon(FilamentIcon::resolve('actions::view-action.grouped') ?? 'heroicon-m-eye');
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -185,7 +185,7 @@ By default, the field name will be used as the column to search. You may specify
Field::make('invitation')->exists(column: 'id')
```
You can further customize the rule by passing a [closure](advanced#closure-customization) to the `callback` parameter:
You can further customize the rule by passing a [closure](advanced#closure-customization) to the `modifyRuleUsing` parameter:
```php
use Illuminate\Validation\Rules\Exists;
@@ -2,6 +2,7 @@
* Source: https://github.com/Ionaru/easy-markdown-editor/blob/master/src/js/easymde.js
* Changes:
* - Removal of line 1 to 15, awaiting https://github.com/Ionaru/easy-markdown-editor/pull/263
* - Added `moveToNextField()` and `moveToPreviousField()` functions, and changed `Tab` and `Shift-Tab` key bindings to only indent the content when there is a selection in the editor. See https://github.com/filamentphp/filament/pull/16144.
*/
// Some variables
@@ -2339,6 +2340,42 @@ EasyMDE.prototype.render = function (el) {
var self = this
var keyMaps = {}
function moveToNextField(cm) {
const form = cm.getInputField().form
if (form) {
const elements = Array.from(form.elements)
const index = elements.indexOf(cm.getInputField())
if (
index !== -1 &&
index + 1 < elements.length &&
elements[index + 1]
) {
elements[index + 1].focus()
}
}
}
function moveToPreviousField(cm) {
const form = cm.getInputField().form
if (form) {
const elements = Array.from(form.elements)
const index = elements.indexOf(cm.getInputField())
if (index !== -1) {
for (let i = index - 1; i >= 0; i--) {
const element = elements[i]
if (
element &&
element.tagName === 'INPUT' &&
!element.closest('.editor-toolbar')
) {
element.focus()
break
}
}
}
}
}
for (var key in options.shortcuts) {
// null stands for "do not bind this command"
if (options.shortcuts[key] !== null && bindings[key] !== null) {
@@ -2356,8 +2393,22 @@ EasyMDE.prototype.render = function (el) {
}
keyMaps['Enter'] = 'newlineAndIndentContinueMarkdownList'
keyMaps['Tab'] = 'tabAndIndentMarkdownList'
keyMaps['Shift-Tab'] = 'shiftTabAndUnindentMarkdownList'
keyMaps['Tab'] = (cm) => {
const selection = cm.getSelection()
if (selection && selection.length > 0) {
cm.execCommand('indentMore')
} else {
moveToNextField(cm)
}
}
keyMaps['Shift-Tab'] = (cm) => {
const selection = cm.getSelection()
if (selection && selection.length > 0) {
cm.execCommand('indentLess')
} else {
moveToPreviousField(cm)
}
}
keyMaps['Esc'] = function (cm) {
if (cm.getOption('fullScreen')) toggleFullScreen(self)
}
@@ -12,11 +12,11 @@ return [
'add' => [
'label' => 'Tambahkan :label',
'label' => 'Tambahkan ke :label',
'modal' => [
'heading' => 'Tambah ke :label',
'heading' => 'Tambahkan ke :label',
'actions' => [
@@ -32,11 +32,11 @@ return [
'add_between' => [
'label' => 'Sisipkan',
'label' => 'Sisipkan di antara blok',
'modal' => [
'heading' => 'Tambah ke :label',
'heading' => 'Tambahkan ke :label',
'actions' => [
@@ -296,7 +296,7 @@ return [
'ordered_list' => 'Daftar berurut',
'redo' => 'Kembalikan',
'strike' => 'Coret',
'table' => 'Table',
'table' => 'Tabel',
'undo' => 'Batalkan',
],
@@ -316,11 +316,11 @@ return [
'actions' => [
'add' => [
'label' => 'Tambahkan :label',
'label' => 'Tambahkan ke :label',
],
'add_between' => [
'label' => 'Sisipkan',
'label' => 'Sisipkan di antara',
],
'delete' => [
@@ -408,6 +408,8 @@ return [
'create_option' => [
'label' => 'Buat',
'modal' => [
'heading' => 'Buat',
@@ -430,6 +432,8 @@ return [
'edit_option' => [
'label' => 'Ubah',
'modal' => [
'heading' => 'Ubah',
@@ -110,7 +110,7 @@
x-on:expand="isCollapsed = false"
x-sortable-item="{{ $uuid }}"
class="fi-fo-builder-item rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-white/5 dark:ring-white/10"
x-bind:class="{ 'fi-collapsed overflow-hidden': isCollapsed }"
x-bind:class="{ 'fi-collapsed': isCollapsed }"
>
@if ($reorderActionIsVisible || $moveUpActionIsVisible || $moveDownActionIsVisible || $hasBlockIcons || $hasBlockLabels || $editActionIsVisible || $cloneActionIsVisible || $deleteActionIsVisible || $isCollapsible || $visibleExtraItemActions)
<div
@@ -104,7 +104,7 @@
x-on:repeater-collapse.window="$event.detail === '{{ $statePath }}' && (isCollapsed = true)"
x-sortable-item="{{ $uuid }}"
class="fi-fo-repeater-item divide-y divide-gray-100 rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:divide-white/10 dark:bg-white/5 dark:ring-white/10"
x-bind:class="{ 'fi-collapsed overflow-hidden': isCollapsed }"
x-bind:class="{ 'fi-collapsed': isCollapsed }"
>
@if ($reorderActionIsVisible || $moveUpActionIsVisible || $moveDownActionIsVisible || filled($itemLabel) || $cloneActionIsVisible || $deleteActionIsVisible || $isCollapsible || $visibleExtraItemActions)
<div
@@ -20,7 +20,7 @@ class HidePasswordAction extends Action
$this->icon(FilamentIcon::resolve('forms::components.text-input.actions.hide-password') ?? 'heroicon-m-eye-slash');
$this->color('gray');
$this->defaultColor('gray');
$this->extraAttributes([
'x-cloak' => true,
@@ -20,7 +20,7 @@ class ShowPasswordAction extends Action
$this->icon(FilamentIcon::resolve('forms::components.text-input.actions.show-password') ?? 'heroicon-m-eye');
$this->color('gray');
$this->defaultColor('gray');
$this->extraAttributes([
'x-show' => '! isPasswordRevealed',
+13 -1
View File
@@ -118,6 +118,18 @@ trait CanBeValidated
return [];
}
return $this->getLivewire()->validate($rules, $this->getValidationMessages(), $this->getValidationAttributes());
$livewire = $this->getLivewire();
// By storing the currently validating form in the Livewire component, we can optimize the validation process
// so that the `prepareForValidation()` method is only called for the current form instead of all forms.
// This can also prevent infinite loops involving schemas that self-validate, such as the table query
// builder which crashes when it is being used while an action is submitted.
$livewire->currentlyValidatingForm($this);
try {
return $livewire->validate($rules, $this->getValidationMessages(), $this->getValidationAttributes());
} finally {
$livewire->currentlyValidatingForm(null);
}
}
}
@@ -4,6 +4,7 @@ namespace Filament\Forms\Concerns;
use Closure;
use Exception;
use Filament\Forms\ComponentContainer;
use Filament\Forms\Components\Component;
use Filament\Forms\Form;
use Filament\Infolists\Infolist;
@@ -36,6 +37,8 @@ trait InteractsWithForms
protected bool $isCachingForms = false;
protected ?ComponentContainer $currentlyValidatingForm = null;
protected bool $hasFormsModalRendered = false;
/**
@@ -253,8 +256,12 @@ trait InteractsWithForms
*/
protected function prepareForValidation($attributes): array
{
foreach ($this->getCachedForms() as $form) {
$attributes = $form->mutateStateForValidation($attributes);
if ($this->currentlyValidatingForm) {
$attributes = $this->currentlyValidatingForm->mutateStateForValidation($attributes);
} else {
foreach ($this->getCachedForms() as $form) {
$attributes = $form->mutateStateForValidation($attributes);
}
}
return $attributes;
@@ -506,4 +513,9 @@ trait InteractsWithForms
{
return $this->getMountedFormComponentAction()->getInfolist();
}
public function currentlyValidatingForm(?ComponentContainer $form): void
{
$this->currentlyValidatingForm = $form;
}
}
@@ -2,6 +2,7 @@
namespace Filament\Forms\Contracts;
use Filament\Forms\ComponentContainer;
use Filament\Forms\Form;
use Filament\Support\Contracts\TranslatableContentDriver;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
@@ -60,4 +61,6 @@ interface HasForms
* @return array<string, mixed>
*/
public function validate($rules = null, $messages = [], $attributes = []);
public function currentlyValidatingForm(?ComponentContainer $form): void;
}
@@ -0,0 +1,38 @@
<?php
return [
'entries' => [
'text' => [
'actions' => [
'collapse_list' => 'Sembunyikan :count lainnya',
'expand_list' => 'Tampilkan :count lainnya',
],
'more_list_items' => 'dan :count lainnya',
],
'key_value' => [
'columns' => [
'key' => [
'label' => 'Kunci',
],
'value' => [
'label' => 'Nilai',
],
],
'placeholder' => 'Tidak ada entri',
],
],
];
@@ -6,23 +6,15 @@
@if (FilamentView::hasSpaMode())
@script
<script>
let formSubmitted = false
document.addEventListener(
'submit',
() => (formSubmitted = true),
)
shouldPreventNavigation = () => {
if (formSubmitted) {
if ($wire?.__instance?.effects?.redirect) {
return
}
return (
window.jsMd5(
JSON.stringify($wire.data).replace(/\\/g, ''),
) !== $wire.savedDataHash ||
$wire?.__instance?.effects?.redirect
) !== $wire.savedDataHash
)
}
@@ -4,7 +4,7 @@ return [
'label' => 'Navigasi halaman',
'overview' => 'Menampilkan :first sampai :last dari :total hasil',
'overview' => '{1} Menampilkan 1 hasil|[2,*] Menampilkan :first sampai :last dari :total hasil',
'fields' => [
@@ -301,4 +301,53 @@
@endif
</button>
@trim
@elseif ($tag === 'div')
<div
role="button"
@if ($keyBindings || $hasTooltip)
x-data="{}"
@endif
@if ($keyBindings)
x-bind:id="$id('key-bindings')"
x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()"
@endif
@if ($hasTooltip)
x-tooltip="{
content: @js($tooltip),
theme: $store.theme,
}"
@endif
{{ $attributes->class([$linkClasses]) }}
>
@if ($icon && $iconPosition === IconPosition::Before)
<x-filament::icon
:alias="$iconAlias"
:icon="$icon"
:class="$iconClasses"
:style="$iconStyles"
/>
@endif
<span class="{{ $labelClasses }}" style="{{ $labelStyles }}">
{{ $slot }}
</span>
@if ($icon && $iconPosition === IconPosition::After)
<x-filament::icon
:alias="$iconAlias"
:icon="$icon"
:class="$iconClasses"
:style="$iconStyles"
/>
@endif
@if (filled($badge))
<div class="{{ $badgeContainerClasses }}">
<x-filament::badge :color="$badgeColor" :size="$badgeSize">
{{ $badge }}
</x-filament::badge>
</div>
@endif
</div>
@trim
@endif
@@ -4,6 +4,7 @@ namespace Filament\Support\Concerns;
use Closure;
use Exception;
use Filament\Tables\Columns\Column;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@@ -308,7 +309,11 @@ trait HasCellState
return null;
}
$recordKey = (string) $record->getKey();
if ($this instanceof Column) {
$recordKey = $this->getLivewire()->getTableRecordKey($record);
} else {
$recordKey = (string) $record->getKey();
}
if (blank($recordKey)) {
return $state();
@@ -33,7 +33,7 @@ return [
],
'no_rules' => '(Keine Bedinungen)',
'no_rules' => '(Keine Bedingungen)',
'item_separators' => [
'and' => 'UND',
@@ -79,13 +79,13 @@ return [
'is_after' => [
'label' => [
'direct' => 'nach',
'inverse' => 'vor',
'direct' => 'ist nach',
'inverse' => 'ist nicht nach',
],
'summary' => [
'direct' => ':attribute is after :date',
'inverse' => ':attribute is not after :date',
'direct' => ':attribute ist nach :date',
'inverse' => ':attribute ist nicht nach :date',
],
],
@@ -94,7 +94,7 @@ return [
'label' => [
'direct' => 'ist vor',
'inverse' => 'ist nach',
'inverse' => 'ist nicht vor',
],
'summary' => [
@@ -107,7 +107,7 @@ return [
'is_date' => [
'label' => [
'direct' => 'ist Datum',
'direct' => 'ist ein Datum',
'inverse' => 'ist kein Datum',
],
@@ -121,7 +121,7 @@ return [
'is_month' => [
'label' => [
'direct' => 'ist ein ',
'direct' => 'ist ein Monat',
'inverse' => 'ist kein Monat',
],
@@ -281,8 +281,8 @@
@if ($isLimitedListExpandable)
<x-filament::link
color="gray"
tag="button"
x-on:click.prevent="isLimited = false"
tag="div"
x-on:click.prevent.stop="isLimited = false"
x-show="isLimited"
>
{{ trans_choice('filament-tables::table.columns.text.actions.expand_list', $limitedArrayStateCount) }}
@@ -290,9 +290,9 @@
<x-filament::link
color="gray"
tag="button"
tag="div"
x-cloak
x-on:click.prevent="isLimited = true"
x-on:click.prevent.stop="isLimited = true"
x-show="! isLimited"
>
{{ trans_choice('filament-tables::table.columns.text.actions.collapse_list', $limitedArrayStateCount) }}
@@ -87,7 +87,7 @@
theme: $store.theme,
}
"
x-on:click.stop.prevent=""
x-on:click.stop=""
>
{{-- format-ignore-start --}}
<x-filament::input
@@ -66,7 +66,7 @@ class AssociateAction extends Action
$this->successNotificationTitle(__('filament-actions::associate.single.notifications.associated.title'));
$this->color('gray');
$this->defaultColor('gray');
$this->form(fn (): array => [$this->getRecordSelect()]);
+1 -1
View File
@@ -66,7 +66,7 @@ class AttachAction extends Action
$this->successNotificationTitle(__('filament-actions::attach.single.notifications.attached.title'));
$this->color('gray');
$this->defaultColor('gray');
$this->form(fn (): array => [$this->getRecordSelect()]);
@@ -14,7 +14,7 @@ class BulkActionGroup extends ActionGroup
$this->icon(FilamentIcon::resolve('tables::actions.open-bulk-actions') ?? 'heroicon-m-ellipsis-vertical');
$this->color('gray');
$this->defaultColor('gray');
$this->button();
+1 -1
View File
@@ -27,7 +27,7 @@ class DeleteAction extends Action
$this->successNotificationTitle(__('filament-actions::delete.single.notifications.deleted.title'));
$this->color('danger');
$this->defaultColor('danger');
$this->icon(FilamentIcon::resolve('actions::delete-action') ?? 'heroicon-m-trash');
@@ -30,7 +30,7 @@ class DeleteBulkAction extends BulkAction
$this->successNotificationTitle(__('filament-actions::delete.multiple.notifications.deleted.title'));
$this->color('danger');
$this->defaultColor('danger');
$this->icon(FilamentIcon::resolve('actions::delete-action') ?? 'heroicon-m-trash');
+1 -1
View File
@@ -29,7 +29,7 @@ class DetachAction extends Action
$this->successNotificationTitle(__('filament-actions::detach.single.notifications.detached.title'));
$this->color('danger');
$this->defaultColor('danger');
$this->icon(FilamentIcon::resolve('actions::detach-action') ?? 'heroicon-m-x-mark');
@@ -30,7 +30,7 @@ class DetachBulkAction extends BulkAction
$this->successNotificationTitle(__('filament-actions::detach.multiple.notifications.detached.title'));
$this->color('danger');
$this->defaultColor('danger');
$this->icon(FilamentIcon::resolve('actions::detach-action') ?? 'heroicon-m-x-mark');
@@ -29,7 +29,7 @@ class DissociateAction extends Action
$this->successNotificationTitle(__('filament-actions::dissociate.single.notifications.dissociated.title'));
$this->color('danger');
$this->defaultColor('danger');
$this->icon(FilamentIcon::resolve('actions::dissociate-action') ?? 'heroicon-m-x-mark');
@@ -30,7 +30,7 @@ class DissociateBulkAction extends BulkAction
$this->successNotificationTitle(__('filament-actions::dissociate.multiple.notifications.dissociated.title'));
$this->color('danger');
$this->defaultColor('danger');
$this->icon(FilamentIcon::resolve('actions::dissociate-action') ?? 'heroicon-m-x-mark');
@@ -27,7 +27,7 @@ class ForceDeleteAction extends Action
$this->successNotificationTitle(__('filament-actions::force-delete.single.notifications.deleted.title'));
$this->color('danger');
$this->defaultColor('danger');
$this->icon(FilamentIcon::resolve('actions::force-delete-action') ?? 'heroicon-m-trash');
@@ -30,7 +30,7 @@ class ForceDeleteBulkAction extends BulkAction
$this->successNotificationTitle(__('filament-actions::force-delete.multiple.notifications.deleted.title'));
$this->color('danger');
$this->defaultColor('danger');
$this->icon(FilamentIcon::resolve('actions::force-delete-action') ?? 'heroicon-m-trash');
@@ -27,7 +27,7 @@ class RestoreAction extends Action
$this->successNotificationTitle(__('filament-actions::restore.single.notifications.restored.title'));
$this->color('gray');
$this->defaultColor('gray');
$this->icon(FilamentIcon::resolve('actions::restore-action') ?? 'heroicon-m-arrow-uturn-left');
@@ -30,7 +30,7 @@ class RestoreBulkAction extends BulkAction
$this->successNotificationTitle(__('filament-actions::restore.multiple.notifications.restored.title'));
$this->color('gray');
$this->defaultColor('gray');
$this->icon(FilamentIcon::resolve('actions::restore-action') ?? 'heroicon-m-arrow-uturn-left');
+1 -1
View File
@@ -28,7 +28,7 @@ class ViewAction extends Action
$this->modalSubmitAction(false);
$this->modalCancelAction(fn (StaticAction $action) => $action->label(__('filament-actions::view.single.modal.actions.close.label')));
$this->color('gray');
$this->defaultColor('gray');
$this->icon(FilamentIcon::resolve('actions::view-action') ?? 'heroicon-m-eye');
@@ -38,8 +38,10 @@ class Count extends Summarizer
$state = [];
foreach ($query->clone()->distinct()->pluck($attribute) as $value) {
$column->record($this->getQuery()->getModel()->setAttribute($attribute, $value));
$column->record($this->getQuery()->getModel()->setKeyName($attribute)->setAttribute($attribute, $value));
$column->clearCachedState();
$columnState = $column->getState();
$column->clearCachedState();
$color = json_encode($column->getColor($columnState));
$icon = $column->getIcon($columnState);
@@ -36,7 +36,7 @@ trait CanReorderRecords
$model = app($this->getTable()->getModel());
$modelKeyName = $model->getKeyName();
$wrappedModelKeyName = $model->getConnection()->getSchemaGrammar()->wrap($modelKeyName);
$wrappedModelKeyName = $model->getConnection()?->getQueryGrammar()?->wrap($modelKeyName) ?? $modelKeyName;
$model
->newModelQuery()