1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace Atk4\Ui\Js;
6:
7: /**
8: * Transparent mapper that will actually translate into JavaScript code. Used
9: * as a glue between Views and your actual JavaScript code.
10: *
11: * IMPORTANT: all methods in this class are prepended with '_', to avoid clashes with JS mapping
12: * IMPORTANT: extend first, see Jquery class for example
13: */
14: class JsChain extends JsExpression
15: {
16: public string $_library;
17:
18: /**
19: * This will represent constructor argument. If no arguments are set, then the library will be executed like this:.
20: * $.hello().
21: *
22: * If arguments are specified they are passed to constructor initializer:
23: * $('foo', 'bar').hello().
24: *
25: * @var list<mixed>
26: */
27: public $_constructorArgs = [];
28:
29: /**
30: * Call chain. All calls to this mapper will be recorded here. Property traversal
31: * is also stored here.
32: *
33: * $js->foo()->bar(1)->baz->test(['abc' => 123']);
34: * will be stored in $chain as
35: * [['foo'], ['bar', [1]], 'baz', ['test', ['abc => 123]]]
36: * will map into:
37: * $.foo().bar(1).baz.test({ abc: 123 ]);
38: *
39: * @var list<string|int|array{string, list<mixed>}>
40: */
41: public $_chain = [];
42:
43: public function __construct(string $library)
44: {
45: parent::__construct();
46:
47: $this->_library = $library;
48: }
49:
50: /**
51: * Records all calls to this chain returning itself every time.
52: *
53: * @param list<mixed> $args
54: *
55: * @return $this
56: */
57: public function __call(string $name, $args)
58: {
59: $this->_chain[] = [$name, $args];
60:
61: return $this;
62: }
63:
64: /**
65: * Allows you to use syntax like this.
66: *
67: * $js->offset()->top
68: * that maps into
69: * $.offset()->top
70: *
71: * @return $this
72: */
73: #[\Override]
74: public function &__get(string $name)
75: {
76: $this->_chain[] = $name;
77:
78: return $this;
79: }
80:
81: /**
82: * Renders JS chain arguments.
83: *
84: * @param list<mixed> $args
85: */
86: private function _renderArgs(array $args = []): string
87: {
88: return '('
89: . implode(', ', array_map(function ($arg) {
90: return $this->_jsEncode($arg);
91: }, $args))
92: . ')';
93: }
94:
95: #[\Override]
96: public function jsRender(): string
97: {
98: $res = $this->_library;
99:
100: if ($this->_constructorArgs) {
101: $res .= $this->_renderArgs($this->_constructorArgs);
102: }
103:
104: foreach ($this->_chain as $chain) {
105: $args = null;
106: if (is_int($chain)) {
107: $name = (string) $chain;
108: } elseif (is_string($chain)) {
109: $name = $chain;
110: } else {
111: $name = $chain[0];
112: $args = $chain[1];
113: }
114:
115: $res .= preg_match('~^(?!\d)\w+$~Du', $name) ? '.' . $name : '[' . $this->_jsEncode($name) . ']';
116: if ($args !== null) {
117: $res .= $this->_renderArgs($args);
118: }
119: }
120:
121: return $res;
122: }
123: }
124: