Skip to content

Commit 39036e9

Browse files
WIP
1 parent 4690279 commit 39036e9

File tree

5 files changed

+83
-18
lines changed

5 files changed

+83
-18
lines changed

doc/fields/ChoiceField.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,14 @@ pages (``index`` and ``detail``)::
9090

9191
The built-in badge styles are the same as Bootstrap: ``'success'``,
9292
``'warning'``, ``'danger'``, ``'info'``, ``'primary'``, ``'secondary'``,
93-
``'light'``, ``'dark'``.
93+
``'light'``, ``'dark'``, but you can also pass a custom full 6-digit hexadecimal background color::
94+
95+
yield ChoiceField::new('...')->renderAsBadges([
96+
// $value => $badgeStyleName
97+
'paid' => '#00FF00',
98+
'pending' => '#FFFF00',
99+
'refunded' => '#FF0000',
100+
]);
94101

95102
renderAsNativeWidget
96103
~~~~~~~~~~~~~~~~~~~~

src/Field/ChoiceField.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ public function renderAsBadges($badgeSelector = true): self
126126

127127
if (\is_array($badgeSelector)) {
128128
foreach ($badgeSelector as $badgeType) {
129-
if (!\in_array($badgeType, self::VALID_BADGE_TYPES, true)) {
130-
throw new \InvalidArgumentException(sprintf('The values of the array passed to the "%s" method must be one of the following valid badge types: "%s" ("%s" given).', __METHOD__, implode(', ', self::VALID_BADGE_TYPES), $badgeType));
129+
if (!self::isSupportedBadge($badgeType)) {
130+
throw new \InvalidArgumentException(sprintf('The values of the array passed to the "%s" method must be a full 6-digit hexadecimal color or one of the following valid badge types: "%s" ("%s" given).', __METHOD__, implode(', ', self::VALID_BADGE_TYPES), $badgeType));
131131
}
132132
}
133133
}
@@ -137,6 +137,16 @@ public function renderAsBadges($badgeSelector = true): self
137137
return $this;
138138
}
139139

140+
public static function isSupportedBadge(string $badgeType): bool
141+
{
142+
return \in_array($badgeType, self::VALID_BADGE_TYPES, true) || self::isSupportedBadgeColor($badgeType);
143+
}
144+
145+
public static function isSupportedBadgeColor(string $badgeColor): bool
146+
{
147+
return 1 === preg_match('/^#[0-9a-f]{6}$/iD', $badgeColor);
148+
}
149+
140150
public function renderAsNativeWidget(bool $asNative = true): self
141151
{
142152
$this->setCustomOption(self::OPTION_WIDGET, $asNative ? self::WIDGET_NATIVE : self::WIDGET_AUTOCOMPLETE);

src/Field/Configurator/ChoiceConfigurator.php

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,15 @@ public function configure(FieldDto $field, EntityDto $entityDto, AdminContext $c
141141
);
142142
}
143143

