znt/lib/zin/core/on.class.php

431 lines
13 KiB
PHP

<?php
declare(strict_types=1);
/**
* The events binding class file of zin lib.
*
* @copyright Copyright 2023 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @author Hao Sun <sunhao@easycorp.ltd>
* @package zin
* @version $Id
* @link https://www.zentao.net
*/
namespace zin;
require_once __DIR__ . DS . 'zin.func.php';
require_once __DIR__ . DS . 'jscallback.class.php';
use zin\node;
/**
* Events binding class.
*/
class on extends jsCallback
{
/**
* The event name.
* 事件名称。
*
* @access public
* @var string
*/
public string $event;
/**
* As a delegate event selector。
* 作为委托事件选择器。
*
* @access public
* @var array
*/
public ?string $selector;
/**
* Whether is compatible mode.
* 是否为兼容模式。
*
* @access public
* @var bool
*/
public bool $compatible = false;
public array $options = array();
/**
* The constructor.
* 构造函数。
*
* @access public
* @param string $event Event name.
* @param string|null $selector As a delegate event selector.
* @param array $options Event options.
*/
public function __construct(string $event, ?string $selector = null, ?array $options = null)
{
parent::__construct('event', 'args');
$options = $options ? $options : array();
if(str_contains($event, '__'))
{
list($event, $flags) = explode('__', $event);
if(str_contains($flags, 'stop')) $options['stop'] = true;
if(str_contains($flags, 'prevent')) $options['prevent'] = true;
if(str_contains($flags, 'self')) $options['self'] = true;
}
$this->options = $options;
$this->selector = $selector;
$this->event = $event;
}
/**
* Set to prevent event default behavior.
* 设置阻止事件默认行为选项。
*
* @access public
* @param bool $prevent Whether prevent event default behavior.
* @return self
*/
public function prevent(bool $prevent = true): self
{
$this->options['prevent'] = $prevent;
return $this;
}
/**
* Set to stop event propagation.
* 设置停止事件冒泡选项。
*
* @access public
* @param bool $stop Whether stop event propagation.
* @return self
*/
public function stop(bool $stop = true): self
{
$this->options['stop'] = $stop;
return $this;
}
/**
* Set to only trigger event on self element.
* 设置只在自身元素上触发事件选项。
*
* @access public
* @param bool $self Whether only trigger event on self element.
* @return self
*/
public function self(bool $self = true): self
{
$this->options['self'] = $self;
return $this;
}
/**
* Set to only trigger event once.
* 设置只触发一次事件选项。
*
* @access public
* @param bool $once Whether only trigger event once.
* @return self
*/
public function once(bool $once = true): self
{
$this->options['once'] = $once;
return $this;
}
/**
* Set the selector.
* 设置选择器。
*
* @access public
* @param string $selector The selector.
* @return self
*/
public function selector(string $selector): self
{
$this->selector = $selector;
return $this;
}
/**
* Convert to js code.
* 转换为 js 代码。
*
* @access public
* @param string $joiner The joiner of each line.
* @return string
*/
public function toJS($joiner = "\n"): string
{
if($this->event === 'init') return parent::buildBody($joiner);
$options = $this->options;
$bindMethod = (isset($options['once']) && $options['once']) ? 'one' : 'on';
$selector = $this->selector ? (',' . json_encode($this->selector)) : '';
$callback = parent::toJS($joiner);
$events = array_map(function($event){return $event . '.zin.on';}, explode('_', $this->event));
$events = implode(' ', $events);
return "\$element.$bindMethod('$events'$selector,$callback);";
}
/**
* Build body code.
* 构建函数体代码。
*
* @access public
* @param string $joiner The joiner of each line.
* @return string
* @override
*/
public function buildBody(string $joiner = "\n"): string
{
$options = $this->options;
$self = isset($options['self']) ? $options['self'] : false;
$prevent = isset($options['prevent']) ? $options['prevent'] : false;
$stop = isset($options['stop']) ? $options['stop'] : false;
$arrow = $this->isArrowFunc;
$codes = array();
if(!$arrow) $codes[] = "const \$this = $(this);";
$codes[] = "const target = event.target;";
if($self && !$arrow) $codes[] = "if(target !== this) return;";
if($prevent) $codes[] = "event.preventDefault();";
if($stop) $codes[] = "event.stopPropagation();";
$codes[] = parent::buildBody($joiner);
return implode($joiner, $codes);
}
/**
* Apply to node.
* 应用到部件。
*
* @access public
* @param node $node The node instance.
* @param string $blockName The block name.
* @return void
* @override
*/
public function apply(node $node, string $blockName): void
{
if($this->compatible)
{
$options = $this->options;
$options['selector'] = $this->selector;
$options['handler'] = parent::buildBody('');
$node->bindEvent($this->event, $options);
return;
}
$node->bindEvent($this->event, $this);
}
/**
* Bind the "change" event to widget.
* 绑定 "change" 事件到部件。
*
* @access public
* @param null|string|jsCallback $selectorOrCallback
* @param null|array|string $handlerOrOptions
* @return on
* @static
*/
public static function change(null|string|jsCallback $selectorOrCallback = null, null|array|string|jsCallback $handlerOrOptions = null): on
{
return static::bind('change', $selectorOrCallback, $handlerOrOptions);
}
/**
* Bind the "click" event to widget.
* 绑定 "click" 事件到部件。
*
* @access public
* @param null|string|jsCallback $selectorOrCallback
* @param null|array|string $handlerOrOptions
* @return on
* @static
*/
public static function click(null|string|jsCallback $selectorOrCallback = null, null|array|string|jsCallback $handlerOrOptions = null): on
{
return static::bind('click', $selectorOrCallback, $handlerOrOptions);
}
/**
* Bind the "dbclick" event to widget.
* 绑定 "dbclick" 事件到部件。
*
* @access public
* @param null|string|jsCallback $selectorOrCallback
* @param null|array|string $handlerOrOptions
* @return on
* @static
*/
public static function dbclick(null|string|jsCallback $selectorOrCallback = null, null|array|string|jsCallback $handlerOrOptions = null): on
{
return static::bind('dbclick', $selectorOrCallback, $handlerOrOptions);
}
/**
* Bind the "focus" event to widget.
* 绑定 "focus" 事件到部件。
*
* @access public
* @param null|string|jsCallback $selectorOrCallback
* @param null|array|string $handlerOrOptions
* @return on
* @static
*/
public static function mouseenter(null|string|jsCallback $selectorOrCallback = null, null|array|string|jsCallback $handlerOrOptions = null): on
{
return static::bind('mouseenter', $selectorOrCallback, $handlerOrOptions);
}
/**
* Bind the "mouseleave" event to widget.
* 绑定 "mouseleave" 事件到部件。
*
* @access public
* @param null|string|jsCallback $selectorOrCallback
* @param null|array|string $handlerOrOptions
* @return on
* @static
*/
public static function mouseleave(null|string|jsCallback $selectorOrCallback = null, null|array|string|jsCallback $handlerOrOptions = null): on
{
return static::bind('mouseleave', $selectorOrCallback, $handlerOrOptions);
}
/**
* Bind the "focus" event to widget.
* 绑定 "focus" 事件到部件。
*
* @access public
* @param null|string|jsCallback $selectorOrCallback
* @param null|array|string $handlerOrOptions
* @return on
* @static
*/
public static function inited(null|string|jsCallback $selectorOrCallback = null, null|array|string|jsCallback $handlerOrOptions = null): on
{
return static::bind('inited', $selectorOrCallback, $handlerOrOptions);
}
/**
* Bind the "focus" event to widget.
* 绑定 "focus" 事件到部件。
*
* @access public
* @param null|string|jsCallback $selectorOrCallback
* @param null|array|string $handlerOrOptions
* @return on
* @static
*/
public static function bind(string $event, null|string|jsCallback $selectorOrCallback = null, null|array|string|jsCallback $handlerOrOptions = null): on
{
if($selectorOrCallback instanceof jsCallback)
{
return static::fromCallback($selectorOrCallback, $event, null, $handlerOrOptions);
}
if($handlerOrOptions instanceof jsCallback)
{
return static::fromCallback($handlerOrOptions, $event, $selectorOrCallback);
}
return on($event, $selectorOrCallback, $handlerOrOptions);
}
/**
* Create on instance from callback.
* 从回调创建 on 实例。
*
* @access public
* @param jsCallback $callback
* @param string $event
* @param string $selector
* @param array $options
* @return on
* @static
*/
public static function fromCallback(jsCallback $callback, string $event, ?string $selector = null, ?array $options = null): on
{
if(!($callback instanceof on))
{
$on = new on($event, $selector, $options);
$on->appendCode($callback->buildBody());
$on->args(...$callback->funcArgs);
$on->name($callback->funcName);
return $on;
}
$callback->event = $event;
if(!is_null($selector)) $callback->selector = $selector;
if(!is_null($options)) $callback->options = array_merge($callback->options, $options);
return $callback;
}
/**
* Bind event to widget with magic method.
* 绑定事件到部件,使用魔术方法。
*
* @access public
* @param string $event
* @param array $args
* @return on
* @static
*/
public static function __callStatic(string $event, array $args)
{
list($selectorOrCallback, $options) = array_merge($args, array(null, null));
return static::bind($event, $selectorOrCallback, $options);
}
}
/**
* Add event listener to widget element.
*
* @param string $event
* @param null|string $selectorOrHandler
* @param null|array|string $handlerOrOptions
* @return on
*/
function on(string $event, string|null $selectorOrHandler = null, null|array|string $handlerOrOptions = null): on
{
$options = array();
$selector = null;
$handler = null;
if(is_string($selectorOrHandler))
{
if(is_string($handlerOrOptions))
{
$selector = $selectorOrHandler;
$handler = $handlerOrOptions;
}
else
{
if(str_contains('.#[', $selectorOrHandler[0])) $selector = $selectorOrHandler;
else $handler = $selectorOrHandler;
}
}
if(is_array($handlerOrOptions)) $options = array_merge($options, $handlerOrOptions);
if(str_contains($event, '__'))
{
list($event, $flags) = explode('__', $event);
if(str_contains($flags, 'stop')) $options['stop'] = true;
if(str_contains($flags, 'prevent')) $options['prevent'] = true;
if(str_contains($flags, 'self')) $options['self'] = true;
}
$on = new on($event, $selector, $options);
if(!is_null($handler))
{
$on->compatible = true;
$on->appendCode($handler);
}
return $on;
}