编程

在 Filament 中创建自定义调色板字段 - 1/3

708 2023-06-01 22:03:00

Filament 允许开发者创建自定义的表单字段类和视图,用以在应用内复用,甚至可以以插件形式发布到社区。本系列将通过介绍如何创建一个拾色器字段来带领大家熟悉如何创建自定义字段。

要使该字段接收选项数组,我们需要在类中声明一个新的方法。该方法接收数组 $options 并将其存入一个对象属性$options 中。

class ColorPalette extends Field
{
    // ...

    protected array $options = [];

    public function options(array $options): static
    {
        $this->options = $options;

        return $this;
    }
}

现在我们可以传入数组选项到该字段:

ColorPalette::make('color')
    ->options([
        '#ffffff' => 'White',
        '#000000' => 'Black',
    ]),

检索选项

Filament 中所有字段和表单组件实际上底层是 Blade 组件。这意味着我们可以在 ColorPalette 类中定义公共方法,它们会被当作可反射的变量传入到 Blade 视图。 

让我们添加 getOptions() 方法到该字段,使之返回给定的选项数组。

class ColorPalette extends Field
{
    // ...

    public function getOptions(): array
    {
        return $this->options;
    }
}

现在在 Blade 视图内部,我们可以调用该方法并遍历返回的数组。

<x-forms::field-wrapper
    :id="$getId()"
    :label="$getLabel()"
    :label-sr-only="$isLabelHidden()"
    :helper-text="$getHelperText()"
    :hint="$getHint()"
    :hint-icon="$getHintIcon()"
    :required="$isRequired()"
    :state-path="$getStatePath()"
>
    <div class="flex items-center space-x-4">
        @foreach($getOptions() as $color => $label)
            <button type="button" class="rounded-full w-8 h-8 border border-gray-500" style="background: {{ $color }}" title="{{ $label }}">
                <span class="sr-only">
                    {{ $label }}
                </span>
            </button>
        @endforeach
    </div>
</x-forms::field-wrapper>

使用内联样式,我们可以将按钮的背景颜色更改为选项的颜色。出于可访问性的原因,我们还将输出提供的 $label,但仅用于屏幕阅读器。

就像这样,我们在页面上渲染颜色选项。

为计算选项添加支持

当前的状态下,ColorPalette 字段只接受静态的颜色选项数组。当你想基于另一个字段值或当前记录改变颜色选项时,会有问题。

这就是 Filament 令人难以置信的强大 Closure 定制系统发挥作用的地方。通过扩大 $options 参数的类型以接受闭包值,我们可以允许开发人员提供一个计算的选项列表,而不是静态列表。

要做到这一点,有几个步骤要做。第一个是扩大类本身的类型。

class ColorPalette extends Field
{
    // ...

    protected array | Closure $options = [];

    public function options(array | Closure $options): static
    {
        $this->options = $options;

        return $this;
    }

    // ...
}

ColorPalette::options() 方法现在通过联合类型接受数组或者闭包。这将允许我们这么做:

Select::make('theme')
    ->reactive()
    ->options([
        'minimal' => 'Minimal',
        'abstract' => 'Abstract',
    ]),
ColorPalette::make('color')
    ->options(function (callable $get) {
        if ($get('theme') === 'abstract') {
            return [
                '#ff69b4' => 'Hot Pink',
                '#32cd32' => 'Lime Green',
            ];
        }

        return [
            '#ffffff' => 'White',
            '#000000' => 'Black',
        ];
    })

现在 ColorPalette 字段将会根据其上面的 Select 字段更新或计算其选项值。会吗?

还有一件事要做。实际上我们需要调用闭包。幸运的是,Filament 有一个 evaluate() API,可以调用 Closure 闭包并根据 Context 提供一些标准化参数。

我们可以通过 $this->evaluate() 返回发送值,而不是在 getOptions() 直接返回 $this->options

class ColorPalette extends Field
{
    // ...
    
    public function getOptions(): array
    {
        return $this->evaluate($this->options);
    }
}

在下一部分中,我们将连接按钮,并将状态持久化到表单 /Livewire 组件。