[ /** * Unset inherited props */ 'after' => null, 'before' => null, /** * Whether to allow alpha transparency in the color */ 'alpha' => function (bool $alpha = false) { return $alpha; }, /** * The CSS format (hex, rgb, hsl) to display and store the value */ 'format' => function (string $format = 'hex'): string { if (in_array($format, ['hex', 'hsl', 'rgb']) === false) { throw new InvalidArgumentException('Unsupported format for color field (supported: hex, rgb, hsl)'); } return $format; }, /** * Change mode to disable the color picker (`input`) or to only * show the `options` as toggles */ 'mode' => function (string $mode = 'picker'): string { if (in_array($mode, ['picker', 'input', 'options']) === false) { throw new InvalidArgumentException('Unsupported mode for color field (supported: picker, input, options)'); } return $mode; }, /** * List of colors that will be shown as buttons * to directly select them */ 'options' => function (array $options = []): array { return $options; } ], 'computed' => [ 'default' => function (): string { return Str::lower($this->default); }, 'options' => function (): array { // resolve options to support manual arrays // alongside api and query options $props = FieldOptions::polyfill($this->props); $options = FieldOptions::factory([ 'text' => '{{ item.value }}', 'value' => '{{ item.key }}', ...$props['options'] ]); $options = $options->render($this->model()); if (empty($options) === true) { return []; } $options = match (true) { // simple array of values // or value=text (from Options class) is_numeric($options[0]['value']) || $options[0]['value'] === $options[0]['text'] => A::map($options, fn ($option) => [ 'value' => $option['text'] ]), // deprecated: name => value, flipping // TODO: start throwing in warning in v5 $this->isColor($options[0]['text']) => A::map($options, fn ($option) => [ 'value' => $option['text'], // ensure that any HTML in the new text is escaped 'text' => Escape::html($option['value']) ]), default => A::map($options, fn ($option) => [ 'value' => $option['value'], 'text' => $option['text'] ]), }; return $options; } ], 'methods' => [ 'isColor' => function (string $value): bool { return $this->isHex($value) || $this->isRgb($value) || $this->isHsl($value); }, 'isHex' => function (string $value): bool { return preg_match('/^#([\da-f]{3,4}){1,2}$/i', $value) === 1; }, 'isHsl' => function (string $value): bool { return preg_match('/^hsla?\(\s*(\d{1,3}\.?\d*)(deg|rad|grad|turn)?(?:,|\s)+(\d{1,3})%(?:,|\s)+(\d{1,3})%(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i', $value) === 1; }, 'isRgb' => function (string $value): bool { return preg_match('/^rgba?\(\s*(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i', $value) === 1; }, ], 'validations' => [ 'color' => function ($value) { if (empty($value) === true) { return true; } if ($this->format === 'hex' && $this->isHex($value) === false) { throw new InvalidArgumentException([ 'key' => 'validation.color', 'data' => ['format' => 'hex'] ]); } if ($this->format === 'rgb' && $this->isRgb($value) === false) { throw new InvalidArgumentException([ 'key' => 'validation.color', 'data' => ['format' => 'rgb'] ]); } if ($this->format === 'hsl' && $this->isHsl($value) === false) { throw new InvalidArgumentException([ 'key' => 'validation.color', 'data' => ['format' => 'hsl'] ]); } } ] ];