Compare commits
21
Commits
512af58411
...
tokens_v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5397ed4fae | ||
|
|
30ddd4c98b | ||
|
|
f00c98b7ff | ||
|
|
3986437800 | ||
|
|
9190987b53 | ||
|
|
cd3cb18665 | ||
|
|
3a930c13b1 | ||
|
|
bd501bce86 | ||
|
|
1f5d38a8b9 | ||
|
|
164a27d267 | ||
|
|
ceebc6783a | ||
|
|
83fee32ac0 | ||
|
|
97f1f67af1 | ||
|
|
362184f81f | ||
|
|
0d9db0470e | ||
|
|
e75d997b02 | ||
|
|
34828f642c | ||
|
|
6020fa300d | ||
|
|
7437bbfc73 | ||
|
|
95c30be9f8 | ||
|
|
d05a750b39 |
No files matched your search
@@ -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,18 @@
|
||||
# `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>
|
||||
```
|
||||
@@ -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} }'
|
||||
```
|
||||
+32
-18
@@ -26,9 +26,9 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
* 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-close tag-close " " " " closing " " "
|
||||
* no-attr Attributes will not be rendered
|
||||
* no-id 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 "\/"
|
||||
* no-attr true Attributes will not be rendered
|
||||
* no-id true ID is not automatically given (* may be removed *)
|
||||
* 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 = [];
|
||||
@@ -148,7 +148,7 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function option(string $var, $value = 1) : self
|
||||
public function option(string $var, mixed $value = true) : self
|
||||
{
|
||||
$this->options[$var] = $value;
|
||||
|
||||
@@ -177,8 +177,10 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
}
|
||||
}
|
||||
else if ( !is_numeric($key) ) {
|
||||
# will output something like <tag $key=$value></tag>
|
||||
$attributesList[] = strpos($value, '"') !== false ? "$key='$value'" : "$key=\"$value\"";
|
||||
$value = htmlspecialchars($value);
|
||||
|
||||
# will output something like <tag $key="$value"></tag>
|
||||
$attributesList[] = "$key=\"$value\"";
|
||||
}
|
||||
else {
|
||||
# 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;
|
||||
}
|
||||
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)
|
||||
if ( in_array('force-tag-open', $this->options, true) ) {
|
||||
if ( $this->options['force-tag-open'] ?? false ) {
|
||||
$opentag = $this->options['force-tag-open'] . $compiledAttributes;
|
||||
}
|
||||
else {
|
||||
@@ -217,11 +219,11 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
$closetag = "";
|
||||
}
|
||||
}
|
||||
elseif ( in_array('force-tag-close', $this->options, true) ) {
|
||||
elseif ( $this->options['force-tag-close'] ?? false ) {
|
||||
$closetag = $this->options['force-tag-close'];
|
||||
}
|
||||
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;
|
||||
@@ -231,7 +233,7 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
public function html($set = null) {
|
||||
if ($set !== null) {
|
||||
$this->content = $set;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -240,7 +242,7 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
|
||||
public function text(?string $set = null) {
|
||||
if ($set !== null) {
|
||||
$this->content = htmlspecialchars( $set, ENT_NOQUOTES );
|
||||
$this->content = htmlspecialchars( $set, \ENT_NOQUOTES );
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -257,7 +259,6 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
{
|
||||
return $this->insert( static::INSERT_MODE_PREPEND, ...( is_array($arguments) ? array_reverse($arguments) : $arguments) );
|
||||
}
|
||||
|
||||
|
||||
protected function insert(int $mode, ...$elements) : self
|
||||
{
|
||||
@@ -285,14 +286,24 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
break;
|
||||
|
||||
case 'style' :
|
||||
foreach($value as $var => $val) {
|
||||
$this->css($var, $val);
|
||||
if ( is_string($value) )
|
||||
{
|
||||
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;
|
||||
|
||||
default:
|
||||
if ( is_numeric($key) ) {
|
||||
$this->attributes[] = $value;
|
||||
$this->attributes[$key] = $value;
|
||||
}
|
||||
elseif ( is_array($this->attributes[$key] ?? false) ) {
|
||||
$this->attributes[$key] = array_replace($value, $this->attributes[$key]);
|
||||
@@ -343,7 +354,8 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
return count($this->childs);
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
public function jsonSerialize() : mixed
|
||||
{
|
||||
return [
|
||||
'tag' => $this->tag,
|
||||
'attr' => $this->attributes,
|
||||
@@ -352,6 +364,7 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
];
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value) {
|
||||
if ( is_numeric($offset) ) {
|
||||
return $this->selected[$offset] = $value;
|
||||
@@ -394,6 +407,7 @@ class UiElement implements \ArrayAccess, \Iterator, \JsonSerializable {
|
||||
return key($this->childs);
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next() : self
|
||||
{
|
||||
return next($this->childs);
|
||||
|
||||
@@ -9,7 +9,7 @@ use Picea\Extension\ExtensionTrait;
|
||||
class UiMessage extends UiElement implements Extension {
|
||||
use ExtensionTrait;
|
||||
|
||||
public string $token = "ui.message";
|
||||
public string $token = "ui:message";
|
||||
|
||||
public string $tag = "input";
|
||||
|
||||
@@ -17,7 +17,7 @@ class UiMessage extends UiElement implements Extension {
|
||||
'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) : string
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -9,22 +9,22 @@ use Picea\Extension\ExtensionTrait;
|
||||
class UiPopup extends UiElement implements Extension {
|
||||
use ExtensionTrait;
|
||||
|
||||
public string $token = "ui.popup";
|
||||
public string $token = "ui:popup";
|
||||
|
||||
public string $tag = "div";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => 'ui-popup',
|
||||
'class' => 'ui-popup',
|
||||
];
|
||||
|
||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string
|
||||
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string
|
||||
{
|
||||
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');
|
||||
}
|
||||
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
<?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) : string
|
||||
{
|
||||
$opt = $context->tokenOptions($token);
|
||||
|
||||
switch(true) {
|
||||
case in_array('checkbox', $opt):
|
||||
$input = new UiCheckbox();
|
||||
break;
|
||||
|
||||
case in_array('color', $opt):
|
||||
$input = new UiColor();
|
||||
break;
|
||||
|
||||
case in_array('date', $opt):
|
||||
$input = new UiDate();
|
||||
break;
|
||||
|
||||
case in_array('datetime', $opt):
|
||||
$input = new UiDatetime();
|
||||
break;
|
||||
|
||||
case in_array('email', $opt):
|
||||
$input = new UiEmail();
|
||||
break;
|
||||
|
||||
case in_array('file', $opt):
|
||||
$input = new UiFile();
|
||||
break;
|
||||
|
||||
case in_array('form', $opt):
|
||||
case in_array('endform', $opt):
|
||||
$input = new UiForm();
|
||||
break;
|
||||
|
||||
case in_array('hidden', $opt):
|
||||
$input = new UiHidden();
|
||||
break;
|
||||
|
||||
case in_array('image', $opt):
|
||||
$input = new UiImage();
|
||||
break;
|
||||
|
||||
case in_array('input', $opt):
|
||||
$input = new UiInput();
|
||||
break;
|
||||
|
||||
case in_array('numeric', $opt):
|
||||
$input = new UiNumeric();
|
||||
break;
|
||||
|
||||
case in_array('password', $opt):
|
||||
$input = new UiPassword();
|
||||
break;
|
||||
|
||||
case in_array('radio', $opt):
|
||||
$input = new UiRadio();
|
||||
break;
|
||||
|
||||
case in_array('range', $opt):
|
||||
$input = new UiRange();
|
||||
break;
|
||||
|
||||
case in_array('search', $opt):
|
||||
$input = new UiSearch();
|
||||
break;
|
||||
|
||||
case in_array('select', $opt):
|
||||
$input = new UiSelect();
|
||||
break;
|
||||
|
||||
case in_array('tel', $opt):
|
||||
$input = new UiTel();
|
||||
break;
|
||||
|
||||
case in_array('text', $opt):
|
||||
$input = new UiText();
|
||||
break;
|
||||
|
||||
case in_array('textarea', $opt):
|
||||
$input = new UiTextarea();
|
||||
break;
|
||||
|
||||
case in_array('time', $opt):
|
||||
$input = new UiText();
|
||||
break;
|
||||
|
||||
case in_array('url', $opt):
|
||||
$input = new UiUrl();
|
||||
break;
|
||||
|
||||
case in_array('week', $opt):
|
||||
$input = new UiWeek();
|
||||
break;
|
||||
|
||||
case in_array('popup', $opt):
|
||||
$input = new UiPopup();
|
||||
break;
|
||||
|
||||
case in_array('message', $opt):
|
||||
$input = new UiMessage();
|
||||
break;
|
||||
}
|
||||
|
||||
if (empty($input)) {
|
||||
throw new \Exception("Missing token ? $token");
|
||||
}
|
||||
|
||||
return $input->parse($context, $arguments, $token);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiCheckbox extends UiRadio {
|
||||
|
||||
public string $token = "ui.checkbox";
|
||||
public string $token = "ui:checkbox";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-checkbox",
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiColor extends UiInput {
|
||||
|
||||
public string $token = "ui.color";
|
||||
public string $token = "ui:color";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-color",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiDate extends UiInput {
|
||||
|
||||
public string $token = "ui.date";
|
||||
public string $token = "ui:date";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-date",
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiDatetime extends UiInput {
|
||||
|
||||
public string $token = "ui.datetime";
|
||||
public string $token = "ui:datetime";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-datetime",
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiEmail extends UiInput {
|
||||
|
||||
public string $token = "ui.email";
|
||||
public string $token = "ui:email";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-email",
|
||||
|
||||
+6
-6
@@ -4,10 +4,10 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiFile extends UiInput {
|
||||
|
||||
public string $token = "ui.file";
|
||||
public string $token = "ui:file";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-file",
|
||||
'type' => "file",
|
||||
];
|
||||
}
|
||||
public array $attributes = [
|
||||
'class' => "ui-file",
|
||||
'type' => "file",
|
||||
];
|
||||
}
|
||||
+44
-28
@@ -11,7 +11,7 @@ class UiForm extends UiElement implements Extension {
|
||||
|
||||
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.get", "ui:form.post", "ui:form.patch", "ui:form.delete", "ui:form.put"*/ ];
|
||||
|
||||
public string $tag = "form";
|
||||
|
||||
@@ -19,44 +19,48 @@ class UiForm extends UiElement implements Extension {
|
||||
'class' => 'ui-form',
|
||||
];
|
||||
|
||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string
|
||||
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string
|
||||
{
|
||||
switch($token) {
|
||||
case 'ui.endform':
|
||||
case 'ui:endform':
|
||||
return "</form>";
|
||||
}
|
||||
|
||||
case "ui.form.get":
|
||||
$method = "get";
|
||||
break;
|
||||
$opt = $context->tokenOptions($token, true);
|
||||
|
||||
case "ui.form.patch":
|
||||
$method = "patch";
|
||||
break;
|
||||
|
||||
case "ui.form.delete":
|
||||
$method = "delete";
|
||||
break;
|
||||
|
||||
case "ui.form.put":
|
||||
$method = "put";
|
||||
break;
|
||||
|
||||
case "ui.form.post":
|
||||
$method = "post";
|
||||
break;
|
||||
if (in_array('get', $opt)) {
|
||||
$method = "get";
|
||||
}
|
||||
elseif (in_array('post', $opt)) {
|
||||
$method = "post";
|
||||
}
|
||||
elseif (in_array('put', $opt)) {
|
||||
$method = "put";
|
||||
}
|
||||
elseif (in_array('delete', $opt)) {
|
||||
$method = "delete";
|
||||
}
|
||||
elseif (in_array('patch', $opt)) {
|
||||
$method = "patch";
|
||||
}
|
||||
|
||||
$method ??= $this->defaultMethod;
|
||||
|
||||
return "<?php echo ( new \\" . static::class . "() )->buildHtml('$method', $arguments) ?>";
|
||||
|
||||
return "<?php echo ( new \\" . static::class . "() )->parseOptions($opt)->buildHtml('$method', $arguments) ?>";
|
||||
}
|
||||
|
||||
|
||||
public function parseOptions($options) : self
|
||||
{
|
||||
$this->options = $options;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function buildHtml(string $method = "get", string $name = "", string $action = "", array $attributes = []) : string
|
||||
{
|
||||
$method = strtolower($method);
|
||||
|
||||
|
||||
$this->option('tag-type', 'single');
|
||||
|
||||
|
||||
if ($attributes['class'] ?? false) {
|
||||
$attributes['class'] .= " {$this->attributes['class']}";
|
||||
@@ -66,11 +70,23 @@ class UiForm extends UiElement implements Extension {
|
||||
$this->attributes([ 'method' => $method, 'action' => $action ] + $attributes);
|
||||
|
||||
if ( $method !== "get" ) {
|
||||
$this->append( ( new UiHidden() )->attributes([ 'name' =>"picea-ui-form[$name]", 'value' => md5( $name . microtime() ) ]) );
|
||||
$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,
|
||||
]));
|
||||
|
||||
$this->attributes([ 'enctype' => "multipart/form-data" ]);
|
||||
}
|
||||
|
||||
|
||||
return $this->render() . PHP_EOL;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiHidden extends UiInput {
|
||||
|
||||
public string $token = "ui.hidden";
|
||||
public string $token = "ui:hidden";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-hidden",
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?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 string $tag = "img";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => 'ui-image',
|
||||
];
|
||||
|
||||
public array $options = [
|
||||
'tag-type' => "single",
|
||||
];
|
||||
|
||||
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : 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 [];
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use Picea\Extension\ExtensionTrait;
|
||||
class UiInput extends UiElement implements Extension {
|
||||
use ExtensionTrait;
|
||||
|
||||
public string $token = "ui.input";
|
||||
public string $token = "ui:input";
|
||||
|
||||
public string $tag = "input";
|
||||
|
||||
@@ -25,12 +25,12 @@ class UiInput extends UiElement implements Extension {
|
||||
|
||||
protected string $name;
|
||||
|
||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string
|
||||
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string
|
||||
{
|
||||
return "<?php echo ( new \\" . static::class . "() )->buildHtml($arguments) ?>";
|
||||
}
|
||||
|
||||
public function buildHtml(string $name, /*? mixed */ $value = null, array $attributes = [], array $options = []) : string
|
||||
public function buildHtml(string $name, mixed $value = null, array $attributes = [], array $options = []) : string
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
@@ -43,7 +43,7 @@ class UiInput extends UiElement implements Extension {
|
||||
}
|
||||
|
||||
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']);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiNumeric extends UiInput {
|
||||
|
||||
public string $token = "ui.numeric";
|
||||
public string $token = "ui:numeric";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-numeric",
|
||||
'type' => "numeric",
|
||||
'type' => "number",
|
||||
];
|
||||
}
|
||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiPassword extends UiInput {
|
||||
|
||||
public string $token = "ui.password";
|
||||
public string $token = "ui:password";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-password",
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiRadio extends UiInput {
|
||||
|
||||
public string $token = "ui.radio";
|
||||
public string $token = "ui:radio";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-radio",
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiRange extends UiInput {
|
||||
|
||||
public string $token = "ui.range";
|
||||
public string $token = "ui:range";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-range",
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiSearch extends UiInput {
|
||||
|
||||
public string $token = "ui.search";
|
||||
public string $token = "ui:search";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-search",
|
||||
|
||||
+16
-7
@@ -9,7 +9,7 @@ use Picea\Extension\ExtensionTrait;
|
||||
class UiSelect extends UiElement implements Extension {
|
||||
use ExtensionTrait;
|
||||
|
||||
public string $token = "ui.select";
|
||||
public string $token = "ui:select";
|
||||
|
||||
public string $tag = "select";
|
||||
|
||||
@@ -17,13 +17,18 @@ class UiSelect extends UiElement implements Extension {
|
||||
'class' => 'ui-select',
|
||||
];
|
||||
|
||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string
|
||||
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string
|
||||
{
|
||||
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->setList($list, $value, $strictComparison);
|
||||
|
||||
@@ -38,7 +43,6 @@ class UiSelect extends UiElement implements Extension {
|
||||
protected function setList(array $list, $selected, bool $strict = true) : void
|
||||
{
|
||||
foreach($list as $key => $item) {
|
||||
|
||||
if ($item instanceof UiElement) {
|
||||
$this->append($item);
|
||||
|
||||
@@ -49,13 +53,13 @@ class UiSelect extends UiElement implements Extension {
|
||||
$obj->attributes(['label' => $key]);
|
||||
|
||||
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 {
|
||||
$obj = $this->createOption($item, $key, $this->isSelected($key, $selected, $strict));
|
||||
$obj = $this->createOption((string) $item, $key, $this->isSelected($key, $selected, $strict));
|
||||
}
|
||||
|
||||
|
||||
$this->append($obj);
|
||||
}
|
||||
}
|
||||
@@ -75,6 +79,11 @@ class UiSelect extends UiElement implements Extension {
|
||||
|
||||
protected function isSelected($check, $value, bool $strict = true) : bool
|
||||
{
|
||||
if ( is_array($value) ) {
|
||||
return in_array($check, $value, $strict);
|
||||
}
|
||||
|
||||
|
||||
return $strict ? $check === $value : $check == $value;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiTel extends UiInput {
|
||||
|
||||
public string $token = "ui.tel";
|
||||
public string $token = "ui:tel";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-tel",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiText extends UiInput {
|
||||
|
||||
public string $token = "ui.text";
|
||||
public string $token = "ui:text";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-text",
|
||||
|
||||
+18
-2
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiTextarea extends UiInput {
|
||||
|
||||
public string $token = "ui.textarea";
|
||||
public array $tokens = [ "ui:textarea" ];
|
||||
|
||||
public string $tag = "textarea";
|
||||
|
||||
@@ -14,8 +14,24 @@ class UiTextarea extends UiInput {
|
||||
|
||||
public array $options = [];
|
||||
|
||||
protected bool $echoRaw = false;
|
||||
|
||||
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string
|
||||
{
|
||||
$raw = in_array('raw', $context->tokenOptions($token, true)) ? 'true' : 'false';
|
||||
|
||||
return "<?php echo ( new \\" . static::class . "() )->echoRaw($raw)->buildHtml($arguments) ?>";
|
||||
}
|
||||
|
||||
protected function setValue($value) : void
|
||||
{
|
||||
$this->html($value);
|
||||
$this->echoRaw ? $this->html($value) : $this->text($value);
|
||||
}
|
||||
|
||||
public function echoRaw(bool $set) : self
|
||||
{
|
||||
$this->echoRaw = $set;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiTime extends UiInput {
|
||||
|
||||
public string $token = "ui.time";
|
||||
public string $token = "ui:time";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-time",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiUrl extends UiInput {
|
||||
|
||||
public string $token = "ui.url";
|
||||
public string $token = "ui:url";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-url",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace Picea\Ui\Form;
|
||||
|
||||
class UiWeek extends UiInput {
|
||||
|
||||
public string $token = "ui.week";
|
||||
public string $token = "ui:week";
|
||||
|
||||
public array $attributes = [
|
||||
'class' => "ui-week",
|
||||
|
||||
+8
-22
@@ -7,9 +7,10 @@ use Picea\Extension\Extension,
|
||||
|
||||
use Picea\Compiler\Context;
|
||||
|
||||
use Picea\Extension\FunctionExtension;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class Form implements Extension {
|
||||
class Form implements Extension, FunctionExtension {
|
||||
use ExtensionTrait;
|
||||
|
||||
public array $tokens;
|
||||
@@ -22,34 +23,19 @@ class Form implements Extension {
|
||||
|
||||
public function __construct(ServerRequestInterface $request, Context $context) {
|
||||
$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) : string { }
|
||||
|
||||
public function register(Context $context) : void
|
||||
public function exportFunctions(): array
|
||||
{
|
||||
$context->pushFunction("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;
|
||||
return [
|
||||
"form" => [ $this, 'formClass' ],
|
||||
];
|
||||
}
|
||||
|
||||
public function formClass(FormInterface $form, ? FormContext $formContext = null) : FormHandler
|
||||
{
|
||||
return new FormHandler($this->request, $form, $formContext);
|
||||
return new FormHandler($formContext ? $formContext->request : $this->request, $form, $formContext);
|
||||
}
|
||||
}
|
||||
@@ -9,25 +9,43 @@ class FormContext implements FormContextInterface
|
||||
{
|
||||
public string $formName;
|
||||
|
||||
public bool $formSent;
|
||||
|
||||
public bool $formSent = false;
|
||||
|
||||
public bool $formExecuted = false;
|
||||
|
||||
public mixed $formExecutionStatus = null;
|
||||
|
||||
public array $values = [];
|
||||
|
||||
public array $files = [];
|
||||
|
||||
public array $messages = [];
|
||||
|
||||
public bool $skipCsrf = false;
|
||||
|
||||
public ServerRequestInterface $request;
|
||||
|
||||
public ? ResponseInterface $response = null;
|
||||
|
||||
public function __construct(ServerRequestInterface $request, ? string $formName = null)
|
||||
public function __construct(ServerRequestInterface $request, ? string $formName = null)
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
|
||||
if ( $formName ) {
|
||||
$this->formName = $formName;
|
||||
$this->formName = $formName;
|
||||
}
|
||||
|
||||
|
||||
$this->values = $request->getParsedBody() ?: [];
|
||||
|
||||
if ( ! $this->values ) {
|
||||
$content = utf8_encode((string) $request->getBody());
|
||||
|
||||
if ( $content && ( $json = json_decode($content, true) ) ) {
|
||||
$this->values = $json;
|
||||
}
|
||||
}
|
||||
|
||||
$this->files = $request->getUploadedFiles() ?: [];
|
||||
}
|
||||
|
||||
public function valid() : bool
|
||||
@@ -40,12 +58,35 @@ class FormContext implements FormContextInterface
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function executed() : bool
|
||||
{
|
||||
return $this->formExecuted;
|
||||
}
|
||||
|
||||
public function formSent() : bool
|
||||
{
|
||||
return $this->formSent;
|
||||
$valid = in_array($this->request->getMethod(), [ 'POST', 'PUT', 'PATCH', 'DELETE', ]);
|
||||
|
||||
if ( ! $this->skipCsrf && ($this->formName ?? false) ) {
|
||||
$token = $this->get('picea-ui-form')[$this->formName] ?? false;
|
||||
|
||||
if ( $token ) {
|
||||
if ($this->validateCsrfToken) {
|
||||
$valid = in_array($token, $_SESSION["picea-ui.form:{$this->formName}"] ?? []);
|
||||
}
|
||||
else {
|
||||
$valid = (bool) $token;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->formSent = $valid;
|
||||
}
|
||||
|
||||
|
||||
public function __set($key, $value)
|
||||
{
|
||||
return $this->set($key, $value);
|
||||
|
||||
+20
-21
@@ -5,15 +5,25 @@ namespace Picea\Ui\Method;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class FormHandler {
|
||||
const DEFAULT_METHODS = [
|
||||
"DELETE", "PATCH", "POST", "PUT",
|
||||
];
|
||||
|
||||
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;
|
||||
|
||||
protected ServerRequestInterface $request;
|
||||
|
||||
protected FormInterface $form;
|
||||
public array $acceptedMethods = self::DEFAULT_METHODS;
|
||||
|
||||
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->request->context = $this->context;
|
||||
|
||||
$this->formSent();
|
||||
$this->request->withAttribute('picea.context', $this->context);
|
||||
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
public 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
|
||||
{
|
||||
$this->form->initialize($this->context);
|
||||
|
||||
if ( $this->sent ) {
|
||||
if ( $this->form->validate($this->context) ) {
|
||||
$this->executionStatus = $this->form->execute($this->context);
|
||||
if ( $this->sent && $this->context->formSent() ) {
|
||||
if ( $this->validationStatus = $this->form->validate($this->context) ) {
|
||||
$this->context->formExecutionStatus = $this->executionStatus = $this->form->execute($this->context);
|
||||
$this->context->formExecuted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function requestSent() : bool
|
||||
{
|
||||
return in_array($this->request->getMethod(), [
|
||||
"DELETE", "PATCH", "POST", "PUT",
|
||||
]);
|
||||
return in_array(strtoupper($this->request->getMethod()), array_map('strtoupper', $this->acceptedMethods));
|
||||
}
|
||||
|
||||
protected function honeyPot() : bool
|
||||
|
||||
@@ -8,5 +8,5 @@ interface FormInterface
|
||||
|
||||
public function validate(FormContextInterface $context) : bool;
|
||||
|
||||
public function execute(FormContextInterface $context) : void;
|
||||
public function execute(FormContextInterface $context);
|
||||
}
|
||||
@@ -7,9 +7,10 @@ use Picea\Extension\Extension,
|
||||
|
||||
use Picea\Compiler\Context;
|
||||
|
||||
use Picea\Extension\FunctionExtension;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class Pagination implements Extension {
|
||||
class Pagination implements Extension, FunctionExtension {
|
||||
use ExtensionTrait;
|
||||
|
||||
public array $tokens;
|
||||
@@ -20,14 +21,16 @@ class Pagination implements Extension {
|
||||
|
||||
public function __construct(ServerRequestInterface $request, Context $context) {
|
||||
$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) : 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
|
||||
|
||||
@@ -41,7 +41,7 @@ class PaginationHandler {
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return "hello world";
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,6 +8,9 @@ class Ui {
|
||||
|
||||
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\UiInput());
|
||||
$compiler->registerExtension(new Form\UiCheckbox());
|
||||
@@ -29,6 +32,7 @@ class Ui {
|
||||
$compiler->registerExtension(new Form\UiUrl());
|
||||
$compiler->registerExtension(new Form\UiWeek());
|
||||
$compiler->registerExtension(new Form\UiSelect());
|
||||
$compiler->registerExtension(new Form\UiImage());
|
||||
|
||||
$compiler->registerExtension(new Component\UiPopup());
|
||||
|
||||
|
||||
Reference in new issue
Block a user