commit a0bf5053771243a89762e9fd032a1d775802b421 Author: Dave Mc Nicoll Date: Wed Sep 4 15:17:57 2019 +0000 - First commit, work done on form input diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..6f965d4 --- /dev/null +++ b/composer.json @@ -0,0 +1,18 @@ +{ + "name": "mcnd/picea-ui", + "description": "A collection of various UI elements to integrate within Picea.", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Dave Mc Nicoll", + "email": "mcndave@gmail.com" + } + ], + "require": {}, + "autoload": { + "psr-4": { + "Picea\\Ui\\": "src/" + } + } +} diff --git a/src/Common/UiElement.php b/src/Common/UiElement.php new file mode 100644 index 0000000..4ff32bb --- /dev/null +++ b/src/Common/UiElement.php @@ -0,0 +1,426 @@ + [], + 'class' => [], + ]; + + public array $childs = []; + + /** + * supported options are for now : + * + * key value uses + * -------------------------------------------------------------------------------------------- + * single-tag 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 "\/" + */ + + public array $options = []; + + public $selected = null; + + public string $content; + + protected ?UiQuery $kwery = null; + + public function __construct() { + if ( ! static::$config ) { + static::pushConfigArray( include(dirname(__FILE__) . "/taglist.php") ); + } + } + + 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 + [ + 'href' => $href, + 'rel' => "stylesheet", + 'type' => "text/css" + ], [ 'tag-type' => 'single' ] + $options); + } + + public static function createScript(string $src, array $attributes = [], array $options = []) : self + { + return static::create('script', $attributes + [ + 'src' => $src, + 'type' => 'text/javascript', + ], $options); + } + + public static function createPhp(array $attributes = [], array $options = []) : self + { + return static::create('', $attributes, [ + 'force-tag-open' => ' ' ?>' + ] + $options); + } + + public static function createPhpEcho(array $attributes = [], array $options = []) : self + { + return static::create('', $attributes, [ + 'force-tag-open' => ' ' ?>' + ] + $options); + } + + public static function createSpan(string $text, array $attributes = [], array $options = []) : self + { + return static::create('span', $attributes, $options)->text($text); + } + + public static function create(string $tag = "div", array $attributes = [], array $options = []) : self + { + $obj = new static(); + + if ( strpos($tag, '.') !== false ) { + $obj->attributes['class'] = explode('.', $tag); + $tag = array_shift($obj->attributes['class']); + } + + $obj->tag = $tag; + $obj->name = $name; + + if ( $attributes ) { + $obj->attributes($attributes); + } + + if ( $custom = static::$config["tags"][$tag] ?? false ) { + if ( $custom['tag'] ?? false ) { + $obj->tag = $custom['tag']; + } + + if ( $custom['attributes'] ?? false ) { + $obj->attributes($custom['attributes']); + } + + if ( $custom['options'] ?? false ) { + $obj->options = array_replace_recursive($obj->options, $custom['options']); + } + } + + return $obj; + } + + public function createIn(string $tag, array $attributes = []) : self + { + $element = static::create($tag, $attributes); + $this->append($element); + return $element; + } + + public function addClass(string $classname) : self + { + $this->attributes['class'] = $classname; + return $this; + } + + public function removeClass($classname) { + if ( $key = array_search(strtolower($classname), array_map('strtolower', $this->attributes['class'])) ) { + unset($this->attributes['class'][$key]); + } + + return $this; + } + + public function option(string $var, $value = 1) { + $this->options[$var] = $value; + return $this; + } + + public function render() { + $attributesList = []; + + foreach ( $this->attr as $key => $value ) { + + if ( is_array($value) ) { + if (empty($value)) continue; + + if ($key === 'style') { + $style = []; + + foreach($value as $k2 => $v2 ) { + $style[] = "$k2:$v2"; + } + + $attributesList[] = "$key=\"".implode(';', $style).'"'; + + } + else { + $attributesList[] = implode(' ', $value); + } + }else if ( !is_numeric($key) ) { + # will output something like + $attributesList[] = "$key=\"$value\""; + } + else { + # will output something like + $attributesList[] = $value; + } + + } + + $content = ""; + + foreach ( $this->childs as $item ) { + if ( is_object($item) ) { + $content .= $item->render(); + } + else if ( is_string($item) ) { + $content .= $item; + } + } + + if ( $this->options->exist('no-tag') ) { + return $this->content . $content; + } + else { + $attributesstr = (count($attributesList) && !$this->options->exist('no-attr') ? " " . implode($attributesList, " ") : ""); + + # Force the node to contain a certain opening tag (php is a good example) + if ( $this->options->exist('force-tag-open') ) { + $opentag = $this->options['force-tag-open'] . $attributesstr; + } + else { + $opentag = $this->tag ? "<{$this->tag}" . $attributesstr . ">" : ""; + } + + if ( $this->options->exist('tag-type') ) { + if ( $this->options['tag-type'] === "single" ) { + return $opentag; + } + } + else if ( $this->options->exist('force-tag-close') ) { + $closetag = $this->options['force-tag-close']; + } + else { + $closetag = $this->tag ? "<" . ($this->options->exist('escape-tag-end') ? "\/" : "/" ) . "{$this->tag}>" : ""; + } + + return $opentag . $this->content . $content . $closetag; + } + } + + public function html($set = null) { + if ($set !== null) { + $this->content = $set; + return $this; + } + + return $this->content; + } + + public function text(?string $set = null) { + if ($set !== null) { + $this->content = htmlspecialchars( $args[0], ENT_NOQUOTES ); + return $this; + } + + return $this->content; + } + + public function append( ...$arguments ) { + return $this->insert( self::INSERT_MODE_APPEND, ...$arguments); + } + + public function prepend( ...$arguments ) { + return $this->insert( self::INSERT_MODE_PREPEND, ...(is_array($arguments) ? array_reverse($arguments) : $arguments)); + } + + public function insert($insert_mode = self::INSERT_MODE_APPEND, ...$arguments) { + if ( ! $arguments || !( $count = count($arguments) ) ) { + return $this; + } + + $insert = function($content) use ( $insert_mode ) { + if ( self::is_node($content) ) { + if ($insert_mode === self::INSERT_MODE_APPEND ) { + $this->childs[] = $content; + } + elseif ($insert_mode === self::INSERT_MODE_PREPEND ) { + array_unshift($this->childs, $content); + } + } + }; + + if ( $count == 1 && !is_array($arguments) ) { + // Single node to add + $insert($arguments); + } + else { + // multiple node to add + foreach ( $arguments as $item ) { + if ( is_array($item) ) { + foreach ( $item as $key => $value ) { + if ( !is_numeric($key) ) { + $value->name = $key; + } + + $this->insert($insert_mode, $value); + } + } + else { + $insert($item); + } + } + } + + return $this; + } + + public function attributes(array $attributes) : self + { + foreach($attributes as $key => $value) { + switch($key) { + case 'class' : + $this->addClass($value); + break; + + case 'style' : + foreach($value as $var => $val) { + $this->css($var, $val); + } + break; + + default: + if ( is_numeric($key) ) { + $this->attributes[] = $value; + } + elseif ( is_array($this->attributes[$key] ?? false) ) { + $this->attributes[$key] = array_replace($value, $this->attributes[$key]); + } + + $this->attributes[$key] = $value; + break; + } + } + + return $this; + } + + public function hasAttributes($key) { + return isset( $this->attributes[$key] ); + } + + /** + * Recursive function, allowing both $array as param and $key, $value + */ + public function css(...$arguments) { + foreach($arguments as $item) { + + if ( is_array($item) ) { + foreach($item as $key => $value) { + $this->css($key, $value); + } + } + else { + $this->attributes['style'][$arguments[0]] = $arguments[1]; + } + } + + return $this; + } + + public function hasClass(string $className) : bool + { + return array_search(strtolower($className), array_map('strtolower', $this->attributes['class']), true); + } + + public function delete() { + + } + + public function count() { + return count($this->childs); + } + + public function jsonSerialize() { + return [ + 'tag' => $this->tag, + 'attr' => $this->attr, + 'childs' => $this->childs, + 'options' => $this->options, + ]; + } + + 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 is_node($obj) : bool + { + return is_object($obj) && get_class($obj) === static::class; + } + + public function __toString() : string + { + return $this->render(); + } +} diff --git a/src/Common/taglist.php b/src/Common/taglist.php new file mode 100644 index 0000000..61486d2 --- /dev/null +++ b/src/Common/taglist.php @@ -0,0 +1,60 @@ + [ + "js" => [ + "tag" => "script", + "attributes" => [ "type" => "text/javascript" ] + ], + "css" => [ + "tag" => "link", + "attributes" => [ "rel" => "stylesheet" , "type" => "text/css" ] + ], + "!doctype" => [ + "attributes" => [ "html" ], + "options" => [ "tag-type" => "single" ] + ], + "document" => [ + "options" => [ "no-tag" => true ] + ], + "link" => [ + "options" => [ "tag-type" => "single" ] + ], + "meta" => [ + "options" => [ "tag-type" => "single" ] + ], + "img" => [ + "options" => [ "tag-type" => "single" ] + ], + "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" ] + ] + ] +]; diff --git a/src/Common/ui-element.json b/src/Common/ui-element.json new file mode 100644 index 0000000..413694a --- /dev/null +++ b/src/Common/ui-element.json @@ -0,0 +1,58 @@ +{ + "tags" : { + "js" : { + "tag" : "script", + "attributes" : { "type" : "text/javascript" } + }, + "css" : { + "tag" : "link", + "attributes" : { "rel" : "stylesheet" , "type" : "text/css" } + }, + "!doctype" : { + "attributes" : [ "html" ], + "options" : { "tag-type" : "single" } + }, + "document" : { + "options" : { "no-tag" : true } + }, + "link" : { + "options" : { "tag-type" : "single" } + }, + "meta" : { + "options" : { "tag-type" : "single" } + }, + "img" : { + "options" : { "tag-type" : "single" } + }, + "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" } + } + } +} diff --git a/src/Form/UiCheckbox.php b/src/Form/UiCheckbox.php new file mode 100644 index 0000000..59bd1fb --- /dev/null +++ b/src/Form/UiCheckbox.php @@ -0,0 +1,13 @@ + "ui-checkbox", + 'type' => "checkbox", + ]; +} diff --git a/src/Form/UiColor.php b/src/Form/UiColor.php new file mode 100644 index 0000000..4b5fd84 --- /dev/null +++ b/src/Form/UiColor.php @@ -0,0 +1,13 @@ + "ui-color", + 'type' => "color", + ]; +} diff --git a/src/Form/UiDate.php b/src/Form/UiDate.php new file mode 100644 index 0000000..a2eeeb4 --- /dev/null +++ b/src/Form/UiDate.php @@ -0,0 +1,13 @@ + "ui-date", + 'type' => "date", + ]; +} diff --git a/src/Form/UiEmail.php b/src/Form/UiEmail.php new file mode 100644 index 0000000..1e4b499 --- /dev/null +++ b/src/Form/UiEmail.php @@ -0,0 +1,13 @@ + "ui-email", + 'type' => "email", + ]; +} diff --git a/src/Form/UiFile.php b/src/Form/UiFile.php new file mode 100644 index 0000000..9a2bff0 --- /dev/null +++ b/src/Form/UiFile.php @@ -0,0 +1,13 @@ + "ui-file", + 'type' => "file", + ]; +} diff --git a/src/Form/UiForm.php b/src/Form/UiForm.php new file mode 100644 index 0000000..337cdde --- /dev/null +++ b/src/Form/UiForm.php @@ -0,0 +1,58 @@ + 'ui-form', + ]; + + public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string + { + list($action, $options) = array_pad(explode(',', $arguments), 2, null); + + switch($token) { + case "ui.form.get": + $this->attributes['method'] = "get"; + break; + + case "ui.form.post": + $this->attributes['method'] = "post"; + break; + + default: + case $this->token: + $this->attributes['method'] = $this->defaultMethod; + break; + } + + $this->attributes['action'] = $action; + $this->attributes($this->parseAttributes($options)); + return $this->render(); + } + + protected function parseAttributes(string $options) : array + { + if ( $options ?? false ) { + $attributes = eval($options); + + if ( ! is_array($attributes) ) { + throw new InvalidArgumentException("Given options `$options` is not a valid attributes array."); + } + + return $attributes; + } + + return []; + } +} diff --git a/src/Form/UiHidden.php b/src/Form/UiHidden.php new file mode 100644 index 0000000..9cb6c18 --- /dev/null +++ b/src/Form/UiHidden.php @@ -0,0 +1,13 @@ + "ui-hidden", + 'type' => "hidden", + ]; +} diff --git a/src/Form/UiInput.php b/src/Form/UiInput.php new file mode 100644 index 0000000..40eac6b --- /dev/null +++ b/src/Form/UiInput.php @@ -0,0 +1,47 @@ + 'ui-input', + ]; + + public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string + { + list($name, $value, $options) = array_pad(explode(',', $arguments), 3, null); + + $this->attributes['name'] = $name; + $this->setValue($value); + $this->attributes($this->parseAttributes($options)); + return $this->render(); + } + + protected function parseAttributes(string $options) : array + { + if ( $options ?? false ) { + $attributes = eval($options); + + if ( ! is_array($attributes) ) { + throw new InvalidArgumentException("Given options `$options` is not a valid attributes array."); + } + + return $attributes; + } + + return []; + } + + protected function setValue($value) : void + { + $this->attributes['value'] = $value; + } +} diff --git a/src/Form/UiNumeric.php b/src/Form/UiNumeric.php new file mode 100644 index 0000000..2c3ad5c --- /dev/null +++ b/src/Form/UiNumeric.php @@ -0,0 +1,13 @@ + "ui-numeric", + 'type' => "numeric", + ]; +} diff --git a/src/Form/UiPassword.php b/src/Form/UiPassword.php new file mode 100644 index 0000000..5c15980 --- /dev/null +++ b/src/Form/UiPassword.php @@ -0,0 +1,13 @@ + "ui-password", + 'type' => "password", + ]; +} diff --git a/src/Form/UiRadio.php b/src/Form/UiRadio.php new file mode 100644 index 0000000..be8d1d7 --- /dev/null +++ b/src/Form/UiRadio.php @@ -0,0 +1,13 @@ + "ui-radio", + 'type' => "radio", + ]; +} diff --git a/src/Form/UiRange.php b/src/Form/UiRange.php new file mode 100644 index 0000000..ae961ac --- /dev/null +++ b/src/Form/UiRange.php @@ -0,0 +1,13 @@ + "ui-range", + 'type' => "range", + ]; +} diff --git a/src/Form/UiSearch.php b/src/Form/UiSearch.php new file mode 100644 index 0000000..e12717f --- /dev/null +++ b/src/Form/UiSearch.php @@ -0,0 +1,13 @@ + "ui-search", + 'type' => "search", + ]; +} diff --git a/src/Form/UiTel.php b/src/Form/UiTel.php new file mode 100644 index 0000000..f117be2 --- /dev/null +++ b/src/Form/UiTel.php @@ -0,0 +1,20 @@ + "ui-tel", + 'type' => "tel", + ]; + + public string $defaultPattern = "[0-9]{1}-[0-9]{3}-[0-9]{3}-[0-9]{4}"; + + protected function parseAttributes(string $options) : array + { + return parent::parseAttributes($options) + [ 'pattern' => $this->defaultPattern ]; + } +} diff --git a/src/Form/UiText.php b/src/Form/UiText.php new file mode 100644 index 0000000..928b376 --- /dev/null +++ b/src/Form/UiText.php @@ -0,0 +1,13 @@ + "ui-text", + 'type' => "text", + ]; +} diff --git a/src/Form/UiTextarea.php b/src/Form/UiTextarea.php new file mode 100644 index 0000000..41c94fc --- /dev/null +++ b/src/Form/UiTextarea.php @@ -0,0 +1,19 @@ + "ui-textarea", + ]; + + protected function setValue($value) : void + { + $this->html($value); + } +} diff --git a/src/Form/UiTime.php b/src/Form/UiTime.php new file mode 100644 index 0000000..6b0e53d --- /dev/null +++ b/src/Form/UiTime.php @@ -0,0 +1,13 @@ + "ui-time", + 'type' => "time", + ]; +} diff --git a/src/Form/UiUrl.php b/src/Form/UiUrl.php new file mode 100644 index 0000000..f5ac8c6 --- /dev/null +++ b/src/Form/UiUrl.php @@ -0,0 +1,13 @@ + "ui-url", + 'type' => "url", + ]; +} diff --git a/src/Form/UiWeek.php b/src/Form/UiWeek.php new file mode 100644 index 0000000..fd3d621 --- /dev/null +++ b/src/Form/UiWeek.php @@ -0,0 +1,13 @@ + "ui-week", + 'type' => "week", + ]; +} diff --git a/src/Ui.php b/src/Ui.php new file mode 100644 index 0000000..a39425f --- /dev/null +++ b/src/Ui.php @@ -0,0 +1,33 @@ +registerExtension(new Form\UiForm()); + $compiler->registerExtension(new Form\UiInput()); + $compiler->registerExtension(new Form\UiCheckbox()); + $compiler->registerExtension(new Form\UiColor()); + $compiler->registerExtension(new Form\UiDate()); + $compiler->registerExtension(new Form\UiEmail()); + $compiler->registerExtension(new Form\UiFile()); + $compiler->registerExtension(new Form\UiHidden()); + $compiler->registerExtension(new Form\UiNumeric()); + $compiler->registerExtension(new Form\UiPassword()); + $compiler->registerExtension(new Form\UiRadio()); + $compiler->registerExtension(new Form\UiRange()); + $compiler->registerExtension(new Form\UiSearch()); + $compiler->registerExtension(new Form\UiTel()); + $compiler->registerExtension(new Form\UiText()); + $compiler->registerExtension(new Form\UiTextarea()); + $compiler->registerExtension(new Form\UiTime()); + $compiler->registerExtension(new Form\UiUrl()); + $compiler->registerExtension(new Form\UiWeek()); + + return $this; + } +}