src/Js/JsChain.php
<?php
declare(strict_types=1);
namespace Atk4\Ui\Js;
/**
* Transparent mapper that will actually translate into JavaScript code. Used
* as a glue between Views and your actual JavaScript code.
*
* IMPORTANT: all methods in this class are prepended with '_', to avoid clashes with JS mapping
* IMPORTANT: extend first, see Jquery class for example
*/
class JsChain extends JsExpression
{
public string $_library;
/**
* This will represent constructor argument. If no arguments are set, then the library will be executed like this:.
* $.hello().
*
* If arguments are specified they are passed to constructor initializer:
* $('foo', 'bar').hello().
*
* @var list<mixed>
*/
public $_constructorArgs = [];
/**
* Call chain. All calls to this mapper will be recorded here. Property traversal
* is also stored here.
*
* $js->foo()->bar(1)->baz->test(['abc' => 123']);
* will be stored in $chain as
* [['foo'], ['bar', [1]], 'baz', ['test', ['abc => 123]]]
* will map into:
* $.foo().bar(1).baz.test({ abc: 123 ]);
*
* @var list<string|int|array{string, list<mixed>}>
*/
public $_chain = [];
public function __construct(string $library)
{
parent::__construct();
$this->_library = $library;
}
/**
* Records all calls to this chain returning itself every time.
*
* @param list<mixed> $args
*
* @return $this
*/
public function __call(string $name, $args)
{
$this->_chain[] = [$name, $args];
return $this;
}
/**
* Allows you to use syntax like this.
*
* $js->offset()->top
* that maps into
* $.offset()->top
*
* @return $this
*/
#[\Override]
public function &__get(string $name)
{
$this->_chain[] = $name;
return $this;
}
/**
* Renders JS chain arguments.
*
* @param list<mixed> $args
*/
private function _renderArgs(array $args = []): string
{
return '('
. implode(', ', array_map(function ($arg) {
return $this->_jsEncode($arg);
}, $args))
. ')';
}
#[\Override]
public function jsRender(): string
{
$res = $this->_library;
if ($this->_constructorArgs) {
$res .= $this->_renderArgs($this->_constructorArgs);
}
foreach ($this->_chain as $chain) {
$args = null;
if (is_int($chain)) {
$name = (string) $chain;
} elseif (is_string($chain)) {
$name = $chain;
} else {
$name = $chain[0];
$args = $chain[1];
}
$res .= preg_match('~^(?!\d)\w+$~Du', $name) ? '.' . $name : '[' . $this->_jsEncode($name) . ']';
if ($args !== null) {
$res .= $this->_renderArgs($args);
}
}
return $res;
}
}