Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow radio form control to be unchecked #2063

Merged
merged 13 commits into from
Jun 5, 2023
12 changes: 6 additions & 6 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ parameters:
# TODO fix contravariance for View::set() method
-
path: 'src/Console.php'
message: '~^Parameter #1 \$fx \(Closure\(\$this\(Atk4\\Ui\\Console\)\): void\) of method Atk4\\Ui\\Console::set\(\) should be contravariant with parameter \$arg1 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
message: '~^Parameter #1 \$fx \(Closure\(\$this\): void\) of method Atk4\\Ui\\Console::set\(\) should be contravariant with parameter \$arg1 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
-
path: 'src/Console.php'
message: '~^Parameter #2 \$event \(bool\|string\) of method Atk4\\Ui\\Console::set\(\) should be contravariant with parameter \$arg2 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
Expand All @@ -233,16 +233,16 @@ parameters:
message: '~^Parameter #2 \$ignore \(\*NEVER\*\) of method Atk4\\Ui\\Form\\Control::set\(\) should be compatible with parameter \$arg2 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
-
path: 'src/Form/Control/Calendar.php'
message: '~^Parameter #1 \$expr \(Atk4\\Ui\\Js\\JsExpressionable\) of method Atk4\\Ui\\Form\\Control\\Calendar::onChange\(\) should be contravariant with parameter \$expr \(array{Closure\(Atk4\\Ui\\Js\\Jquery, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): Atk4\\Ui\\Js\\JsExpressionable\|Atk4\\Ui\\View\|string\|void}\|Atk4\\Ui\\Js\\JsExpressionable\|\(Closure\(Atk4\\Ui\\Js\\Jquery, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): Atk4\\Ui\\Js\\JsExpressionable\|Atk4\\Ui\\View\|string\|void\)\) of method Atk4\\Ui\\Form\\Control::onChange\(\)$~'
message: '~^Parameter #1 \$expr \(Atk4\\Ui\\Js\\JsExpressionable\) of method Atk4\\Ui\\Form\\Control\\Calendar::onChange\(\) should be contravariant with parameter \$expr \(array\{Closure\(Atk4\\Ui\\Js\\Jquery, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): \(Atk4\\Ui\\Js\\JsExpressionable\|Atk4\\Ui\\View\|string\|void\)\}\|Atk4\\Ui\\Js\\JsExpressionable\|\(Closure\(Atk4\\Ui\\Js\\Jquery, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): \(Atk4\\Ui\\Js\\JsExpressionable\|Atk4\\Ui\\View\|string\|void\)\)\) of method Atk4\\Ui\\Form\\Control::onChange\(\)$~'
-
path: 'src/Form/Control/Upload.php'
message: '~^Parameter #1 \$fileId \(string\) of method Atk4\\Ui\\Form\\Control\\Upload::set\(\) should be contravariant with parameter \$value \(mixed\) of method Atk4\\Ui\\Form\\Control::set\(\)$~'
-
path: 'src/JsCallback.php'
message: '~^Parameter #1 \$fx \(Closure\(Atk4\\Ui\\Js\\Jquery, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): Atk4\\Ui\\Js\\JsExpressionable\|Atk4\\Ui\\View\|string\|void\) of method Atk4\\Ui\\JsCallback::set\(\) should be contravariant with parameter \$fx \(Closure\(mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): mixed\) of method Atk4\\Ui\\Callback::set\(\)$~'
message: '~^Parameter #1 \$fx \(Closure\(Atk4\\Ui\\Js\\Jquery, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): \(Atk4\\Ui\\Js\\JsExpressionable\|Atk4\\Ui\\View\|string\|void\)\) of method Atk4\\Ui\\JsCallback::set\(\) should be contravariant with parameter \$fx \(Closure\(mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): mixed\) of method Atk4\\Ui\\Callback::set\(\)$~'
-
path: 'src/Loader.php'
message: '~^Parameter #1 \$fx \(Closure\(\$this\(Atk4\\Ui\\Loader\)\): void\) of method Atk4\\Ui\\Loader::set\(\) should be contravariant with parameter \$arg1 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
message: '~^Parameter #1 \$fx \(Closure\(\$this\): void\) of method Atk4\\Ui\\Loader::set\(\) should be contravariant with parameter \$arg1 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
-
path: 'src/Loader.php'
message: '~^Parameter #2 \$ignore \(\*NEVER\*\) of method Atk4\\Ui\\Loader::set\(\) should be compatible with parameter \$arg2 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
Expand All @@ -260,10 +260,10 @@ parameters:
message: '~^Parameter #2 \$ignore \(\*NEVER\*\) of method Atk4\\Ui\\Popup::set\(\) should be compatible with parameter \$arg2 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
-
path: 'src/VirtualPage.php'
message: '~^Parameter #1 \$fx \(Closure\(\$this\(Atk4\\Ui\\VirtualPage\), mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): void\) of method Atk4\\Ui\\VirtualPage::set\(\) should be contravariant with parameter \$arg1 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
message: '~^Parameter #1 \$fx \(Closure\(\$this, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): void\) of method Atk4\\Ui\\VirtualPage::set\(\) should be contravariant with parameter \$arg1 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
-
path: 'src/VirtualPage.php'
message: '~^Parameter #1 \$fx of method Atk4\\Ui\\Callback::set\(\) expects \(Closure\(mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): void\)\|null, Closure\(\$this\(Atk4\\Ui\\VirtualPage\), mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): void given.$~'
message: '~^Parameter #1 \$fx of method Atk4\\Ui\\Callback::set\(\) expects \(Closure\(mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): void\)\|null, Closure\(\$this, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed\): void given\.$~'
-
path: 'src/VirtualPage.php'
message: '~^Parameter #2 \$fxArgs \(array\) of method Atk4\\Ui\\VirtualPage::set\(\) should be contravariant with parameter \$arg2 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
Expand Down
6 changes: 2 additions & 4 deletions src/Form/Control.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,12 @@ class Control extends View
public $hint;

/**
* Is input field disabled?
* Disabled input fields are not editable and will not be submitted.
* Disabled field is not editable and will not be submitted.
*/
public bool $disabled = false;

/**
* Is input field read only?
* Read only input fields are not editable, but will be submitted.
* Read-only field is not editable, but will be submitted.
*/
public bool $readOnly = false;

Expand Down
7 changes: 3 additions & 4 deletions src/Form/Control/Checkbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,12 @@ protected function renderView(): void

$this->content = null;

if ($this->readOnly) {
$this->addClass('read-only');
}

if ($this->disabled) {
$this->addClass('disabled');
$this->template->dangerouslySetHtml('disabled', 'disabled="disabled"');
} elseif ($this->readOnly) {
$this->addClass('read-only');
$this->template->dangerouslySetHtml('disabled', 'readonly="readonly"');
}

$this->js(true)->checkbox();
Expand Down
44 changes: 21 additions & 23 deletions src/Form/Control/Dropdown.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
namespace Atk4\Ui\Form\Control;

use Atk4\Ui\HtmlTemplate;
use Atk4\Ui\Js\Jquery;
use Atk4\Ui\Js\JsExpression;
use Atk4\Ui\Js\JsExpressionable;
use Atk4\Ui\Js\JsFunction;

class Dropdown extends Input
{
public $ui = 'dropdown fluid search selection';
public $defaultTemplate = 'form/control/dropdown.html';

public string $inputType = 'hidden';
Expand All @@ -33,16 +33,6 @@ class Dropdown extends Input
/** @var string The string to set as an empty values. */
public $empty = "\u{00a0}"; // Unicode NBSP

/**
* The icon to display at the dropdown menu.
* The template default is set to: 'dropdown'.
* Note: dropdown icon is show on the right side of the menu
* while other icon are usually display on the left side.
*
* @var string|null
*/
public $dropIcon;

/** @var array Dropdown options as per Fomantic-UI dropdown options. */
public $dropdownOptions = [];

Expand Down Expand Up @@ -175,12 +165,23 @@ public function setDropdownOptions($options): void
$this->dropdownOptions = array_merge($this->dropdownOptions, $options);
}

/**
* @param bool|string $when
* @param JsExpressionable $action
*
* @return Jquery
*/
protected function jsDropdown($when = false, $action = null): JsExpressionable
{
return $this->js($when, $action, 'div.ui.dropdown:has(> #' . $this->name . '_input)');
}

/**
* Render JS for dropdown.
*/
protected function jsRenderDropdown(): JsExpressionable
{
return $this->js(true)->dropdown($this->dropdownOptions);
return $this->jsDropdown(true)->dropdown($this->dropdownOptions);
}

/**
Expand Down Expand Up @@ -220,31 +221,28 @@ protected function htmlRenderValue(): void
protected function renderView(): void
{
if ($this->multiple) {
$this->addClass('multiple');
$this->template->dangerouslySetHtml('multipleClass', 'multiple');
}

if ($this->readOnly || $this->disabled) {
$this->setDropdownOption('allowTab', false);
$this->removeClass('search');
if ($this->multiple) {
$this->js(true)->find('a i.delete.icon')->attr('class', 'disabled');
$this->jsDropdown(true)->find('a i.delete.icon')->attr('class', 'disabled');
}
}

if ($this->disabled) {
$this->addClass('disabled');
}
$this->template->set('disabledClass', 'disabled');
$this->template->dangerouslySetHtml('disabled', 'disabled="disabled"');
} elseif ($this->readOnly) {
$this->template->set('disabledClass', 'read-only');
$this->template->dangerouslySetHtml('disabled', 'readonly="readonly"');

if ($this->readOnly) {
$this->setDropdownOption('allowTab', false);
$this->setDropdownOption('onShow', new JsFunction([], [new JsExpression('return false')]));
}

if ($this->dropIcon) {
$this->template->trySet('DropIcon', $this->dropIcon);
}

$this->template->trySet('DefaultText', $this->empty);
$this->template->set('DefaultText', $this->empty);

$this->htmlRenderValue();
$this->jsRenderDropdown();
Expand Down
8 changes: 4 additions & 4 deletions src/Form/Control/DropdownCascade.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ protected function init(): void
$expr = [
function (Jquery $j) use ($cascadeFromValue) {
return new JsBlock([
$this->js()->dropdown('change values', $this->getNewValues($cascadeFromValue)),
$this->js()->removeClass('loading'),
$this->jsDropdown()->dropdown('change values', $this->getNewValues($cascadeFromValue)),
$this->jsDropdown()->removeClass('loading'),
]);
},
$this->js()->dropdown('clear'),
$this->js()->addClass('loading'),
$this->jsDropdown()->dropdown('clear'),
$this->jsDropdown()->addClass('loading'),
];

$this->cascadeFrom->onChange($expr, ['args' => [$this->cascadeFrom->name => $this->cascadeFrom->jsInput()->val()]]);
Expand Down
2 changes: 1 addition & 1 deletion src/Form/Control/Input.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ public function getInput()
'placeholder' => $this->inputType !== 'hidden' ? $this->placeholder : false,
'id' => $this->name . '_input',
'value' => $this->getValue(),
'readonly' => $this->readOnly && $this->inputType !== 'hidden',
'disabled' => $this->disabled && $this->inputType !== 'hidden',
'readonly' => $this->readOnly && $this->inputType !== 'hidden' && !$this->disabled,
], $this->inputAttr));
}

Expand Down
29 changes: 20 additions & 9 deletions src/Form/Control/Lookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Atk4\Ui\Js\Jquery;
use Atk4\Ui\Js\JsBlock;
use Atk4\Ui\Js\JsExpression;
use Atk4\Ui\Js\JsExpressionable;
use Atk4\Ui\Js\JsFunction;
use Atk4\Ui\Js\JsModal;
use Atk4\Ui\Js\JsToast;
Expand Down Expand Up @@ -130,7 +131,6 @@ protected function init(): void
parent::init();

$this->template->set([
'inputId' => $this->name . '-ac',
'placeholder' => $this->placeholder,
]);

Expand All @@ -142,6 +142,17 @@ protected function init(): void
});
}

/**
* @param bool|string $when
* @param JsExpressionable $action
*
* @return Jquery
*/
protected function jsDropdown($when = false, $action = null): JsExpressionable
{
return $this->js($when, $action, 'div.ui.dropdown:has(> #' . $this->name . '_input)');
}

/**
* Returns URL which would respond with first 50 matching records.
*/
Expand Down Expand Up @@ -261,7 +272,7 @@ protected function initQuickNewRecord(): void
$res->addStatement((new Jquery())->closest('.atk-modal')->modal('hide'));

$row = $this->renderRow($form->model);
$chain = new Jquery('#' . $this->name . '-ac');
$chain = $this->jsDropdown();
$chain->dropdown('set value', $row['value'])->dropdown('set text', $row['title']);
$res->addStatement($chain);

Expand Down Expand Up @@ -352,17 +363,17 @@ protected function renderView(): void
}

if ($this->disabled) {
$this->settings['allowTab'] = false;

$this->template->dangerouslySetHtml('disabled', 'disabled="disabled"');
$this->template->set('disabledClass', 'disabled');
}
$this->template->dangerouslySetHtml('disabled', 'disabled="disabled"');

$this->settings['allowTab'] = false;
} elseif ($this->readOnly) {
$this->template->set('disabledClass', 'read-only');
$this->template->dangerouslySetHtml('disabled', 'readonly="readonly"');

if ($this->readOnly) {
$this->settings['allowTab'] = false;
$this->settings['apiSettings'] = null;
$this->settings['onShow'] = new JsFunction([], [new JsExpression('return false')]);
$this->template->dangerouslySetHtml('readonly', 'readonly="readonly"');
}

if ($this->dependency) {
Expand All @@ -371,7 +382,7 @@ protected function renderView(): void
], $this->apiConfig['data'] ?? []);
}

