Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TASK: Render menu prototypes without fluid #4623

Merged
merged 5 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
namespace Neos\ContentRepository\Core\SharedModel\Node;

use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\Nodes;

/**
* An immutable collection of NodeAggregateIds, indexed by their value
Expand Down Expand Up @@ -74,13 +75,10 @@ public static function fromJsonString(string $jsonString): self
return self::fromArray(\json_decode($jsonString, true));
}

/**
* @param Node[] $nodes
*/
public static function fromNodes(array $nodes): self
public static function fromNodes(Nodes $nodes): self
{
return self::fromArray(
array_map(fn(Node $node) => $node->nodeAggregateId, $nodes)
array_map(fn(Node $node) => $node->nodeAggregateId, iterator_to_array($nodes))
);
}

Expand All @@ -92,6 +90,11 @@ public function merge(self $other): self
));
}

public function contain(NodeAggregateId $nodeAggregateId): bool
Sebobo marked this conversation as resolved.
Show resolved Hide resolved
{
return array_key_exists($nodeAggregateId->value, $this->nodeAggregateIds);
}

/**
* @return array<string,NodeAggregateId>
*/
Expand Down
6 changes: 6 additions & 0 deletions Neos.Fusion/Classes/Service/RenderAttributesTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ protected function renderAttributes(iterable $attributes, bool $allowEmpty = tru
foreach ($attributeValue as $attributeValuePart) {
if ($attributeValuePart instanceof \Stringable) {
$attributeValuePart = $attributeValuePart->__toString();
} elseif ($attributeValuePart instanceof \BackedEnum) {
$attributeValuePart = $attributeValuePart->value;
}
$joinedAttributeValue .= match (gettype($attributeValuePart)) {
'boolean', 'NULL' => '',
Expand All @@ -50,6 +52,10 @@ protected function renderAttributes(iterable $attributes, bool $allowEmpty = tru
};
}
$attributeValue = trim($joinedAttributeValue);
} elseif ($attributeValue instanceof \Stringable) {
$attributeValue = $attributeValue->__toString();
} elseif ($attributeValue instanceof \BackedEnum) {
$attributeValue = $attributeValue->value;
}
$encodedAttributeName = htmlspecialchars((string)$attributeName, ENT_COMPAT, 'UTF-8', false);
if ($attributeValue === true || $attributeValue === '') {
Expand Down
32 changes: 31 additions & 1 deletion Neos.Fusion/Tests/Unit/Service/HtmlAugmenterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,14 @@ public function __toString() {
return "casted value";
}
}
enum BackedStringEnum: string {
case Example = "enum value";
}
');

/** @noinspection PhpUndefinedClassInspection */
$mockObject = new \ClassWithToStringMethod();
$mockEnum = \BackedStringEnum::Example;

return [
// object values with __toString method
Expand All @@ -61,7 +66,15 @@ public function __toString() {
'allowEmpty' => true,
'expectedResult' => '<div object="casted value"></div>'
],

// object values with BackendEnum value
[
'html' => '',
'attributes' => ['enum' => $mockEnum],
'fallbackTagName' => null,
'exclusiveAttributes' => null,
'allowEmpty' => true,
'expectedResult' => '<div enum="enum value"></div>'
],
// empty source
[
'html' => '',
Expand Down Expand Up @@ -344,6 +357,23 @@ public function __toString() {
'allowEmpty' => false,
'expectedResult' => '<p data-stringable="casted value">Stringable attribute</p>',
],
// Adding of Enum attributes
[
'html' => '<p>Enum attribute</p>',
'attributes' => ['data-enum' => $mockEnum],
'fallbackTagName' => null,
'exclusiveAttributes' => null,
'allowEmpty' => true,
'expectedResult' => '<p data-enum="enum value">Enum attribute</p>',
],
[
'html' => '<p>Enum attribute</p>',
'attributes' => ['data-enum' => $mockEnum],
'fallbackTagName' => null,
'exclusiveAttributes' => null,
'allowEmpty' => false,
'expectedResult' => '<p data-enum="enum value">Enum attribute</p>',
],
// Adding of array attributes
[
'html' => '<p>Array attribute</p>',
Expand Down
31 changes: 31 additions & 0 deletions Neos.Neos/Classes/Fusion/AbstractMenuItemsImplementation.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ abstract class AbstractMenuItemsImplementation extends AbstractFusionObject
*/
protected $renderHiddenInIndex;

/**
* Internal cache for the calculateItemStates property.
*
* @var boolean
*/
protected $calculateItemStates;
mhsdesign marked this conversation as resolved.
Show resolved Hide resolved

/**
* Rootline of all nodes from the current node to the site root node, keys are depth of nodes.
*
Expand All @@ -70,6 +77,19 @@ abstract class AbstractMenuItemsImplementation extends AbstractFusionObject
#[Flow\Inject]
protected ContentRepositoryRegistry $contentRepositoryRegistry;

/**
* Whether the active/current state of menu items is calculated on the server side.
* This has an effect on performance and caching
*/
public function isCalculateItemStatesEnabled(): bool
{
if ($this->calculateItemStates === null) {
$this->calculateItemStates = (bool)$this->fusionValue('calculateItemStates');
}

return $this->calculateItemStates;
}

/**
* Should nodes that have "hiddenInIndex" set still be visible in this menu.
*
Expand Down Expand Up @@ -169,4 +189,15 @@ protected function getCurrentNodeRootline(): array

return $this->currentNodeRootline;
}

protected function buildUri(Node $node): string
mficzel marked this conversation as resolved.
Show resolved Hide resolved
{
$this->runtime->pushContextArray([
'itemNode' => $node,
'documentNode' => $node,
]);
$uri = $this->runtime->render($this->path . '/itemUriRenderer');
$this->runtime->popContext();
return $uri;
}
}
26 changes: 26 additions & 0 deletions Neos.Neos/Classes/Fusion/DimensionMenuItem.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Neos\Neos\Fusion;

use Neos\ContentRepository\Core\Projection\ContentGraph\Node;

/**
* A menu item for dimension menus
* Compared to the default {@see MenuItem} it has no `menuLevel` property, but one for the `targetDimensions`
*/
final readonly class DimensionMenuItem
{
/**
* @param array<string,mixed>|null $targetDimensions
*/
public function __construct(
public ?Node $node,
public ?MenuItemState $state = null,
public ?string $label = null,
public ?array $targetDimensions = null,
public ?string $uri = null
) {
}
}
33 changes: 17 additions & 16 deletions Neos.Neos/Classes/Fusion/DimensionsMenuItemsImplementation.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function getDimension(): array

/**
* Builds the array of Menu items for this variant menu
* @return array<int,array<string,mixed>>
* @return array<int,DimensionMenuItem>
*/
protected function buildItems(): array
{
Expand Down Expand Up @@ -86,12 +86,13 @@ protected function buildItems(): array
$metadata = $this->determineMetadata($dimensionSpacePoint, $dimensionMenuItemsImplementationInternals);

if ($variant === null || !$this->isNodeHidden($variant)) {
$menuItems[] = [
'node' => $variant,
'state' => $this->calculateItemState($variant),
'label' => $this->determineLabel($variant, $metadata),
'targetDimensions' => $metadata
];
$menuItems[] = new DimensionMenuItem(
$variant,
$this->isCalculateItemStatesEnabled() ? $this->calculateItemState($variant) : null,
$this->determineLabel($variant, $metadata),
$metadata,
$variant ? $this->buildUri($variant) : null
);
}
}
}
Expand All @@ -100,15 +101,15 @@ protected function buildItems(): array
if ($contentDimensionIdentifierToLimitTo && $valuesToRestrictTo) {
$order = array_flip($valuesToRestrictTo);
usort($menuItems, function (
array $menuItemA,
array $menuItemB
DimensionMenuItem $menuItemA,
DimensionMenuItem $menuItemB
) use (
$order,
$contentDimensionIdentifierToLimitTo
) {
return (int)$order[$menuItemA['node']?->subgraphIdentity->dimensionSpacePoint->getCoordinate(
return (int)$order[$menuItemA->node?->subgraphIdentity->dimensionSpacePoint->getCoordinate(
$contentDimensionIdentifierToLimitTo
)] <=> (int)$order[$menuItemB['node']?->subgraphIdentity->dimensionSpacePoint->getCoordinate(
)] <=> (int)$order[$menuItemB->node?->subgraphIdentity->dimensionSpacePoint->getCoordinate(
$contentDimensionIdentifierToLimitTo
)];
});
Expand Down Expand Up @@ -218,19 +219,19 @@ protected function determineLabel(?Node $variant = null, array $metadata = []):
}
}

protected function calculateItemState(?Node $variant = null): string
protected function calculateItemState(?Node $variant = null): MenuItemState
{
if (is_null($variant)) {
return self::STATE_ABSENT;
return MenuItemState::ABSENT;
}

if ($variant === $this->currentNode) {
return self::STATE_CURRENT;
return MenuItemState::CURRENT;
}

return self::STATE_NORMAL;
return MenuItemState::NORMAL;
}


/**
* In some cases generalization of the other dimension values is feasible
* to find a dimension space point in which a variant can be resolved
Expand Down
57 changes: 8 additions & 49 deletions Neos.Neos/Classes/Fusion/MenuItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,60 +9,19 @@
/**
* A menu item
*/
final class MenuItem
final readonly class MenuItem
{
protected Node $node;

protected ?MenuItemState $state;

protected ?string $label;

protected int $menuLevel;

/**
* @var array<int,MenuItem>
*/
protected array $children;

protected ?string $uri;

/**
* @param array<int,MenuItem> $children
*/
public function __construct(
Node $node,
?MenuItemState $state = null,
?string $label = null,
int $menuLevel = 1,
array $children = [],
string $uri = null
public Node $node,
public ?MenuItemState $state = null,
public ?string $label = null,
public int $menuLevel = 1,
public array $children = [],
public ?string $uri = null
) {
$this->node = $node;
$this->state = $state;
$this->label = $label;
$this->menuLevel = $menuLevel;
$this->children = $children;
$this->uri = $uri;
}

public function getNode(): Node
{
return $this->node;
}

public function getState(): ?MenuItemState
{
return $this->state;
}

public function getLabel(): ?string
{
return $this->label;
}

public function getMenuLevel(): int
{
return $this->menuLevel;
}

/**
Expand All @@ -75,7 +34,7 @@ public function getChildren(): array

/**
* @return array<int,MenuItem>
* @deprecated Use getChildren instead
* @deprecated Use children instead
*/
public function getSubItems(): array
{
Expand Down
53 changes: 5 additions & 48 deletions Neos.Neos/Classes/Fusion/MenuItemState.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,10 @@
/**
* The menu item state value object
*/
final class MenuItemState
enum MenuItemState: string
{
public const STATE_NORMAL = 'normal';
public const STATE_CURRENT = 'current';
public const STATE_ACTIVE = 'active';
public const STATE_ABSENT = 'absent';

/**
* @var string
*/
protected $state;

/**
* @param string $state
* @throws Exception\InvalidMenuItemStateException
*/
public function __construct(string $state)
{
if (
$state !== self::STATE_NORMAL
&& $state !== self::STATE_CURRENT
&& $state !== self::STATE_ACTIVE
&& $state !== self::STATE_ABSENT
) {
throw new Exception\InvalidMenuItemStateException(
'"' . $state . '" is no valid menu item state',
1519668881
);
}

$this->state = $state;
}


/**
* @return MenuItemState
*/
public static function normal(): MenuItemState
{
return new MenuItemState(self::STATE_NORMAL);
}

/**
* @return string
*/
public function __toString(): string
{
return $this->state;
}
case NORMAL = 'normal';
case CURRENT = 'current';
case ACTIVE = 'active';
case ABSENT = 'absent';
}
Loading
Loading