1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace Atk4\Ui;
6:
7: use Atk4\Ui\Js\JsChain;
8: use Atk4\Ui\Js\JsExpressionable;
9:
10: class JsSortable extends JsCallback
11: {
12: /** @var string The HTML element that contains others element for reordering. */
13: public $container = 'tbody';
14:
15: /** @var string The HTML element inside the container that need reordering. */
16: public $draggable = 'tr';
17:
18: /**
19: * The data label set as data-label attribute on the HTML element.
20: * The callback will send source parameter on the moved element using this attribute.
21: * default to data-id.
22: *
23: * If the data-{label} attribute is not set for each list element, then the $_POST['order']
24: * value will be empty. Only origIndex and newIndex will be sent in callback request.
25: *
26: * @var string
27: */
28: public $dataLabel = 'id';
29:
30: /**
31: * The CSS class name of the handle element for dragging purpose.
32: * If null, the entire element become the dragging handle.
33: *
34: * @var string|null
35: */
36: public $handleClass;
37:
38: /** @var bool Whether callback will be fire automatically or not. */
39: public $autoFireCb = true;
40:
41: /** @var View|null The View that need reordering. */
42: public $view;
43:
44: #[\Override]
45: protected function init(): void
46: {
47: parent::init();
48:
49: if (!$this->view) {
50: $this->view = $this->getOwner();
51: }
52: $this->getApp()->requireJs($this->getApp()->cdn['atk'] . '/external/@shopify/draggable/build/umd/index.min.js');
53:
54: $this->view->js(true)->atkJsSortable([
55: 'url' => $this->getJsUrl(),
56: 'urlOptions' => $this->args,
57: 'container' => $this->container,
58: 'draggable' => $this->draggable,
59: 'handleClass' => $this->handleClass,
60: 'dataLabel' => $this->dataLabel,
61: 'autoFireCb' => $this->autoFireCb,
62: ]);
63: }
64:
65: /**
66: * Callback when container has been reorder.
67: *
68: * @param \Closure(list<string>, string, int, int): (JsExpressionable|View|string|void) $fx
69: */
70: public function onReorder(\Closure $fx): void
71: {
72: $this->set(function () use ($fx) {
73: $sortOrders = explode(',', $this->getApp()->getRequestPostParam('order'));
74: $source = $this->getApp()->getRequestPostParam('source');
75: $newIndex = (int) $this->getApp()->getRequestPostParam('newIndex');
76: $origIndex = (int) $this->getApp()->getRequestPostParam('origIndex');
77:
78: return $fx($sortOrders, $source, $newIndex, $origIndex);
79: });
80: }
81:
82: /**
83: * Return JS action to retrieve order.
84: *
85: * @param array<string, string>|null $urlOptions
86: *
87: * @return JsChain
88: */
89: public function jsSendSortOrders($urlOptions = null): JsExpressionable
90: {
91: return $this->view->js()->atkJsSortable('sendSortOrders', [$urlOptions]);
92: }
93: }
94: