Skip to content

Commit

Permalink
Merge pull request #10 from CarbonPackages/feature/config_tailwindmerge
Browse files Browse the repository at this point in the history
New: Make Tailwind.merge configurable
  • Loading branch information
jonnitto authored Apr 17, 2024
2 parents d3d7252 + 1fdcad0 commit d138430
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 13 deletions.
18 changes: 7 additions & 11 deletions Classes/EelHelper/TailwindHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
namespace Carbon\Eel\EelHelper;

use Carbon\Eel\Service\MergeClassesService;
use Carbon\Eel\Service\TailwindMergeService;
use Neos\Eel\ProtectedContextAwareInterface;
use Neos\Flow\Annotations as Flow;
use YieldStudio\TailwindMerge\TailwindMerge;

/**
* @Flow\Proxy(false)
*/
class TailwindHelper implements ProtectedContextAwareInterface
{
/**
* @var TailwindMergeService
* @Flow\Inject
*/
protected $mergeService;

/**
* Merge multiple Tailwind CSS classes and automatically resolves conflicts between them without headaches.
Expand All @@ -22,13 +24,7 @@ class TailwindHelper implements ProtectedContextAwareInterface
public function merge(...$arguments): ?string
{
$mergedString = MergeClassesService::merge(...$arguments);

if ($mergedString) {
$twMerge = TailwindMerge::instance();
return $twMerge->merge($mergedString);
}

return null;
return $mergedString ? $this->mergeService->merge($mergedString) : null;
}


Expand Down
30 changes: 30 additions & 0 deletions Classes/Package.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
namespace Carbon\Eel;

use Neos\Flow\Cache\CacheManager;
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\Monitor\FileMonitor;
use Neos\Flow\Package\Package as BasePackage;

class Package extends BasePackage
{
/**
* @param Bootstrap $bootstrap The current bootstrap
* @return void
*/
public function boot(Bootstrap $bootstrap)
{
$dispatcher = $bootstrap->getSignalSlotDispatcher();

$flushTailwindCache = function () use ($bootstrap) {
$cacheManager = $bootstrap->getEarlyInstance(CacheManager::class);
$cacheManager->getCache('Carbon_Eel_Tailwind')->flush();
};

$dispatcher->connect(FileMonitor::class, 'filesHaveChanged', function ($fileMonitorIdentifier, array $changedFiles) use ($flushTailwindCache) {
if ($fileMonitorIdentifier == 'Flow_ConfigurationFiles') {
$flushTailwindCache();
}
});
}
}
121 changes: 121 additions & 0 deletions Classes/Service/TailwindMergeService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php

namespace Carbon\Eel\Service;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cache\CacheManager;
use TailwindMerge\TailwindMerge;
use TailwindMerge\Validators\AnyValueValidator;
use TailwindMerge\Validators\ArbitraryImageValidator;
use TailwindMerge\Validators\ArbitraryLengthValidator;
use TailwindMerge\Validators\ArbitraryNumberValidator;
use TailwindMerge\Validators\ArbitraryPositionValidator;
use TailwindMerge\Validators\ArbitraryShadowValidator;
use TailwindMerge\Validators\ArbitrarySizeValidator;
use TailwindMerge\Validators\ArbitraryValueValidator;
use TailwindMerge\Validators\IntegerValidator;
use TailwindMerge\Validators\LengthValidator;
use TailwindMerge\Validators\NumberValidator;
use TailwindMerge\Validators\PercentValidator;
use TailwindMerge\Validators\TshirtSizeValidator;
use function is_array;

