Compare commits
53
Commits
8842ae4eaa
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00e5a0380a | ||
|
|
e093495909 | ||
|
|
9ddb93b847 | ||
|
|
363da77669 | ||
|
|
94903d6f05 | ||
|
|
3d412d6a46 | ||
|
|
ca2d71e3ed | ||
|
|
7e2d4c7dc2 | ||
|
|
2e3788a295 | ||
|
|
6e7a8f12fb | ||
|
|
a1c51bed10 | ||
|
|
1044e35946 | ||
|
|
6ccf07825a | ||
|
|
23f24fef50 | ||
|
|
33e77b5129 | ||
|
|
a34ebaf0df | ||
|
|
388b2a70f8 | ||
|
|
195586a078 | ||
|
|
5e9ad29f3c | ||
|
|
1991122c32 | ||
|
|
0a7b1cdc12 | ||
|
|
804deb69a2 | ||
|
|
328a4d282c | ||
|
|
fd4e30bd42 | ||
|
|
ea780c697e | ||
|
|
6aaa05b37d | ||
|
|
72940b3615 | ||
|
|
aba66479be | ||
|
|
77d05ae051 | ||
|
|
d984ddc540 | ||
|
|
5397ed4fae | ||
|
|
30ddd4c98b | ||
|
|
f00c98b7ff | ||
|
|
3986437800 | ||
|
|
9190987b53 | ||
|
|
cd3cb18665 | ||
|
|
3a930c13b1 | ||
|
|
bd501bce86 | ||
|
|
1f5d38a8b9 | ||
|
|
164a27d267 | ||
|
|
ceebc6783a | ||
|
|
83fee32ac0 | ||
|
|
97f1f67af1 | ||
|
|
362184f81f | ||
|
|
0d9db0470e | ||
|
|
e75d997b02 | ||
|
|
34828f642c | ||
|
|
6020fa300d | ||
|
|
7437bbfc73 | ||
|
|
95c30be9f8 | ||
|
|
d05a750b39 | ||
|
|
512af58411 | ||
|
|
fd73d8e614 |
No files matched your search
+10
-1
@@ -6,7 +6,7 @@
|
|||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Dave Mc Nicoll",
|
"name": "Dave Mc Nicoll",
|
||||||
"email": "mcndave@gmail.com"
|
"email": "info@mcnd.ca"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
@@ -22,5 +22,14 @@
|
|||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Picea\\Ui\\": "src/"
|
"Picea\\Ui\\": "src/"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"lean": {
|
||||||
|
"autoload": {
|
||||||
|
"definitions": [
|
||||||
|
"meta/definitions.php"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# Picea UI
|
||||||
|
|
||||||
|
Welcome to the `picea-ui` documentation.
|
||||||
|
|
||||||
|
This quick documentation will guide you through the use of the form system, every available
|
||||||
|
UI elements and how to add customs one and individual components.
|
||||||
|
|
||||||
|
Mostly everything you need to build and process form is available in this package.
|
||||||
|
|
||||||
|
The first part is most input callable as a widget :
|
||||||
|
|
||||||
|
```
|
||||||
|
{% ui:text "fullname", "John Doe" %}
|
||||||
|
```
|
||||||
|
|
||||||
|
Current input support is :
|
||||||
|
```php
|
||||||
|
- Input (string $source, array $attributes = [], array $options = [])
|
||||||
|
- Checkbox (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Color (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Date (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Datetime (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Email (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- File (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Hidden (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Image (string $source, array $attributes = [], array $options = [])
|
||||||
|
- Numeric (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Password (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Radio (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Range (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Search (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Select (string $name, array $list, mixed $value = null, array $attributes = [], bool $strictComparison = true)
|
||||||
|
- Tel (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Text (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Textarea (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Time (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Url (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
- Week (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
```
|
||||||
|
|
||||||
|
**[PICEA]** So, using this code:
|
||||||
|
|
||||||
|
```html
|
||||||
|
{% ui:form.post "user.save", current_url() %}
|
||||||
|
<div class="field fullname">
|
||||||
|
<label>Full name</label>
|
||||||
|
{% ui:text "fullname", $user->fullname, [ 'required' => 'required' ] %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field email">
|
||||||
|
<label>Email address</label>
|
||||||
|
{% ui:text "email", $user->email %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field job">
|
||||||
|
<label>Have a job</label>
|
||||||
|
<label>{% ui:radio "job", 'yes', [], [ 'value' => $entity->job ] %} Yes</label>
|
||||||
|
<label>{% ui:radio "job", 'no', [], [ 'value' => $entity->job ] %} No</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field role">
|
||||||
|
<label>Role</label>
|
||||||
|
{% ui:select "role", [ 'user' => "User", 'moderator' => "Moderator", 'admin' => "Admin" ], $user->role, [ 'required' => 'required' ] %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit">Save</button>
|
||||||
|
{% ui:endform %}
|
||||||
|
```
|
||||||
|
|
||||||
|
**[HTML]** Would render as such:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form class="ui-form" method="post" action="http://127.0.0.1/" enctype="multipart/form-data">
|
||||||
|
<input class="ui-hidden" type="hidden" name="picea-ui-form[user.save]" value="617fb40f1999db63fbd0b4f680a77750">
|
||||||
|
|
||||||
|
<div class="field fullname">
|
||||||
|
<label>Full name</label>
|
||||||
|
<input class="ui-text" type="text" value="" name="fullname" required="required">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field email">
|
||||||
|
<label>Email address</label>
|
||||||
|
<input class="ui-text" type="text" value="" name="email">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field job">
|
||||||
|
<label>Have a job</label>
|
||||||
|
<label><input class="ui-radio" type="radio" value="yes" name="job"> Yes</label>
|
||||||
|
<label><input class="ui-radio" type="radio" value="no" name="job"> No</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field role">
|
||||||
|
<label>Role</label>
|
||||||
|
<select class="ui-select" name="role" required="required">
|
||||||
|
<option value="user">User</option>
|
||||||
|
<option value="moderator">Moderator</option>
|
||||||
|
<option value="admin">Admin</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit">Save</button>
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use your forms in controller
|
||||||
|
|
||||||
|
Forms can be used to handle how your data is handled, from loading (get) to processing (post).
|
||||||
|
|
||||||
|
The process is as simple as defining a Form, implementing FormInterface :
|
||||||
|
|
||||||
|
```php
|
||||||
|
interface FormInterface
|
||||||
|
{
|
||||||
|
public function initialize(FormContextInterface $context) : void;
|
||||||
|
|
||||||
|
public function validate(FormContextInterface $context) : bool;
|
||||||
|
|
||||||
|
public function execute(FormContextInterface $context);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`initialize` is called directly on `form()` method call.
|
||||||
|
|
||||||
|
`validate` will be called if the request's method is either `post`, `put`, `patch` or `delete`, which implies **adding** / **editing** / **deleting**.
|
||||||
|
|
||||||
|
`execute` will finally launch only if `validate` returns `true`. This is usually where you want to **save** / **destroy** your entities.
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:checkbox` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:radio` component, which itself extends `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-checkbox"`
|
||||||
|
- `'type' => "checkbox"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-checkbox" type="checkbox" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:color` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-color"`
|
||||||
|
- `'type' => "color"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-color" type="color" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:date` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-date"`
|
||||||
|
- `'type' => "date"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-date" type="date" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:datetime` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-datetime"`
|
||||||
|
- `'type' => "datetime-local"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-datetime" type="datetime-local" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:email` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-email"`
|
||||||
|
- `'type' => "email"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-email" type="email" value="" name="">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:file` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-file"`
|
||||||
|
- `'type' => "file"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-file" type="file" value="" name="">
|
||||||
|
```
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# `ui:form` / `ui:form.get` / `ui:form.post` (string $method = "get", string $name = "", string $action = "", array $attributes = [])
|
||||||
|
|
||||||
|
A version exists also for unsupported methods (as of HTML5) on forms `ui:form.put` / `ui:form.patch` / `ui:form.delete`, which
|
||||||
|
could find a use within a Javascript environment.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-form"`
|
||||||
|
- `'enctype' => "multipart/form-data"`
|
||||||
|
- `'method' => "get|post|put|patch|delete"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form class="ui-form" enctype="multipart/form-data" action="$action" method="$method">
|
||||||
|
<input class="ui-hidden" type="hidden" name="picea-ui-form[$name]" value="{ random md5 hash }">
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Additional options
|
||||||
|
|
||||||
|
You can now remove the enctype attribute `no-enctype` or the CSRF token `no-csrf` using those option
|
||||||
|
on the form tag :
|
||||||
|
|
||||||
|
```html
|
||||||
|
{% ui:form.post.no-enctype.no-csrf "my.form" %} {% endform %}
|
||||||
|
```
|
||||||
|
|
||||||
|
and would render as such :
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form class="ui-form" action="$action" method="$method">
|
||||||
|
</form>
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:hidden` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-hidden"`
|
||||||
|
- `'type' => "hidden"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-hidden" type="hidden" value="" name="">
|
||||||
|
```
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# `ui:image` (string $source, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-image"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<img class="ui-image" src="$source">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:input` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
This is small
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-input"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
|
||||||
|
```html
|
||||||
|
<input class="ui-input" type="input" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:numeric` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-numeric"`
|
||||||
|
- `'type' => "numeric"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-numeric" type="numeric" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:password` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-password"`
|
||||||
|
- `'type' => "password"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-password" type="password" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:radio` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-radio"`
|
||||||
|
- `'type' => "radio"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-radio" type="radio" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:range` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-range"`
|
||||||
|
- `'type' => "range"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-range" type="range" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:search` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-search"`
|
||||||
|
- `'type' => "search"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-search" type="search" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:select` (string $name, array $list, $value = null, array $attributes = [], bool $strictComparison = true)
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-select"`
|
||||||
|
- `'type' => "select"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-select" type="select" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:tel` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-tel"`
|
||||||
|
- `'type' => "tel"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-tel" type="tel" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:text` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-text"`
|
||||||
|
- `'type' => "text"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-text" type="text" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# `ui:textarea` / `ui:textarea.raw` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`. This extension also comes with a raw modifier which skips the escaping.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-textarea"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<textarea class="ui-textarea" name="$name">$value</textarea>
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:time` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-time"`
|
||||||
|
- `'type' => "time"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-time" type="time" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:url` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-url"`
|
||||||
|
- `'type' => "url"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-url" type="url" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# `ui:week` (string $name, mixed $value = null, array $attributes = [], array $options = [])
|
||||||
|
|
||||||
|
Built on top of `ui:input`.
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
- `'class' => "ui-week"`
|
||||||
|
- `'type' => "week"`
|
||||||
|
|
||||||
|
and will typically be rendered such as :
|
||||||
|
```html
|
||||||
|
<input class="ui-week" type="week" value="$value" name="$name">
|
||||||
|
```
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# `form`(FormInterface $form, ? FormContext $formContext = null) : FormHandler
|
||||||
|
|
||||||
|
The `form()` method will process a form in three steps :
|
||||||
|
|
||||||
|
1. `initialize(FormContextInterface $context)` is first called on form processing.
|
||||||
|
2. `validate(FormContextInterface $context) : bool` will execute on POST. If you define a name to the form, it will match the one found in the POST (`picea-ui-form[$context->name]`) ; if the form is not the target, it will be skipped. Also, if an error si found within the message list, the method will return `false`.
|
||||||
|
3. `execute(FormContextInterface $context)` is the last method to run, which is usually where you save your entity and finilize your messages. The return value is accessible from the `formHandler` which `form()` returns.
|
||||||
|
|
||||||
|
**[PHP]** So, using this code:
|
||||||
|
|
||||||
|
in */app/controller/user.php*
|
||||||
|
```php
|
||||||
|
form(new \MyApp\Form\User\Create());
|
||||||
|
```
|
||||||
|
|
||||||
|
*/app/form/create.php*
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyProject\Form;
|
||||||
|
|
||||||
|
use MyProject\{ Lib, Entity };
|
||||||
|
use Picea\Ui\Method\{ FormContextInterface, Message\ErrorMessage };
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
use function MyProject\View\{ lang };
|
||||||
|
|
||||||
|
class Create implements \Picea\Ui\Method\FormInterface {
|
||||||
|
|
||||||
|
public function initialize(FormContextInterface $context) : void
|
||||||
|
{
|
||||||
|
$this->user = new Entity\User();
|
||||||
|
|
||||||
|
# $context->{varname} will be populated by $_POST data.
|
||||||
|
$this->user = (string) $context->fullname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(FormContextInterface $context) : bool
|
||||||
|
{
|
||||||
|
if ( strlen($this->user) < 3 ) {
|
||||||
|
# This won't allow `execute` to run because of the error message.
|
||||||
|
$context->pushMessage(Lib\Message::generateError(
|
||||||
|
lang("form.error.provide_fullName")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $context->valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(FormContextInterface $context) : void
|
||||||
|
{
|
||||||
|
# Saving a new user here
|
||||||
|
Entity\User::repository()->save($this->user);
|
||||||
|
|
||||||
|
$context->pushMessage(Lib\Message::generateSuccess(
|
||||||
|
lang("form.save")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
which implements this interface :
|
||||||
|
|
||||||
|
```php
|
||||||
|
interface FormInterface
|
||||||
|
{
|
||||||
|
public function initialize(FormContextInterface $context) : void;
|
||||||
|
|
||||||
|
public function validate(FormContextInterface $context) : bool;
|
||||||
|
|
||||||
|
public function execute(FormContextInterface $context);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Would yield the same result such as:
|
||||||
|
```php
|
||||||
|
true
|
||||||
|
true
|
||||||
|
```
|
||||||
|
|
||||||
|
Trying to access either variable before `execute` is called will return `null`.
|
||||||
|
|
||||||
|
## `FormContext` object
|
||||||
|
|
||||||
|
`__construct(ServerRequestInterface $request, ? string $formName = null)`
|
||||||
|
|
||||||
|
Creating a form context manually allows to define the formName, which is essential if you want to use the
|
||||||
|
Cross-Scripting Request Forgery (CSRF).
|
||||||
|
|
||||||
|
It is populated with either $_POST or request's body which is json_decode. A property called `files` is also populated from PSR's `$request->getUploadedFiles()`.
|
||||||
|
|
||||||
|
It's `valid() : bool` method called into a form `validate()` function is first checking every message which were pushed into, using `pushMessage(FormMessage $message)`, and returns false if an error message is found in the list.
|
||||||
|
|
||||||
|
### Retrieve a form execution status / value
|
||||||
|
|
||||||
|
There is two ways to get the execution status (from the `execute` return value):
|
||||||
|
|
||||||
|
```php
|
||||||
|
$formContext = new \Picea\Ui\Method\FormContext($request, 'form.user_create');
|
||||||
|
$form = form(new \MyProject\Form\User\Create(), $formContext);
|
||||||
|
|
||||||
|
if ($formContext->formExecuted) {
|
||||||
|
echo $form->executionStatus;
|
||||||
|
echo $formContext->formExecutionStatus;
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# `ui:popup` (string $name, array $variables = [], array $options = [])
|
||||||
|
|
||||||
|
The following attributes are assigned by this extension :
|
||||||
|
|
||||||
|
and will typically be rendered such as (directly inside an opening tag) :
|
||||||
|
```html
|
||||||
|
ui-popup='{ "name":$name,"vars":{$variables},"options":{$options} }'
|
||||||
|
```
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use function DI\autowire, DI\create, DI\get;
|
||||||
|
|
||||||
|
use Picea\{ Method\Request };
|
||||||
|
|
||||||
|
use Picea\Extension\{ LanguageExtension, TitleExtension, NumberExtension, UrlExtension, DiExtension };
|
||||||
|
|
||||||
|
use Picea\Ui\{ Method, Ui };
|
||||||
|
|
||||||
|
return [
|
||||||
|
Ui::class => autowire(Ui::class),
|
||||||
|
|
||||||
|
'picea.extensions' => function(\Psr\Container\ContainerInterface $c) {
|
||||||
|
return array_merge([
|
||||||
|
$c->get(LanguageExtension::class),
|
||||||
|
$c->get(TitleExtension::class),
|
||||||
|
$c->get(NumberExtension::class),
|
||||||
|
$c->get(UrlExtension::class),
|
||||||
|
$c->get(Method\Form::class),
|
||||||
|
$c->get(Method\Pagination::class),
|
||||||
|
$c->get(Request::class),
|
||||||
|
$c->get(DiExtension::class),
|
||||||
|
|
||||||
|
], class_exists(\Taxus\Picea\Extension::class) ? [ $c->get(\Taxus\Picea\Extension::class) ] : [],
|
||||||
|
array_map(fn($class) => $c->get($class), $c->get(Lean\Lean::class)->getPiceaExtensions())
|
||||||
|
);
|
||||||
|
},
|
||||||
|
];
|
||||||
+67
-117
@@ -2,20 +2,35 @@
|
|||||||
|
|
||||||
namespace Picea\Ui\Common;
|
namespace Picea\Ui\Common;
|
||||||
|
|
||||||
class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
enum UiElementInsertMode
|
||||||
|
{
|
||||||
|
case append;
|
||||||
|
case prepend;
|
||||||
|
}
|
||||||
|
|
||||||
static array $config = [];
|
class UiElement implements \JsonSerializable {
|
||||||
|
public const TAG_CONFIG_OPTIONS = [
|
||||||
const INSERT_MODE_APPEND = 1;
|
"!doctype" => [ "attributes" => [ "html" ], "options" => [ "tag-type" => "single" ] ],
|
||||||
const INSERT_MODE_PREPEND = 2;
|
"js" => [ "tag" => "script", "attributes" => [ "type" => "text/javascript" ] ],
|
||||||
|
"css" => [ "tag" => "link", "attributes" => [ "rel" => "stylesheet" , "type" => "text/css" ] ],
|
||||||
public string $tag = 'div';
|
"document" => [ "options" => [ "no-tag" => true ] ],
|
||||||
|
"link" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
public array $attributes = [
|
"meta" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
'style' => [],
|
"img" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
'class' => [],
|
"input" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
|
"br" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
|
"hr" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
|
"iframe" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
|
"area" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
|
"col" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
|
"frame" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
|
"param" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
|
"video" => [ "options" => [ "tag-type" => "single" ] ],
|
||||||
|
"wbr" => [ "options" => [ "tag-type" => "single" ] ]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public array $attributes = [ 'style' => [], 'class' => [], ];
|
||||||
|
|
||||||
public array $childs = [];
|
public array $childs = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,35 +41,20 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
* tag-type single Only render a single tag. Cannot contains any childrens
|
* tag-type single Only render a single tag. Cannot contains any childrens
|
||||||
* force-tag-open tag Control the way the opening tag is rendered
|
* force-tag-open tag Control the way the opening tag is rendered
|
||||||
* force-tag-close tag-close " " " " closing " " "
|
* force-tag-close tag-close " " " " closing " " "
|
||||||
* no-attr Attributes will not be rendered
|
* no-attr true Attributes will not be rendered
|
||||||
* no-id ID is not automatically given (* may be removed *)
|
* no-id true ID is not automatically given (* may be removed *)
|
||||||
* escape-tag-end If you need to echo the Node inside another string, it may be useful to escape the "/" char using "\/"
|
* escape-tag-end true If you need to echo the Node inside another string, it may be useful to escape the "/" char using "\/"
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public array $options = [];
|
public array $options = [];
|
||||||
|
|
||||||
public $selected = null;
|
|
||||||
|
|
||||||
public string $content = "";
|
public string $content = "";
|
||||||
|
|
||||||
protected ?UiQuery $kwery = null;
|
public function __construct(
|
||||||
|
public string $tag = "div",
|
||||||
|
) {}
|
||||||
|
|
||||||
public function __construct(? string $tag = null) {
|
public static function stylesheet(string $href, $attributes = [], $options = []) : self
|
||||||
if ( ! static::$config ) {
|
|
||||||
static::pushConfigArray( include(dirname(__FILE__) . "/taglist.php") );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($tag !== null) {
|
|
||||||
$this->tag = $tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function pushConfigArray(array $array) : void
|
|
||||||
{
|
|
||||||
static::$config = array_replace_recursive(static::$config, $array);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function createStylesheet(string $href, $attributes = [], $options = []) : self
|
|
||||||
{
|
{
|
||||||
return static::create('link', $attributes + [
|
return static::create('link', $attributes + [
|
||||||
'href' => $href,
|
'href' => $href,
|
||||||
@@ -63,7 +63,7 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
], [ 'tag-type' => 'single' ] + $options);
|
], [ 'tag-type' => 'single' ] + $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function createScript(string $src, array $attributes = [], array $options = []) : self
|
public static function script(string $src, array $attributes = [], array $options = []) : self
|
||||||
{
|
{
|
||||||
return static::create('script', $attributes + [
|
return static::create('script', $attributes + [
|
||||||
'src' => $src,
|
'src' => $src,
|
||||||
@@ -71,7 +71,7 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
], $options);
|
], $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function createPhp(array $attributes = [], array $options = []) : self
|
public static function php(array $attributes = [], array $options = []) : self
|
||||||
{
|
{
|
||||||
return static::create('', $attributes, [
|
return static::create('', $attributes, [
|
||||||
'force-tag-open' => '<?php ',
|
'force-tag-open' => '<?php ',
|
||||||
@@ -108,7 +108,7 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
$obj->attributes($attributes);
|
$obj->attributes($attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( false !== ( $custom = static::$config["tags"][$tag] ?? false ) ) {
|
if ( false !== ( $custom = static::TAG_CONFIG_OPTIONS[strtolower($tag)] ?? false ) ) {
|
||||||
if ( $custom['tag'] ?? false ) {
|
if ( $custom['tag'] ?? false ) {
|
||||||
$obj->tag = $custom['tag'];
|
$obj->tag = $custom['tag'];
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function option(string $var, $value = 1) : self
|
public function option(string $var, mixed $value = true) : self
|
||||||
{
|
{
|
||||||
$this->options[$var] = $value;
|
$this->options[$var] = $value;
|
||||||
|
|
||||||
@@ -177,8 +177,10 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( !is_numeric($key) ) {
|
else if ( !is_numeric($key) ) {
|
||||||
# will output something like <tag $key=$value></tag>
|
$value = htmlspecialchars($value);
|
||||||
$attributesList[] = strpos($value, '"') !== false ? "$key='$value'" : "$key=\"$value\"";
|
|
||||||
|
# will output something like <tag $key="$value"></tag>
|
||||||
|
$attributesList[] = "$key=\"$value\"";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
# will output something like <tag $value></tag>
|
# will output something like <tag $value></tag>
|
||||||
@@ -198,14 +200,14 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( in_array('no-tag', $this->options, true) ) {
|
if ( $this->options['no-tag'] ?? false ) {
|
||||||
return $this->content . $content;
|
return $this->content . $content;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$compiledAttributes = (count($attributesList) && ! in_array('no-attr', $this->options, true) ? " " . implode(" ", $attributesList) : "");
|
$compiledAttributes = $this->options['no-attr'] ?? false ? "" : " " . implode(" ", $attributesList);
|
||||||
|
|
||||||
# Force the node to contain a certain opening tag (php is a good example)
|
# Force the node to contain a certain opening tag (php is a good example)
|
||||||
if ( in_array('force-tag-open', $this->options, true) ) {
|
if ( $this->options['force-tag-open'] ?? false ) {
|
||||||
$opentag = $this->options['force-tag-open'] . $compiledAttributes;
|
$opentag = $this->options['force-tag-open'] . $compiledAttributes;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -217,11 +219,11 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
$closetag = "";
|
$closetag = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif ( in_array('force-tag-close', $this->options, true) ) {
|
elseif ( $this->options['force-tag-close'] ?? false ) {
|
||||||
$closetag = $this->options['force-tag-close'];
|
$closetag = $this->options['force-tag-close'];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$closetag = $this->tag ? "<" . (in_array('escape-tag-end', $this->options) ? "\/" : "/" ) . "{$this->tag}>" : "";
|
$closetag = $this->tag ? "<" . ( $this->options['escape-tag-end'] ?? false ? "\/" : "/" ) . "{$this->tag}>" : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $opentag . $this->content . $content . $closetag;
|
return $opentag . $this->content . $content . $closetag;
|
||||||
@@ -240,7 +242,7 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
|
|
||||||
public function text(?string $set = null) {
|
public function text(?string $set = null) {
|
||||||
if ($set !== null) {
|
if ($set !== null) {
|
||||||
$this->content = htmlspecialchars( $set, ENT_NOQUOTES );
|
$this->content = htmlspecialchars( $set, \ENT_NOQUOTES );
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -250,24 +252,23 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
|
|
||||||
public function append( ...$arguments ) : self
|
public function append( ...$arguments ) : self
|
||||||
{
|
{
|
||||||
return $this->insert( static::INSERT_MODE_APPEND, ...$arguments);
|
return $this->insert( UiElementInsertMode::append, ...$arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function prepend( ...$arguments ) : self
|
public function prepend( ...$arguments ) : self
|
||||||
{
|
{
|
||||||
return $this->insert( static::INSERT_MODE_PREPEND, ...( is_array($arguments) ? array_reverse($arguments) : $arguments) );
|
return $this->insert( UiElementInsertMode::prepend, ...( is_array($arguments) ? array_reverse($arguments) : $arguments) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function insert(UiElementInsertMode $mode, ...$elements) : self
|
||||||
protected function insert(int $mode, ...$elements) : self
|
|
||||||
{
|
{
|
||||||
foreach($elements as $item) {
|
foreach($elements as $item) {
|
||||||
switch($mode) {
|
switch($mode) {
|
||||||
case static::INSERT_MODE_APPEND:
|
case UiElementInsertMode::append:
|
||||||
array_push($this->childs, $item);
|
array_push($this->childs, $item);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case static::INSERT_MODE_PREPEND:
|
case UiElementInsertMode::prepend:
|
||||||
array_unshift($this->childs, $item);
|
array_unshift($this->childs, $item);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -285,14 +286,24 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'style' :
|
case 'style' :
|
||||||
foreach($value as $var => $val) {
|
if ( is_string($value) )
|
||||||
$this->css($var, $val);
|
{
|
||||||
|
foreach(array_filter(explode(";", $value)) as $vars) {
|
||||||
|
list($key, $value) = explode(':', $vars);
|
||||||
|
|
||||||
|
$this->css($key, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ( is_array($value) ) {
|
||||||
|
foreach($value as $var => $val) {
|
||||||
|
$this->css($var, $val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if ( is_numeric($key) ) {
|
if ( is_numeric($key) ) {
|
||||||
$this->attributes[] = $value;
|
$this->attributes[$key] = $value;
|
||||||
}
|
}
|
||||||
elseif ( is_array($this->attributes[$key] ?? false) ) {
|
elseif ( is_array($this->attributes[$key] ?? false) ) {
|
||||||
$this->attributes[$key] = array_replace($value, $this->attributes[$key]);
|
$this->attributes[$key] = array_replace($value, $this->attributes[$key]);
|
||||||
@@ -316,7 +327,6 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
*/
|
*/
|
||||||
public function css(...$arguments) {
|
public function css(...$arguments) {
|
||||||
foreach($arguments as $item) {
|
foreach($arguments as $item) {
|
||||||
|
|
||||||
if ( is_array($item) ) {
|
if ( is_array($item) ) {
|
||||||
foreach($item as $key => $value) {
|
foreach($item as $key => $value) {
|
||||||
$this->css($key, $value);
|
$this->css($key, $value);
|
||||||
@@ -335,15 +345,12 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
return array_search(strtolower($className), array_map('strtolower', $this->attributes['class']), true);
|
return array_search(strtolower($className), array_map('strtolower', $this->attributes['class']), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function count() {
|
public function count() {
|
||||||
return count($this->childs);
|
return count($this->childs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize() {
|
public function jsonSerialize() : mixed
|
||||||
|
{
|
||||||
return [
|
return [
|
||||||
'tag' => $this->tag,
|
'tag' => $this->tag,
|
||||||
'attr' => $this->attributes,
|
'attr' => $this->attributes,
|
||||||
@@ -352,63 +359,6 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function offsetSet($offset, $value) {
|
|
||||||
if ( is_numeric($offset) ) {
|
|
||||||
return $this->selected[$offset] = $value;
|
|
||||||
}
|
|
||||||
elseif ( is_null($offset) ) {
|
|
||||||
return $this->childs[] = $value;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return $this->childs[$offset] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetExists($query) : bool
|
|
||||||
{
|
|
||||||
return count($this->kwery()->find($query)) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetUnset($query) : void
|
|
||||||
{
|
|
||||||
$this->kwery()->find($query)->remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetGet($query) : self
|
|
||||||
{
|
|
||||||
return $this->kwery()->find($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rewind() : void
|
|
||||||
{
|
|
||||||
reset($this->childs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function current() : self
|
|
||||||
{
|
|
||||||
return current($this->childs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function key() : int
|
|
||||||
{
|
|
||||||
return key($this->childs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function next() : self
|
|
||||||
{
|
|
||||||
return next($this->childs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function valid() : bool
|
|
||||||
{
|
|
||||||
return ! in_array(key($this->childs), [ NULL, FALSE ], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function isNode($obj) : bool
|
|
||||||
{
|
|
||||||
return $obj instanceof UiElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string
|
public function __toString() : string
|
||||||
{
|
{
|
||||||
return $this->render();
|
return $this->render();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use Picea\Extension\ExtensionTrait;
|
|||||||
class UiMessage extends UiElement implements Extension {
|
class UiMessage extends UiElement implements Extension {
|
||||||
use ExtensionTrait;
|
use ExtensionTrait;
|
||||||
|
|
||||||
public string $token = "ui.message";
|
public string $token = "ui:message";
|
||||||
|
|
||||||
public string $tag = "input";
|
public string $tag = "input";
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ class UiMessage extends UiElement implements Extension {
|
|||||||
'class' => 'ui-message',
|
'class' => 'ui-message',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string
|
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,22 +9,25 @@ use Picea\Extension\ExtensionTrait;
|
|||||||
class UiPopup extends UiElement implements Extension {
|
class UiPopup extends UiElement implements Extension {
|
||||||
use ExtensionTrait;
|
use ExtensionTrait;
|
||||||
|
|
||||||
public string $token = "ui.popup";
|
public string $token = "ui:popup";
|
||||||
|
|
||||||
public string $tag = "div";
|
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => 'ui-popup',
|
'class' => 'ui-popup',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string
|
public function __construct(
|
||||||
|
public string $tag = "div",
|
||||||
|
) {}
|
||||||
|
|
||||||
|
|
||||||
|
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string
|
||||||
{
|
{
|
||||||
return "<?php echo 'ui-popup=\"' . ( new \\" . static::class . "() )->buildAttributes($arguments) . '\"' ?>";
|
return "<?php echo 'ui-popup=\"' . ( new \\" . static::class . "() )->buildAttributes($arguments) . '\"' ?>";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildAttributes(string $name, array $variables = []) : string
|
public function buildAttributes(string $name, array $variables = [], array $options = []) : string
|
||||||
{
|
{
|
||||||
return htmlentities(json_encode([ "name" => $name, "vars" => $variables ], JSON_HEX_APOS | JSON_HEX_QUOT), ENT_QUOTES, 'UTF-8');
|
return htmlentities(json_encode([ "name" => $name, "vars" => $variables, "options" => $options ], JSON_HEX_APOS | JSON_HEX_QUOT), \ENT_QUOTES, 'UTF-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Picea\Ui\Form;
|
||||||
|
|
||||||
|
use Picea\Extension\Extension;
|
||||||
|
use Picea\Extension\ExtensionTrait;
|
||||||
|
use Picea\Ui\Common\UiElement;
|
||||||
|
use Picea\Ui\Component\UiMessage;
|
||||||
|
use Picea\Ui\Component\UiPopup;
|
||||||
|
|
||||||
|
class Ui extends UiElement implements Extension {
|
||||||
|
use ExtensionTrait;
|
||||||
|
|
||||||
|
public string $token = "ui";
|
||||||
|
|
||||||
|
protected /* ? mixed */ $value;
|
||||||
|
|
||||||
|
protected string $name;
|
||||||
|
|
||||||
|
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string
|
||||||
|
{
|
||||||
|
switch(true) {
|
||||||
|
case in_array('checkbox', $options):
|
||||||
|
$input = new UiCheckbox();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('color', $options):
|
||||||
|
$input = new UiColor();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('date', $options):
|
||||||
|
$input = new UiDate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('datetime', $options):
|
||||||
|
$input = new UiDatetime();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('email', $options):
|
||||||
|
$input = new UiEmail();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('file', $options):
|
||||||
|
$input = new UiFile();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('form', $options):
|
||||||
|
case in_array('endform', $options):
|
||||||
|
$input = new UiForm();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('hidden', $options):
|
||||||
|
$input = new UiHidden();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('img', $options):
|
||||||
|
case in_array('image', $options):
|
||||||
|
$input = new UiImage();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('input', $options):
|
||||||
|
$input = new UiInput();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('numeric', $options):
|
||||||
|
$input = new UiNumeric();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('password', $options):
|
||||||
|
$input = new UiPassword();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('radio', $options):
|
||||||
|
$input = new UiRadio();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('range', $options):
|
||||||
|
$input = new UiRange();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('search', $options):
|
||||||
|
$input = new UiSearch();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('select', $options):
|
||||||
|
$input = new UiSelect();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('tel', $options):
|
||||||
|
$input = new UiTel();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('text', $options):
|
||||||
|
$input = new UiText();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('textarea', $options):
|
||||||
|
$input = new UiTextarea();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('time', $options):
|
||||||
|
$input = new UiTime();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('url', $options):
|
||||||
|
$input = new UiUrl();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('week', $options):
|
||||||
|
$input = new UiWeek();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('popup', $options):
|
||||||
|
$input = new UiPopup();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case in_array('message', $options):
|
||||||
|
$input = new UiMessage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($input)) {
|
||||||
|
throw new \Exception("Missing token ? $token $arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $input->parse($context, $arguments, $token, $options);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiCheckbox extends UiRadio {
|
class UiCheckbox extends UiRadio {
|
||||||
|
|
||||||
public string $token = "ui.checkbox";
|
public string $token = "ui:checkbox";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-checkbox",
|
'class' => "ui-checkbox",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiColor extends UiInput {
|
class UiColor extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.color";
|
public string $token = "ui:color";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-color",
|
'class' => "ui-color",
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiDate extends UiInput {
|
class UiDate extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.date";
|
public string $token = "ui:date";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-date",
|
'class' => "ui-date",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiDatetime extends UiInput {
|
class UiDatetime extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.datetime";
|
public string $token = "ui:datetime";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-datetime",
|
'class' => "ui-datetime",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiEmail extends UiInput {
|
class UiEmail extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.email";
|
public string $token = "ui:email";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-email",
|
'class' => "ui-email",
|
||||||
|
|||||||
+5
-5
@@ -4,10 +4,10 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiFile extends UiInput {
|
class UiFile extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.file";
|
public string $token = "ui:file";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-file",
|
'class' => "ui-file",
|
||||||
'type' => "file",
|
'type' => "file",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
+66
-27
@@ -11,58 +11,97 @@ class UiForm extends UiElement implements Extension {
|
|||||||
|
|
||||||
public string $defaultMethod = "get";
|
public string $defaultMethod = "get";
|
||||||
|
|
||||||
public array $token = [ "ui.form", "ui.endform", "ui.form.get", "ui.form.post", "ui.form.patch", "ui.form.delete", "ui.form.put" ];
|
public array $token = [ "ui:form", "ui:endform", "ui:/form" ];
|
||||||
|
|
||||||
public string $tag = "form";
|
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => 'ui-form',
|
'class' => 'ui-form',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string
|
public function __construct(
|
||||||
|
public string $tag = "form",
|
||||||
|
public bool $enctype = true,
|
||||||
|
public bool $csrf = true,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string
|
||||||
{
|
{
|
||||||
|
$constructor = [];
|
||||||
|
|
||||||
switch($token) {
|
switch($token) {
|
||||||
case 'ui.endform':
|
case 'ui.endform': # bw compat
|
||||||
|
case 'ui:endform':
|
||||||
|
case 'ui:/form':
|
||||||
return "</form>";
|
return "</form>";
|
||||||
|
}
|
||||||
|
|
||||||
case "ui.form.get":
|
if (in_array('no-enctype', $options)) {
|
||||||
$method = "get";
|
$constructor[] = "enctype: false";
|
||||||
break;
|
}
|
||||||
|
|
||||||
case "ui.form.patch":
|
if (in_array('no-csrf', $options)) {
|
||||||
$method = "patch";
|
$constructor[] = "csrf: false";
|
||||||
break;
|
}
|
||||||
|
|
||||||
case "ui.form.delete":
|
if (in_array('get', $options)) {
|
||||||
$method = "delete";
|
$method = "get";
|
||||||
break;
|
}
|
||||||
|
elseif (in_array('post', $options)) {
|
||||||
case "ui.form.put":
|
$method = "post";
|
||||||
$method = "put";
|
}
|
||||||
break;
|
elseif (in_array('put', $options)) {
|
||||||
|
$method = "put";
|
||||||
case "ui.form.post":
|
}
|
||||||
$method = "post";
|
elseif (in_array('delete', $options)) {
|
||||||
break;
|
$method = "delete";
|
||||||
|
}
|
||||||
|
elseif (in_array('patch', $options)) {
|
||||||
|
$method = "patch";
|
||||||
}
|
}
|
||||||
|
|
||||||
$method ??= $this->defaultMethod;
|
$method ??= $this->defaultMethod;
|
||||||
|
|
||||||
return "<?php echo ( new \\" . static::class . "() )->buildHtml('$method', $arguments) ?>";
|
$opt = var_export($options, true);
|
||||||
|
|
||||||
|
$constructor = implode(',', $constructor);
|
||||||
|
|
||||||
|
return "<?php echo ( new \\" . static::class . "($constructor) )->buildHtml('$method', $arguments) ?>";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildHtml(string $method = "get", string $name = "", string $action = "", array $attributes = []) : string
|
public function buildHtml(string $method = "get", string $name = "", string $action = "", array $attributes = []) : string
|
||||||
{
|
{
|
||||||
$method = strtolower($method);
|
# Method passed in arguments take precedents over options
|
||||||
|
$method = strtolower($attributes['method'] ?? $method);
|
||||||
|
|
||||||
$this->option('tag-type', 'single');
|
$this->option('tag-type', 'single');
|
||||||
|
|
||||||
$this->attributes([ 'method' => $method, 'action' => $action ] + $attributes);
|
if ($attributes['class'] ?? false) {
|
||||||
|
$attributes['class'] .= " {$this->attributes['class']}";
|
||||||
|
unset($this->attributes['class']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->attributes([ 'action' => $action, 'method' => $method, ] + $attributes);
|
||||||
|
|
||||||
if ( $method !== "get" ) {
|
if ( $method !== "get" ) {
|
||||||
$this->append( ( new UiHidden() )->attributes([ 'name' =>"picea-ui-form[$name]", 'value' => md5( $name . microtime() ) ]) );
|
|
||||||
|
|
||||||
$this->attributes([ 'enctype' => "multipart/form-data" ]);
|
if ($this->csrf) {
|
||||||
|
$token = md5($name . microtime());
|
||||||
|
$key = "picea-ui.form:{$name}";
|
||||||
|
|
||||||
|
if (count($_SESSION[$key] ?? []) > 100) {
|
||||||
|
array_shift($_SESSION[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION[$key][] = $token;
|
||||||
|
|
||||||
|
$this->append((new UiHidden())->attributes([
|
||||||
|
'name' => "picea-ui-form[$name]",
|
||||||
|
'value' => $token,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->enctype) {
|
||||||
|
$this->attributes([ 'enctype' => "multipart/form-data" ]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render() . PHP_EOL;
|
return $this->render() . PHP_EOL;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiHidden extends UiInput {
|
class UiHidden extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.hidden";
|
public string $token = "ui:hidden";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-hidden",
|
'class' => "ui-hidden",
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Picea\Ui\Form;
|
||||||
|
|
||||||
|
use Picea\Ui\Common\UiElement;
|
||||||
|
use Picea\Extension\Extension;
|
||||||
|
use Picea\Extension\ExtensionTrait;
|
||||||
|
|
||||||
|
class UiImage extends UiElement implements Extension {
|
||||||
|
use ExtensionTrait;
|
||||||
|
|
||||||
|
public array $tokens = [ "ui:img", "ui:image" ];
|
||||||
|
|
||||||
|
public array $attributes = [
|
||||||
|
'class' => 'ui-image',
|
||||||
|
];
|
||||||
|
|
||||||
|
public array $options = [
|
||||||
|
'tag-type' => "single",
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public string $tag = "img",
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string
|
||||||
|
{
|
||||||
|
return "<?php echo ( new \\" . static::class . "() )->buildHtml($arguments) ?>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildHtml(string $source, array $attributes = [], array $options = []) : string
|
||||||
|
{
|
||||||
|
if ($options) {
|
||||||
|
$this->options += $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($attributes['class'] ?? false) {
|
||||||
|
$attributes['class'] .= " {$this->attributes['class']}";
|
||||||
|
|
||||||
|
unset($this->attributes['class']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->attributes([ 'src' => $source ] + $attributes + $this->attributes + $this->objectAttribute());
|
||||||
|
|
||||||
|
return $this->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function objectAttribute() : array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-10
@@ -3,15 +3,12 @@
|
|||||||
namespace Picea\Ui\Form;
|
namespace Picea\Ui\Form;
|
||||||
|
|
||||||
use Picea\Ui\Common\UiElement;
|
use Picea\Ui\Common\UiElement;
|
||||||
use Picea\Extension\Extension;
|
use Picea\Extension\{ Extension, FunctionExtension, ExtensionTrait };
|
||||||
use Picea\Extension\ExtensionTrait;
|
|
||||||
|
|
||||||
class UiInput extends UiElement implements Extension {
|
class UiInput extends UiElement implements Extension, FunctionExtension {
|
||||||
use ExtensionTrait;
|
use ExtensionTrait;
|
||||||
|
|
||||||
public string $token = "ui.input";
|
public string $token = "ui:input";
|
||||||
|
|
||||||
public string $tag = "input";
|
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => 'ui-input',
|
'class' => 'ui-input',
|
||||||
@@ -21,16 +18,33 @@ class UiInput extends UiElement implements Extension {
|
|||||||
'tag-type' => "single",
|
'tag-type' => "single",
|
||||||
];
|
];
|
||||||
|
|
||||||
protected /* ? mixed */ $value;
|
protected mixed $value;
|
||||||
|
|
||||||
protected string $name;
|
protected string $name;
|
||||||
|
|
||||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string
|
public function __construct(
|
||||||
|
public string $tag = "input",
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string
|
||||||
{
|
{
|
||||||
return "<?php echo ( new \\" . static::class . "() )->buildHtml($arguments) ?>";
|
return "<?php echo ( new \\" . static::class . "() )->buildHtml($arguments) ?>";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildHtml(string $name, /*? mixed */ $value = null, array $attributes = [], array $options = []) : string
|
public function exportFunctions(): array
|
||||||
|
{
|
||||||
|
if ( null === $type = $this->attributes['type'] ?? null ) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = str_replace('-', '_', 'ui_'. ( $this->attributes['type'] ?? 'text' ));
|
||||||
|
|
||||||
|
return [
|
||||||
|
$key => fn(string $name, mixed $value = null, array $attributes = [], array $options = []) : string => $this->buildHtml($name, $value, $attributes, $options),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildHtml(string $name, mixed $value = null, array $attributes = [], array $options = []) : string
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
|
|
||||||
@@ -43,7 +57,7 @@ class UiInput extends UiElement implements Extension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($attributes['class'] ?? false) {
|
if ($attributes['class'] ?? false) {
|
||||||
$attributes['class'] .= " {$this->attributes['class']}";
|
$attributes['class'] = implode(" ", array_merge((array) $attributes['class'], (array) $this->attributes['class']));
|
||||||
unset($this->attributes['class']);
|
unset($this->attributes['class']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiNumeric extends UiInput {
|
class UiNumeric extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.numeric";
|
public string $token = "ui:numeric";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-numeric",
|
'class' => "ui-numeric",
|
||||||
'type' => "numeric",
|
'type' => "number",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiPassword extends UiInput {
|
class UiPassword extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.password";
|
public string $token = "ui:password";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-password",
|
'class' => "ui-password",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiRadio extends UiInput {
|
class UiRadio extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.radio";
|
public string $token = "ui:radio";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-radio",
|
'class' => "ui-radio",
|
||||||
@@ -13,7 +13,7 @@ class UiRadio extends UiInput {
|
|||||||
|
|
||||||
protected function objectAttribute() : array
|
protected function objectAttribute() : array
|
||||||
{
|
{
|
||||||
if ( ( $this->options['value'] ?? false ) && ( $this->options['value'] === $this->value ) ) {
|
if ( isset($this->options['value']) && ( $this->options['value'] === $this->value ) ) {
|
||||||
return [
|
return [
|
||||||
'checked' => "checked"
|
'checked' => "checked"
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiRange extends UiInput {
|
class UiRange extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.range";
|
public string $token = "ui:range";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-range",
|
'class' => "ui-range",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiSearch extends UiInput {
|
class UiSearch extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.search";
|
public string $token = "ui:search";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-search",
|
'class' => "ui-search",
|
||||||
|
|||||||
+30
-9
@@ -9,21 +9,28 @@ use Picea\Extension\ExtensionTrait;
|
|||||||
class UiSelect extends UiElement implements Extension {
|
class UiSelect extends UiElement implements Extension {
|
||||||
use ExtensionTrait;
|
use ExtensionTrait;
|
||||||
|
|
||||||
public string $token = "ui.select";
|
public string $token = "ui:select";
|
||||||
|
|
||||||
public string $tag = "select";
|
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => 'ui-select',
|
'class' => 'ui-select',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string
|
public function __construct(
|
||||||
|
public string $tag = "select",
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string
|
||||||
{
|
{
|
||||||
return "<?php echo ( new \\" . static::class . "() )->buildHtml($arguments) ?>";
|
return "<?php echo ( new \\" . static::class . "() )->buildHtml($arguments) ?>";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildHtml(string $name, array $list, $value = null, array $attributes = [], bool $strictComparison = true) : string
|
public function buildHtml(string $name, array $list, mixed $value = null, array $attributes = [], bool $strictComparison = true) : string
|
||||||
{
|
{
|
||||||
|
if ($attributes['class'] ?? false) {
|
||||||
|
$attributes['class'] = implode(" ", array_merge((array) $attributes['class'], (array) $this->attributes['class']));
|
||||||
|
unset($this->attributes['class']);
|
||||||
|
}
|
||||||
|
|
||||||
$this->attributes([ 'name' => $name ] + $attributes + $this->attributes);
|
$this->attributes([ 'name' => $name ] + $attributes + $this->attributes);
|
||||||
$this->setList($list, $value, $strictComparison);
|
$this->setList($list, $value, $strictComparison);
|
||||||
|
|
||||||
@@ -38,7 +45,6 @@ class UiSelect extends UiElement implements Extension {
|
|||||||
protected function setList(array $list, $selected, bool $strict = true) : void
|
protected function setList(array $list, $selected, bool $strict = true) : void
|
||||||
{
|
{
|
||||||
foreach($list as $key => $item) {
|
foreach($list as $key => $item) {
|
||||||
|
|
||||||
if ($item instanceof UiElement) {
|
if ($item instanceof UiElement) {
|
||||||
$this->append($item);
|
$this->append($item);
|
||||||
|
|
||||||
@@ -49,11 +55,11 @@ class UiSelect extends UiElement implements Extension {
|
|||||||
$obj->attributes(['label' => $key]);
|
$obj->attributes(['label' => $key]);
|
||||||
|
|
||||||
foreach($item as $subKey => $subItem) {
|
foreach($item as $subKey => $subItem) {
|
||||||
$obj->append($this->createOption($subItem, $subKey, $this->isSelected($subKey, $selected, $strict)));
|
$obj->append($this->createOption((string) $subItem, $subKey, $this->isSelected($subKey, $selected, $strict)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$obj = $this->createOption($item, $key, $this->isSelected($key, $selected, $strict));
|
$obj = $this->createOption((string) $item, $key, $this->isSelected($key, $selected, $strict));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->append($obj);
|
$this->append($obj);
|
||||||
@@ -73,8 +79,23 @@ class UiSelect extends UiElement implements Extension {
|
|||||||
return $option;
|
return $option;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isSelected($check, $value, bool $strict = true) : bool
|
protected function isSelected($check, mixed $value, bool $strict = true) : bool
|
||||||
{
|
{
|
||||||
|
if ( is_array($value) ) {
|
||||||
|
return in_array($check, array_map(fn($e) => $this->normalizeValue($e), $value), $strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->normalizeValue($value);
|
||||||
|
|
||||||
return $strict ? $check === $value : $check == $value;
|
return $strict ? $check === $value : $check == $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function normalizeValue(mixed $value) : mixed
|
||||||
|
{
|
||||||
|
if ($value instanceof \BackedEnum) {
|
||||||
|
return $value->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiTel extends UiInput {
|
class UiTel extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.tel";
|
public string $token = "ui:tel";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-tel",
|
'class' => "ui-tel",
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiText extends UiInput {
|
class UiText extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.text";
|
public string $token = "ui:text";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-text",
|
'class' => "ui-text",
|
||||||
|
|||||||
+22
-4
@@ -4,9 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiTextarea extends UiInput {
|
class UiTextarea extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.textarea";
|
public array $tokens = [ "ui:textarea" ];
|
||||||
|
|
||||||
public string $tag = "textarea";
|
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-textarea",
|
'class' => "ui-textarea",
|
||||||
@@ -14,8 +12,28 @@ class UiTextarea extends UiInput {
|
|||||||
|
|
||||||
public array $options = [];
|
public array $options = [];
|
||||||
|
|
||||||
|
protected bool $echoRaw = false;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public string $tag = "textarea",
|
||||||
|
public bool $raw = false,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string
|
||||||
|
{
|
||||||
|
$raw = in_array('raw', $options) ? 'true' : 'false';
|
||||||
|
|
||||||
|
return "<?php echo ( new \\" . static::class . "(raw: $raw) )->buildHtml($arguments) ?>";
|
||||||
|
}
|
||||||
protected function setValue($value) : void
|
protected function setValue($value) : void
|
||||||
{
|
{
|
||||||
$this->html($value);
|
$this->echoRaw ? $this->html($value) : $this->text($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function echoRaw(bool $value) : self
|
||||||
|
{
|
||||||
|
$this->echoRaw = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiTime extends UiInput {
|
class UiTime extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.time";
|
public string $token = "ui:time";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-time",
|
'class' => "ui-time",
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiUrl extends UiInput {
|
class UiUrl extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.url";
|
public string $token = "ui:url";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-url",
|
'class' => "ui-url",
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
|||||||
|
|
||||||
class UiWeek extends UiInput {
|
class UiWeek extends UiInput {
|
||||||
|
|
||||||
public string $token = "ui.week";
|
public string $token = "ui:week";
|
||||||
|
|
||||||
public array $attributes = [
|
public array $attributes = [
|
||||||
'class' => "ui-week",
|
'class' => "ui-week",
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Picea\Ui\Method\Context;
|
||||||
|
|
||||||
|
interface FormContextCastableInterface
|
||||||
|
{
|
||||||
|
public static function instance(mixed $value);
|
||||||
|
}
|
||||||
+8
-22
@@ -7,9 +7,10 @@ use Picea\Extension\Extension,
|
|||||||
|
|
||||||
use Picea\Compiler\Context;
|
use Picea\Compiler\Context;
|
||||||
|
|
||||||
|
use Picea\Extension\FunctionExtension;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
class Form implements Extension {
|
class Form implements Extension, FunctionExtension {
|
||||||
use ExtensionTrait;
|
use ExtensionTrait;
|
||||||
|
|
||||||
public array $tokens;
|
public array $tokens;
|
||||||
@@ -22,34 +23,19 @@ class Form implements Extension {
|
|||||||
|
|
||||||
public function __construct(ServerRequestInterface $request, Context $context) {
|
public function __construct(ServerRequestInterface $request, Context $context) {
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
$this->register($context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string { }
|
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string { }
|
||||||
|
|
||||||
public function register(Context $context) : void
|
public function exportFunctions(): array
|
||||||
{
|
{
|
||||||
$context->pushFunction("form", [ $this, 'formClass' ]);
|
return [
|
||||||
}
|
"form" => [ $this, 'formClass' ],
|
||||||
|
];
|
||||||
public function form_csrf(string $field, string $value) {
|
|
||||||
$values = $this->session("View.form.csrf.$field") ?: [];
|
|
||||||
|
|
||||||
# keeps 20 (from config) latest CSRF key for this form into session,
|
|
||||||
# allowing more than one tab opened and preventing information loss
|
|
||||||
if ( count($values) >= 20 ) {
|
|
||||||
#array_shift($values);
|
|
||||||
}
|
|
||||||
|
|
||||||
$values[] = $value;
|
|
||||||
|
|
||||||
$this->session("View.form.csrf.$field", $values);
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function formClass(FormInterface $form, ? FormContext $formContext = null) : FormHandler
|
public function formClass(FormInterface $form, ? FormContext $formContext = null) : FormHandler
|
||||||
{
|
{
|
||||||
return new FormHandler($this->request, $form, $formContext);
|
return new FormHandler($formContext ? $formContext->getRequest() : $this->request, $form, $formContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+179
-22
@@ -7,32 +7,67 @@ use Psr\Http\Message\ServerRequestInterface,
|
|||||||
|
|
||||||
class FormContext implements FormContextInterface
|
class FormContext implements FormContextInterface
|
||||||
{
|
{
|
||||||
public string $formName;
|
public string $formContextMethod;
|
||||||
|
|
||||||
public bool $formSent;
|
public bool $formSent = false;
|
||||||
|
|
||||||
public array $values = [];
|
public bool $formExecuted = false;
|
||||||
|
|
||||||
public array $messages = [];
|
public mixed $formExecutionStatus = null;
|
||||||
|
|
||||||
public ServerRequestInterface $request;
|
protected string $formContextValuesRoot;
|
||||||
|
|
||||||
public ? ResponseInterface $response = null;
|
public array $formContextValues = [];
|
||||||
|
|
||||||
public function __construct(ServerRequestInterface $request, ? string $formName = null)
|
public array $formContextFiles = [];
|
||||||
{
|
|
||||||
$this->request = $request;
|
|
||||||
|
|
||||||
if ( $formName ) {
|
public array $formContextMessages = [];
|
||||||
$this->formName = $formName;
|
|
||||||
|
public bool $formSkipCsrf = false;
|
||||||
|
|
||||||
|
protected array $formContextDefinedProperties = [];
|
||||||
|
|
||||||
|
protected array $formContextCatchedMethods = [ 'POST', 'PUT', 'PATCH', 'DELETE', ];
|
||||||
|
|
||||||
|
protected array $formConstructorProperties = [];
|
||||||
|
|
||||||
|
public ? ResponseInterface $formContextResponse = null;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public ServerRequestInterface $formContextRequest,
|
||||||
|
public readonly ? string $formName = null
|
||||||
|
) {
|
||||||
|
# From constructor
|
||||||
|
$reflection = new \ReflectionClass($this);
|
||||||
|
|
||||||
|
$this->formConstructorProperties = array_map(fn($e) => strtolower($e->getName()), array_filter($reflection->getConstructor()->getParameters(), fn($e) => $e->isPromoted()));
|
||||||
|
|
||||||
|
$this->formContextValues = $formContextRequest->getParsedBody() ?: [];
|
||||||
|
|
||||||
|
if ( ! $this->formContextValues ) {
|
||||||
|
$content = mb_convert_encoding((string) $formContextRequest->getBody(), 'UTF-8');
|
||||||
|
|
||||||
|
if ( $content && ( $json = json_decode($content, true) ) ) {
|
||||||
|
$this->formContextValues = $json;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->values = $request->getParsedBody() ?: [];
|
if ($this->formContextValuesRoot ?? false) {
|
||||||
|
$this->formContextValues = $this->formContextValues[$this->formContextValuesRoot] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->fillValues();
|
||||||
|
|
||||||
|
$this->formContextFiles = $formContextRequest->getUploadedFiles() ?: [];
|
||||||
|
|
||||||
|
$this->initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function initialize() : void {}
|
||||||
|
|
||||||
public function valid() : bool
|
public function valid() : bool
|
||||||
{
|
{
|
||||||
foreach($this->messages as $message) {
|
foreach($this->formContextMessages as $message) {
|
||||||
if ( $message->isError() ) {
|
if ( $message->isError() ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -41,9 +76,47 @@ class FormContext implements FormContextInterface
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function executed() : bool
|
||||||
|
{
|
||||||
|
return $this->formExecutionStatus === false ? false : $this->formExecuted;
|
||||||
|
}
|
||||||
|
|
||||||
public function formSent() : bool
|
public function formSent() : bool
|
||||||
{
|
{
|
||||||
return $this->formSent;
|
$valid = in_array($this->requestMethod(), $this->formContextCatchedMethods);
|
||||||
|
|
||||||
|
if ( (! $this->formSkipCsrf) && ! empty($this->formName) ) {
|
||||||
|
$token = $this->get('picea-ui-form')[$this->formName] ?? false;
|
||||||
|
|
||||||
|
if ( $token ) {
|
||||||
|
if (! $this->formSkipCsrf) {
|
||||||
|
$valid = in_array($token, $_SESSION["picea-ui.form:{$this->formName}"] ?? []);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$valid = (bool) $token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->formSent = $valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExecutionStatus() : mixed
|
||||||
|
{
|
||||||
|
return $this->formExecutionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequest() : ServerRequestInterface
|
||||||
|
{
|
||||||
|
return $this->formContextRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requestMethod() : string
|
||||||
|
{
|
||||||
|
return $this->formContextRequest->getMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __set($key, $value)
|
public function __set($key, $value)
|
||||||
@@ -58,7 +131,7 @@ class FormContext implements FormContextInterface
|
|||||||
|
|
||||||
public function __isset($key)
|
public function __isset($key)
|
||||||
{
|
{
|
||||||
return array_key_exists($key, $this->values);
|
return array_key_exists($key, $this->formContextValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __unset($key)
|
public function __unset($key)
|
||||||
@@ -66,28 +139,112 @@ class FormContext implements FormContextInterface
|
|||||||
$this->delete($key);
|
$this->delete($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(string $key, $default = null)
|
public function get(string $key, mixed $default = null)
|
||||||
{
|
{
|
||||||
return $this->has($key) ? $this->values[$key] : $default;
|
return $this->has($key) ? $this->formContextValues[$key] : $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function set(string $key, $value)
|
public function set(string $key, mixed $value)
|
||||||
{
|
{
|
||||||
return $this->values[$key] = $value;
|
|
||||||
|
if ($this->canWriteProperty($key)) {
|
||||||
|
$this->$key = $this->castValue($key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->formContextValues[$key] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function sets(array $values) : static
|
||||||
|
{
|
||||||
|
foreach($values as $key =>$value) {
|
||||||
|
$this->set($key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function delete(string $key) : void
|
public function delete(string $key) : void
|
||||||
{
|
{
|
||||||
unset($this->values[$key]);
|
unset($this->formContextValues[$key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function has(string $key) : bool
|
public function has(string $key) : bool
|
||||||
{
|
{
|
||||||
return array_key_exists($key, $this->values);
|
return array_key_exists($key, $this->formContextValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pushMessage(FormMessage $message) : void
|
public function pushMessage(FormMessage $message) : void
|
||||||
{
|
{
|
||||||
$this->messages[] = $message;
|
if ($message->isError() /* && $message->isSuccess() @TODO next major v. */ ) {
|
||||||
|
$this->formContextMessages = array_merge([ $message ], $this->formContextMessages);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->formContextMessages[] = $message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages() : array
|
||||||
|
{
|
||||||
|
return $this->formContextMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function fillValues() : void
|
||||||
|
{
|
||||||
|
foreach($this->formContextValues as $property => $value) {
|
||||||
|
if ($this->canWriteProperty($property)) {
|
||||||
|
$this->$property = $this->castValue($property, $value);
|
||||||
|
$this->formContextDefinedProperties[$property] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canWriteProperty(string $property) : bool
|
||||||
|
{
|
||||||
|
# Skipping overrides of this particular class vars as a security measure
|
||||||
|
static $skipping = array_keys(array_change_key_case(get_class_vars(FormContext::class), CASE_LOWER));
|
||||||
|
|
||||||
|
return ! in_array(strtolower($property), array_merge($skipping, $this->formConstructorProperties)) && property_exists($this, $property);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function definedProperty(string $property) : bool
|
||||||
|
{
|
||||||
|
return $this->formContextDefinedProperties[$property] ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function castValue(string $property, mixed $value) : mixed
|
||||||
|
{
|
||||||
|
$cast = fn($e) => match(true) {
|
||||||
|
is_a($e, Context\FormContextCastableInterface::class, true) => $e::instance($value),
|
||||||
|
$e === "array" => (function(array|string $value) use ($property) {
|
||||||
|
if (is_string($value)) {
|
||||||
|
return json_validate($value) ? json_decode($value, true) ?? [] : throw new \InvalidArgumentException("Field '$property' is awaiting JSON encoded content.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
})($value),
|
||||||
|
is_null($value) => null,
|
||||||
|
enum_exists($e) => ( $value instanceof \BackedEnum ? $value : $e::tryFrom($value) ) ?? throw new \InvalidArgumentException(sprintf("Field '\$$property' awaiting values : '%s'.", implode("', '", array_map(fn($e) => $e->value, $e::cases())))),
|
||||||
|
function_exists($e) => $e($value),
|
||||||
|
class_exists($e) => new $e(...$value),
|
||||||
|
interface_exists($e) => throw new \InvalidArgumentException("There is no way provided to match an interface object yet."),
|
||||||
|
default => $value
|
||||||
|
};
|
||||||
|
|
||||||
|
$types = ( new \ReflectionProperty($this, $property) )->getType();
|
||||||
|
|
||||||
|
if ($types instanceof \ReflectionNamedType) {
|
||||||
|
$value = $cast($types->getName());
|
||||||
|
}
|
||||||
|
elseif ($types instanceof \ReflectionIntersectionType || $types instanceof \ReflectionUnionType) {
|
||||||
|
foreach($types->getTypes() as $type) {
|
||||||
|
return $cast($type->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($types->isBuiltin()) {
|
||||||
|
return $cast($types->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,16 @@
|
|||||||
|
|
||||||
namespace Picea\Ui\Method;
|
namespace Picea\Ui\Method;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
interface FormContextInterface {
|
interface FormContextInterface {
|
||||||
public function valid() : bool;
|
public function valid() : bool;
|
||||||
|
|
||||||
public function formSent() : bool;
|
public function formSent() : bool;
|
||||||
|
|
||||||
|
public function requestMethod() : string;
|
||||||
|
|
||||||
|
public function getRequest() : ServerRequestInterface;
|
||||||
|
|
||||||
|
public function getExecutionStatus() : mixed;
|
||||||
}
|
}
|
||||||
+18
-19
@@ -5,15 +5,25 @@ namespace Picea\Ui\Method;
|
|||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
class FormHandler {
|
class FormHandler {
|
||||||
|
const DEFAULT_METHODS = [
|
||||||
|
"DELETE", "PATCH", "POST", "PUT",
|
||||||
|
];
|
||||||
|
|
||||||
public bool $sent = false;
|
public bool $sent = false;
|
||||||
|
|
||||||
public ? bool $executionStatus = null;
|
public bool $validateCsrfToken = true;
|
||||||
|
|
||||||
|
public /* mixed */ $executionStatus = null;
|
||||||
|
|
||||||
|
public ? bool $validationStatus = null;
|
||||||
|
|
||||||
|
public FormInterface $form;
|
||||||
|
|
||||||
public FormContext $context;
|
public FormContext $context;
|
||||||
|
|
||||||
protected ServerRequestInterface $request;
|
protected ServerRequestInterface $request;
|
||||||
|
|
||||||
protected FormInterface $form;
|
public array $acceptedMethods = self::DEFAULT_METHODS;
|
||||||
|
|
||||||
public function __construct(ServerRequestInterface $request, FormInterface $form, ? FormContextInterface $context = null)
|
public function __construct(ServerRequestInterface $request, FormInterface $form, ? FormContextInterface $context = null)
|
||||||
{
|
{
|
||||||
@@ -28,37 +38,26 @@ class FormHandler {
|
|||||||
$this->context = method_exists($form, 'getContext') ? $form->getContext($request) : new FormContext($request);
|
$this->context = method_exists($form, 'getContext') ? $form->getContext($request) : new FormContext($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->request->context = $this->context;
|
$this->request->withAttribute('picea.context', $this->context);
|
||||||
|
|
||||||
$this->formSent();
|
|
||||||
$this->initialize();
|
$this->initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function formSent() : void
|
|
||||||
{
|
|
||||||
if ( false !== $this->context->formSent = $this->sent ) {
|
|
||||||
if ( $this->context->formName ?? false ) {
|
|
||||||
$this->sent = $this->context->formSent = (bool) ( $this->request->getParsedBody()['picea-ui-form'][$this->context->formName] ?? false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function initialize() : void
|
protected function initialize() : void
|
||||||
{
|
{
|
||||||
$this->form->initialize($this->context);
|
$this->form->initialize($this->context);
|
||||||
|
|
||||||
if ( $this->sent ) {
|
if ( $this->sent && $this->context->formSent() ) {
|
||||||
if ( $this->form->validate($this->context) ) {
|
if ( $this->validationStatus = $this->form->validate($this->context) ) {
|
||||||
$this->executionStatus = $this->form->execute($this->context);
|
$this->context->formExecutionStatus = $this->executionStatus = $this->form->execute($this->context);
|
||||||
|
$this->context->formExecuted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function requestSent() : bool
|
protected function requestSent() : bool
|
||||||
{
|
{
|
||||||
return in_array($this->request->getMethod(), [
|
return in_array(strtoupper($this->request->getMethod()), array_map('strtoupper', $this->acceptedMethods));
|
||||||
"DELETE", "PATCH", "POST", "PUT",
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function honeyPot() : bool
|
protected function honeyPot() : bool
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ interface FormInterface
|
|||||||
|
|
||||||
public function validate(FormContextInterface $context) : bool;
|
public function validate(FormContextInterface $context) : bool;
|
||||||
|
|
||||||
public function execute(FormContextInterface $context) : void;
|
public function execute(FormContextInterface $context);
|
||||||
}
|
}
|
||||||
@@ -4,4 +4,5 @@ namespace Picea\Ui\Method;
|
|||||||
|
|
||||||
interface FormMessage {
|
interface FormMessage {
|
||||||
public function isError() : bool;
|
public function isError() : bool;
|
||||||
|
# public function isSuccess() : bool; # Should be added in next major version
|
||||||
}
|
}
|
||||||
@@ -7,9 +7,10 @@ use Picea\Extension\Extension,
|
|||||||
|
|
||||||
use Picea\Compiler\Context;
|
use Picea\Compiler\Context;
|
||||||
|
|
||||||
|
use Picea\Extension\FunctionExtension;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
class Pagination implements Extension {
|
class Pagination implements Extension, FunctionExtension {
|
||||||
use ExtensionTrait;
|
use ExtensionTrait;
|
||||||
|
|
||||||
public array $tokens;
|
public array $tokens;
|
||||||
@@ -20,14 +21,16 @@ class Pagination implements Extension {
|
|||||||
|
|
||||||
public function __construct(ServerRequestInterface $request, Context $context) {
|
public function __construct(ServerRequestInterface $request, Context $context) {
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
$this->register($context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string { }
|
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string { }
|
||||||
|
|
||||||
public function register(Context $context) : void
|
|
||||||
|
public function exportFunctions(): array
|
||||||
{
|
{
|
||||||
$context->pushFunction("pagination", [ $this, 'paginationClass' ]);
|
return [
|
||||||
|
"pagination" => [ $this, 'paginationClass' ]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function paginationClass(string $name, ?string $url = null, ?array $params = null) : PaginationHandler
|
public function paginationClass(string $name, ?string $url = null, ?array $params = null) : PaginationHandler
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class PaginationHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function __toString() {
|
public function __toString() {
|
||||||
return "hello world";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,9 @@ class Ui {
|
|||||||
|
|
||||||
public function registerFormExtension(Compiler $compiler) : self
|
public function registerFormExtension(Compiler $compiler) : self
|
||||||
{
|
{
|
||||||
|
# BW compatibility with v1.x syntax
|
||||||
|
$compiler->registerExtension(new Form\Ui());
|
||||||
|
|
||||||
$compiler->registerExtension(new Form\UiForm());
|
$compiler->registerExtension(new Form\UiForm());
|
||||||
$compiler->registerExtension(new Form\UiInput());
|
$compiler->registerExtension(new Form\UiInput());
|
||||||
$compiler->registerExtension(new Form\UiCheckbox());
|
$compiler->registerExtension(new Form\UiCheckbox());
|
||||||
@@ -29,6 +32,7 @@ class Ui {
|
|||||||
$compiler->registerExtension(new Form\UiUrl());
|
$compiler->registerExtension(new Form\UiUrl());
|
||||||
$compiler->registerExtension(new Form\UiWeek());
|
$compiler->registerExtension(new Form\UiWeek());
|
||||||
$compiler->registerExtension(new Form\UiSelect());
|
$compiler->registerExtension(new Form\UiSelect());
|
||||||
|
$compiler->registerExtension(new Form\UiImage());
|
||||||
|
|
||||||
$compiler->registerExtension(new Component\UiPopup());
|
$compiler->registerExtension(new Component\UiPopup());
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user