Skip to content

Commit cb993ec

Browse files
authoredJun 5, 2023
Allow radio form control to be unchecked (#2063)
1 parent 6fee84e commit cb993ec

29 files changed

+142
-117
lines changed
 

‎phpstan.neon.dist

+6-6
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ parameters:
224224
# TODO fix contravariance for View::set() method
225225
-
226226
path: 'src/Console.php'
227-
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\(\)$~'
227+
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\(\)$~'
228228
-
229229
path: 'src/Console.php'
230230
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\(\)$~'
@@ -233,16 +233,16 @@ parameters:
233233
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\(\)$~'
234234
-
235235
path: 'src/Form/Control/Calendar.php'
236-
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\(\)$~'
236+
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\(\)$~'
237237
-
238238
path: 'src/Form/Control/Upload.php'
239239
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\(\)$~'
240240
-
241241
path: 'src/JsCallback.php'
242-
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\(\)$~'
242+
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\(\)$~'
243243
-
244244
path: 'src/Loader.php'
245-
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\(\)$~'
245+
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\(\)$~'
246246
-
247247
path: 'src/Loader.php'
248248
message: '~^Parameter #2 \$ignore \(\*NEVER\*\) of method Atk4\\Ui\\Loader::set\(\) should be compatible with parameter \$arg2 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
@@ -260,10 +260,10 @@ parameters:
260260
message: '~^Parameter #2 \$ignore \(\*NEVER\*\) of method Atk4\\Ui\\Popup::set\(\) should be compatible with parameter \$arg2 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'
261261
-
262262
path: 'src/VirtualPage.php'
263-
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\(\)$~'
263+
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\(\)$~'
264264
-
265265
path: 'src/VirtualPage.php'
266-
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.$~'
266+
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\.$~'
267267
-
268268
path: 'src/VirtualPage.php'
269269
message: '~^Parameter #2 \$fxArgs \(array\) of method Atk4\\Ui\\VirtualPage::set\(\) should be contravariant with parameter \$arg2 \(mixed\) of method Atk4\\Ui\\View::set\(\)$~'

‎src/Form/Control.php

+2-4
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,12 @@ class Control extends View
6464
public $hint;
6565

6666
/**
67-
* Is input field disabled?
68-
* Disabled input fields are not editable and will not be submitted.
67+
* Disabled field is not editable and will not be submitted.
6968
*/
7069
public bool $disabled = false;
7170

7271
/**
73-
* Is input field read only?
74-
* Read only input fields are not editable, but will be submitted.
72+
* Read-only field is not editable, but will be submitted.
7573
*/
7674
public bool $readOnly = false;
7775

‎src/Form/Control/Checkbox.php

+3-4
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,12 @@ protected function renderView(): void
6363

6464
$this->content = null;
6565

66-
if ($this->readOnly) {
67-
$this->addClass('read-only');
68-
}
69-
7066
if ($this->disabled) {
7167
$this->addClass('disabled');
7268
$this->template->dangerouslySetHtml('disabled', 'disabled="disabled"');
69+
} elseif ($this->readOnly) {
70+
$this->addClass('read-only');
71+
$this->template->dangerouslySetHtml('disabled', 'readonly="readonly"');
7372
}
7473

7574
$this->js(true)->checkbox();

‎src/Form/Control/Dropdown.php

+21-23
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
namespace Atk4\Ui\Form\Control;
66

77
use Atk4\Ui\HtmlTemplate;
8+
use Atk4\Ui\Js\Jquery;
89
use Atk4\Ui\Js\JsExpression;
910
use Atk4\Ui\Js\JsExpressionable;
1011
use Atk4\Ui\Js\JsFunction;
1112

1213
class Dropdown extends Input
1314
{
14-
public $ui = 'dropdown fluid search selection';
1515
public $defaultTemplate = 'form/control/dropdown.html';
1616

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

36-
/**
37-
* The icon to display at the dropdown menu.
38-
* The template default is set to: 'dropdown'.
39-
* Note: dropdown icon is show on the right side of the menu
40-
* while other icon are usually display on the left side.
41-
*
42-
* @var string|null
43-
*/
44-
public $dropIcon;
45-
4636
/** @var array Dropdown options as per Fomantic-UI dropdown options. */
4737
public $dropdownOptions = [];
4838

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

168+
/**
169+
* @param bool|string $when
170+
* @param JsExpressionable $action
171+
*
172+
* @return Jquery
173+
*/
174+
protected function jsDropdown($when = false, $action = null): JsExpressionable
175+
{
176+
return $this->js($when, $action, 'div.ui.dropdown:has(> #' . $this->name . '_input)');
177+
}
178+
178179
/**
179180
* Render JS for dropdown.
180181
*/
181182
protected function jsRenderDropdown(): JsExpressionable
182183
{
183-
return $this->js(true)->dropdown($this->dropdownOptions);
184+
return $this->jsDropdown(true)->dropdown($this->dropdownOptions);
184185
}
185186

186187
/**
@@ -220,31 +221,28 @@ protected function htmlRenderValue(): void
220221
protected function renderView(): void
221222
{
222223
if ($this->multiple) {
223-
$this->addClass('multiple');
224+
$this->template->dangerouslySetHtml('multipleClass', 'multiple');
224225
}
225226

226227
if ($this->readOnly || $this->disabled) {
227228
$this->setDropdownOption('allowTab', false);
228-
$this->removeClass('search');
229229
if ($this->multiple) {
230-
$this->js(true)->find('a i.delete.icon')->attr('class', 'disabled');
230+
$this->jsDropdown(true)->find('a i.delete.icon')->attr('class', 'disabled');
231231
}
232232
}
233233

234234
if ($this->disabled) {
235-
$this->addClass('disabled');
236-
}
235+
$this->template->set('disabledClass', 'disabled');
236+
$this->template->dangerouslySetHtml('disabled', 'disabled="disabled"');
237+
} elseif ($this->readOnly) {
238+
$this->template->set('disabledClass', 'read-only');
239+
$this->template->dangerouslySetHtml('disabled', 'readonly="readonly"');
237240

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

243-
if ($this->dropIcon) {
244-
$this->template->trySet('DropIcon', $this->dropIcon);
245-
}
246-
247-
$this->template->trySet('DefaultText', $this->empty);
245+
$this->template->set('DefaultText', $this->empty);
248246

249247
$this->htmlRenderValue();
250248
$this->jsRenderDropdown();

‎src/Form/Control/DropdownCascade.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ protected function init(): void
4343
$expr = [
4444
function (Jquery $j) use ($cascadeFromValue) {
4545
return new JsBlock([
46-
$this->js()->dropdown('change values', $this->getNewValues($cascadeFromValue)),
47-
$this->js()->removeClass('loading'),
46+
$this->jsDropdown()->dropdown('change values', $this->getNewValues($cascadeFromValue)),
47+
$this->jsDropdown()->removeClass('loading'),
4848
]);
4949
},
50-
$this->js()->dropdown('clear'),
51-
$this->js()->addClass('loading'),
50+
$this->jsDropdown()->dropdown('clear'),
51+
$this->jsDropdown()->addClass('loading'),
5252
];
5353

5454
$this->cascadeFrom->onChange($expr, ['args' => [$this->cascadeFrom->name => $this->cascadeFrom->jsInput()->val()]]);

‎src/Form/Control/Input.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ public function getInput()
110110
'placeholder' => $this->inputType !== 'hidden' ? $this->placeholder : false,
111111
'id' => $this->name . '_input',
112112
'value' => $this->getValue(),
113-
'readonly' => $this->readOnly && $this->inputType !== 'hidden',
114113
'disabled' => $this->disabled && $this->inputType !== 'hidden',
114+
'readonly' => $this->readOnly && $this->inputType !== 'hidden' && !$this->disabled,
115115
], $this->inputAttr));
116116
}
117117

‎src/Form/Control/Lookup.php

+20-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Atk4\Ui\Js\Jquery;
1414
use Atk4\Ui\Js\JsBlock;
1515
use Atk4\Ui\Js\JsExpression;
16+
use Atk4\Ui\Js\JsExpressionable;
1617
use Atk4\Ui\Js\JsFunction;
1718
use Atk4\Ui\Js\JsModal;
1819
use Atk4\Ui\Js\JsToast;
@@ -130,7 +131,6 @@ protected function init(): void
130131
parent::init();
131132

132133
$this->template->set([
133-
'inputId' => $this->name . '-ac',
134134
'placeholder' => $this->placeholder,
135135
]);
136136

@@ -142,6 +142,17 @@ protected function init(): void
142142
});
143143
}
144144

145+
/**
146+
* @param bool|string $when
147+
* @param JsExpressionable $action
148+
*
149+
* @return Jquery
150+
*/
151+
protected function jsDropdown($when = false, $action = null): JsExpressionable
152+
{
153+
return $this->js($when, $action, 'div.ui.dropdown:has(> #' . $this->name . '_input)');
154+
}
155+
145156
/**
146157
* Returns URL which would respond with first 50 matching records.
147158
*/
@@ -261,7 +272,7 @@ protected function initQuickNewRecord(): void
261272
$res->addStatement((new Jquery())->closest('.atk-modal')->modal('hide'));
262273

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

@@ -352,17 +363,17 @@ protected function renderView(): void
352363
}
353364

354365
if ($this->disabled) {
355-
$this->settings['allowTab'] = false;
356-
357-
$this->template->dangerouslySetHtml('disabled', 'disabled="disabled"');
358366
$this->template->set('disabledClass', 'disabled');
359-
}
367+
$this->template->dangerouslySetHtml('disabled', 'disabled="disabled"');
368+
369+
$this->settings['allowTab'] = false;
370+
} elseif ($this->readOnly) {
371+
$this->template->set('disabledClass', 'read-only');
372+
$this->template->dangerouslySetHtml('disabled', 'readonly="readonly"');
360373

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

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

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

376387
$this->initDropdown($chain);
377388

‎src/Form/Control/Radio.php

+10-4
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,21 @@ protected function renderView(): void
3939
$this->lister->setModel($this->model);
4040

4141
$this->lister->onHook(Lister::HOOK_BEFORE_ROW, function (Lister $lister) use ($value) {
42-
if ($this->readOnly) {
43-
$lister->tRow->dangerouslySetHtml('disabled', $value !== (string) $lister->model->getId() ? 'disabled="disabled"' : '');
44-
} elseif ($this->disabled) {
42+
if ($this->disabled) {
43+
$lister->tRow->dangerouslySetHtml('disabledClass', 'disabled');
4544
$lister->tRow->dangerouslySetHtml('disabled', 'disabled="disabled"');
45+
} elseif ($this->readOnly) {
46+
$lister->tRow->dangerouslySetHtml('disabledClass', 'read-only');
47+
$lister->tRow->dangerouslySetHtml('disabled', 'readonly="readonly"');
4648
}
4749

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

53+
$this->js(true, null, '.ui.checkbox.radio')->checkbox([
54+
'uncheckable' => !$this->entityField || ($this->entityField->getField()->nullable || !$this->entityField->getField()->required),
55+
]);
56+
5157
parent::renderView();
5258
}
5359

‎src/Form/Control/Textarea.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ public function getInput()
1616
'rows' => $this->rows,
1717
'placeholder' => $this->placeholder,
1818
'id' => $this->name . '_input',
19-
'readonly' => $this->readOnly,
2019
'disabled' => $this->disabled,
20+
'readonly' => $this->readOnly && !$this->disabled,
2121
], $this->inputAttr), $this->getValue() ?? '');
2222
}
2323
}

