Skip to content

Instantly share code, notes, and snippets.

@devhammed
Last active January 22, 2025 10:36
Show Gist options
  • Save devhammed/17f28a651fd791b70f7311a00bea2661 to your computer and use it in GitHub Desktop.
Save devhammed/17f28a651fd791b70f7311a00bea2661 to your computer and use it in GitHub Desktop.
Filament Money Input Component (this assumes that you are using the MoneyCast from https://github.com/akaunting/laravel-money package for the field in your model, this package is depended on by default in FilamentPHP so this should work out of the box))
<?php
namespace App\Frontend\Forms\Components;
use Closure;
use Akaunting\Money\Money;
use Filament\Support\RawJs;
use Akaunting\Money\Currency;
use Filament\Forms\Components\TextInput;
class MoneyInput extends TextInput
{
protected bool|Closure|null $masking = null;
protected bool|Closure|null $allowNegative = null;
protected string|Currency|Closure|null $currency = null;
protected function setUp(): void
{
parent::setUp();
$this
->inputMode('decimal')
->extraInputAttributes(['class' => '!text-xl'])
->prefix(fn(MoneyInput $component) => $component->getCurrency()->getPrefix(), isInline: true)
->suffix(fn(MoneyInput $component) => $component->getCurrency()->getSuffix(), isInline: true)
->extraAttributes(['class' => h('[&_.fi-input-wrp-label]:text-3xl [&_.fi-input-wrp-label]:text-primary [&_.fi-input-wrp-label]:select-none')])
->formatStateUsing(fn(MoneyInput $component, ?array $state): ?string => is_array($state) ? number_format(
$state['value'] ?? 0,
$component->getCurrency()->getPrecision(),
$component->getCurrency()->getDecimalMark(),
$component->getCurrency()->getThousandsSeparator(),
) : null)
->dehydrateStateUsing(fn(MoneyInput $component, ?string $state): Money => money(
amount: floatval(
str_replace(
$component->getCurrency()->getDecimalMark(),
'.',
str_replace(
$component->getCurrency()->getThousandsSeparator(),
'',
$state ?? '0',
),
),
),
currency: $component->getCurrency()->getCurrency(),
convert: true,
))
->rule(fn(MoneyInput $component) => function (string $attribute, ?string $value, Closure $fail) use (
$component
) {
if ( ! $component->getAllowNegative() && $component->getMoney()->isNegative()) {
$fail(__('Field must be a positive number.'));
}
});
}
public function currency(string|Currency|Closure|null $currency = null): static
{
$this->currency = $currency;
return $this;
}
public function masking(bool|Closure $masking = true): static
{
$this->masking = $masking;
return $this;
}
public function allowNegative(bool|Closure $allowNegative = true): static
{
$this->allowNegative = $allowNegative;
return $this;
}
public function getMoney(): ?Money
{
return $this->getStateToDehydrate()[$this->getStatePath()] ?? null;
}
public function getCurrency(): Currency
{
$currency = $this->evaluate($this->currency);
if ($currency instanceof Currency) {
return $currency;
}
return currency($currency);
}
public function getMasking(): bool
{
return $this->evaluate($this->masking ?? true);
}
public function getAllowNegative(): bool
{
return $this->evaluate($this->allowNegative ?? false);
}
public function getMask(): string|RawJs|null
{
if ( ! $this->getMasking()) {
return null;
}
return RawJs::make(sprintf(
"\$money(\$input, '%s', '%s', %d);",
$this->getCurrency()->getDecimalMark(),
$this->getCurrency()->getThousandsSeparator(),
$this->getCurrency()->getPrecision(),
));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment