21 Commits

Author SHA1 Message Date
brabli
0d9fbc3aed Update readme 2025-11-13 16:37:38 +00:00
brabli
14fd9ca2ec Tweak error message 2025-11-13 16:36:58 +00:00
brabli
93c4dd1e64 Add accent badge colour 2025-11-13 16:34:58 +00:00
brabli
61eaa62406 Update changelog 2025-11-13 16:30:22 +00:00
brabli
bcd97f20a3 Add zinc badge colour 2025-11-13 16:30:11 +00:00
brabli
9c1c0c1ba1 Add new colours 2025-11-13 16:26:35 +00:00
brabli
42fcaef53c Update changelog 2025-02-26 11:44:15 +00:00
brabli
beeeb2bf50 Add mit license 2025-02-26 11:43:15 +00:00
brabli
f0c19dad5e Update readme 2025-02-26 11:41:21 +00:00
brabli
62e279e0c9 Use new badge colour enum name 2025-02-26 11:36:55 +00:00
brabli
f6f3c64f25 Rename badge enum to badgecolour 2025-02-26 11:36:29 +00:00
brabli
b7a3f273a3 Organise colours alphabetically, add NAVY colour 2025-02-26 11:33:58 +00:00
brabli
03e2e39185 Update changelog 2025-02-26 11:33:43 +00:00
brabli
9141e21a02 Update readme 2025-02-26 10:59:42 +00:00
brabli
b25bbfd33c Set label property 2025-02-26 10:46:04 +00:00
brabli
eebd38d17f Update changelog 2025-02-26 10:41:14 +00:00
brabli
2a7230298e Add label attribute 2025-02-26 10:41:07 +00:00
brabli
ebc73e1b24 Update docblock 2025-02-26 09:55:28 +00:00
brabli
6883382420 Pass in attributes to badge template 2025-02-26 09:49:21 +00:00
brabli
fc9161b2d8 Autoformat 2025-02-26 09:49:10 +00:00
brabli
623fc60af2 Replace makefile with justfile 2025-02-26 09:47:03 +00:00
11 changed files with 149 additions and 94 deletions

View File

@@ -1,6 +1,14 @@
# Changelog
## [X.X.X] - XXXX-XX-XX
## [1.1.0] - 2025-11-13
- Add new badge colours
- Tweak error message
## [1.0.0] - 2025-02-26
- Add label attribute to set badge text
- Rename `Badge` enum to `BadgeColour`
- Pass misc attributes to badge template
- Update documentation and docblocks
## [0.1.1] - 2024-09-18
- Fixed black badge text colour not being black

View File

@@ -1 +1,7 @@
License
Copyright © 2025-present PCM Systems
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,16 +0,0 @@
PHP = docker compose run php
.PHONY: composer_install composer_update static_analysis tests
composer_install:
@$(PHP) composer install
composer_update:
@$(PHP) composer update
static_analysis:
@$(PHP) vendor/bin/psalm
tests:
@$(PHP) rm -rf var/cache
@$(PHP) vendor/bin/phpunit

View File