‎src/Form/Control/Upload.php

+18-12
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,11 @@ protected function init(): void
6565

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

68-
if (!$this->action) {
69-
$this->action = new Button(['icon' => 'upload', 'class.disabled' => $this->disabled || $this->readOnly]);
68+
if ($this->action === null) {
69+
$this->action = new Button([
70+
'icon' => 'upload',
71+
'class.disabled' => $this->disabled || $this->readOnly,
72+
]);
7073
}
7174
}
7275

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

124127
/**
125128
* Add a JS action to be returned to server on callback.
126-
*
127-
* @param JsExpressionable $action
128129
*/
129-
public function addJsAction($action): void
130+
public function addJsAction(JsExpressionable $action): void
130131
{
131132
$this->jsActions[] = $action;
132133
}
@@ -162,7 +163,10 @@ public function onUpload(\Closure $fx): void
162163
$this->setInput($fileId);
163164
}
164165

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

167171
if (count($postFiles) > 0 && reset($postFiles)['error'] === 0) {
168172
$this->addJsAction(
@@ -186,7 +190,11 @@ public function onDelete(\Closure $fx): void
186190
if (($_POST['fUploadAction'] ?? null) === self::DELETE_ACTION) {
187191
$this->cb->set(function () use ($fx) {
188192
$fileId = $_POST['fUploadId'];
189-
$this->addJsAction($fx($fileId));
193+
194+
$jsRes = $fx($fileId);
195+
if ($jsRes !== null) { // @phpstan-ignore-line https://github.com/phpstan/phpstan/issues/9388
196+
$this->addJsAction($jsRes);
197+
}
190198

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

196204
protected function renderView(): void
197205
{
198-
// need before parent rendering.
199-
if ($this->disabled) {
200-
$this->addClass('disabled');
201-
}
202206
parent::renderView();
203207

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

229+
$this->template->set('placeholderReadonly', $this->disabled ? 'disabled="disabled"' : 'readonly="readonly"');
230+
225231
if ($this->placeholder) {
226-
$this->template->trySet('PlaceHolder', $this->placeholder);
232+
$this->template->set('Placeholder', $this->placeholder);
227233
}
228234

229235
$this->js(true)->atkFileUpload([

0 commit comments

Comments
 (0)
Please sign in to comment.