Skip to content

Commit

Permalink
Merge pull request #432 from infinum/release/9.2.0
Browse files Browse the repository at this point in the history
9.2.0
  • Loading branch information
goranalkovic-infinum authored Oct 1, 2024
2 parents b502871 + 8d901d2 commit 965f23b
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 1 deletion.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file.

This projects adheres to [Semantic Versioning](https://semver.org/) and [Keep a CHANGELOG](https://keepachangelog.com/).

## [9.2.0]

### Added
- Introduced new, more flexible, and simpler to use `tailwindClasses` function. Replaces `getTwPart`, `getTwDynamicPart`, and `getTwClasses`.
- **Potentially breaking**: `twClassesEditor` is now appended to `twClasses`. If you need editor-only classes, you can now use the `twClassesEditorOnly` key. Editor-only classes replace `twClasses`, but will also have classes from `twClassesEditor`.
- **Potentially breaking**: `parts` key in manifest now supports specifying multiple parts just with a comma-separated string.
- You can now apply classes to multiple parts within one option or combination! Also work with responsive options.
- There are now (basic) warnings for misconfigurations of parts and options.

## [9.1.6]

### Fixed
Expand Down Expand Up @@ -663,6 +672,7 @@ Init setup

[Unreleased]: https://github.com/infinum/eightshift-libs/compare/main...HEAD

[9.2.0]: https://github.com/infinum/eightshift-libs/compare/9.1.6...9.2.0
[9.1.6]: https://github.com/infinum/eightshift-libs/compare/9.1.5...9.1.6
[9.1.5]: https://github.com/infinum/eightshift-libs/compare/9.1.4...9.1.5
[9.1.4]: https://github.com/infinum/eightshift-libs/compare/9.1.3...9.1.4
Expand Down
2 changes: 1 addition & 1 deletion src/Cli/CliHelpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ protected function getShortenCliPathOutput(string $path, string $ref = 'projectR
protected function getFolderItems(string $path): array
{
$output = \array_diff(\scandir($path), ['..', '.']);
$output = \array_values($output);
$output = \array_values($output); // @phpstan-ignore-line

return $output;
}
Expand Down
212 changes: 212 additions & 0 deletions src/Helpers/TailwindTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

namespace EightshiftLibs\Helpers;

use Exception;
use JsonException;

/**
* Class TailwindTrait Helper.
*/
Expand Down Expand Up @@ -48,6 +51,8 @@ public static function getTwBreakpoints($desktopFirst = false)
* @param array<mixed> $manifest Component/block manifest data.
* @param array<string> ...$custom Additional custom classes.
*
* @deprecated 9.2.0 Use `tailwindClasses` instead.
*
* @return string
*/
public static function getTwPart($part, $manifest, ...$custom)
Expand Down Expand Up @@ -75,6 +80,8 @@ public static function getTwPart($part, $manifest, ...$custom)
* @param array<mixed> $manifest Component/block manifest data.
* @param array<string> ...$custom Additional custom classes.
*
* @deprecated 9.2.0 Use `tailwindClasses` instead.
*
* @return string
*/
public static function getTwDynamicPart($part, $attributes, $manifest, ...$custom)
Expand Down Expand Up @@ -170,6 +177,8 @@ public static function getTwDynamicPart($part, $attributes, $manifest, ...$custo
* @param array<mixed> $manifest Component/block manifest data.
* @param array<string> ...$custom Additional custom classes.
*
* @deprecated 9.2.0 Use `tailwindClasses` instead.
*
* @return string
*/
public static function getTwClasses($attributes, $manifest, ...$custom)
Expand Down Expand Up @@ -289,4 +298,207 @@ public static function getTwClasses($attributes, $manifest, ...$custom)

return Helpers::classnames([$baseClasses, ...$mainClasses, ...$combinationClasses, ...$custom]);
}

/**
* Unifies the given input classes into a single string.
*
* Takes an array or string of CSS classes and unifies them into a single string,
* ensuring that there are no duplicate classes and that the classes are properly formatted.
*
* @param mixed $input The input classes to be unified. This can be a string or an array of strings.
*
* @return string The unified string of CSS classes.
*/
private static function unifyClasses($input): string
{
if (\is_array($input)) {
return Helpers::classnames($input);
}

return \trim($input);
}

/**
* Processes the given option for a specific part name.
*
* This method processes the option value for a given part name based on the provided definitions.
* It ensures that the option value is correctly handled according to the definitions.
*
* @param string $partName The name of the part for which the option is being processed.
* @param mixed $optionValue The value of the option to be processed.
* @param array<mixed> $defs The definitions that dictate how the option should be processed.
*
* @return string The processed option value.
*/
private static function processOption($partName, $optionValue, $defs): string
{
$optionClasses = [];

$isResponsive = $defs['responsive'] ?? false;
$itemPartName = isset($defs['part']) ? $defs['part'] : 'base';
$isSingleValue = isset($defs['twClasses']) || isset($defs['twClassesEditor']);

// Part checks.
if (!$isSingleValue && !isset($defs[$partName])) {
return '';
}

if ($isSingleValue && !\str_contains($itemPartName, $partName)) {
return '';
}

// Non-responsive options.
if (!$isResponsive) {
$rawValue = $defs['twClasses'][$optionValue] ?? $defs[$partName]['twClasses'][$optionValue] ?? '';

return self::unifyClasses($rawValue);
}

// Responsive options.
$breakpoints = \array_keys($optionValue);

if (\in_array('_desktopFirst', $breakpoints, true)) {
$breakpoints = \array_filter($breakpoints, fn($breakpoint) => $breakpoint !== '_desktopFirst');
}

foreach ($breakpoints as $breakpoint) {
$breakpointValue = $optionValue[$breakpoint];

if (!$breakpointValue) {
continue;
}

$rawValue = $defs['twClasses'][$breakpointValue] ?? $defs[$partName]['twClasses'][$breakpointValue] ?? '';
$rawClasses = self::unifyClasses($rawValue);

if ($breakpoint === '_default') {
$optionClasses[] = $rawClasses;

continue;
}

$splitClasses = \explode(' ', $rawClasses);
$splitClasses = \array_map(fn($cn) => empty($cn) ? null : "{$breakpoint}:{$cn}", $splitClasses);

$optionClasses = [...$optionClasses, ...$splitClasses];
}

return self::unifyClasses($optionClasses);
}

/**
* Processes the given combination for a specific part name.
*
* This method processes the combination value for a given part name based on the provided attributes and manifest.
* It ensures that the combination is correctly handled according to the attributes and manifest.
*
* @param string $partName The name of the part for which the combination is being processed.
* @param mixed $combo The combination value to be processed.
* @param array<mixed> $attributes The attributes that dictate how the combination should be processed.
* @param array<mixed> $manifest The manifest that provides additional context for processing the combination.
*
* @throws JsonException If the combination was not defined correctly.
*
* @return string The processed combination value.
*/
private static function processCombination($partName, $combo, $attributes, $manifest): string
{
$matches = true;

foreach ($combo['attributes'] as $attributeName => $allowedValue) {
$optionValue = Helpers::checkAttr($attributeName, $attributes, $manifest, true);

if (\is_bool($optionValue)) {
$optionValue = $optionValue ? 'true' : 'false';
}

if (\is_array($allowedValue) && !\in_array($optionValue, $allowedValue, true)) {
$matches = false;
break;
}

if ($optionValue !== $allowedValue) {
$matches = false;
break;
}
}

if (!$matches) {
return '';
}

$itemPartName = isset($combo['part']) ? $combo['part'] : 'base';
$isSingleValue = isset($combo['twClasses']) || isset($combo['twClassesEditor']);

if ($isSingleValue && !\str_contains($itemPartName, $partName)) {
return '';
}

$rawValue = $combo['output'][$partName]['twClasses'] ?? $combo['twClasses'] ?? '';

if (\is_array($rawValue) && !\array_is_list($rawValue)) {
throw new JsonException('Combination was not defined correctly. Please check the combination definition in the manifest.');
}

return self::unifyClasses($rawValue);
}

/**
* Get Tailwind classes for the given component/block.
*
* @param string $part Part to get classes for.
* @param array<mixed> $attributes Component/block attributes.
* @param array<mixed> $manifest Component/block manifest data.
* @param array<string> ...$custom Additional custom classes.
*
* @throws Exception If the part is not defined in the manifest.
*
* @return string
*/
public static function tailwindClasses($part, $attributes, $manifest, ...$custom): string
{
// If nothing is set, return custom classes as a fallback.
if (!$attributes || !$manifest || !isset($manifest['tailwind']) || \array_keys($manifest['tailwind']) === []) {
return $custom ? Helpers::classnames($custom) : ''; // @phpstan-ignore-line
}

$allParts = isset($manifest['tailwind']['parts']) ? ['base', ...\array_keys($manifest['tailwind']['parts'])] : ['base'];

$partName = 'base';

if (!empty($part) && isset($manifest['tailwind']['parts'][$part]) && \in_array($part, $allParts, true)) {
$partName = $part;
} elseif ($part !== 'base') {
throw new Exception("Part '{$part}' is not defined in the manifest.");
}

// Base classes.
$baseClasses = self::unifyClasses($manifest['tailwind']['parts'][$partName]['twClasses'] ?? $manifest['tailwind']['base']['twClasses'] ?? ['']);

// Option classes.
$options = $manifest['tailwind']['options'] ?? [];

$optionClasses = [];

foreach ($options as $attributeName => $defs) {
$optionValue = Helpers::checkAttr($attributeName, $attributes, $manifest, true);

if (\is_bool($optionValue)) {
$optionValue = $optionValue ? 'true' : 'false';
}

$optionClasses[] = self::processOption($partName, $optionValue, $defs);
}

// Combinations.
$combinations = $manifest['tailwind']['combinations'] ?? [];

$combinationClasses = [];

foreach ($combinations as $combo) {
$combinationClasses[] = self::processCombination($partName, $combo, $attributes, $manifest);
}

return Helpers::classnames([$baseClasses, ...$optionClasses, ...$combinationClasses, ...$custom]);
}
}

0 comments on commit 965f23b

Please sign in to comment.