144+
[$cssClass, $style] = $isRenderedAsBadge
145+
? $this->getBadgeCssClassAndStyle($badgeSelector, $selectedValue, $field)
146+
: [null, null];
147+
144148
/** @var TranslatableMessage $choiceMessage */
145149
$choiceMessages[] = new TranslatableChoiceMessage(
146150
$choiceMessage,
147-
$isRenderedAsBadge ? $this->getBadgeCssClass($badgeSelector, $selectedValue, $field) : null
151+
$cssClass,
152+
$style,
148153
);
149154
}
150155
}
@@ -171,10 +176,12 @@ private function getChoices(array|callable|null $choiceGenerator, EntityDto $ent
171176

172177
/**
173178
* @param array<string>|bool|callable|null $badgeSelector
179+
*
180+
* @return array{string, string|null}
174181
*/
175-
private function getBadgeCssClass(array|bool|callable|null $badgeSelector, mixed $value, FieldDto $field): string
182+
private function getBadgeCssClassAndStyle(array|bool|callable|null $badgeSelector, mixed $value, FieldDto $field): array
176183
{
177-
$commonBadgeCssClass = 'badge';
184+
$cssClass = 'badge';
178185

179186
$badgeType = '';
180187
if (true === $badgeSelector) {
@@ -183,14 +190,35 @@ private function getBadgeCssClass(array|bool|callable|null $badgeSelector, mixed
183190
$badgeType = $badgeSelector[$value] ?? 'badge-secondary';
184191
} elseif (\is_callable($badgeSelector)) {
185192
$badgeType = $badgeSelector($value, $field);
186-
if (!\in_array($badgeType, ChoiceField::VALID_BADGE_TYPES, true)) {
187-
throw new \RuntimeException(sprintf('The value returned by the callable passed to the "renderAsBadges()" method must be one of the following valid badge types: "%s" ("%s" given).', implode(', ', ChoiceField::VALID_BADGE_TYPES), $badgeType));
193+
if (!ChoiceField::isSupportedBadge($badgeType)) {
194+
throw new \RuntimeException(sprintf('The value returned by the callable passed to the "renderAsBadges()" method must be a full 6-digit hexadecimal color or one of the following valid badge types: "%s" ("%s" given).', implode(', ', ChoiceField::VALID_BADGE_TYPES), $badgeType));
188195
}
189196
}
190197

191-
$badgeTypeCssClass = '' === $badgeType ? '' : u($badgeType)->ensureStart('badge-')->toString();
198+
if ('' !== $badgeType && !ChoiceField::isSupportedBadgeColor($badgeType)) {
199+
$cssClass .= ' '.u($badgeType)->ensureStart('badge-')->toString();
200+
}
201+
202+
return [$cssClass, $this->getBadgeStyle($badgeType)];
203+
}
204+
205+
private function getBadgeStyle(string $bgColor): ?string
206+
{
207+
if (!ChoiceField::isSupportedBadgeColor($bgColor)) {
208+
return null;
209+
}
210+
211+
[$r, $g, $b] = [
212+
hexdec(substr($bgColor, 1, 2)),
213+
hexdec(substr($bgColor, 3, 2)),
214+
hexdec(substr($bgColor, 5, 2)),
215+
];
216+
217+
$luminance = (0.299 * $r + 0.587 * $g + 0.114 * $b) / 255;
218+
219+
$color = $luminance > 0.5 ? '#000000' : '#FFFFFF';
192220

193-
return $commonBadgeCssClass.' '.$badgeTypeCssClass;
221+
return sprintf('background-color:%s; color:%s;', $bgColor, $color);
194222
}
195223

196224
/**

src/Translation/TranslatableChoiceMessage.php

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,34 @@ final class TranslatableChoiceMessage implements TranslatableInterface
1919
public function __construct(
2020
private TranslatableInterface $message,
2121
private ?string $cssClass,
22+
private ?string $style = null,
2223
) {
2324
}
2425

2526
public function trans(TranslatorInterface $translator, ?string $locale = null): string
2627
{
2728
$message = $this->message->trans($translator, $locale);
2829

29-
if (null !== $this->cssClass) {
30-
return sprintf('<span class="%s">%s</span>', $this->cssClass, $message);
31-
}
32-
33-
return $message;
30+
return $this->generateHtml($message);
3431
}
3532

3633
public function __toString(): string
3734
{
38-
if (null !== $this->cssClass) {
39-
return sprintf('<span class="%s">%s</span>', $this->cssClass, $this->message);
35+
return $this->generateHtml((string) $this->message);
36+
}
37+
38+
private function generateHtml(string $message): string
39+
{
40+
if (null !== $this->cssClass || null !== $this->style) {
41+
return sprintf(
42+
'<span %s%s%s>%s</span>',
43+
null !== $this->cssClass ? sprintf('class="%s"', $this->cssClass) : '',
44+
null !== $this->cssClass && null !== $this->style ? ' ' : '',
45+
null !== $this->style ? sprintf('style="%s"', $this->style) : '',
46+
$message
47+
);
4048
}
4149

42-
return (string) $this->message;
50+
return $message;
4351
}
4452
}

tests/Field/ChoiceFieldTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,5 +171,17 @@ public function testBadges()
171171

172172
$field->setValue([1, 3])->renderAsBadges(function ($value) { return $value > 1 ? 'success' : 'primary'; });
173173
self::assertSame('<span class="badge badge-primary">a</span><span class="badge badge-success">c</span>', (string) $this->configure($field)->getFormattedValue());
174+
175+
$field->setValue(1)->renderAsBadges([1 => '#123456', '3' => '#AAAAAA']);
176+
self::assertSame('<span class="badge" style="background-color:#123456; color:#FFFFFF;">a</span>', (string) $this->configure($field)->getFormattedValue());
177+
178+
$field->setValue([1, 3])->renderAsBadges([1 => '#123456', '3' => '#AAAAAA']);
179+
self::assertSame('<span class="badge" style="background-color:#123456; color:#FFFFFF;">a</span><span class="badge" style="background-color:#AAAAAA; color:#000000;">c</span>', (string) $this->configure($field)->getFormattedValue());
180+
181+
$field->setValue(1)->renderAsBadges(function ($value) { return $value > 1 ? '#AAAAAA' : '#123456'; });
182+
self::assertSame('<span class="badge" style="background-color:#123456; color:#FFFFFF;">a</span>', (string) $this->configure($field)->getFormattedValue());
183+
184+
$field->setValue([1, 3])->renderAsBadges(function ($value) { return $value > 1 ? '#AAAAAA' : '#123456'; });
185+
self::assertSame('<span class="badge" style="background-color:#123456; color:#FFFFFF;">a</span><span class="badge" style="background-color:#AAAAAA; color:#000000;">c</span>', (string) $this->configure($field)->getFormattedValue());
174186
}
175187
}

0 commit comments

Comments
 (0)