Merge pull request #17772 from People-Sea/fix/issue/17751

fix: non native reactive `DateTimePicker` infinite update
This commit is contained in:
Dan Harrin
2025-09-14 12:14:10 +01:00
committed by GitHub
4 changed files with 92 additions and 76 deletions
@@ -8,6 +8,8 @@ use Carbon\Exceptions\InvalidFormatException;
use Closure;
use DateTime;
use Filament\Schemas\Components\Contracts\HasAffixActions;
use Filament\Schemas\Components\StateCasts\Contracts\StateCast;
use Filament\Schemas\Components\StateCasts\DateTimeStateCast;
use Filament\Support\Concerns\HasExtraAlpineAttributes;
use Filament\Support\Facades\FilamentTimezone;
use Filament\Support\Icons\Heroicon;
@@ -84,71 +86,44 @@ class DateTimePicker extends Field implements HasAffixActions
{
parent::setUp();
$this->afterStateHydrated(static function (DateTimePicker $component, $state): void {
if (blank($state)) {
return;
}
if (! $state instanceof CarbonInterface) {
try {
$state = Carbon::createFromFormat($component->getFormat(), (string) $state, config('app.timezone'));
} catch (InvalidFormatException $exception) {
try {
$state = Carbon::parse($state, config('app.timezone'));
} catch (InvalidFormatException $exception) {
$component->state(null);
return;
}
}
}
$state = $state->setTimezone($component->getTimezone());
if (! $component->isNative()) {
$component->state((string) $state);
return;
}
if (! $component->hasTime()) {
$component->state($state->toDateString());
return;
}
$precision = $component->hasSeconds() ? 'second' : 'minute';
if (! $component->hasDate()) {
$component->state($state->toTimeString($precision));
return;
}
$component->state($state->toDateTimeString($precision));
});
$this->dehydrateStateUsing(static function (DateTimePicker $component, $state) {
if (blank($state)) {
return null;
}
if (! $state instanceof CarbonInterface) {
$state = Carbon::parse($state);
}
$state->shiftTimezone($component->getTimezone());
$state->setTimezone(config('app.timezone'));
return $state->format($component->getFormat());
});
$this->rule(
'date',
static fn (DateTimePicker $component): bool => $component->hasDate(),
);
}
/**
* @return array<StateCast>
*/
public function getDefaultStateCasts(): array
{
return [
...parent::getDefaultStateCasts(),
app(DateTimeStateCast::class, [
'format' => $this->getFormat(),
'internalFormat' => $this->getInternalFormat(),
'timezone' => $this->getTimezone(),
]),
];
}
public function getInternalFormat(): string
{
if (! $this->isNative()) {
return 'Y-m-d H:i:s';
}
if (! $this->hasTime()) {
return 'Y-m-d';
}
if (! $this->hasDate()) {
return $this->hasSeconds() ? 'H:i:s' : 'H:i';
}
return 'Y-m-d H:i:s';
}
public function displayFormat(string | Closure | null $format): static
{
$this->displayFormat = $format;
-15
View File
@@ -1367,21 +1367,6 @@ class Select extends Field implements Contracts\CanDisableOptions, Contracts\Has
return (bool) $this->evaluate($this->canOptionLabelsWrap);
}
public function hydrateDefaultState(?array &$hydratedDefaultState): void
{
parent::hydrateDefaultState($hydratedDefaultState);
if (is_bool($state = $this->getState())) {
$state = $state ? 1 : 0;
$this->state($state);
if (is_array($hydratedDefaultState)) {
Arr::set($hydratedDefaultState, $this->getStatePath(), $state); /** @phpstan-ignore parameterByRef.type */
}
}
}
public function getQualifiedRelatedKeyNameForRelationship(Relation $relationship): string
{
if ($relationship instanceof BelongsToMany) {
@@ -451,14 +451,14 @@ trait HasState
}
if (! $this->hasDefaultState()) {
$this->hasStatePath() && $this->state(null);
$this->hasStatePath() && $this->rawState(null);
return;
}
$defaultState = $this->getDefaultState();
$this->state($defaultState);
$this->rawState($defaultState);
Arr::set($hydratedDefaultState, $statePath, $defaultState); /** @phpstan-ignore parameterByRef.type */
}
@@ -0,0 +1,56 @@
<?php
namespace Filament\Schemas\Components\StateCasts;
use Carbon\CarbonInterface;
use Carbon\Exceptions\InvalidFormatException;
use Filament\Schemas\Components\StateCasts\Contracts\StateCast;
use Illuminate\Support\Carbon;
class DateTimeStateCast implements StateCast
{
public function __construct(
protected string $format,
protected string $internalFormat,
protected string $timezone,
) {}
public function get(mixed $state): ?string
{
if (blank($state)) {
return null;
}
if (! $state instanceof CarbonInterface) {
$state = Carbon::parse($state);
}
$state->shiftTimezone($this->timezone);
$state->setTimezone(config('app.timezone'));
return $state->format($this->format);
}
public function set(mixed $state): ?string
{
if (blank($state)) {
return null;
}
if (! $state instanceof CarbonInterface) {
try {
$state = Carbon::createFromFormat($this->format, (string) $state, config('app.timezone'));
} catch (InvalidFormatException) {
try {
$state = Carbon::parse($state, config('app.timezone'));
} catch (InvalidFormatException) {
return null;
}
}
}
$state = $state->setTimezone($this->timezone);
return $state->format($this->internalFormat);
}
}