class TailwindMergeService
{
/**
* @var array
* @Flow\InjectConfiguration(path="tailwindMergeConfig")
*/
protected $mergeConfig;

/**
* @var CacheManager
*/
protected $flowCacheManager;

/**
* @param CacheManager $flowCacheManager
*/
public function __construct(CacheManager $flowCacheManager)
{
$this->flowCacheManager = $flowCacheManager;
}

protected $instance;

/**
* Merge the given classes
*
* @param iterable|mixed $arguments Optional variable list of arrays / values
* @return string|null The merged classes
*/
public function merge(...$arguments): ?string
{
if (!$this->instance) {
$this->instance = $this->createInstance();
}

return $this->instance->merge(...$arguments);
}

/**
* Create a new TailwindMerge instance
*
* @return TailwindMerge
*/
protected function createInstance(): TailwindMerge {
$cache = $this->flowCacheManager->getSimpleCache('Carbon_Eel_Tailwind');
if (isset($this->mergeConfig) && is_array($this->mergeConfig)) {
return TailwindMerge::factory()->withConfiguration($this->parseConfig($this->mergeConfig))->withCache($cache)->make();
}

return TailwindMerge::factory()->withCache($cache)->make();
}

/**
* Merge the configuration and replace the constants with the actual validators
*
* @param iterable|mixed $config The configuration
* @return mixed
*/
protected function parseConfig($config) {
if (is_array($config)) {
$parsedConfig = [];
foreach ($config as $key => $value) {
$parsedConfig[$key] = $this->parseConfig($value);
}
return $parsedConfig;
}

switch ($config) {
case 'ANY_VALUE_VALIDATOR':
return AnyValueValidator::validate(...);
case 'ARBITRARY_IMAGE_VALIDATOR':
return ArbitraryImageValidator::validate(...);
case 'ARBITRARY_LENGTH_VALIDATOR':
return ArbitraryLengthValidator::validate(...);
case 'ARBITRARY_NUMBER_VALIDATOR':
return ArbitraryNumberValidator::validate(...);
case 'ARBITRARY_POSITION_VALIDATOR':
return ArbitraryPositionValidator::validate(...);
case 'ARBITRARY_SHADOW_VALIDATOR':
return ArbitraryShadowValidator::validate(...);
case 'ARBITRARY_SIZE_VALIDATOR':
return ArbitrarySizeValidator::validate(...);
case 'ARBITRARY_VALUE_VALIDATOR':
return ArbitraryValueValidator::validate(...);
case 'INTEGER_VALIDATOR':
return IntegerValidator::validate(...);
case 'LENGTH_VALIDATOR':
return LengthValidator::validate(...);
case 'NUMBER_VALIDATOR':
return NumberValidator::validate(...);
case 'PERCENT_VALIDATOR':
return PercentValidator::validate(...);
case 'TSHIRT_SIZE_VALIDATOR':
return TshirtSizeValidator::validate(...);
}

return $config;
}
}
3 changes: 3 additions & 0 deletions Configuration/Caches.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Carbon_Eel_Tailwind:
frontend: Neos\Cache\Frontend\StringFrontend
backend: Neos\Cache\Backend\FileBackend
3 changes: 3 additions & 0 deletions Configuration/Settings.Carbon.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Carbon:
Eel:
tailwindMergeConfig: null
53 changes: 52 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,55 @@ Examples:

**Return** The merged string

#### Configuration

If you are using Tailwind CSS without any extra config, you can use the Eel helper right away. And stop reading here.

If you're using a custom Tailwind config, you may need to configure the Eel helper as well to merge classes properly.

By default it is configured in a way that you can still use it if all the following apply to your Tailwind config:

- Only using color names which don't clash with other Tailwind class names
- Only deviating by number values from number-based Tailwind classes
- Only using font-family classes which don't clash with default font-weight classes
- Sticking to default Tailwind config for everything else

If some of these points don't apply to you, you need to customize the configuration.

This is an example to add a custom font size of `very-large`:

```yaml
Carbon:
Eel:
tailwindMergeConfig:
classGroups:
'font-size':
- text: ['very-large']
```
You can also enable different [validators], to make everything easier. For instance, if you use the
[Tailwind OKLCH Plugin], you can set it like that:
```yaml
Carbon:
Eel:
tailwindMergeConfig:
classGroups:
'fill-lightness':
- 'fill-lightness-offset': ['INTEGER_VALIDATOR', 'ARBITRARY_NUMBER_VALIDATOR']
'border-lightness':
- 'border-lightness-offset': ['INTEGER_VALIDATOR', 'ARBITRARY_NUMBER_VALIDATOR']
'text-lightness':
- 'text-lightness-offset': ['INTEGER_VALIDATOR', 'ARBITRARY_NUMBER_VALIDATOR']
'bg-lightness':
- 'bg-lightness-offset': ['INTEGER_VALIDATOR', 'ARBITRARY_NUMBER_VALIDATOR']
```
If you want to use a certain validator, just change it to constant case and add it as a string.
So, for examle, if you want to use the `TshirtSizeValidator`, just add `TSHIRT_SIZE_VALIDATOR` to the list.

> The merge service uses a its own cache `Carbon_Eel_Tailwind`. Make sure to clear the cache when you are making changes to the configuration.

## AlpineJS Helper

### `AlpineJS.object(arg1, arg2, ..argN)`
Expand Down Expand Up @@ -682,4 +731,6 @@ Some of the Eel helpers were inspired and or copied from [punkt.de]
[`dateinterval` format]: https://www.php.net/manual/en/dateinterval.format.php
[`dateinterval`]: https://www.php.net/manual/de/class.dateinterval.php
[Tailwind CSS]: https://tailwindcss.com
[tailwind-merge-php]: https://github.com/YieldStudio/tailwind-merge-php
[tailwind-merge-php]: https://github.com/gehrisandro/tailwind-merge-php
[validators]: https://github.com/gehrisandro/tailwind-merge-php/tree/main/src/Validators
[tailwind oklch plugin]: https://github.com/MartijnCuppens/tailwindcss-oklch
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"php": "^8.1",
"neos/flow": "^7.0 || ^8.0 || ^9.0",
"behat/transliterator": "^1.2",
"yieldstudio/tailwind-merge-php": "^0.0.3",
"gehrisandro/tailwind-merge-php": "^1.0",
"matthiasmullie/minify": "^1.3"
},
"authors": [
Expand Down

0 comments on commit d138430

Please sign in to comment.