Skip to content

Commit

Permalink
Refactored Stimulus ViewHelpers (#189)
Browse files Browse the repository at this point in the history
  • Loading branch information
svenpet90 authored Apr 1, 2023
1 parent 07e4f32 commit fd9c38f
Show file tree
Hide file tree
Showing 11 changed files with 476 additions and 218 deletions.
180 changes: 165 additions & 15 deletions Classes/ViewHelpers/Stimulus/AbstractViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,182 @@

namespace Ssch\Typo3Encore\ViewHelpers\Stimulus;

use Ssch\Typo3Encore\ViewHelpers\Stimulus\Dto\StimulusActionsDto;
use Ssch\Typo3Encore\ViewHelpers\Stimulus\Dto\StimulusControllersDto;
use Ssch\Typo3Encore\ViewHelpers\Stimulus\Dto\StimulusTargetsDto;

abstract class AbstractViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper
{
protected $escapeOutput = false;

/**
* Normalize a Stimulus controller name into its HTML equivalent (no special character and / becomes --).
*
* @see https://stimulus.hotwired.dev/reference/controllers
* @param string|array $controllerName the Stimulus controller name
* @param array $controllerValues array of controller values
* @param array $controllerClasses array of controller CSS classes
*/
protected function normalizeControllerName(string $str): string
{
return (string) preg_replace('/^@/', '', str_replace('_', '-', str_replace('/', '--', $str)));
public function renderStimulusController(
$controllerName,
array $controllerValues = [],
array $controllerClasses = []
): StimulusControllersDto {
$dto = new StimulusControllersDto();

if (\is_array($controllerName)) {
trigger_deprecation(
'symfony/webpack-encore-bundle',
'v1.15.0',
'Passing an array as first argument of stimulus_controller() is deprecated.',
E_USER_DEPRECATED
);

if ([] !== $controllerValues || [] !== $controllerClasses) {
throw new \InvalidArgumentException(
'You cannot pass an array to the first and second/third argument of stimulus_controller(): check the documentation.'
);
}

$data = $controllerName;

foreach ($data as $controller => $values) {
$dto->addController($controller, $values);
}

return $dto;
}

$dto->addController($controllerName, $controllerValues, $controllerClasses);

return $dto;
}

/**
* @param string|array $controllerName the Stimulus controller name
* @param array $parameters Parameters to pass to the action. Optional.
*/
public function renderStimulusAction(
$controllerName,
string $actionName = null,
string $eventName = null,
array $parameters = []
): StimulusActionsDto {
$dto = new StimulusActionsDto();

if (\is_array($controllerName)) {
trigger_deprecation(
'symfony/webpack-encore-bundle',
'v1.15.0',
'Passing an array as first argument of stimulus_action() is deprecated.',
E_USER_DEPRECATED
);

if (null !== $actionName || null !== $eventName || [] !== $parameters) {
throw new \InvalidArgumentException(
'You cannot pass a string to the second or third argument nor an array to the fourth argument while passing an array to the first argument of stimulus_action(): check the documentation.'
);
}

$data = $controllerName;

foreach ($data as $controller => $controllerActions) {
if (\is_string($controllerActions)) {
$controllerActions = [[$controllerActions]];
}

foreach ($controllerActions as $possibleEventName => $controllerAction) {
if (\is_string($possibleEventName) && \is_string($controllerAction)) {
$controllerAction = [
$possibleEventName => $controllerAction,
];
} elseif (\is_string($controllerAction)) {
$controllerAction = [$controllerAction];
}

foreach ($controllerAction as $event => $action) {
$dto->addAction($controller, $action, \is_string($event) ? $event : null);
}
}
}

return $dto;
}

$dto->addAction($controllerName, (string) $actionName, $eventName, $parameters);

return $dto;
}

public function appendStimulusController(
StimulusControllersDto $dto,
string $controllerName,
array $controllerValues = [],
array $controllerClasses = []
): StimulusControllersDto {
$dto->addController($controllerName, $controllerValues, $controllerClasses);

return $dto;
}

/**
* @param array $parameters Parameters to pass to the action. Optional.
*/
public function appendStimulusAction(
StimulusActionsDto $dto,
string $controllerName,
string $actionName,
string $eventName = null,
array $parameters = []
): StimulusActionsDto {
$dto->addAction($controllerName, $actionName, $eventName, $parameters);

return $dto;
}

/**
* Normalize a Stimulus Value API key into its HTML equivalent ("kebab case"). Backport features from
* symfony/string.
*
* @see https://stimulus.hotwired.dev/reference/values
* @param string|array $controllerName the Stimulus controller name
* @param string|null $targetNames The space-separated list of target names if a string is passed to the 1st argument. Optional.
*/
protected function normalizeKeyName(string $str): string
public function renderStimulusTarget($controllerName, string $targetNames = null): StimulusTargetsDto
{
// Adapted from ByteString::camel
$str = ucfirst(str_replace(' ', '', ucwords((string) preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $str))));
$dto = new StimulusTargetsDto();
if (\is_array($controllerName)) {
trigger_deprecation(
'symfony/webpack-encore-bundle',
'v1.15.0',
'Passing an array as first argument of stimulus_target() is deprecated.',
E_USER_DEPRECATED
);

if (null !== $targetNames) {
throw new \InvalidArgumentException(
'You cannot pass a string to the second argument while passing an array to the first argument of stimulus_target(): check the documentation.'
);
}

$data = $controllerName;

foreach ($data as $controller => $targets) {
$dto->addTarget($controller, $targets);
}

return $dto;
}

$dto->addTarget($controllerName, $targetNames);

return $dto;
}

/**
* @param string $controllerName the Stimulus controller name
* @param string|null $targetNames The space-separated list of target names if a string is passed to the 1st argument. Optional.
*/
public function appendStimulusTarget(
StimulusTargetsDto $dto,
string $controllerName,
string $targetNames = null
): StimulusTargetsDto {
$dto->addTarget($controllerName, $targetNames);

// Adapted from ByteString::snake
return strtolower((string) preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1-\2', $str));
return $dto;
}
}
77 changes: 10 additions & 67 deletions Classes/ViewHelpers/Stimulus/ActionViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,91 +11,34 @@

namespace Ssch\Typo3Encore\ViewHelpers\Stimulus;

use InvalidArgumentException;

/**
* Copyright (c) 2004-2018 Fabien Potencier
*/
final class ActionViewHelper extends AbstractViewHelper
{
public function initializeArguments(): void
{
$this->registerArgument(
'dataOrControllerName',
'string|array',
'This can either be a map of controller names as keys set to their "values". Or this can be a string controller name and data is passed as the 2nd argument.',
true
);
$this->registerArgument('controllerName', 'string|array', 'The Stimulus controller name to render.', true);
$this->registerArgument(
'eventName',
'string',
'The action to trigger if a string is passed to the 1st argument. Optional.',
false
'The event to listen to trigger if a string is passed to the 1st argument. Optional.',
);
$this->registerArgument(
'actionName',
'string',
'The event to listen to trigger if a string is passed to the 1st argument. Optional.',
false
'The action to trigger if a string is passed to the 1st argument. Optional.'
);
$this->registerArgument('parameters', 'array', 'Parameters to pass to the action. Optional.', false, []);
}

public function render(): string
{
$dataOrControllerName = $this->arguments['dataOrControllerName'];
$eventName = $this->arguments['eventName'];
$actionName = $this->arguments['actionName'];

if (\is_string($dataOrControllerName)) {
$data = [
$dataOrControllerName => null === $eventName ? [[$actionName]] : [[
$eventName => $actionName,
]],
];
} else {
if ($actionName || $eventName) {
throw new InvalidArgumentException(
'You cannot pass a string to the second or third argument while passing an array to the first argument of stimulus_action(): check the documentation.'
);
}

$data = $dataOrControllerName;

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

$actions = [];

foreach ($data as $controllerName => $controllerActions) {
$controllerName = $this->normalizeControllerName($controllerName);

if (\is_string($controllerActions)) {
$controllerActions = [[$controllerActions]];
}

foreach ($controllerActions as $possibleEventName => $controllerAction) {
if (\is_string($possibleEventName) && \is_string($controllerAction)) {
$controllerAction = [
$possibleEventName => $controllerAction,
];
} elseif (\is_string($controllerAction)) {
$controllerAction = [$controllerAction];
}

foreach ($controllerAction as $internalEventName => $internalActionName) {
$action = $controllerName . '#' . $internalActionName;

if (\is_string($internalEventName)) {
$action = $internalEventName . '->' . $action;
}

$actions[] = $action;
}
}
}

return 'data-action="' . implode(' ', $actions) . '"';
return $this->renderStimulusAction(
$this->arguments['controllerName'],
$this->arguments['actionName'],
$this->arguments['eventName'],
$this->arguments['parameters']
)->__toString();
}
}
69 changes: 13 additions & 56 deletions Classes/ViewHelpers/Stimulus/ControllerViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,76 +11,33 @@

namespace Ssch\Typo3Encore\ViewHelpers\Stimulus;

use InvalidArgumentException;

final class ControllerViewHelper extends AbstractViewHelper
{
public function initializeArguments(): void
{
$this->registerArgument(
'dataOrControllerName',
'string|array',
'This can either be a map of controller names as keys set to their "values". Or this can be a string controller name and data is passed as the 2nd argument.',
true
);
$this->registerArgument('controllerName', 'string|array', 'The Stimulus controller name to render.', true);
$this->registerArgument(
'controllerValues',
'array',
'array of data if a string is passed to the 1st argument',
false,
[]
);
$this->registerArgument(
'controllerClasses',
'array',
'Array of classes to add to the controller',
false,
[]
);
}

public function render(): string
{
$dataOrControllerName = $this->arguments['dataOrControllerName'];
$controllerValues = $this->arguments['controllerValues'];

if (\is_string($dataOrControllerName)) {
$data = [
$dataOrControllerName => $controllerValues,
];
} else {
if ($controllerValues) {
throw new InvalidArgumentException(
'You cannot pass an array to the first and second argument of stimulus_controller(): check the documentation.'
);
}

$data = $dataOrControllerName;

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

$controllers = [];
$values = [];

foreach ($data as $controllerName => $controllerValue) {
$controllerName = $this->normalizeControllerName($controllerName);
$controllers[] = $controllerName;

foreach ($controllerValue as $key => $value) {
if (null === $value) {
continue;
}

if (! is_scalar($value)) {
$value = json_encode($value, JSON_THROW_ON_ERROR);
}

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

$key = $this->normalizeKeyName($key);

$values[] = 'data-' . $controllerName . '-' . $key . '-value="' . $value . '"';
}
}

return rtrim('data-controller="' . implode(' ', $controllers) . '" ' . implode(' ', $values));
return $this->renderStimulusController(
$this->arguments['controllerName'],
$this->arguments['controllerValues'],
$this->arguments['controllerClasses']
)->__toString();
}
}
Loading

0 comments on commit fd9c38f

Please sign in to comment.