$chain = new Jquery('#' . $this->name . '-ac');
$chain = $this->jsDropdown();

$this->initDropdown($chain);

Expand Down
14 changes: 10 additions & 4 deletions src/Form/Control/Radio.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,21 @@ protected function renderView(): void
$this->lister->setModel($this->model);

$this->lister->onHook(Lister::HOOK_BEFORE_ROW, function (Lister $lister) use ($value) {
if ($this->readOnly) {
$lister->tRow->dangerouslySetHtml('disabled', $value !== (string) $lister->model->getId() ? 'disabled="disabled"' : '');
} elseif ($this->disabled) {
if ($this->disabled) {
$lister->tRow->dangerouslySetHtml('disabledClass', 'disabled');
$lister->tRow->dangerouslySetHtml('disabled', 'disabled="disabled"');
} elseif ($this->readOnly) {
$lister->tRow->dangerouslySetHtml('disabledClass', 'read-only');
$lister->tRow->dangerouslySetHtml('disabled', 'readonly="readonly"');
}

$lister->tRow->dangerouslySetHtml('checked', $value === (string) $lister->model->getId() ? 'checked="checked"' : '');
$lister->tRow->dangerouslySetHtml('checked', $lister->model->compare($lister->model->idField, $value) ? 'checked="checked"' : '');
});

$this->js(true, null, '.ui.checkbox.radio')->checkbox([
'uncheckable' => !$this->entityField || ($this->entityField->getField()->nullable || !$this->entityField->getField()->required),
]);

parent::renderView();
}

Expand Down
2 changes: 1 addition & 1 deletion src/Form/Control/Textarea.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public function getInput()
'rows' => $this->rows,
'placeholder' => $this->placeholder,
'id' => $this->name . '_input',
'readonly' => $this->readOnly,
'disabled' => $this->disabled,
'readonly' => $this->readOnly && !$this->disabled,
], $this->inputAttr), $this->getValue() ?? '');
}
}
30 changes: 18 additions & 12 deletions src/Form/Control/Upload.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,11 @@ protected function init(): void

$this->cb = JsCallback::addTo($this);

if (!$this->action) {
$this->action = new Button(['icon' => 'upload', 'class.disabled' => $this->disabled || $this->readOnly]);
if ($this->action === null) {
$this->action = new Button([
'icon' => 'upload',
'class.disabled' => $this->disabled || $this->readOnly,
]);
}
}

Expand Down Expand Up @@ -123,10 +126,8 @@ public function setFileId($id): void

/**
* Add a JS action to be returned to server on callback.
*
* @param JsExpressionable $action
*/
public function addJsAction($action): void
public function addJsAction(JsExpressionable $action): void
{
$this->jsActions[] = $action;
}
Expand Down Expand Up @@ -162,7 +163,10 @@ public function onUpload(\Closure $fx): void
$this->setInput($fileId);
}

$this->addJsAction($fx(...$postFiles));
$jsRes = $fx(...$postFiles);
if ($jsRes !== null) { // @phpstan-ignore-line https://github.com/phpstan/phpstan/issues/9388
$this->addJsAction($jsRes);
}

if (count($postFiles) > 0 && reset($postFiles)['error'] === 0) {
$this->addJsAction(
Expand All @@ -186,7 +190,11 @@ public function onDelete(\Closure $fx): void
if (($_POST['fUploadAction'] ?? null) === self::DELETE_ACTION) {
$this->cb->set(function () use ($fx) {
$fileId = $_POST['fUploadId'];
$this->addJsAction($fx($fileId));

$jsRes = $fx($fileId);
if ($jsRes !== null) { // @phpstan-ignore-line https://github.com/phpstan/phpstan/issues/9388
$this->addJsAction($jsRes);
}

return new JsBlock($this->jsActions);
});
Expand All @@ -195,10 +203,6 @@ public function onDelete(\Closure $fx): void

protected function renderView(): void
{
// need before parent rendering.
if ($this->disabled) {
$this->addClass('disabled');
}
parent::renderView();

if ($this->cb->canTerminate()) {
Expand All @@ -222,8 +226,10 @@ protected function renderView(): void
$this->template->dangerouslySetHtml('multiple', 'multiple="multiple"');
}

$this->template->set('placeholderReadonly', $this->disabled ? 'disabled="disabled"' : 'readonly="readonly"');

if ($this->placeholder) {
$this->template->trySet('PlaceHolder', $this->placeholder);
$this->template->set('Placeholder', $this->placeholder);
}

$this->js(true)->atkFileUpload([
Expand Down
Loading