@@ -16,30 +16,30 @@ Create badges from objects or as standalone elements.
<br>
# Badgeable interface
# Creating badges with `BadgeableInterface`
Any object that you would like to be able to be turned into a badge must implement `BadgeableInterface`.
This interface specifies a single method `getBadgeColour()` and is used to determine what colour the badge should be rendered as.
This interface specifies a single method `getBadgeColour()` which expects an instance of the enum `BadgeColour` to be returned and is used to determine the colour of the rendered badge.
This method can contain as much logic in it as you'd like to return different colours of badge under different circumstances. EG:
```php
// Job.php
public function getBadgeColour(): Badge
public function getBadgeColour(): BadgeColour
{
return match ($this->getKind()) {
JobKind::Investigation => Badge::BLUE,
JobKind::Interrogation => Badge::RED,
JobKind::Shootout => Badge::BLACK,
JobKind::SipWhiskey => Badge::FOREST,
default => Badge::GREY
JobKind::Investigation => BadgeColour::BLUE,
JobKind::Interrogation => BadgeColour::RED,
JobKind::Shootout => BadgeColour::BLACK,
JobKind::SipWhiskey => BadgeColour::FOREST,
default => BadgeColour::GREY
};
}
```
You then specify the object using the `obj` prop when rendering the badge:
You then specify the object using the `:obj` prop when rendering the badge:
```twig
{# job.html.twig #}
@@ -74,7 +74,16 @@ obj="{{ job.kind }}"
`outline` - A boolean attribute that changes the style of the badge to an outline.
`class` - Extra classes you want to add to the badge element. These will override the base classes in case of conflicts.
`class` - Extra classes you want to add to the badge element. These are merged with the badge base classes taking priority in case of conflicts.
`label` - Badge label text. Content inside the content block will be prioritised over the label attribute if present.
```php
{# Both of these render the same markup. #}
<twig:Pcm:Badge colour="red" label="Warning!" />
<twig:Pcm:Badge colour="red">Warning!<twig:Pcm:Badge>
```
<br>

View File

@@ -1,4 +1,7 @@
<div class="{{ this.finalClasses }}">
{% block content %}
{% endblock %}
<div {{ attributes }} class="{{ this.finalClasses }}">
{% if block('content') is not empty %}
{% block content %}{% endblock %}
{% else %}
{{- this.label -}}
{% endif %}
</div>

15
justfile Normal file
View File

@@ -0,0 +1,15 @@
php := "docker compose run php"
composer_install:
@{{php}} composer install
composer_update:
@{{php}} composer update
static_analysis:
@{{php}} vendor/bin/psalm
tests:
@{{php}} rm -rf var/cache
@{{php}} vendor/bin/phpunit

View File

@@ -1,46 +0,0 @@
<?php
declare(strict_types=1);
namespace Pcm\BadgeBundle\Enum;
use Pcm\BadgeBundle\Model\BadgePalette;
enum Badge
{
case AMBER;
case BLACK;
case BLUE;
case DEFAULT;
case FOREST;
case GREEN;
case GREY;
case MAROON;
case OCHRE;
case ORANGE;
case RED;
case ROSE;
case STRIPE;
case YELLOW;
public function getPalette(): BadgePalette
{
return match ($this) {
$this::RED => new BadgePalette('text-red-600', 'border-red-600', 'bg-red-600'),
$this::MAROON => new BadgePalette('text-red-900', 'border-red-900', 'bg-red-900'),
$this::BLUE => new BadgePalette('text-sky-700', 'border-sky-700', 'bg-sky-700'),
$this::FOREST => new BadgePalette('text-green-800', 'border-green-800', 'bg-green-800'),
$this::GREEN => new BadgePalette('text-green-600', 'border-green-600', 'bg-green-600'),
$this::OCHRE => new BadgePalette('text-yellow-800', 'border-yellow-800', 'bg-yellow-800'),
$this::ORANGE => new BadgePalette('text-orange-500', 'border-orange-500', 'bg-orange-500'),
$this::AMBER => new BadgePalette('text-amber-500', 'border-amber-500', 'bg-amber-500'),
$this::ROSE => new BadgePalette('text-rose-600', 'border-rose-600', 'bg-rose-600'),
$this::GREY => new BadgePalette('text-neutral-400', 'border-neutral-400', 'bg-neutral-400'),
$this::YELLOW => new BadgePalette('text-yellow-500', 'border-yellow-500', 'bg-yellow-500'),
$this::STRIPE => new BadgePalette('text-indigo-500', 'border-indigo-500', 'bg-indigo-500'),
$this::BLACK => new BadgePalette('text-zinc-900', 'border-zinc-900', 'bg-zinc-900'),
$this::DEFAULT => new BadgePalette('text-primary', 'border-primary', 'bg-primary'),
};
}
}

66
src/Enum/BadgeColour.php Normal file
View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Pcm\BadgeBundle\Enum;
use Pcm\BadgeBundle\Model\BadgePalette;
enum BadgeColour
{
case ACCENT;
case AMBER;
case BLACK;
case BLUE;
case BROWN;
case DEFAULT;
case EMERALD;
case FOREST;
case FUCHSIA;
case GOLD;
case GREEN;
case GREY;
case LIME;
case RED;
case ROSE;
case SKY;
case MAROON;
case NAVY;
case OCHRE;
case ORANGE;
case PINK;
case STRIPE;
case YELLOW;
case ZINC;
public function getPalette(): BadgePalette
{
return match ($this) {
$this::ACCENT => new BadgePalette('text-accent', 'border-accent', 'bg-accent'),
$this::AMBER => new BadgePalette('text-amber-500', 'border-amber-500', 'bg-amber-500'),
$this::BLACK => new BadgePalette('text-zinc-900', 'border-zinc-900', 'bg-zinc-900'),
$this::BLUE => new BadgePalette('text-sky-700', 'border-sky-700', 'bg-sky-700'),
$this::BROWN => new BadgePalette('text-orange-900', 'border-orange-900', 'bg-orange-900'),
$this::DEFAULT => new BadgePalette('text-primary', 'border-primary', 'bg-primary'),
$this::EMERALD => new BadgePalette('text-emerald-600', 'border-emerald-600', 'bg-emerald-600'),
$this::FOREST => new BadgePalette('text-green-800', 'border-green-800', 'bg-green-800'),
$this::FUCHSIA => new BadgePalette('text-fuchsia-500', 'border-fuchsia-500', 'bg-fuchsia-500'),
$this::GOLD => new BadgePalette('text-yellow-600', 'border-yellow-600', 'bg-yellow-600'),
$this::GREEN => new BadgePalette('text-green-600', 'border-green-600', 'bg-green-600'),
$this::GREY => new BadgePalette('text-neutral-400', 'border-neutral-400', 'bg-neutral-400'),
$this::LIME => new BadgePalette('text-lime-600', 'border-lime-600', 'bg-lime-600'),
$this::RED => new BadgePalette('text-red-600', 'border-red-600', 'bg-red-600'),
$this::ROSE => new BadgePalette('text-rose-600', 'border-rose-600', 'bg-rose-600'),
$this::SKY => new BadgePalette('text-sky-500', 'border-sky-500', 'bg-sky-500'),
$this::MAROON => new BadgePalette('text-red-900', 'border-red-900', 'bg-red-900'),
$this::NAVY => new BadgePalette('text-blue-900', 'border-blue-900', 'bg-blue-900'),
$this::OCHRE => new BadgePalette('text-yellow-800', 'border-yellow-800', 'bg-yellow-800'),
$this::ORANGE => new BadgePalette('text-orange-500', 'border-orange-500', 'bg-orange-500'),
$this::PINK => new BadgePalette('text-pink-400', 'border-pink-400', 'bg-pink-400'),
$this::STRIPE => new BadgePalette('text-indigo-500', 'border-indigo-500', 'bg-indigo-500'),
$this::YELLOW => new BadgePalette('text-yellow-500', 'border-yellow-500', 'bg-yellow-500'),
$this::ZINC => new BadgePalette('text-zinc-500', 'border-zinc-500', 'bg-zinc-500'),
};
}
}

View File

@@ -4,13 +4,18 @@ declare(strict_types=1);
namespace Pcm\BadgeBundle\Interface;
use Pcm\BadgeBundle\Enum\Badge;
use Pcm\BadgeBundle\Enum\BadgeColour;
/**
* Enables this class to be rendered as a badge using the Badge and BadgeOutline Twig components.
* Allows rendering the implementing class as a badge using the PCM Badge twig component.
*/
interface BadgeableInterface
{
public function getBadgeColour(): Badge;
/**
* Get the badge colour that should be used when rendering this object as a badge.
*
* @return BadgeColour Colour of badge to render
*/
public function getBadgeColour(): BadgeColour;
}

View File

@@ -15,5 +15,7 @@ final readonly class BadgePalette
* @param string $backgroundColourClass Background colour Tailwind class
*/
public function __construct(public string $textColourClass, public string $borderColourClass, public string $backgroundColourClass)
{}
{
}
}

View File

@@ -4,36 +4,39 @@ declare(strict_types=1);
namespace Pcm\BadgeBundle\Twig\Component;
use Pcm\BadgeBundle\Enum\Badge as EnumBadge;
use Pcm\BadgeBundle\Enum\BadgeColour;
use Pcm\BadgeBundle\Interface\BadgeableInterface;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
use TailwindMerge\TailwindMerge;
#[AsTwigComponent(name: 'Pcm:Badge', template: "@PcmBadge/Badge.html.twig")]
#[AsTwigComponent(name: 'Pcm:Badge', template: '@PcmBadge/Badge.html.twig')]
final class Badge
{
public string $finalClasses;
public ?string $label = null;
public function __construct(private string $baseClasses)
{
}
/**
* @param ?BadgeableInterface $obj The object to be converted into a badge.
* @param ?BadgeableInterface $obj the object to be converted into a badge
* @param ?string $class Extra classes to add to the badge element.
* These will override the base classes in case
* of conflicts.
* @param ?string $colour Specify the colour of an objectless badge.
* @param bool $outline If the badge should be rendered as an outline.
* @param ?string $colour specify the colour of an objectless badge
* @param bool $outline if the badge should be rendered as an outline
*/
public function mount(?BadgeableInterface $obj = null, ?string $class = null, string $colour = null, bool $outline = false): void
public function mount(?BadgeableInterface $obj = null, ?string $class = null, ?string $colour = null, ?string $label = null, bool $outline = false): void
{
$this->label = $label;
if (!$obj && !$colour) {
throw new \RuntimeException(sprintf("You must specify either a colour an instance of \"%s\".", BadgeableInterface::class));
throw new \RuntimeException(sprintf('You must specify either a colour an instance of "%s".', BadgeableInterface::class));
}
if ($obj && $colour) {
throw new \RuntimeException(sprintf("You have specified both the colour \"%s\" and an instance of \"%s\". Please use one or the other.", $colour, $obj::class));
throw new \RuntimeException(sprintf('You have specified both the colour "%s" and an instance of "%s". Please use one or the other.', $colour, $obj::class));
}
if ($obj) {
@@ -41,15 +44,15 @@ final class Badge
}
if ($colour) {
$cases = array_map(fn(EnumBadge $b) => strtolower($b->name), EnumBadge::cases());
$cases = array_map(fn (BadgeColour $b) => strtolower($b->name), BadgeColour::cases());
if (!in_array($colour, $cases)) {
$formattedCases = implode(", ", array_map(fn(string $s) => '"'.$s.'"', $cases));
throw new \RuntimeException(sprintf('"%s" is not a valid Badge colour. Available options are: %s', $colour, $formattedCases));
$formattedCases = implode(', ', array_map(fn (string $s) => '"'.$s.'"', $cases));
throw new \RuntimeException(sprintf('"%s" is not a valid badge colour. Available options are: %s.', $colour, $formattedCases));
}
$colour = strtoupper($colour);
$palette = EnumBadge::{$colour}->getPalette();
$colour = strtoupper($colour);
$palette = BadgeColour::{$colour}->getPalette();
}
$merger = TailwindMerge::instance();