Skip to content

Commit a7981cc

Browse files
committed
update
1 parent d51b488 commit a7981cc

File tree

5 files changed

+293
-1
lines changed

5 files changed

+293
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ $form = E::form([
8484
E::field(E::password('password'), E::label('Password')),
8585
E::field(E::submit('Login')),
8686
]);
87-
$form->render(true);
87+
$form->render();
8888
```
8989

9090
And the output will be form in the familiar Bulma style:

src/Bulma/SelectOrType.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace MintyPHP\Form\Bulma;
4+
5+
use DOMElement;
6+
use MintyPHP\Form\SelectOrType as Base;
7+
8+
class SelectOrType extends Base
9+
{
10+
public function setError(string $message): void
11+
{
12+
if ($message) {
13+
$this->addClass('is-danger');
14+
} else {
15+
$this->removeClass('is-danger');
16+
}
17+
}
18+
19+
public function renderDom(\DOMDocument $doc): \DOMElement
20+
{
21+
$wrapper = $doc->createElement('div');
22+
$classes = array_merge(['select'], $this->classes);
23+
$wrapper->setAttribute('class', implode(' ', $classes));
24+
$select = parent::renderDom($doc);
25+
$select->removeAttribute('class');
26+
$wrapper->appendChild($select);
27+
return $wrapper;
28+
}
29+
}

src/Elements.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,25 @@ public static function select(string $name = '', array $options = []): Select
166166
return $select;
167167
}
168168

169+
/**
170+
* @param array<string|int, string> $options
171+
*/
172+
public static function selectOrType(string $name = '', array $options = [], string $typeCaption = ''): SelectOrType
173+
{
174+
/** @var SelectOrType */
175+
$select = self::create('SelectOrType');
176+
if ($name) {
177+
$select->name($name);
178+
}
179+
if ($options) {
180+
$select->options($options);
181+
}
182+
if ($typeCaption) {
183+
$select->typeCaption($typeCaption);
184+
}
185+
return $select;
186+
}
187+
169188
/**
170189
* @param array<string, string> $options
171190
*/

src/SelectOrType.php

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
<?php
2+
3+
namespace MintyPHP\Form;
4+
5+
use DOMElement;
6+
use MintyPHP\Form\Validator\Validator;
7+
8+
class SelectOrType implements Control
9+
{
10+
use HtmlElement;
11+
12+
protected string $name = '';
13+
/** @var array<string|int,string> $options */
14+
protected array $options = [];
15+
/** @var array<string|int,string> $originalOptions */
16+
protected array $originalOptions = [];
17+
protected string $value = '';
18+
protected string $placeholder = '';
19+
protected string $typeCaption = 'Type a value ...';
20+
21+
protected bool $disabled = false;
22+
protected bool $readonly = false;
23+
protected bool $required = false;
24+
protected bool $autofocus = false;
25+
protected string $autocomplete = '';
26+
27+
protected bool $inputMode = false;
28+
29+
public function __construct()
30+
{
31+
$this->tag('select');
32+
}
33+
34+
public function name(string $name): self
35+
{
36+
$this->name = $name;
37+
return $this;
38+
}
39+
40+
public function getName(): string
41+
{
42+
return $this->name;
43+
}
44+
45+
public function disabled(): self
46+
{
47+
$this->disabled = true;
48+
return $this;
49+
}
50+
51+
public function readonly(): self
52+
{
53+
$this->readonly = true;
54+
return $this;
55+
}
56+
57+
public function required(): self
58+
{
59+
$this->required = true;
60+
return $this;
61+
}
62+
63+
public function autofocus(): self
64+
{
65+
$this->autofocus = true;
66+
return $this;
67+
}
68+
69+
public function autocomplete(string $value): self
70+
{
71+
$this->autocomplete = $value;
72+
return $this;
73+
}
74+
75+
/**
76+
* @param array<string|int,string> $options
77+
*/
78+
public function options(array $options): self
79+
{
80+
$this->options = $options;
81+
if (!isset($this->options[''])) {
82+
$this->options = ['' => '...'] + $this->options; // Add empty option if not present
83+
}
84+
$this->originalOptions = $this->options;
85+
$this->options = $this->options + ['!type!' => $this->typeCaption];
86+
return $this;
87+
}
88+
89+
public function value(string $value): self
90+
{
91+
$this->value = $value;
92+
$this->options = $this->originalOptions;
93+
if ($this->value && !in_array($this->value, $this->options)) {
94+
$this->options[$this->value] = $this->value;
95+
}
96+
$this->options = $this->options + ['!type!' => $this->typeCaption];
97+
return $this;
98+
}
99+
100+
public function placeholder(string $placeholder): self
101+
{
102+
$this->placeholder = $placeholder;
103+
return $this;
104+
}
105+
106+
public function typeCaption(string $typeCaption): self
107+
{
108+
$this->typeCaption = $typeCaption;
109+
$this->options['!type!'] = $this->typeCaption;
110+
return $this;
111+
}
112+
113+
/**
114+
* @param array<string, string|null> $data
115+
*/
116+
public function fill(array $data): void
117+
{
118+
$this->value($data[$this->name] ?? '');
119+
}
120+
121+
/**
122+
* @return array<string, string|string[]|null> $data
123+
*/
124+
public function extract(bool $withNulls = false): array
125+
{
126+
if (!$this->name) {
127+
return [];
128+
}
129+
if ($this->disabled) {
130+
return [];
131+
}
132+
$value = trim($this->values[0] ?? '');
133+
if ($withNulls) {
134+
$value = strlen($value) ? $value : null;
135+
}
136+
return [$this->name => $value];
137+
}
138+
139+
public function validate(Validator $validator): string
140+
{
141+
if (!$this->required && !$this->value) {
142+
return '';
143+
}
144+
if (!in_array($this->value, $this->options)) {
145+
return 'Invalid option value';
146+
}
147+
return $validator->validate($this->value);
148+
}
149+
150+
public function setError(string $message): void {}
151+
152+
public function renderDom(\DOMDocument $doc): \DOMElement
153+
{
154+
$select = $this->renderElement($doc);
155+
$select->setAttribute('name', $this->name);
156+
if ($this->required) {
157+
$select->setAttribute('required', 'required');
158+
}
159+
$i = 0;
160+
foreach ($this->options as $key => $value) {
161+
$option = $doc->createElement('option', $value);
162+
$option->setAttribute('value', strval($key));
163+
if ($key == $this->value) {
164+
$option->setAttribute('selected', 'selected');
165+
}
166+
$select->appendChild($option);
167+
$i += 1;
168+
if ($i == count($this->originalOptions)) {
169+
$hr = $doc->createElement('hr');
170+
$select->appendChild($hr);
171+
}
172+
}
173+
$select->setAttribute('onchange', "var last = this.options[this.options.length - 1]; var hasPrevious = last.previousSibling.nodeName == 'HR'; if (this.options.length - 1 == this.selectedIndex) { var str = prompt(last.text,last.previousSibling.text); if (str) { if (hasPrevious) { opt = document.createElement('option'); this.insertBefore(opt, last); } else { opt = last.previousSibling; } opt.value = opt.text = str; this.selectedIndex -= 1; } else { this.selectedIndex = this.dataset.lastIndex; } } this.dataset.lastIndex = this.selectedIndex;");
174+
return $select;
175+
}
176+
}

tests/Forms/DifferentFormTest.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace MintyPHP\Tests\Forms;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
use MintyPHP\Form\Form;
8+
use MintyPHP\Form\Elements as E;
9+
use MintyPHP\Form\Validator\Validators as V;
10+
11+
class DifferentFormTest extends TestCase
12+
{
13+
private function createForm(string $style): Form
14+
{
15+
$sources = [
16+
'ad' => 'Ad',
17+
'blog' => 'Blog',
18+
'magazine' => 'Magazine',
19+
'newspaper' => 'Newspaper',
20+
];
21+
E::$style = $style;
22+
return E::form([
23+
E::field(E::selectOrType('source', $sources, 'Different ...'), E::label('How did you find us?'), [V::required('Field cannot be empty')]),
24+
]);
25+
}
26+
27+
public function testRenderForm(): void
28+
{
29+
$form = $this->createForm('none');
30+
$lines = [
31+
'<div>',
32+
' <label for="source">How did you find us?</label>',
33+
' <select id="source" name="source" onchange="var last = this.options[this.options.length - 1]; var hasPrevious = last.previousSibling.nodeName == \'HR\'; if (this.options.length - 1 == this.selectedIndex) { var str = prompt(last.text,last.previousSibling.text); if (str) { if (hasPrevious) { opt = document.createElement(\'option\'); this.insertBefore(opt, last); } else { opt = last.previousSibling; } opt.value = opt.text = str; this.selectedIndex -= 1; } else { this.selectedIndex = this.dataset.lastIndex; } } this.dataset.lastIndex = this.selectedIndex;">',
34+
' <option value="" selected="selected">...</option>',
35+
' <option value="ad">Ad</option>',
36+
' <option value="blog">Blog</option>',
37+
' <option value="magazine">Magazine</option>',
38+
' <option value="newspaper">Newspaper</option>',
39+
' <hr/>',
40+
' <option value="!type!">Different ...</option>',
41+
' </select>',
42+
'</div>',
43+
];
44+
$this->assertEquals(implode("\n", $lines), $form->toString(false, false));
45+
}
46+
47+
public function testRenderBulma(): void
48+
{
49+
$form = $this->createForm('bulma');
50+
$lines = [
51+
'<div class="field">',
52+
' <label class="label" for="source">How did you find us?</label>',
53+
' <div class="select">',
54+
' <select id="source" name="source" onchange="var last = this.options[this.options.length - 1]; var hasPrevious = last.previousSibling.nodeName == \'HR\'; if (this.options.length - 1 == this.selectedIndex) { var str = prompt(last.text,last.previousSibling.text); if (str) { if (hasPrevious) { opt = document.createElement(\'option\'); this.insertBefore(opt, last); } else { opt = last.previousSibling; } opt.value = opt.text = str; this.selectedIndex -= 1; } else { this.selectedIndex = this.dataset.lastIndex; } } this.dataset.lastIndex = this.selectedIndex;">',
55+
' <option value="" selected="selected">...</option>',
56+
' <option value="ad">Ad</option>',
57+
' <option value="blog">Blog</option>',
58+
' <option value="magazine">Magazine</option>',
59+
' <option value="newspaper">Newspaper</option>',
60+
' <hr/>',
61+
' <option value="!type!">Different ...</option>',
62+
' </select>',
63+
' </div>',
64+
'</div>',
65+
];
66+
$this->assertEquals(implode("\n", $lines), $form->toString(false, false));
67+
}
68+
}

0 commit comments

Comments
 (0)