/** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Response/Response.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST; use ARUNOX\HTTP; use ARUNOX\Core\Hooks; use ARUNOX\Core\Errors; use ARUNOX\Database\BaseEntity; use ARUNOX\Plugins\REST\Notifications; use ARUNOX\Plugins\REST\Wrapper\Modal; use ARUNOX\Plugins\REST\Notifications\Type; class Response { /* ### VARIABLES ################################################ # */ protected static array $RESPONSE = []; /* ### PUBLIC FUNCTIONS ######################################### # */ public static function setTemplate( string $name ) { static::$RESPONSE = array_filter( static::$RESPONSE, function( $item ) { return $item->type != "template"; } ); return static::add([ "type" => "template", "template" => $name, "title" => Hooks::filter( 'template::pagetitle', NULL ), "copyright" => "2025 ARUNOX", ]); } /* ### PUBLIC ADD FUNCTIONS ##################################### # */ public static function add( mixed $item ) { $item = (object)$item; return static::$RESPONSE[] = Hooks::filterWithNames( 'rest::response->add', $item->type, $item ); } public static function addUpdater( array $uuid, mixed $data = [], bool $status = TRUE ) { return static::add([ "type" => "updater", "updater" => preg_replace('/[^a-zA-Z0-9]/', '', base64_encode( implode(":", $uuid ) ) ), "status" => $status, "data" => Hooks::filter( 'rest::response->data', $data ), ]); } public static function addLink( string $url, string $rel = "" ) { return static::add([ "type" => "generator", "generator" => "header/link", "data" => [ "href" => $url, "rel" => $rel, ], ]); } public static function addClick( array $uuid, null|string $selector = NULL ) { return static::add([ "type" => "click", "parent" => base64_encode( implode(":", $uuid ) ), "selector" => $selector ? "[data-key^='{$selector}']" : NULL, ]); } public static function addWrapper( string $name, ...$args ) { if( !static::hasTemplate() ) { static::setTemplate( "default" ); } $name = ucfirst( $name ); $class = "ARUNOX\\Plugins\\REST\\Wrapper\\{$name}"; $object = $class::create( ...$args ); return static::add( $object ); } public static function addModal( string $name, ...$args ) { $name = strtolower( $name ); $object = Modal::{$name}( ...$args ); $object->setId( base64_encode( REQUEST_ROUTE ) ); return static::add( $object ); } public static function addNotify( string|Type $type, string $message, ?string $icon = NULL, ?int $duration = NULL, ?string $title = NULL, ...$args) { $notify = Notifications::create( $type, $message, $icon, $duration, $title ); foreach( static::$RESPONSE as &$response ) { if( is_a( $response, Notifications::class ) ) { $response = $notify->top(); return $notify; } } static::add( $notify->top() ); return $notify; } /* ### PUBLIC GET FUNCTIONS ##################################### # */ public static function get( string $type, ?string $key = NULL, ?string $value = NULL ) { return array_filter( static::$RESPONSE, function( $item ) use( $type, $key, $value ) { if( $item->type != $type ) return FALSE; if( $key && $value ) { return $item->{$key} == $value; } return TRUE; }); } public static function getTemplate( ?string $name = NULL ) { return static::get( "template", "template", $name ); } public static function getUpdater() { return static::get( "updater" ); } public static function getRoute() { return !!static::get( "route" ); } public static function getWrapper() { return static::get( "wrapper" ); } public static function getGenerator() { return static::get( "generator" ); } /* ### PUBLIC HAS FUNCTIONS ##################################### # */ public static function hasTemplate( ?string $name = NULL ) { return !!static::get( "template", "template", $name ); } public static function hasRoute() { return !!static::get( "route" ); } public static function hasUpdater() { return !!static::get( "updater" ); } public static function hasWrapper() { return !!static::get( "wrapper" ); } public static function hasGenerator() { return !!static::get( "generator" ); } /* ### PUBLIC FUNCTIONS ######################################### # */ public static function closeModal( string $name ){ return static::add( Modal::close( base64_encode( $name ) ) ); } public static function closeModals(){ return static::add( Modal::close("#ALL") ); } public static function redirect( string $route ){ return static::add([ "type" => "route", "route" => $route ]); } public static function refresh( string $hash = "" ){ return static::redirect( "/" . HTTP\Request::referer("path") . "#" . preg_replace( "/^#/", "", $hash ) ); } /* ### PUBLIC RESPONSE FUNCTIONS ################################ # */ public static function &response() : array { // if( !static::hasTemplate() ) { // static::setTemplate( "default" ); // } // if( Errors::has() ) { // foreach( Errors::getAll() as $error ) { // static::addNotify(...[ // "type" => $error["color"], // "message" => $error["message"], // "title" => $error["title"], // "icon" => $error["icon"], // ]); // } // } $template = NULL; $content = []; $generator = []; foreach( static::$RESPONSE as $item ) { if( $item->type == "template" ) $template = $item; else if( $item->type == "wrapper" ) $content[] = $item; else $generator[] = $item; } if( $template ) { $template->content = $content; $generator[] = $template; } else $generator = array_merge( $generator, $content ); // show( static::$RESPONSE, "STOP" ); return $generator; show( $generator, static::$RESPONSE, FALSE ); return static::$RESPONSE; } public static function json() : string { // show( json_encode( static::response() ), json_last_error(), json_last_error_msg(), static::response() ); return json_encode( static::response() ); } public static function jsonResponse( int $status = 200, ?array $data = NULL ) : HTTP\Response { $data = Hooks::filter( 'response::data', $data ); return new HTTP\Response( $status, [ "Content-Type" => "application/json" ], json_encode( $data ?? static::response() ) ); } public static function autoResponse( int $status = 200, ?string $message = NULL, ?array $data = NULL, null|string $title = "Serverantwort" ) : HTTP\Response { $data = Hooks::filter( 'response::data', $data ); // if( $status !== 200 || $message ) Errors::add( $status, $message, $title ); // $json = $data ?? static::response(); if( $status !== 200 ) $json = array_merge( [ "code" => $status, "message" => $message ?? static::$ERRORMSG[ $status ], "title" => $title ], $data ?? [] ); else if( $message ) $json = array_merge( [ "code" => $status, "message" => $message, "title" => $title ], $data ?? [] ); else $json = $data ?? static::response(); return new HTTP\Response( $status, [ "Content-Type" => "application/json" ], json_encode( $json ) ); } /* ### PRIVATE FUNCTIONS ######################################## # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Helpers/ButtonHelpers.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Helpers; use ARUNOX\Plugins\REST\Generator\Components\Button; trait ButtonHelpers { /* ### TRAIT INIT FUNCTIONS ##################################### # */ public function ButtonHelpersInit() { $this->addAllowedConfig( "buttons", [ "generator" => Button::class, "args" => [], ]); } /* ### PUBLIC FUNCTIONS ######################################### # */ public function setButtons( $args ) { return $this->addButtons( ...$args ); } public function addButton( ...$args ) { return $this->add( "buttons", ...$args ); } public function addButtons( ...$args ) { foreach( $args as $arg ) { $this->addButton( ...$arg ); } return $this; } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Helpers/ActionHelpers.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Helpers; trait ActionHelpers { /* ### TRAIT INIT FUNCTIONS ##################################### # */ public function ActionHelpersInit() { $this->addAllowedConfig( "action", [ "generator" => NULL, "args" => [], ]); } /* ### PUBLIC FUNCTIONS ######################################### # */ public function setNamedAction( string $name = "action", null|bool|string|array $route = NULL, ?string $method = 'GET', null|array $query = [] ) : self { if( $route === FALSE ) return $this; if( is_array( $route ) ) { extract( array_replace([ "route" => NULL, "method" => "GET", ], $route )); } $this->config[ $name ] = []; // if( $api && !str_starts_with( $api, '/api/' ) ) { // throw new \Exception("❌ Ungültige API-URL für Formular: {$api}"); // } if( $route ) $this->config[ $name ]["route"] = $route; if( $method ) $this->config[ $name ]["method"] = $method; if( $query ) $this->config[ $name ]["query"] = $query; return $this; } public function getNamedAction( string $name = "action", ...$args ) { if( key_exists( $name, $this->config ) ) { $value = $this->config[ $name ]; foreach( $args as $arg ) { if( key_exists( $arg, $value ) ) { $value = $value[ $arg ]; } } return $value; } return NULL; } public function setAction( null|string|array $route = NULL, ?string $method = 'GET' ) : self { return $this->setNamedAction( "action", $route, $method ); } public function getAction( ...$args ) { return $this->getNamedAction( "action", ...$args ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Helpers/TextHelpers.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Helpers; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Components\Headline; use ARUNOX\Plugins\REST\Generator\Components\Paragraph; trait TextHelpers { /* ### TRAIT INIT FUNCTIONS ##################################### # */ public function TextHelpersInit() { $this->addAllowedContent( "headline", [ "generator" => Headline::class, "args" => [ Config\Type::String ], ]); $this->addAllowedContent( "text", [ "generator" => Paragraph::class, "args" => [ Config\Type::String ], ]); } /* ### PUBLIC FUNCTIONS ######################################### # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Generator.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; class Generator { /* ### PUBLIC FUNCTIONS ######################################### # */ public static function create( string $generator, ...$args ) : BaseGenerator { if( !class_exists( $generator ) ) { $generator = self::resolveClassName( $generator ); } $generator = new $generator( ...$args ); return $generator->getReturnObject(); } /* ### PRIVATE FUNCTIONS ######################################## # */ private static function resolveClassName( string $type ) : string { $type = str_replace(__CLASS__ , "", $type ); $type = trim( $type, "\\/" ); $type = explode('/', $type ); $type = array_map('ucfirst', $type ); $type = implode( "\\", $type ); return __CLASS__ . '\\' . $type; } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Table/Helpers/TableHelpers.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Table\Helpers; use ARUNOX\Plugins\REST\Generator\Data\Table; trait TableHelpers { /* ### TRAIT INIT FUNCTIONS ##################################### # */ public function TableHelpersInit() { $this->addAllowedContent( "table", [ "generator" => Table::class, "args" => [], ]); } /* ### PUBLIC FUNCTIONS ######################################### # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Table/Column/Type.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Table\Column; /* ### ENUMS ######################################################## # */ enum Type : string { case Date = "date"; case Tel = "tel"; case URL = "url"; case Text = "text"; case Icon = "icon"; case Small = "small"; case Time = "time"; case Email = "email"; case Amount = "amount"; case Status = "status"; case Actions = "actions"; case DateTime = "datetime"; /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Table/Column/Column.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Table; use ARUNOX\Plugins\REST\Generator; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; use ARUNOX\Plugins\REST\Generator\Helpers\ActionHelpers; use ARUNOX\Plugins\REST\Generator\Helpers\ButtonHelpers; use ARUNOX\Plugins\REST\Generator\Data\Table\Column\Type; class Column extends BaseGenerator { /* ### TRAITS ################################################### # */ use ActionHelpers, ButtonHelpers; /* ### VARIABLES ################################################ # */ protected static ?string $PRIMARYKEY = "key"; protected array $ALLOWED_CONFIG = [ "key" => Config\Type::String, "label" => Config\Type::String, "title" => Config\Type::String, "icon" => Config\Type::String, "emoji" => Config\Type::String, "visible" => Config\Type::Bool, "sortable" => Config\Type::Bool, "searchable" => Config\Type::Bool, "type" => Type::class, "color" => Config\Color::class, "options" => Config\Type::Array, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function beforeColumn( ...$args ) { if( isset( $args["type"] ) ) { $class = "ARUNOX\\Plugins\\REST\\Generator\\Data\\Table\\Column\\" . $args["type"]->name; if( class_exists( $class ) ) { $field = Generator::create( $class, ...$args ); $this->before( "columns", $field ); return $this; } } $this->before( "columns", ...$args ); return $this; } public function afterColumn( ...$args ) { if( isset( $args["type"] ) ) { $class = "ARUNOX\\Plugins\\REST\\Generator\\Data\\Table\\Column\\" . $args["type"]->name; if( class_exists( $class ) ) { $field = Generator::create( $class, ...$args ); $this->after( "columns", $field ); return $this; } } $this->after( "columns", ...$args ); return $this; } /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Table/Column/Status.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Table\Column; use ARUNOX\Plugins\REST\Generator; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Helpers\ActionHelpers; class Status extends Generator\Data\Table\Column { /* ### TRAITS ################################################### # */ use ActionHelpers; /* ### VARIABLES ################################################ # */ protected array $ALLOWED_CONTENT = []; protected array $ALLOWED_CONFIG = [ "options" => Config\Type::Array, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Table/Column/Actions.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Table\Column; use ARUNOX\Plugins\REST\Generator; use ARUNOX\Plugins\REST\Generator\Helpers\ButtonHelpers; class Actions extends Generator\Data\Table\Column { /* ### TRAITS ################################################### # */ use ButtonHelpers; /* ### VARIABLES ################################################ # */ protected static array $CONFIGS = []; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Table/Table.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data; use ARUNOX\Database\BaseEntity; use ARUNOX\Plugins\REST\Generator; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseWrapper; use ARUNOX\Plugins\REST\Generator\Data\Table\Column; use ARUNOX\Plugins\REST\Generator\Helpers\ActionHelpers; use ARUNOX\Plugins\REST\Generator\Helpers\ButtonHelpers; class Table extends BaseWrapper { /* ### TRAITS ################################################### # */ use ActionHelpers, ButtonHelpers; /* ### VARIABLES ################################################ # */ protected ?string $GENERATOR = "table"; protected string $CHILDREN_NAME = "columns"; protected array $ALLOWED_CONTENT = []; protected array $ALLOWED_DATA = [ "data" => BaseEntity::class, ]; protected array $ALLOWED_CONFIG = [ "search" => Config\Type::Bool, "columns" => [ "generator" => Column::class, "args" => [], ] ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function setApi( ...$args ) { return $this->setNamedAction( "api", ...$args ); } public function getApi( ...$args ) { return $this->getNamedAction( "api", ...$args ); } public function setOrder( string $column, string $order = "asc", array $rules = [], array $buttons = [] ) : self { $this->config["order"] = [ "column" => strtolower( $column ), "order" => strtolower( $order ), "rules" => $rules, "buttons" => [], ]; foreach( $buttons as $button ) { $this->config["order"]["buttons"][] = Generator::create( 'ARUNOX\Plugins\REST\Generator\Components\Button', ...$button ); } return $this; } public function getOrder( ...$args ) { return parent::getConfig( "order", ...$args ); } public function addColumn( ...$args ) { if( isset( $args["type"] ) ) { $class = "ARUNOX\\Plugins\\REST\\Generator\\Data\\Table\\Column\\" . $args["type"]->name; if( class_exists( $class ) ) { $field = Generator::create( $class, ...$args ); $this->add( "columns", $field ); return $this; } } $this->add( "columns", ...$args ); return $this; } public function getColumn( ?string $name = NULL, ...$args ) { if( $name ) { foreach( $this->config["columns"] as $column ) { if( $column->getKey() == $name ) { return $column; } } } else return end( $this->config["columns"] ); } public function getColumns() { return $this->config["columns"] ?? []; } public function addColumns( ...$args ) { foreach( $args as $arg ) { $this->addColumn( ...$arg ); } } public function removeColumn( string $name ) { $column = $this->getColumn( $name ); return $column->removeColumns(); } /* ### JSON FUNCTIONS ########################################### # */ public function jsonSerialize() : array { if( $api = $this->getApi() ) { $routes = []; $columns = []; $routes[] = $api["route"]; $routes[] = $this->getAction("route"); foreach( $this->getColumns() as $column ) { if( $column->getConfig("type") == Column\Type::Actions ) continue; $columns[] = $column->getKey(); $routes[] = $column->getAction("route"); } foreach( $this->getOrder( "rules" ) as $name => $rule ) { if( !isset( $rule["replace" ] ) ) continue; $columns[] = $name; foreach( $rule["replace" ] as $column => $replace ) { $columns[] = $replace; } } $routes = array_filter( $routes ); $routes = array_unique( $routes ); foreach( $routes as $route ) { if( preg_match_all('/\{([^\}]+)\}/', $route, $matches ) ) { $columns = array_merge( $columns, $matches[1] ); } } // show( $routes, $this->getColumn() ); $columns = array_filter( $columns ); $columns = array_unique( $columns ); // show( $columns, $api ); $api["query"] ??= []; $api["query"]["columns"] = implode( ",", $columns ); // $api["route"] = http_build_url( $api["route"], [ // ]); // show( $columns, $api ); $this->setApi( ...$api ); } return parent::jsonSerialize(); } /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Helpers/GroupHelpers.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form\Helpers; use ARUNOX\Plugins\REST\Generator\Data\Form\Group; trait GroupHelpers { /* ### TRAIT INIT FUNCTIONS ##################################### # */ public function GroupHelpersInit() { $this->addAllowedContent( "group", [ "generator" => Group::class, "args" => [], ]); } /* ### PUBLIC FUNCTIONS ######################################### # */ public function addGroup( ...$args ) { $this->add( "group", ...$args ); return isset( $args[1] ) ? $this : end( $this->content ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Helpers/ControlHelpers.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form\Helpers; use ARUNOX\Plugins\REST\Generator; use ARUNOX\Plugins\REST\Generator\Data\Form\Control; trait ControlHelpers { /* ### TRAIT INIT FUNCTIONS ##################################### # */ public function ControlHelpersInit() { $this->addAllowedContent( "field", [ "generator" => Control::class, "args" => [], ]); } /* ### PUBLIC FUNCTIONS ######################################### # */ public function addField( ...$args ) { if( isset( $args["type"] ) ) { $class = "ARUNOX\\Plugins\\REST\\Generator\\Data\\Form\\Control\\" . $args["type"]->name; } if( class_exists( $class ) ) { $field = Generator::create( $class, ...$args ); $this->add( "field", $field ); } else $this->add( "field", ...$args ); return $this; } public function addFields( ...$args ) { foreach( $args as $arg ) { $this->addField( ...$arg ); } } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Form.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseWrapper; use ARUNOX\Plugins\REST\Generator\Helpers\TextHelpers; use ARUNOX\Plugins\REST\Generator\Helpers\ActionHelpers; use ARUNOX\Plugins\REST\Generator\Helpers\ButtonHelpers; use ARUNOX\Plugins\REST\Generator\Data\Form\Helpers\GroupHelpers; use ARUNOX\Plugins\REST\Generator\Data\Form\Helpers\ControlHelpers; class Form extends BaseWrapper { /* ### TRAITS ################################################### # */ use TextHelpers, ActionHelpers, ControlHelpers, ButtonHelpers, GroupHelpers; /* ### VARIABLES ################################################ # */ protected ?string $GENERATOR = "form"; protected array $ALLOWED_CONTENT = []; protected array $ALLOWED_CONFIG = []; /* ### PUBLIC FUNCTIONS ######################################### # */ public function setApi( ...$args ) { return $this->setNamedAction( "api", ...$args ); } public function getApi( ...$args ) { return $this->getNamedAction( "api", ...$args ); } public function addTab( ...$args ) { $this->addContent( "layouts/tabs", ...$args ); return end( $this->content ); } /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Group.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; use ARUNOX\Plugins\REST\Generator\Helpers\TextHelpers; use ARUNOX\Plugins\REST\Generator\Data\Form\Helpers\ControlHelpers; class Group extends BaseGenerator { /* ### TRAITS ################################################### # */ use TextHelpers, ControlHelpers; /* ### VARIABLES ################################################ # */ protected static ?string $PRIMARYKEY = "title"; protected ?string $GENERATOR = "data/form/group"; protected array $ALLOWED_CONTENT = []; protected array $ALLOWED_CONFIG = [ "title" => Config\Type::String, "border" => Config\Type::Bool, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( ?string $title = NULL, ...$fields ) { parent::__construct(); if( $title ) $this->setTitle( $title ); $this->addFields( ...$fields ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Control/Progress.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form\Control; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Data\Form\Control; class Progress extends Control { /* ### VARIABLES ################################################ # */ protected array $ALLOWED_CONFIG = [ "api" => Config\Type::String, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Control/Checkbox.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form\Control; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Data\Form\Control; class Checkbox extends Control { /* ### VARIABLES ################################################ # */ protected array $ALLOWED_CONFIG = [ "options" => Config\Type::Array, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Control/Range.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form\Control; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Data\Form\Control; class Range extends Control { /* ### VARIABLES ################################################ # */ protected array $ALLOWED_CONFIG = [ "min" => Config\Type::Numeric, "max" => Config\Type::Numeric, "step" => Config\Type::Numeric, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Control/Textarea.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form\Control; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Data\Form\Control; class Textarea extends Control { /* ### VARIABLES ################################################ # */ protected array $ALLOWED_CONFIG = [ "lines" => Config\Type::Numeric, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Control/Radio.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form\Control; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Data\Form\Control; class Radio extends Control { /* ### VARIABLES ################################################ # */ protected array $ALLOWED_CONFIG = [ "options" => Config\Type::Array, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Control/Type.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form\Control; /* ### ENUMS ######################################################## # */ enum Type : string { case Tel = "tel"; case URL = "url"; case Text = "text"; case Code = "code"; case Date = "date"; case Time = "time"; case File = "file"; case Range = "range"; case Email = "email"; case Radio = "radio"; case Select = "select"; case Hidden = "hidden"; case Number = "number"; case Custom = "custom"; case DateTime = "datetime-local"; case Textarea = "textarea"; case Checkbox = "checkbox"; case Password = "password"; case Progress = "progress"; /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Control/Password.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form\Control; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Data\Form\Control; class Password extends Control { /* ### VARIABLES ################################################ # */ protected array $ALLOWED_CONFIG = [ "confirm" => Config\Type::String, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Control/Select.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form\Control; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Data\Form\Control; class Select extends Control { /* ### VARIABLES ################################################ # */ protected array $ALLOWED_CONFIG = [ "choose" => Config\Type::String, "choose_id" => [ Config\Type::String, Config\Type::Numeric ], "options" => Config\Type::Array, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Data/Form/Control/Control.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Data\Form; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; use ARUNOX\Plugins\REST\Generator\Helpers\ActionHelpers; use ARUNOX\Plugins\REST\Generator\Data\Form\Control\Type; class Control extends BaseGenerator { /* ### TRAITS ################################################### # */ use ActionHelpers; /* ### VARIABLES ################################################ # */ protected static ?string $PRIMARYKEY = "key"; protected ?string $GENERATOR = "control"; protected array $ALLOWED_CONFIG = [ "key" => Config\Type::String, "label" => Config\Type::String, "title" => Config\Type::String, "hint" => Config\Type::String, "required" => Config\Type::String, "readonly" => Config\Type::Bool, "validate" => [ Config\Type::Array, Config\Type::String ], "value" => [ Config\Type::Scalar, Config\Type::Array ], "type" => Control\Type::class ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Components/Headline.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Components; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; class Headline extends BaseGenerator { /* ### VARIABLES ################################################ # */ protected static ?string $PRIMARYKEY = "title"; protected ?string $GENERATOR = "headline"; protected array $ALLOWED_CONFIG = [ "title" => Config\Type::String, "size" => Config\Type::Int, "icon" => Config\Type::String, "emoji" => Config\Type::String, "color" => Config\Color::class, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( string $title, int $size = 1 ) { parent::__construct(); $this->setTitle( $title ); $this->setSize( $size ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Components/Button/Button.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Components; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; use ARUNOX\Plugins\REST\Generator\Helpers\ActionHelpers; use ARUNOX\Plugins\REST\Generator\Components\Button\Type; class Button extends BaseGenerator { /* ### TRAITS ################################################### # */ use ActionHelpers; /* ### VARIABLES ################################################ # */ protected static ?string $PRIMARYKEY = "label"; protected ?string $GENERATOR = "button"; protected array $ALLOWED_CONFIG = [ "value" => Config\Type::Scalar, "name" => Config\Type::String, "label" => Config\Type::String, "href" => Config\Type::String, "target" => Config\Type::String, "title" => Config\Type::String, "icon" => Config\Type::String, "emoji" => Config\Type::String, "plural" => Config\Type::String, "order" => Config\Type::Array, "visibility" => Config\Type::Array, "color" => Config\Color::class, "type" => Type::class, "dropdowns" => [ "generator" => Button::class, "args" => [], ], ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function setDropdowns( $args ) { return $this->addDropdowns( ...$args ); } public function addDropdown( ...$args ) { // show( "dropdowns", $args ); return $this->add( "dropdowns", ...$args ); } public function addDropdowns( ...$args ) { foreach( $args as $arg ) { $this->addDropdown( ...$arg ); } return $this; } /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Components/Button/Type.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Components\Button; /* ### ENUMS ######################################################## # */ enum Type : string { case Link = "link"; case Button = "button"; case Submit = "submit"; case Cancel = "cancel"; case Reset = "reset"; /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Components/Paragraph.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Components; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; class Paragraph extends BaseGenerator { /* ### VARIABLES ################################################ # */ protected static ?string $PRIMARYKEY = "text"; protected ?string $GENERATOR = "paragraph"; protected array $ALLOWED_CONFIG = [ "text" => Config\Type::String, "icon" => Config\Type::String, "emoji" => Config\Type::String, "color" => Config\Color::class, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( ?string $text = NULL ) { parent::__construct(); $this->setText( $text ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Layouts/Tabs/Tabs.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Layouts; use ARUNOX\Plugins\REST\Generator; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Layouts\Tabs\Tab; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; class Tabs extends BaseGenerator { /* ### VARIABLES ################################################ # */ protected ?string $GENERATOR = "tabs"; protected string $CHILDREN_NAME = "tabs"; protected array $ALLOWED_CONTENT = []; protected array $ALLOWED_CONFIG = [ "tabs" => [ "generator" => Tab::class, "args" => [ Config\Type::String, ], ], ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function addTab( ...$args ) { if( isset( $args["type"] ) ) { $class = "ARUNOX\\Plugins\\REST\\Generator\\Layout\\Tabs\\Tab\\" . $args["type"]->name; if( class_exists( $class ) ) { $field = Generator::create( $class, ...$args ); $this->add( "tabs", $field ); return $this; } } $this->add( "tabs", ...$args ); return $this; } public function getTab( ?string $name = NULL, ...$args ) { if( $name ) { foreach( $this->config["tabs"] as $tab ) { if( $tab->getKey() == $name ) { return $tab; } } } else return end( $this->config["tabs"] ); } public function getTabs() { return $this->config["tabs"] ?? []; } public function addTabs( ...$args ) { foreach( $args as $arg ) { $this->addTab( ...$arg ); } } public function removeTab( string $name ) { $tab = $this->getTab( $name ); return $tab->removeTabs(); } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( ...$args ) { parent::__construct(); $this->addTab( ...$args ); // $this->add( "tab", ...$args ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Layouts/Tabs/Tab.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Layouts\Tabs; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseWrapper; use ARUNOX\Plugins\REST\Generator\Data\Form\Control; use ARUNOX\Plugins\REST\Generator\Components\Headline; use ARUNOX\Plugins\REST\Generator\Components\Paragraph; use ARUNOX\Plugins\REST\Generator\Helpers\ActionHelpers; use ARUNOX\Plugins\REST\Generator\Components\Button; use ARUNOX\Plugins\REST\Generator\Data\Form\Helpers\GroupHelpers; use ARUNOX\Plugins\REST\Generator\Data\Table\Helpers\TableHelpers; use ARUNOX\Plugins\REST\Generator\Data\Form\Helpers\ControlHelpers; class Tab extends BaseWrapper { /* ### TRAITS ################################################### # */ use ActionHelpers, ControlHelpers, GroupHelpers, TableHelpers; /* ### VARIABLES ################################################ # */ protected ?string $GENERATOR = NULL; protected static ?string $PRIMARYKEY = "label"; protected array $ALLOWED_CONTENT = [ "headline" => [ "generator" => Headline::class, "args" => [ Config\Type::String ], ], "text" => [ "generator" => Paragraph::class, "args" => [ Config\Type::String ], ], ]; protected array $ALLOWED_CONFIG = [ "id" => Config\Type::String, "label" => Config\Type::String, "title" => Config\Type::String, "icon" => Config\Type::String, "emoji" => Config\Type::String, "active" => Config\Type::Bool, "type" => Button\Type::class, "color" => Config\Color::class, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function setId( string $id ) { $id = strtolower( $id ); $id = iconv("UTF-8", "ASCII//TRANSLIT", $id ); $id = filter_var( $id, FILTER_SANITIZE_EMAIL ); $this->config["id"] = $id; return $this; } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( string $label, ?string $id = NULL ) { parent::__construct(); $this->setLabel( __( $label ) ); $this->setId( strtolower( $id ?? $label ) ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Config/Color.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Config; /* ### ENUMS ######################################################## # */ use ARUNOX\API\PHP\Traits; use ARUNOX\Interfaces\Supports; enum Color : string implements Supports\AsOptions { use Traits\Enums\AsOptions; /* ### CASES #################################################### # */ case Primary = "primary"; case Secondary = "secondary"; case Light = "border"; case Default = "text"; case Medium = "gray"; case Info = "info"; case Success = "success"; case Warning = "warning"; case Danger = "danger"; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Config/Type.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Config; /* ### ENUMS ######################################################## # */ enum Type : string { case Int = "int"; case Null = "null"; case Bool = "bool"; case Array = "array"; case Float = "float"; case String = "string"; case Scalar = "scalar"; case Numeric = "numeric"; case Traversable = "..."; /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Config/Icon.php **/ "\f101", Icon::Alarm => "\f102", Icon::AlignBottom => "\f103", Icon::AlignCenter => "\f104", Icon::AlignEnd => "\f105", Icon::AlignMiddle => "\f106", Icon::AlignStart => "\f107", Icon::AlignTop => "\f108", Icon::Alt => "\f109", Icon::AppIndicator => "\f10a", Icon::App => "\f10b", Icon::ArchiveFill => "\f10c", Icon::Archive => "\f10d", Icon::Arrow90degDown => "\f10e", Icon::Arrow90degLeft => "\f10f", Icon::Arrow90degRight => "\f110", Icon::Arrow90degUp => "\f111", Icon::ArrowBarDown => "\f112", Icon::ArrowBarLeft => "\f113", Icon::ArrowBarRight => "\f114", Icon::ArrowBarUp => "\f115", Icon::ArrowClockwise => "\f116", Icon::ArrowCounterclockwise => "\f117", Icon::ArrowDownCircleFill => "\f118", Icon::ArrowDownCircle => "\f119", Icon::ArrowDownLeftCircleFill => "\f11a", Icon::ArrowDownLeftCircle => "\f11b", Icon::ArrowDownLeftSquareFill => "\f11c", Icon::ArrowDownLeftSquare => "\f11d", Icon::ArrowDownLeft => "\f11e", Icon::ArrowDownRightCircleFill => "\f11f", Icon::ArrowDownRightCircle => "\f120", Icon::ArrowDownRightSquareFill => "\f121", Icon::ArrowDownRightSquare => "\f122", Icon::ArrowDownRight => "\f123", Icon::ArrowDownShort => "\f124", Icon::ArrowDownSquareFill => "\f125", Icon::ArrowDownSquare => "\f126", Icon::ArrowDownUp => "\f127", Icon::ArrowDown => "\f128", Icon::ArrowLeftCircleFill => "\f129", Icon::ArrowLeftCircle => "\f12a", Icon::ArrowLeftRight => "\f12b", Icon::ArrowLeftShort => "\f12c", Icon::ArrowLeftSquareFill => "\f12d", Icon::ArrowLeftSquare => "\f12e", Icon::ArrowLeft => "\f12f", Icon::ArrowRepeat => "\f130", Icon::ArrowReturnLeft => "\f131", Icon::ArrowReturnRight => "\f132", Icon::ArrowRightCircleFill => "\f133", Icon::ArrowRightCircle => "\f134", Icon::ArrowRightShort => "\f135", Icon::ArrowRightSquareFill => "\f136", Icon::ArrowRightSquare => "\f137", Icon::ArrowRight => "\f138", Icon::ArrowUpCircleFill => "\f139", Icon::ArrowUpCircle => "\f13a", Icon::ArrowUpLeftCircleFill => "\f13b", Icon::ArrowUpLeftCircle => "\f13c", Icon::ArrowUpLeftSquareFill => "\f13d", Icon::ArrowUpLeftSquare => "\f13e", Icon::ArrowUpLeft => "\f13f", Icon::ArrowUpRightCircleFill => "\f140", Icon::ArrowUpRightCircle => "\f141", Icon::ArrowUpRightSquareFill => "\f142", Icon::ArrowUpRightSquare => "\f143", Icon::ArrowUpRight => "\f144", Icon::ArrowUpShort => "\f145", Icon::ArrowUpSquareFill => "\f146", Icon::ArrowUpSquare => "\f147", Icon::ArrowUp => "\f148", Icon::ArrowsAngleContract => "\f149", Icon::ArrowsAngleExpand => "\f14a", Icon::ArrowsCollapse => "\f14b", Icon::ArrowsExpand => "\f14c", Icon::ArrowsFullscreen => "\f14d", Icon::ArrowsMove => "\f14e", Icon::AspectRatioFill => "\f14f", Icon::AspectRatio => "\f150", Icon::Asterisk => "\f151", Icon::At => "\f152", Icon::AwardFill => "\f153", Icon::Award => "\f154", Icon::Back => "\f155", Icon::BackspaceFill => "\f156", Icon::BackspaceReverseFill => "\f157", Icon::BackspaceReverse => "\f158", Icon::Backspace => "\f159", Icon::Badge3dFill => "\f15a", Icon::Badge3d => "\f15b", Icon::Badge4kFill => "\f15c", Icon::Badge4k => "\f15d", Icon::Badge8kFill => "\f15e", Icon::Badge8k => "\f15f", Icon::BadgeAdFill => "\f160", Icon::BadgeAd => "\f161", Icon::BadgeArFill => "\f162", Icon::BadgeAr => "\f163", Icon::BadgeCcFill => "\f164", Icon::BadgeCc => "\f165", Icon::BadgeHdFill => "\f166", Icon::BadgeHd => "\f167", Icon::BadgeTmFill => "\f168", Icon::BadgeTm => "\f169", Icon::BadgeVoFill => "\f16a", Icon::BadgeVo => "\f16b", Icon::BadgeVrFill => "\f16c", Icon::BadgeVr => "\f16d", Icon::BadgeWcFill => "\f16e", Icon::BadgeWc => "\f16f", Icon::BagCheckFill => "\f170", Icon::BagCheck => "\f171", Icon::BagDashFill => "\f172", Icon::BagDash => "\f173", Icon::BagFill => "\f174", Icon::BagPlusFill => "\f175", Icon::BagPlus => "\f176", Icon::BagXFill => "\f177", Icon::BagX => "\f178", Icon::Bag => "\f179", Icon::BarChartFill => "\f17a", Icon::BarChartLineFill => "\f17b", Icon::BarChartLine => "\f17c", Icon::BarChartSteps => "\f17d", Icon::BarChart => "\f17e", Icon::BasketFill => "\f17f", Icon::Basket => "\f180", Icon::Basket2Fill => "\f181", Icon::Basket2 => "\f182", Icon::Basket3Fill => "\f183", Icon::Basket3 => "\f184", Icon::BatteryCharging => "\f185", Icon::BatteryFull => "\f186", Icon::BatteryHalf => "\f187", Icon::Battery => "\f188", Icon::BellFill => "\f189", Icon::Bell => "\f18a", Icon::Bezier => "\f18b", Icon::Bezier2 => "\f18c", Icon::Bicycle => "\f18d", Icon::BinocularsFill => "\f18e", Icon::Binoculars => "\f18f", Icon::BlockquoteLeft => "\f190", Icon::BlockquoteRight => "\f191", Icon::BookFill => "\f192", Icon::BookHalf => "\f193", Icon::Book => "\f194", Icon::BookmarkCheckFill => "\f195", Icon::BookmarkCheck => "\f196", Icon::BookmarkDashFill => "\f197", Icon::BookmarkDash => "\f198", Icon::BookmarkFill => "\f199", Icon::BookmarkHeartFill => "\f19a", Icon::BookmarkHeart => "\f19b", Icon::BookmarkPlusFill => "\f19c", Icon::BookmarkPlus => "\f19d", Icon::BookmarkStarFill => "\f19e", Icon::BookmarkStar => "\f19f", Icon::BookmarkXFill => "\f1a0", Icon::BookmarkX => "\f1a1", Icon::Bookmark => "\f1a2", Icon::BookmarksFill => "\f1a3", Icon::Bookmarks => "\f1a4", Icon::Bookshelf => "\f1a5", Icon::BootstrapFill => "\f1a6", Icon::BootstrapReboot => "\f1a7", Icon::Bootstrap => "\f1a8", Icon::BorderAll => "\f1a9", Icon::BorderBottom => "\f1aa", Icon::BorderCenter => "\f1ab", Icon::BorderInner => "\f1ac", Icon::BorderLeft => "\f1ad", Icon::BorderMiddle => "\f1ae", Icon::BorderOuter => "\f1af", Icon::BorderRight => "\f1b0", Icon::BorderStyle => "\f1b1", Icon::BorderTop => "\f1b2", Icon::BorderWidth => "\f1b3", Icon::Border => "\f1b4", Icon::BoundingBoxCircles => "\f1b5", Icon::BoundingBox => "\f1b6", Icon::BoxArrowDownLeft => "\f1b7", Icon::BoxArrowDownRight => "\f1b8", Icon::BoxArrowDown => "\f1b9", Icon::BoxArrowInDownLeft => "\f1ba", Icon::BoxArrowInDownRight => "\f1bb", Icon::BoxArrowInDown => "\f1bc", Icon::BoxArrowInLeft => "\f1bd", Icon::BoxArrowInRight => "\f1be", Icon::BoxArrowInUpLeft => "\f1bf", Icon::BoxArrowInUpRight => "\f1c0", Icon::BoxArrowInUp => "\f1c1", Icon::BoxArrowLeft => "\f1c2", Icon::BoxArrowRight => "\f1c3", Icon::BoxArrowUpLeft => "\f1c4", Icon::BoxArrowUpRight => "\f1c5", Icon::BoxArrowUp => "\f1c6", Icon::BoxSeam => "\f1c7", Icon::Box => "\f1c8", Icon::Braces => "\f1c9", Icon::Bricks => "\f1ca", Icon::BriefcaseFill => "\f1cb", Icon::Briefcase => "\f1cc", Icon::BrightnessAltHighFill => "\f1cd", Icon::BrightnessAltHigh => "\f1ce", Icon::BrightnessAltLowFill => "\f1cf", Icon::BrightnessAltLow => "\f1d0", Icon::BrightnessHighFill => "\f1d1", Icon::BrightnessHigh => "\f1d2", Icon::BrightnessLowFill => "\f1d3", Icon::BrightnessLow => "\f1d4", Icon::BroadcastPin => "\f1d5", Icon::Broadcast => "\f1d6", Icon::BrushFill => "\f1d7", Icon::Brush => "\f1d8", Icon::BucketFill => "\f1d9", Icon::Bucket => "\f1da", Icon::BugFill => "\f1db", Icon::Bug => "\f1dc", Icon::Building => "\f1dd", Icon::Bullseye => "\f1de", Icon::CalculatorFill => "\f1df", Icon::Calculator => "\f1e0", Icon::CalendarCheckFill => "\f1e1", Icon::CalendarCheck => "\f1e2", Icon::CalendarDateFill => "\f1e3", Icon::CalendarDate => "\f1e4", Icon::CalendarDayFill => "\f1e5", Icon::CalendarDay => "\f1e6", Icon::CalendarEventFill => "\f1e7", Icon::CalendarEvent => "\f1e8", Icon::CalendarFill => "\f1e9", Icon::CalendarMinusFill => "\f1ea", Icon::CalendarMinus => "\f1eb", Icon::CalendarMonthFill => "\f1ec", Icon::CalendarMonth => "\f1ed", Icon::CalendarPlusFill => "\f1ee", Icon::CalendarPlus => "\f1ef", Icon::CalendarRangeFill => "\f1f0", Icon::CalendarRange => "\f1f1", Icon::CalendarWeekFill => "\f1f2", Icon::CalendarWeek => "\f1f3", Icon::CalendarXFill => "\f1f4", Icon::CalendarX => "\f1f5", Icon::Calendar => "\f1f6", Icon::Calendar2CheckFill => "\f1f7", Icon::Calendar2Check => "\f1f8", Icon::Calendar2DateFill => "\f1f9", Icon::Calendar2Date => "\f1fa", Icon::Calendar2DayFill => "\f1fb", Icon::Calendar2Day => "\f1fc", Icon::Calendar2EventFill => "\f1fd", Icon::Calendar2Event => "\f1fe", Icon::Calendar2Fill => "\f1ff", Icon::Calendar2MinusFill => "\f200", Icon::Calendar2Minus => "\f201", Icon::Calendar2MonthFill => "\f202", Icon::Calendar2Month => "\f203", Icon::Calendar2PlusFill => "\f204", Icon::Calendar2Plus => "\f205", Icon::Calendar2RangeFill => "\f206", Icon::Calendar2Range => "\f207", Icon::Calendar2WeekFill => "\f208", Icon::Calendar2Week => "\f209", Icon::Calendar2XFill => "\f20a", Icon::Calendar2X => "\f20b", Icon::Calendar2 => "\f20c", Icon::Calendar3EventFill => "\f20d", Icon::Calendar3Event => "\f20e", Icon::Calendar3Fill => "\f20f", Icon::Calendar3RangeFill => "\f210", Icon::Calendar3Range => "\f211", Icon::Calendar3WeekFill => "\f212", Icon::Calendar3Week => "\f213", Icon::Calendar3 => "\f214", Icon::Calendar4Event => "\f215", Icon::Calendar4Range => "\f216", Icon::Calendar4Week => "\f217", Icon::Calendar4 => "\f218", Icon::CameraFill => "\f219", Icon::CameraReelsFill => "\f21a", Icon::CameraReels => "\f21b", Icon::CameraVideoFill => "\f21c", Icon::CameraVideoOffFill => "\f21d", Icon::CameraVideoOff => "\f21e", Icon::CameraVideo => "\f21f", Icon::Camera => "\f220", Icon::Camera2 => "\f221", Icon::CapslockFill => "\f222", Icon::Capslock => "\f223", Icon::CardChecklist => "\f224", Icon::CardHeading => "\f225", Icon::CardImage => "\f226", Icon::CardList => "\f227", Icon::CardText => "\f228", Icon::CaretDownFill => "\f229", Icon::CaretDownSquareFill => "\f22a", Icon::CaretDownSquare => "\f22b", Icon::CaretDown => "\f22c", Icon::CaretLeftFill => "\f22d", Icon::CaretLeftSquareFill => "\f22e", Icon::CaretLeftSquare => "\f22f", Icon::CaretLeft => "\f230", Icon::CaretRightFill => "\f231", Icon::CaretRightSquareFill => "\f232", Icon::CaretRightSquare => "\f233", Icon::CaretRight => "\f234", Icon::CaretUpFill => "\f235", Icon::CaretUpSquareFill => "\f236", Icon::CaretUpSquare => "\f237", Icon::CaretUp => "\f238", Icon::CartCheckFill => "\f239", Icon::CartCheck => "\f23a", Icon::CartDashFill => "\f23b", Icon::CartDash => "\f23c", Icon::CartFill => "\f23d", Icon::CartPlusFill => "\f23e", Icon::CartPlus => "\f23f", Icon::CartXFill => "\f240", Icon::CartX => "\f241", Icon::Cart => "\f242", Icon::Cart2 => "\f243", Icon::Cart3 => "\f244", Icon::Cart4 => "\f245", Icon::CashStack => "\f246", Icon::Cash => "\f247", Icon::Cast => "\f248", Icon::ChatDotsFill => "\f249", Icon::ChatDots => "\f24a", Icon::ChatFill => "\f24b", Icon::ChatLeftDotsFill => "\f24c", Icon::ChatLeftDots => "\f24d", Icon::ChatLeftFill => "\f24e", Icon::ChatLeftQuoteFill => "\f24f", Icon::ChatLeftQuote => "\f250", Icon::ChatLeftTextFill => "\f251", Icon::ChatLeftText => "\f252", Icon::ChatLeft => "\f253", Icon::ChatQuoteFill => "\f254", Icon::ChatQuote => "\f255", Icon::ChatRightDotsFill => "\f256", Icon::ChatRightDots => "\f257", Icon::ChatRightFill => "\f258", Icon::ChatRightQuoteFill => "\f259", Icon::ChatRightQuote => "\f25a", Icon::ChatRightTextFill => "\f25b", Icon::ChatRightText => "\f25c", Icon::ChatRight => "\f25d", Icon::ChatSquareDotsFill => "\f25e", Icon::ChatSquareDots => "\f25f", Icon::ChatSquareFill => "\f260", Icon::ChatSquareQuoteFill => "\f261", Icon::ChatSquareQuote => "\f262", Icon::ChatSquareTextFill => "\f263", Icon::ChatSquareText => "\f264", Icon::ChatSquare => "\f265", Icon::ChatTextFill => "\f266", Icon::ChatText => "\f267", Icon::Chat => "\f268", Icon::CheckAll => "\f269", Icon::CheckCircleFill => "\f26a", Icon::CheckCircle => "\f26b", Icon::CheckSquareFill => "\f26c", Icon::CheckSquare => "\f26d", Icon::Check => "\f26e", Icon::Check2All => "\f26f", Icon::Check2Circle => "\f270", Icon::Check2Square => "\f271", Icon::Check2 => "\f272", Icon::ChevronBarContract => "\f273", Icon::ChevronBarDown => "\f274", Icon::ChevronBarExpand => "\f275", Icon::ChevronBarLeft => "\f276", Icon::ChevronBarRight => "\f277", Icon::ChevronBarUp => "\f278", Icon::ChevronCompactDown => "\f279", Icon::ChevronCompactLeft => "\f27a", Icon::ChevronCompactRight => "\f27b", Icon::ChevronCompactUp => "\f27c", Icon::ChevronContract => "\f27d", Icon::ChevronDoubleDown => "\f27e", Icon::ChevronDoubleLeft => "\f27f", Icon::ChevronDoubleRight => "\f280", Icon::ChevronDoubleUp => "\f281", Icon::ChevronDown => "\f282", Icon::ChevronExpand => "\f283", Icon::ChevronLeft => "\f284", Icon::ChevronRight => "\f285", Icon::ChevronUp => "\f286", Icon::CircleFill => "\f287", Icon::CircleHalf => "\f288", Icon::CircleSquare => "\f289", Icon::Circle => "\f28a", Icon::ClipboardCheck => "\f28b", Icon::ClipboardData => "\f28c", Icon::ClipboardMinus => "\f28d", Icon::ClipboardPlus => "\f28e", Icon::ClipboardX => "\f28f", Icon::Clipboard => "\f290", Icon::ClockFill => "\f291", Icon::ClockHistory => "\f292", Icon::Clock => "\f293", Icon::CloudArrowDownFill => "\f294", Icon::CloudArrowDown => "\f295", Icon::CloudArrowUpFill => "\f296", Icon::CloudArrowUp => "\f297", Icon::CloudCheckFill => "\f298", Icon::CloudCheck => "\f299", Icon::CloudDownloadFill => "\f29a", Icon::CloudDownload => "\f29b", Icon::CloudDrizzleFill => "\f29c", Icon::CloudDrizzle => "\f29d", Icon::CloudFill => "\f29e", Icon::CloudFogFill => "\f29f", Icon::CloudFog => "\f2a0", Icon::CloudFog2Fill => "\f2a1", Icon::CloudFog2 => "\f2a2", Icon::CloudHailFill => "\f2a3", Icon::CloudHail => "\f2a4", Icon::CloudHazeFill => "\f2a6", Icon::CloudHaze => "\f2a7", Icon::CloudHaze2Fill => "\f2a8", Icon::CloudLightningFill => "\f2a9", Icon::CloudLightningRainFill => "\f2aa", Icon::CloudLightningRain => "\f2ab", Icon::CloudLightning => "\f2ac", Icon::CloudMinusFill => "\f2ad", Icon::CloudMinus => "\f2ae", Icon::CloudMoonFill => "\f2af", Icon::CloudMoon => "\f2b0", Icon::CloudPlusFill => "\f2b1", Icon::CloudPlus => "\f2b2", Icon::CloudRainFill => "\f2b3", Icon::CloudRainHeavyFill => "\f2b4", Icon::CloudRainHeavy => "\f2b5", Icon::CloudRain => "\f2b6", Icon::CloudSlashFill => "\f2b7", Icon::CloudSlash => "\f2b8", Icon::CloudSleetFill => "\f2b9", Icon::CloudSleet => "\f2ba", Icon::CloudSnowFill => "\f2bb", Icon::CloudSnow => "\f2bc", Icon::CloudSunFill => "\f2bd", Icon::CloudSun => "\f2be", Icon::CloudUploadFill => "\f2bf", Icon::CloudUpload => "\f2c0", Icon::Cloud => "\f2c1", Icon::CloudsFill => "\f2c2", Icon::Clouds => "\f2c3", Icon::CloudyFill => "\f2c4", Icon::Cloudy => "\f2c5", Icon::CodeSlash => "\f2c6", Icon::CodeSquare => "\f2c7", Icon::Code => "\f2c8", Icon::CollectionFill => "\f2c9", Icon::CollectionPlayFill => "\f2ca", Icon::CollectionPlay => "\f2cb", Icon::Collection => "\f2cc", Icon::ColumnsGap => "\f2cd", Icon::Columns => "\f2ce", Icon::Command => "\f2cf", Icon::CompassFill => "\f2d0", Icon::Compass => "\f2d1", Icon::ConeStriped => "\f2d2", Icon::Cone => "\f2d3", Icon::Controller => "\f2d4", Icon::CpuFill => "\f2d5", Icon::Cpu => "\f2d6", Icon::CreditCard2BackFill => "\f2d7", Icon::CreditCard2Back => "\f2d8", Icon::CreditCard2FrontFill => "\f2d9", Icon::CreditCard2Front => "\f2da", Icon::CreditCardFill => "\f2db", Icon::CreditCard => "\f2dc", Icon::Crop => "\f2dd", Icon::CupFill => "\f2de", Icon::CupStraw => "\f2df", Icon::Cup => "\f2e0", Icon::CursorFill => "\f2e1", Icon::CursorText => "\f2e2", Icon::Cursor => "\f2e3", Icon::DashCircleDotted => "\f2e4", Icon::DashCircleFill => "\f2e5", Icon::DashCircle => "\f2e6", Icon::DashSquareDotted => "\f2e7", Icon::DashSquareFill => "\f2e8", Icon::DashSquare => "\f2e9", Icon::Dash => "\f2ea", Icon::Diagram2Fill => "\f2eb", Icon::Diagram2 => "\f2ec", Icon::Diagram3Fill => "\f2ed", Icon::Diagram3 => "\f2ee", Icon::DiamondFill => "\f2ef", Icon::DiamondHalf => "\f2f0", Icon::Diamond => "\f2f1", Icon::Dice1Fill => "\f2f2", Icon::Dice1 => "\f2f3", Icon::Dice2Fill => "\f2f4", Icon::Dice2 => "\f2f5", Icon::Dice3Fill => "\f2f6", Icon::Dice3 => "\f2f7", Icon::Dice4Fill => "\f2f8", Icon::Dice4 => "\f2f9", Icon::Dice5Fill => "\f2fa", Icon::Dice5 => "\f2fb", Icon::Dice6Fill => "\f2fc", Icon::Dice6 => "\f2fd", Icon::DiscFill => "\f2fe", Icon::Disc => "\f2ff", Icon::Discord => "\f300", Icon::DisplayFill => "\f301", Icon::Display => "\f302", Icon::DistributeHorizontal => "\f303", Icon::DistributeVertical => "\f304", Icon::DoorClosedFill => "\f305", Icon::DoorClosed => "\f306", Icon::DoorOpenFill => "\f307", Icon::DoorOpen => "\f308", Icon::Dot => "\f309", Icon::Download => "\f30a", Icon::DropletFill => "\f30b", Icon::DropletHalf => "\f30c", Icon::Droplet => "\f30d", Icon::Earbuds => "\f30e", Icon::EaselFill => "\f30f", Icon::Easel => "\f310", Icon::EggFill => "\f311", Icon::EggFried => "\f312", Icon::Egg => "\f313", Icon::EjectFill => "\f314", Icon::Eject => "\f315", Icon::EmojiAngryFill => "\f316", Icon::EmojiAngry => "\f317", Icon::EmojiDizzyFill => "\f318", Icon::EmojiDizzy => "\f319", Icon::EmojiExpressionlessFill => "\f31a", Icon::EmojiExpressionless => "\f31b", Icon::EmojiFrownFill => "\f31c", Icon::EmojiFrown => "\f31d", Icon::EmojiHeartEyesFill => "\f31e", Icon::EmojiHeartEyes => "\f31f", Icon::EmojiLaughingFill => "\f320", Icon::EmojiLaughing => "\f321", Icon::EmojiNeutralFill => "\f322", Icon::EmojiNeutral => "\f323", Icon::EmojiSmileFill => "\f324", Icon::EmojiSmileUpsideDownFill => "\f325", Icon::EmojiSmileUpsideDown => "\f326", Icon::EmojiSmile => "\f327", Icon::EmojiSunglassesFill => "\f328", Icon::EmojiSunglasses => "\f329", Icon::EmojiWinkFill => "\f32a", Icon::EmojiWink => "\f32b", Icon::EnvelopeFill => "\f32c", Icon::EnvelopeOpenFill => "\f32d", Icon::EnvelopeOpen => "\f32e", Icon::Envelope => "\f32f", Icon::EraserFill => "\f330", Icon::Eraser => "\f331", Icon::ExclamationCircleFill => "\f332", Icon::ExclamationCircle => "\f333", Icon::ExclamationDiamondFill => "\f334", Icon::ExclamationDiamond => "\f335", Icon::ExclamationOctagonFill => "\f336", Icon::ExclamationOctagon => "\f337", Icon::ExclamationSquareFill => "\f338", Icon::ExclamationSquare => "\f339", Icon::ExclamationTriangleFill => "\f33a", Icon::ExclamationTriangle => "\f33b", Icon::Exclamation => "\f33c", Icon::Exclude => "\f33d", Icon::EyeFill => "\f33e", Icon::EyeSlashFill => "\f33f", Icon::EyeSlash => "\f340", Icon::Eye => "\f341", Icon::Eyedropper => "\f342", Icon::Eyeglasses => "\f343", Icon::Facebook => "\f344", Icon::FileArrowDownFill => "\f345", Icon::FileArrowDown => "\f346", Icon::FileArrowUpFill => "\f347", Icon::FileArrowUp => "\f348", Icon::FileBarGraphFill => "\f349", Icon::FileBarGraph => "\f34a", Icon::FileBinaryFill => "\f34b", Icon::FileBinary => "\f34c", Icon::FileBreakFill => "\f34d", Icon::FileBreak => "\f34e", Icon::FileCheckFill => "\f34f", Icon::FileCheck => "\f350", Icon::FileCodeFill => "\f351", Icon::FileCode => "\f352", Icon::FileDiffFill => "\f353", Icon::FileDiff => "\f354", Icon::FileEarmarkArrowDownFill => "\f355", Icon::FileEarmarkArrowDown => "\f356", Icon::FileEarmarkArrowUpFill => "\f357", Icon::FileEarmarkArrowUp => "\f358", Icon::FileEarmarkBarGraphFill => "\f359", Icon::FileEarmarkBarGraph => "\f35a", Icon::FileEarmarkBinaryFill => "\f35b", Icon::FileEarmarkBinary => "\f35c", Icon::FileEarmarkBreakFill => "\f35d", Icon::FileEarmarkBreak => "\f35e", Icon::FileEarmarkCheckFill => "\f35f", Icon::FileEarmarkCheck => "\f360", Icon::FileEarmarkCodeFill => "\f361", Icon::FileEarmarkCode => "\f362", Icon::FileEarmarkDiffFill => "\f363", Icon::FileEarmarkDiff => "\f364", Icon::FileEarmarkEaselFill => "\f365", Icon::FileEarmarkEasel => "\f366", Icon::FileEarmarkExcelFill => "\f367", Icon::FileEarmarkExcel => "\f368", Icon::FileEarmarkFill => "\f369", Icon::FileEarmarkFontFill => "\f36a", Icon::FileEarmarkFont => "\f36b", Icon::FileEarmarkImageFill => "\f36c", Icon::FileEarmarkImage => "\f36d", Icon::FileEarmarkLockFill => "\f36e", Icon::FileEarmarkLock => "\f36f", Icon::FileEarmarkLock2Fill => "\f370", Icon::FileEarmarkLock2 => "\f371", Icon::FileEarmarkMedicalFill => "\f372", Icon::FileEarmarkMedical => "\f373", Icon::FileEarmarkMinusFill => "\f374", Icon::FileEarmarkMinus => "\f375", Icon::FileEarmarkMusicFill => "\f376", Icon::FileEarmarkMusic => "\f377", Icon::FileEarmarkPersonFill => "\f378", Icon::FileEarmarkPerson => "\f379", Icon::FileEarmarkPlayFill => "\f37a", Icon::FileEarmarkPlay => "\f37b", Icon::FileEarmarkPlusFill => "\f37c", Icon::FileEarmarkPlus => "\f37d", Icon::FileEarmarkPostFill => "\f37e", Icon::FileEarmarkPost => "\f37f", Icon::FileEarmarkPptFill => "\f380", Icon::FileEarmarkPpt => "\f381", Icon::FileEarmarkRichtextFill => "\f382", Icon::FileEarmarkRichtext => "\f383", Icon::FileEarmarkRuledFill => "\f384", Icon::FileEarmarkRuled => "\f385", Icon::FileEarmarkSlidesFill => "\f386", Icon::FileEarmarkSlides => "\f387", Icon::FileEarmarkSpreadsheetFill => "\f388", Icon::FileEarmarkSpreadsheet => "\f389", Icon::FileEarmarkTextFill => "\f38a", Icon::FileEarmarkText => "\f38b", Icon::FileEarmarkWordFill => "\f38c", Icon::FileEarmarkWord => "\f38d", Icon::FileEarmarkXFill => "\f38e", Icon::FileEarmarkX => "\f38f", Icon::FileEarmarkZipFill => "\f390", Icon::FileEarmarkZip => "\f391", Icon::FileEarmark => "\f392", Icon::FileEaselFill => "\f393", Icon::FileEasel => "\f394", Icon::FileExcelFill => "\f395", Icon::FileExcel => "\f396", Icon::FileFill => "\f397", Icon::FileFontFill => "\f398", Icon::FileFont => "\f399", Icon::FileImageFill => "\f39a", Icon::FileImage => "\f39b", Icon::FileLockFill => "\f39c", Icon::FileLock => "\f39d", Icon::FileLock2Fill => "\f39e", Icon::FileLock2 => "\f39f", Icon::FileMedicalFill => "\f3a0", Icon::FileMedical => "\f3a1", Icon::FileMinusFill => "\f3a2", Icon::FileMinus => "\f3a3", Icon::FileMusicFill => "\f3a4", Icon::FileMusic => "\f3a5", Icon::FilePersonFill => "\f3a6", Icon::FilePerson => "\f3a7", Icon::FilePlayFill => "\f3a8", Icon::FilePlay => "\f3a9", Icon::FilePlusFill => "\f3aa", Icon::FilePlus => "\f3ab", Icon::FilePostFill => "\f3ac", Icon::FilePost => "\f3ad", Icon::FilePptFill => "\f3ae", Icon::FilePpt => "\f3af", Icon::FileRichtextFill => "\f3b0", Icon::FileRichtext => "\f3b1", Icon::FileRuledFill => "\f3b2", Icon::FileRuled => "\f3b3", Icon::FileSlidesFill => "\f3b4", Icon::FileSlides => "\f3b5", Icon::FileSpreadsheetFill => "\f3b6", Icon::FileSpreadsheet => "\f3b7", Icon::FileTextFill => "\f3b8", Icon::FileText => "\f3b9", Icon::FileWordFill => "\f3ba", Icon::FileWord => "\f3bb", Icon::FileXFill => "\f3bc", Icon::FileX => "\f3bd", Icon::FileZipFill => "\f3be", Icon::FileZip => "\f3bf", Icon::File => "\f3c0", Icon::FilesAlt => "\f3c1", Icon::Files => "\f3c2", Icon::Film => "\f3c3", Icon::FilterCircleFill => "\f3c4", Icon::FilterCircle => "\f3c5", Icon::FilterLeft => "\f3c6", Icon::FilterRight => "\f3c7", Icon::FilterSquareFill => "\f3c8", Icon::FilterSquare => "\f3c9", Icon::Filter => "\f3ca", Icon::FlagFill => "\f3cb", Icon::Flag => "\f3cc", Icon::Flower1 => "\f3cd", Icon::Flower2 => "\f3ce", Icon::Flower3 => "\f3cf", Icon::FolderCheck => "\f3d0", Icon::FolderFill => "\f3d1", Icon::FolderMinus => "\f3d2", Icon::FolderPlus => "\f3d3", Icon::FolderSymlinkFill => "\f3d4", Icon::FolderSymlink => "\f3d5", Icon::FolderX => "\f3d6", Icon::Folder => "\f3d7", Icon::Folder2Open => "\f3d8", Icon::Folder2 => "\f3d9", Icon::Fonts => "\f3da", Icon::ForwardFill => "\f3db", Icon::Forward => "\f3dc", Icon::Front => "\f3dd", Icon::FullscreenExit => "\f3de", Icon::Fullscreen => "\f3df", Icon::FunnelFill => "\f3e0", Icon::Funnel => "\f3e1", Icon::GearFill => "\f3e2", Icon::GearWideConnected => "\f3e3", Icon::GearWide => "\f3e4", Icon::Gear => "\f3e5", Icon::Gem => "\f3e6", Icon::GeoAltFill => "\f3e7", Icon::GeoAlt => "\f3e8", Icon::GeoFill => "\f3e9", Icon::Geo => "\f3ea", Icon::GiftFill => "\f3eb", Icon::Gift => "\f3ec", Icon::Github => "\f3ed", Icon::Globe => "\f3ee", Icon::Globe2 => "\f3ef", Icon::Google => "\f3f0", Icon::GraphDown => "\f3f1", Icon::GraphUp => "\f3f2", Icon::Grid1x2Fill => "\f3f3", Icon::Grid1x2 => "\f3f4", Icon::Grid3x2GapFill => "\f3f5", Icon::Grid3x2Gap => "\f3f6", Icon::Grid3x2 => "\f3f7", Icon::Grid3x3GapFill => "\f3f8", Icon::Grid3x3Gap => "\f3f9", Icon::Grid3x3 => "\f3fa", Icon::GridFill => "\f3fb", Icon::Grid => "\f3fc", Icon::GripHorizontal => "\f3fd", Icon::GripVertical => "\f3fe", Icon::Hammer => "\f3ff", Icon::HandIndexFill => "\f400", Icon::HandIndexThumbFill => "\f401", Icon::HandIndexThumb => "\f402", Icon::HandIndex => "\f403", Icon::HandThumbsDownFill => "\f404", Icon::HandThumbsDown => "\f405", Icon::HandThumbsUpFill => "\f406", Icon::HandThumbsUp => "\f407", Icon::HandbagFill => "\f408", Icon::Handbag => "\f409", Icon::Hash => "\f40a", Icon::HddFill => "\f40b", Icon::HddNetworkFill => "\f40c", Icon::HddNetwork => "\f40d", Icon::HddRackFill => "\f40e", Icon::HddRack => "\f40f", Icon::HddStackFill => "\f410", Icon::HddStack => "\f411", Icon::Hdd => "\f412", Icon::Headphones => "\f413", Icon::Headset => "\f414", Icon::HeartFill => "\f415", Icon::HeartHalf => "\f416", Icon::Heart => "\f417", Icon::HeptagonFill => "\f418", Icon::HeptagonHalf => "\f419", Icon::Heptagon => "\f41a", Icon::HexagonFill => "\f41b", Icon::HexagonHalf => "\f41c", Icon::Hexagon => "\f41d", Icon::HourglassBottom => "\f41e", Icon::HourglassSplit => "\f41f", Icon::HourglassTop => "\f420", Icon::Hourglass => "\f421", Icon::HouseDoorFill => "\f422", Icon::HouseDoor => "\f423", Icon::HouseFill => "\f424", Icon::House => "\f425", Icon::Hr => "\f426", Icon::Hurricane => "\f427", Icon::ImageAlt => "\f428", Icon::ImageFill => "\f429", Icon::Image => "\f42a", Icon::Images => "\f42b", Icon::InboxFill => "\f42c", Icon::Inbox => "\f42d", Icon::InboxesFill => "\f42e", Icon::Inboxes => "\f42f", Icon::InfoCircleFill => "\f430", Icon::InfoCircle => "\f431", Icon::InfoSquareFill => "\f432", Icon::InfoSquare => "\f433", Icon::Info => "\f434", Icon::InputCursorText => "\f435", Icon::InputCursor => "\f436", Icon::Instagram => "\f437", Icon::Intersect => "\f438", Icon::JournalAlbum => "\f439", Icon::JournalArrowDown => "\f43a", Icon::JournalArrowUp => "\f43b", Icon::JournalBookmarkFill => "\f43c", Icon::JournalBookmark => "\f43d", Icon::JournalCheck => "\f43e", Icon::JournalCode => "\f43f", Icon::JournalMedical => "\f440", Icon::JournalMinus => "\f441", Icon::JournalPlus => "\f442", Icon::JournalRichtext => "\f443", Icon::JournalText => "\f444", Icon::JournalX => "\f445", Icon::Journal => "\f446", Icon::Journals => "\f447", Icon::Joystick => "\f448", Icon::JustifyLeft => "\f449", Icon::JustifyRight => "\f44a", Icon::Justify => "\f44b", Icon::KanbanFill => "\f44c", Icon::Kanban => "\f44d", Icon::KeyFill => "\f44e", Icon::Key => "\f44f", Icon::KeyboardFill => "\f450", Icon::Keyboard => "\f451", Icon::Ladder => "\f452", Icon::LampFill => "\f453", Icon::Lamp => "\f454", Icon::LaptopFill => "\f455", Icon::Laptop => "\f456", Icon::LayerBackward => "\f457", Icon::LayerForward => "\f458", Icon::LayersFill => "\f459", Icon::LayersHalf => "\f45a", Icon::Layers => "\f45b", Icon::LayoutSidebarInsetReverse => "\f45c", Icon::LayoutSidebarInset => "\f45d", Icon::LayoutSidebarReverse => "\f45e", Icon::LayoutSidebar => "\f45f", Icon::LayoutSplit => "\f460", Icon::LayoutTextSidebarReverse => "\f461", Icon::LayoutTextSidebar => "\f462", Icon::LayoutTextWindowReverse => "\f463", Icon::LayoutTextWindow => "\f464", Icon::LayoutThreeColumns => "\f465", Icon::LayoutWtf => "\f466", Icon::LifePreserver => "\f467", Icon::LightbulbFill => "\f468", Icon::LightbulbOffFill => "\f469", Icon::LightbulbOff => "\f46a", Icon::Lightbulb => "\f46b", Icon::LightningChargeFill => "\f46c", Icon::LightningCharge => "\f46d", Icon::LightningFill => "\f46e", Icon::Lightning => "\f46f", Icon::Link45deg => "\f470", Icon::Link => "\f471", Icon::Linkedin => "\f472", Icon::ListCheck => "\f473", Icon::ListNested => "\f474", Icon::ListOl => "\f475", Icon::ListStars => "\f476", Icon::ListTask => "\f477", Icon::ListUl => "\f478", Icon::List => "\f479", Icon::LockFill => "\f47a", Icon::Lock => "\f47b", Icon::Mailbox => "\f47c", Icon::Mailbox2 => "\f47d", Icon::MapFill => "\f47e", Icon::Map => "\f47f", Icon::MarkdownFill => "\f480", Icon::Markdown => "\f481", Icon::Mask => "\f482", Icon::MegaphoneFill => "\f483", Icon::Megaphone => "\f484", Icon::MenuAppFill => "\f485", Icon::MenuApp => "\f486", Icon::MenuButtonFill => "\f487", Icon::MenuButtonWideFill => "\f488", Icon::MenuButtonWide => "\f489", Icon::MenuButton => "\f48a", Icon::MenuDown => "\f48b", Icon::MenuUp => "\f48c", Icon::MicFill => "\f48d", Icon::MicMuteFill => "\f48e", Icon::MicMute => "\f48f", Icon::Mic => "\f490", Icon::MinecartLoaded => "\f491", Icon::Minecart => "\f492", Icon::Moisture => "\f493", Icon::MoonFill => "\f494", Icon::MoonStarsFill => "\f495", Icon::MoonStars => "\f496", Icon::Moon => "\f497", Icon::MouseFill => "\f498", Icon::Mouse => "\f499", Icon::Mouse2Fill => "\f49a", Icon::Mouse2 => "\f49b", Icon::Mouse3Fill => "\f49c", Icon::Mouse3 => "\f49d", Icon::MusicNoteBeamed => "\f49e", Icon::MusicNoteList => "\f49f", Icon::MusicNote => "\f4a0", Icon::MusicPlayerFill => "\f4a1", Icon::MusicPlayer => "\f4a2", Icon::Newspaper => "\f4a3", Icon::NodeMinusFill => "\f4a4", Icon::NodeMinus => "\f4a5", Icon::NodePlusFill => "\f4a6", Icon::NodePlus => "\f4a7", Icon::NutFill => "\f4a8", Icon::Nut => "\f4a9", Icon::OctagonFill => "\f4aa", Icon::OctagonHalf => "\f4ab", Icon::Octagon => "\f4ac", Icon::Option => "\f4ad", Icon::Outlet => "\f4ae", Icon::PaintBucket => "\f4af", Icon::PaletteFill => "\f4b0", Icon::Palette => "\f4b1", Icon::Palette2 => "\f4b2", Icon::Paperclip => "\f4b3", Icon::Paragraph => "\f4b4", Icon::PatchCheckFill => "\f4b5", Icon::PatchCheck => "\f4b6", Icon::PatchExclamationFill => "\f4b7", Icon::PatchExclamation => "\f4b8", Icon::PatchMinusFill => "\f4b9", Icon::PatchMinus => "\f4ba", Icon::PatchPlusFill => "\f4bb", Icon::PatchPlus => "\f4bc", Icon::PatchQuestionFill => "\f4bd", Icon::PatchQuestion => "\f4be", Icon::PauseBtnFill => "\f4bf", Icon::PauseBtn => "\f4c0", Icon::PauseCircleFill => "\f4c1", Icon::PauseCircle => "\f4c2", Icon::PauseFill => "\f4c3", Icon::Pause => "\f4c4", Icon::PeaceFill => "\f4c5", Icon::Peace => "\f4c6", Icon::PenFill => "\f4c7", Icon::Pen => "\f4c8", Icon::PencilFill => "\f4c9", Icon::PencilSquare => "\f4ca", Icon::Pencil => "\f4cb", Icon::PentagonFill => "\f4cc", Icon::PentagonHalf => "\f4cd", Icon::Pentagon => "\f4ce", Icon::PeopleFill => "\f4cf", Icon::People => "\f4d0", Icon::Percent => "\f4d1", Icon::PersonBadgeFill => "\f4d2", Icon::PersonBadge => "\f4d3", Icon::PersonBoundingBox => "\f4d4", Icon::PersonCheckFill => "\f4d5", Icon::PersonCheck => "\f4d6", Icon::PersonCircle => "\f4d7", Icon::PersonDashFill => "\f4d8", Icon::PersonDash => "\f4d9", Icon::PersonFill => "\f4da", Icon::PersonLinesFill => "\f4db", Icon::PersonPlusFill => "\f4dc", Icon::PersonPlus => "\f4dd", Icon::PersonSquare => "\f4de", Icon::PersonXFill => "\f4df", Icon::PersonX => "\f4e0", Icon::Person => "\f4e1", Icon::PhoneFill => "\f4e2", Icon::PhoneLandscapeFill => "\f4e3", Icon::PhoneLandscape => "\f4e4", Icon::PhoneVibrateFill => "\f4e5", Icon::PhoneVibrate => "\f4e6", Icon::Phone => "\f4e7", Icon::PieChartFill => "\f4e8", Icon::PieChart => "\f4e9", Icon::PinAngleFill => "\f4ea", Icon::PinAngle => "\f4eb", Icon::PinFill => "\f4ec", Icon::Pin => "\f4ed", Icon::PipFill => "\f4ee", Icon::Pip => "\f4ef", Icon::PlayBtnFill => "\f4f0", Icon::PlayBtn => "\f4f1", Icon::PlayCircleFill => "\f4f2", Icon::PlayCircle => "\f4f3", Icon::PlayFill => "\f4f4", Icon::Play => "\f4f5", Icon::PlugFill => "\f4f6", Icon::Plug => "\f4f7", Icon::PlusCircleDotted => "\f4f8", Icon::PlusCircleFill => "\f4f9", Icon::PlusCircle => "\f4fa", Icon::PlusSquareDotted => "\f4fb", Icon::PlusSquareFill => "\f4fc", Icon::PlusSquare => "\f4fd", Icon::Plus => "\f4fe", Icon::Power => "\f4ff", Icon::PrinterFill => "\f500", Icon::Printer => "\f501", Icon::PuzzleFill => "\f502", Icon::Puzzle => "\f503", Icon::QuestionCircleFill => "\f504", Icon::QuestionCircle => "\f505", Icon::QuestionDiamondFill => "\f506", Icon::QuestionDiamond => "\f507", Icon::QuestionOctagonFill => "\f508", Icon::QuestionOctagon => "\f509", Icon::QuestionSquareFill => "\f50a", Icon::QuestionSquare => "\f50b", Icon::Question => "\f50c", Icon::Rainbow => "\f50d", Icon::ReceiptCutoff => "\f50e", Icon::Receipt => "\f50f", Icon::Reception0 => "\f510", Icon::Reception1 => "\f511", Icon::Reception2 => "\f512", Icon::Reception3 => "\f513", Icon::Reception4 => "\f514", Icon::RecordBtnFill => "\f515", Icon::RecordBtn => "\f516", Icon::RecordCircleFill => "\f517", Icon::RecordCircle => "\f518", Icon::RecordFill => "\f519", Icon::Record => "\f51a", Icon::Record2Fill => "\f51b", Icon::Record2 => "\f51c", Icon::ReplyAllFill => "\f51d", Icon::ReplyAll => "\f51e", Icon::ReplyFill => "\f51f", Icon::Reply => "\f520", Icon::RssFill => "\f521", Icon::Rss => "\f522", Icon::Rulers => "\f523", Icon::SaveFill => "\f524", Icon::Save => "\f525", Icon::Save2Fill => "\f526", Icon::Save2 => "\f527", Icon::Scissors => "\f528", Icon::Screwdriver => "\f529", Icon::Search => "\f52a", Icon::SegmentedNav => "\f52b", Icon::Server => "\f52c", Icon::ShareFill => "\f52d", Icon::Share => "\f52e", Icon::ShieldCheck => "\f52f", Icon::ShieldExclamation => "\f530", Icon::ShieldFillCheck => "\f531", Icon::ShieldFillExclamation => "\f532", Icon::ShieldFillMinus => "\f533", Icon::ShieldFillPlus => "\f534", Icon::ShieldFillX => "\f535", Icon::ShieldFill => "\f536", Icon::ShieldLockFill => "\f537", Icon::ShieldLock => "\f538", Icon::ShieldMinus => "\f539", Icon::ShieldPlus => "\f53a", Icon::ShieldShaded => "\f53b", Icon::ShieldSlashFill => "\f53c", Icon::ShieldSlash => "\f53d", Icon::ShieldX => "\f53e", Icon::Shield => "\f53f", Icon::ShiftFill => "\f540", Icon::Shift => "\f541", Icon::ShopWindow => "\f542", Icon::Shop => "\f543", Icon::Shuffle => "\f544", Icon::Signpost2Fill => "\f545", Icon::Signpost2 => "\f546", Icon::SignpostFill => "\f547", Icon::SignpostSplitFill => "\f548", Icon::SignpostSplit => "\f549", Icon::Signpost => "\f54a", Icon::SimFill => "\f54b", Icon::Sim => "\f54c", Icon::SkipBackwardBtnFill => "\f54d", Icon::SkipBackwardBtn => "\f54e", Icon::SkipBackwardCircleFill => "\f54f", Icon::SkipBackwardCircle => "\f550", Icon::SkipBackwardFill => "\f551", Icon::SkipBackward => "\f552", Icon::SkipEndBtnFill => "\f553", Icon::SkipEndBtn => "\f554", Icon::SkipEndCircleFill => "\f555", Icon::SkipEndCircle => "\f556", Icon::SkipEndFill => "\f557", Icon::SkipEnd => "\f558", Icon::SkipForwardBtnFill => "\f559", Icon::SkipForwardBtn => "\f55a", Icon::SkipForwardCircleFill => "\f55b", Icon::SkipForwardCircle => "\f55c", Icon::SkipForwardFill => "\f55d", Icon::SkipForward => "\f55e", Icon::SkipStartBtnFill => "\f55f", Icon::SkipStartBtn => "\f560", Icon::SkipStartCircleFill => "\f561", Icon::SkipStartCircle => "\f562", Icon::SkipStartFill => "\f563", Icon::SkipStart => "\f564", Icon::Slack => "\f565", Icon::SlashCircleFill => "\f566", Icon::SlashCircle => "\f567", Icon::SlashSquareFill => "\f568", Icon::SlashSquare => "\f569", Icon::Slash => "\f56a", Icon::Sliders => "\f56b", Icon::Smartwatch => "\f56c", Icon::Snow => "\f56d", Icon::Snow2 => "\f56e", Icon::Snow3 => "\f56f", Icon::SortAlphaDownAlt => "\f570", Icon::SortAlphaDown => "\f571", Icon::SortAlphaUpAlt => "\f572", Icon::SortAlphaUp => "\f573", Icon::SortDownAlt => "\f574", Icon::SortDown => "\f575", Icon::SortNumericDownAlt => "\f576", Icon::SortNumericDown => "\f577", Icon::SortNumericUpAlt => "\f578", Icon::SortNumericUp => "\f579", Icon::SortUpAlt => "\f57a", Icon::SortUp => "\f57b", Icon::Soundwave => "\f57c", Icon::SpeakerFill => "\f57d", Icon::Speaker => "\f57e", Icon::Speedometer => "\f57f", Icon::Speedometer2 => "\f580", Icon::Spellcheck => "\f581", Icon::SquareFill => "\f582", Icon::SquareHalf => "\f583", Icon::Square => "\f584", Icon::Stack => "\f585", Icon::StarFill => "\f586", Icon::StarHalf => "\f587", Icon::Star => "\f588", Icon::Stars => "\f589", Icon::StickiesFill => "\f58a", Icon::Stickies => "\f58b", Icon::StickyFill => "\f58c", Icon::Sticky => "\f58d", Icon::StopBtnFill => "\f58e", Icon::StopBtn => "\f58f", Icon::StopCircleFill => "\f590", Icon::StopCircle => "\f591", Icon::StopFill => "\f592", Icon::Stop => "\f593", Icon::StoplightsFill => "\f594", Icon::Stoplights => "\f595", Icon::StopwatchFill => "\f596", Icon::Stopwatch => "\f597", Icon::Subtract => "\f598", Icon::SuitClubFill => "\f599", Icon::SuitClub => "\f59a", Icon::SuitDiamondFill => "\f59b", Icon::SuitDiamond => "\f59c", Icon::SuitHeartFill => "\f59d", Icon::SuitHeart => "\f59e", Icon::SuitSpadeFill => "\f59f", Icon::SuitSpade => "\f5a0", Icon::SunFill => "\f5a1", Icon::Sun => "\f5a2", Icon::Sunglasses => "\f5a3", Icon::SunriseFill => "\f5a4", Icon::Sunrise => "\f5a5", Icon::SunsetFill => "\f5a6", Icon::Sunset => "\f5a7", Icon::SymmetryHorizontal => "\f5a8", Icon::SymmetryVertical => "\f5a9", Icon::Table => "\f5aa", Icon::TabletFill => "\f5ab", Icon::TabletLandscapeFill => "\f5ac", Icon::TabletLandscape => "\f5ad", Icon::Tablet => "\f5ae", Icon::TagFill => "\f5af", Icon::Tag => "\f5b0", Icon::TagsFill => "\f5b1", Icon::Tags => "\f5b2", Icon::Telegram => "\f5b3", Icon::TelephoneFill => "\f5b4", Icon::TelephoneForwardFill => "\f5b5", Icon::TelephoneForward => "\f5b6", Icon::TelephoneInboundFill => "\f5b7", Icon::TelephoneInbound => "\f5b8", Icon::TelephoneMinusFill => "\f5b9", Icon::TelephoneMinus => "\f5ba", Icon::TelephoneOutboundFill => "\f5bb", Icon::TelephoneOutbound => "\f5bc", Icon::TelephonePlusFill => "\f5bd", Icon::TelephonePlus => "\f5be", Icon::TelephoneXFill => "\f5bf", Icon::TelephoneX => "\f5c0", Icon::Telephone => "\f5c1", Icon::TerminalFill => "\f5c2", Icon::Terminal => "\f5c3", Icon::TextCenter => "\f5c4", Icon::TextIndentLeft => "\f5c5", Icon::TextIndentRight => "\f5c6", Icon::TextLeft => "\f5c7", Icon::TextParagraph => "\f5c8", Icon::TextRight => "\f5c9", Icon::TextareaResize => "\f5ca", Icon::TextareaT => "\f5cb", Icon::Textarea => "\f5cc", Icon::ThermometerHalf => "\f5cd", Icon::ThermometerHigh => "\f5ce", Icon::ThermometerLow => "\f5cf", Icon::ThermometerSnow => "\f5d0", Icon::ThermometerSun => "\f5d1", Icon::Thermometer => "\f5d2", Icon::ThreeDotsVertical => "\f5d3", Icon::ThreeDots => "\f5d4", Icon::ToggleOff => "\f5d5", Icon::ToggleOn => "\f5d6", Icon::Toggle2Off => "\f5d7", Icon::Toggle2On => "\f5d8", Icon::Toggles => "\f5d9", Icon::Toggles2 => "\f5da", Icon::Tools => "\f5db", Icon::Tornado => "\f5dc", Icon::TrashFill => "\f5dd", Icon::Trash => "\f5de", Icon::Trash2Fill => "\f5df", Icon::Trash2 => "\f5e0", Icon::TreeFill => "\f5e1", Icon::Tree => "\f5e2", Icon::TriangleFill => "\f5e3", Icon::TriangleHalf => "\f5e4", Icon::Triangle => "\f5e5", Icon::TrophyFill => "\f5e6", Icon::Trophy => "\f5e7", Icon::TropicalStorm => "\f5e8", Icon::TruckFlatbed => "\f5e9", Icon::Truck => "\f5ea", Icon::Tsunami => "\f5eb", Icon::TvFill => "\f5ec", Icon::Tv => "\f5ed", Icon::Twitch => "\f5ee", Icon::Twitter => "\f5ef", Icon::TypeBold => "\f5f0", Icon::TypeH1 => "\f5f1", Icon::TypeH2 => "\f5f2", Icon::TypeH3 => "\f5f3", Icon::TypeItalic => "\f5f4", Icon::TypeStrikethrough => "\f5f5", Icon::TypeUnderline => "\f5f6", Icon::Type => "\f5f7", Icon::UiChecksGrid => "\f5f8", Icon::UiChecks => "\f5f9", Icon::UiRadiosGrid => "\f5fa", Icon::UiRadios => "\f5fb", Icon::UmbrellaFill => "\f5fc", Icon::Umbrella => "\f5fd", Icon::Union => "\f5fe", Icon::UnlockFill => "\f5ff", Icon::Unlock => "\f600", Icon::UpcScan => "\f601", Icon::Upc => "\f602", Icon::Upload => "\f603", Icon::VectorPen => "\f604", Icon::ViewList => "\f605", Icon::ViewStacked => "\f606", Icon::VinylFill => "\f607", Icon::Vinyl => "\f608", Icon::Voicemail => "\f609", Icon::VolumeDownFill => "\f60a", Icon::VolumeDown => "\f60b", Icon::VolumeMuteFill => "\f60c", Icon::VolumeMute => "\f60d", Icon::VolumeOffFill => "\f60e", Icon::VolumeOff => "\f60f", Icon::VolumeUpFill => "\f610", Icon::VolumeUp => "\f611", Icon::Vr => "\f612", Icon::WalletFill => "\f613", Icon::Wallet => "\f614", Icon::Wallet2 => "\f615", Icon::Watch => "\f616", Icon::Water => "\f617", Icon::Whatsapp => "\f618", Icon::Wifi1 => "\f619", Icon::Wifi2 => "\f61a", Icon::WifiOff => "\f61b", Icon::Wifi => "\f61c", Icon::Wind => "\f61d", Icon::WindowDock => "\f61e", Icon::WindowSidebar => "\f61f", Icon::Window => "\f620", Icon::Wrench => "\f621", Icon::XCircleFill => "\f622", Icon::XCircle => "\f623", Icon::XDiamondFill => "\f624", Icon::XDiamond => "\f625", Icon::XOctagonFill => "\f626", Icon::XOctagon => "\f627", Icon::XSquareFill => "\f628", Icon::XSquare => "\f629", Icon::X => "\f62a", Icon::Youtube => "\f62b", Icon::ZoomIn => "\f62c", Icon::ZoomOut => "\f62d", Icon::Bank => "\f62e", Icon::Bank2 => "\f62f", Icon::BellSlashFill => "\f630", Icon::BellSlash => "\f631", Icon::CashCoin => "\f632", Icon::CheckLg => "\f633", Icon::Coin => "\f634", Icon::CurrencyBitcoin => "\f635", Icon::CurrencyDollar => "\f636", Icon::CurrencyEuro => "\f637", Icon::CurrencyExchange => "\f638", Icon::CurrencyPound => "\f639", Icon::CurrencyYen => "\f63a", Icon::DashLg => "\f63b", Icon::ExclamationLg => "\f63c", Icon::FileEarmarkPdfFill => "\f63d", Icon::FileEarmarkPdf => "\f63e", Icon::FilePdfFill => "\f63f", Icon::FilePdf => "\f640", Icon::GenderAmbiguous => "\f641", Icon::GenderFemale => "\f642", Icon::GenderMale => "\f643", Icon::GenderTrans => "\f644", Icon::HeadsetVr => "\f645", Icon::InfoLg => "\f646", Icon::Mastodon => "\f647", Icon::Messenger => "\f648", Icon::PiggyBankFill => "\f649", Icon::PiggyBank => "\f64a", Icon::PinMapFill => "\f64b", Icon::PinMap => "\f64c", Icon::PlusLg => "\f64d", Icon::QuestionLg => "\f64e", Icon::Recycle => "\f64f", Icon::Reddit => "\f650", Icon::SafeFill => "\f651", Icon::Safe2Fill => "\f652", Icon::Safe2 => "\f653", Icon::SdCardFill => "\f654", Icon::SdCard => "\f655", Icon::Skype => "\f656", Icon::SlashLg => "\f657", Icon::Translate => "\f658", Icon::XLg => "\f659", Icon::Safe => "\f65a", Icon::Apple => "\f65b", Icon::Microsoft => "\f65d", Icon::Windows => "\f65e", Icon::Behance => "\f65c", Icon::Dribbble => "\f65f", Icon::Line => "\f660", Icon::Medium => "\f661", Icon::Paypal => "\f662", Icon::Pinterest => "\f663", Icon::Signal => "\f664", Icon::Snapchat => "\f665", Icon::Spotify => "\f666", Icon::StackOverflow => "\f667", Icon::Strava => "\f668", Icon::Wordpress => "\f669", Icon::Vimeo => "\f66a", Icon::Activity => "\f66b", Icon::Easel2Fill => "\f66c", Icon::Easel2 => "\f66d", Icon::Easel3Fill => "\f66e", Icon::Easel3 => "\f66f", Icon::Fan => "\f670", Icon::Fingerprint => "\f671", Icon::GraphDownArrow => "\f672", Icon::GraphUpArrow => "\f673", Icon::Hypnotize => "\f674", Icon::Magic => "\f675", Icon::PersonRolodex => "\f676", Icon::PersonVideo => "\f677", Icon::PersonVideo2 => "\f678", Icon::PersonVideo3 => "\f679", Icon::PersonWorkspace => "\f67a", Icon::Radioactive => "\f67b", Icon::WebcamFill => "\f67c", Icon::Webcam => "\f67d", Icon::YinYang => "\f67e", Icon::BandaidFill => "\f680", Icon::Bandaid => "\f681", Icon::Bluetooth => "\f682", Icon::BodyText => "\f683", Icon::Boombox => "\f684", Icon::Boxes => "\f685", Icon::DpadFill => "\f686", Icon::Dpad => "\f687", Icon::EarFill => "\f688", Icon::Ear => "\f689", Icon::EnvelopeCheckFill => "\f68b", Icon::EnvelopeCheck => "\f68c", Icon::EnvelopeDashFill => "\f68e", Icon::EnvelopeDash => "\f68f", Icon::EnvelopeExclamationFill => "\f691", Icon::EnvelopeExclamation => "\f692", Icon::EnvelopePlusFill => "\f693", Icon::EnvelopePlus => "\f694", Icon::EnvelopeSlashFill => "\f696", Icon::EnvelopeSlash => "\f697", Icon::EnvelopeXFill => "\f699", Icon::EnvelopeX => "\f69a", Icon::ExplicitFill => "\f69b", Icon::Explicit => "\f69c", Icon::Git => "\f69d", Icon::Infinity => "\f69e", Icon::ListColumnsReverse => "\f69f", Icon::ListColumns => "\f6a0", Icon::Meta => "\f6a1", Icon::NintendoSwitch => "\f6a4", Icon::PcDisplayHorizontal => "\f6a5", Icon::PcDisplay => "\f6a6", Icon::PcHorizontal => "\f6a7", Icon::Pc => "\f6a8", Icon::Playstation => "\f6a9", Icon::PlusSlashMinus => "\f6aa", Icon::ProjectorFill => "\f6ab", Icon::Projector => "\f6ac", Icon::QrCodeScan => "\f6ad", Icon::QrCode => "\f6ae", Icon::Quora => "\f6af", Icon::Quote => "\f6b0", Icon::Robot => "\f6b1", Icon::SendCheckFill => "\f6b2", Icon::SendCheck => "\f6b3", Icon::SendDashFill => "\f6b4", Icon::SendDash => "\f6b5", Icon::SendExclamationFill => "\f6b7", Icon::SendExclamation => "\f6b8", Icon::SendFill => "\f6b9", Icon::SendPlusFill => "\f6ba", Icon::SendPlus => "\f6bb", Icon::SendSlashFill => "\f6bc", Icon::SendSlash => "\f6bd", Icon::SendXFill => "\f6be", Icon::SendX => "\f6bf", Icon::Send => "\f6c0", Icon::Steam => "\f6c1", Icon::TerminalDash => "\f6c3", Icon::TerminalPlus => "\f6c4", Icon::TerminalSplit => "\f6c5", Icon::TicketDetailedFill => "\f6c6", Icon::TicketDetailed => "\f6c7", Icon::TicketFill => "\f6c8", Icon::TicketPerforatedFill => "\f6c9", Icon::TicketPerforated => "\f6ca", Icon::Ticket => "\f6cb", Icon::Tiktok => "\f6cc", Icon::WindowDash => "\f6cd", Icon::WindowDesktop => "\f6ce", Icon::WindowFullscreen => "\f6cf", Icon::WindowPlus => "\f6d0", Icon::WindowSplit => "\f6d1", Icon::WindowStack => "\f6d2", Icon::WindowX => "\f6d3", Icon::Xbox => "\f6d4", Icon::Ethernet => "\f6d5", Icon::HdmiFill => "\f6d6", Icon::Hdmi => "\f6d7", Icon::UsbCFill => "\f6d8", Icon::UsbC => "\f6d9", Icon::UsbFill => "\f6da", Icon::UsbPlugFill => "\f6db", Icon::UsbPlug => "\f6dc", Icon::UsbSymbol => "\f6dd", Icon::Usb => "\f6de", Icon::BoomboxFill => "\f6df", Icon::Displayport => "\f6e1", Icon::GpuCard => "\f6e2", Icon::Memory => "\f6e3", Icon::ModemFill => "\f6e4", Icon::Modem => "\f6e5", Icon::MotherboardFill => "\f6e6", Icon::Motherboard => "\f6e7", Icon::OpticalAudioFill => "\f6e8", Icon::OpticalAudio => "\f6e9", Icon::PciCard => "\f6ea", Icon::RouterFill => "\f6eb", Icon::Router => "\f6ec", Icon::ThunderboltFill => "\f6ef", Icon::Thunderbolt => "\f6f0", Icon::UsbDriveFill => "\f6f1", Icon::UsbDrive => "\f6f2", Icon::UsbMicroFill => "\f6f3", Icon::UsbMicro => "\f6f4", Icon::UsbMiniFill => "\f6f5", Icon::UsbMini => "\f6f6", Icon::CloudHaze2 => "\f6f7", Icon::DeviceHddFill => "\f6f8", Icon::DeviceHdd => "\f6f9", Icon::DeviceSsdFill => "\f6fa", Icon::DeviceSsd => "\f6fb", Icon::DisplayportFill => "\f6fc", Icon::MortarboardFill => "\f6fd", Icon::Mortarboard => "\f6fe", Icon::TerminalX => "\f6ff", Icon::ArrowThroughHeartFill => "\f700", Icon::ArrowThroughHeart => "\f701", Icon::BadgeSdFill => "\f702", Icon::BadgeSd => "\f703", Icon::BagHeartFill => "\f704", Icon::BagHeart => "\f705", Icon::BalloonFill => "\f706", Icon::BalloonHeartFill => "\f707", Icon::BalloonHeart => "\f708", Icon::Balloon => "\f709", Icon::Box2Fill => "\f70a", Icon::Box2HeartFill => "\f70b", Icon::Box2Heart => "\f70c", Icon::Box2 => "\f70d", Icon::BracesAsterisk => "\f70e", Icon::CalendarHeartFill => "\f70f", Icon::CalendarHeart => "\f710", Icon::Calendar2HeartFill => "\f711", Icon::Calendar2Heart => "\f712", Icon::ChatHeartFill => "\f713", Icon::ChatHeart => "\f714", Icon::ChatLeftHeartFill => "\f715", Icon::ChatLeftHeart => "\f716", Icon::ChatRightHeartFill => "\f717", Icon::ChatRightHeart => "\f718", Icon::ChatSquareHeartFill => "\f719", Icon::ChatSquareHeart => "\f71a", Icon::ClipboardCheckFill => "\f71b", Icon::ClipboardDataFill => "\f71c", Icon::ClipboardFill => "\f71d", Icon::ClipboardHeartFill => "\f71e", Icon::ClipboardHeart => "\f71f", Icon::ClipboardMinusFill => "\f720", Icon::ClipboardPlusFill => "\f721", Icon::ClipboardPulse => "\f722", Icon::ClipboardXFill => "\f723", Icon::Clipboard2CheckFill => "\f724", Icon::Clipboard2Check => "\f725", Icon::Clipboard2DataFill => "\f726", Icon::Clipboard2Data => "\f727", Icon::Clipboard2Fill => "\f728", Icon::Clipboard2HeartFill => "\f729", Icon::Clipboard2Heart => "\f72a", Icon::Clipboard2MinusFill => "\f72b", Icon::Clipboard2Minus => "\f72c", Icon::Clipboard2PlusFill => "\f72d", Icon::Clipboard2Plus => "\f72e", Icon::Clipboard2PulseFill => "\f72f", Icon::Clipboard2Pulse => "\f730", Icon::Clipboard2XFill => "\f731", Icon::Clipboard2X => "\f732", Icon::Clipboard2 => "\f733", Icon::EmojiKissFill => "\f734", Icon::EmojiKiss => "\f735", Icon::EnvelopeHeartFill => "\f736", Icon::EnvelopeHeart => "\f737", Icon::EnvelopeOpenHeartFill => "\f738", Icon::EnvelopeOpenHeart => "\f739", Icon::EnvelopePaperFill => "\f73a", Icon::EnvelopePaperHeartFill => "\f73b", Icon::EnvelopePaperHeart => "\f73c", Icon::EnvelopePaper => "\f73d", Icon::FiletypeAac => "\f73e", Icon::FiletypeAi => "\f73f", Icon::FiletypeBmp => "\f740", Icon::FiletypeCs => "\f741", Icon::FiletypeCss => "\f742", Icon::FiletypeCsv => "\f743", Icon::FiletypeDoc => "\f744", Icon::FiletypeDocx => "\f745", Icon::FiletypeExe => "\f746", Icon::FiletypeGif => "\f747", Icon::FiletypeHeic => "\f748", Icon::FiletypeHtml => "\f749", Icon::FiletypeJava => "\f74a", Icon::FiletypeJpg => "\f74b", Icon::FiletypeJs => "\f74c", Icon::FiletypeJsx => "\f74d", Icon::FiletypeKey => "\f74e", Icon::FiletypeM4p => "\f74f", Icon::FiletypeMd => "\f750", Icon::FiletypeMdx => "\f751", Icon::FiletypeMov => "\f752", Icon::FiletypeMp3 => "\f753", Icon::FiletypeMp4 => "\f754", Icon::FiletypeOtf => "\f755", Icon::FiletypePdf => "\f756", Icon::FiletypePhp => "\f757", Icon::FiletypePng => "\f758", Icon::FiletypePpt => "\f75a", Icon::FiletypePsd => "\f75b", Icon::FiletypePy => "\f75c", Icon::FiletypeRaw => "\f75d", Icon::FiletypeRb => "\f75e", Icon::FiletypeSass => "\f75f", Icon::FiletypeScss => "\f760", Icon::FiletypeSh => "\f761", Icon::FiletypeSvg => "\f762", Icon::FiletypeTiff => "\f763", Icon::FiletypeTsx => "\f764", Icon::FiletypeTtf => "\f765", Icon::FiletypeTxt => "\f766", Icon::FiletypeWav => "\f767", Icon::FiletypeWoff => "\f768", Icon::FiletypeXls => "\f76a", Icon::FiletypeXml => "\f76b", Icon::FiletypeYml => "\f76c", Icon::HeartArrow => "\f76d", Icon::HeartPulseFill => "\f76e", Icon::HeartPulse => "\f76f", Icon::HeartbreakFill => "\f770", Icon::Heartbreak => "\f771", Icon::Hearts => "\f772", Icon::HospitalFill => "\f773", Icon::Hospital => "\f774", Icon::HouseHeartFill => "\f775", Icon::HouseHeart => "\f776", Icon::Incognito => "\f777", Icon::MagnetFill => "\f778", Icon::Magnet => "\f779", Icon::PersonHeart => "\f77a", Icon::PersonHearts => "\f77b", Icon::PhoneFlip => "\f77c", Icon::Plugin => "\f77d", Icon::PostageFill => "\f77e", Icon::PostageHeartFill => "\f77f", Icon::PostageHeart => "\f780", Icon::Postage => "\f781", Icon::PostcardFill => "\f782", Icon::PostcardHeartFill => "\f783", Icon::PostcardHeart => "\f784", Icon::Postcard => "\f785", Icon::SearchHeartFill => "\f786", Icon::SearchHeart => "\f787", Icon::Sliders2Vertical => "\f788", Icon::Sliders2 => "\f789", Icon::Trash3Fill => "\f78a", Icon::Trash3 => "\f78b", Icon::Valentine => "\f78c", Icon::Valentine2 => "\f78d", Icon::WrenchAdjustableCircleFill => "\f78e", Icon::WrenchAdjustableCircle => "\f78f", Icon::WrenchAdjustable => "\f790", Icon::FiletypeJson => "\f791", Icon::FiletypePptx => "\f792", Icon::FiletypeXlsx => "\f793", Icon::Circle1Fill => "\f796", Icon::Circle1 => "\f797", Icon::Square1Fill => "\f798", Icon::Square1 => "\f799", Icon::Circle2Fill => "\f79c", Icon::Circle2 => "\f79d", Icon::Square2Fill => "\f79e", Icon::Square2 => "\f79f", Icon::Circle3Fill => "\f7a2", Icon::Circle3 => "\f7a3", Icon::Square3Fill => "\f7a4", Icon::Square3 => "\f7a5", Icon::Circle4Fill => "\f7a8", Icon::Circle4 => "\f7a9", Icon::Square4Fill => "\f7aa", Icon::Square4 => "\f7ab", Icon::Circle5Fill => "\f7ae", Icon::Circle5 => "\f7af", Icon::Square5Fill => "\f7b0", Icon::Square5 => "\f7b1", Icon::Circle6Fill => "\f7b4", Icon::Circle6 => "\f7b5", Icon::Square6Fill => "\f7b6", Icon::Square6 => "\f7b7", Icon::Circle7Fill => "\f7ba", Icon::Circle7 => "\f7bb", Icon::Square7Fill => "\f7bc", Icon::Square7 => "\f7bd", Icon::Circle8Fill => "\f7c0", Icon::Circle8 => "\f7c1", Icon::Square8Fill => "\f7c2", Icon::Square8 => "\f7c3", Icon::Circle9Fill => "\f7c6", Icon::Circle9 => "\f7c7", Icon::Square9Fill => "\f7c8", Icon::Square9 => "\f7c9", Icon::AirplaneEnginesFill => "\f7ca", Icon::AirplaneEngines => "\f7cb", Icon::AirplaneFill => "\f7cc", Icon::Airplane => "\f7cd", Icon::Alexa => "\f7ce", Icon::Alipay => "\f7cf", Icon::Android => "\f7d0", Icon::Android2 => "\f7d1", Icon::BoxFill => "\f7d2", Icon::BoxSeamFill => "\f7d3", Icon::BrowserChrome => "\f7d4", Icon::BrowserEdge => "\f7d5", Icon::BrowserFirefox => "\f7d6", Icon::BrowserSafari => "\f7d7", Icon::CCircleFill => "\f7da", Icon::CCircle => "\f7db", Icon::CSquareFill => "\f7dc", Icon::CSquare => "\f7dd", Icon::CapsulePill => "\f7de", Icon::Capsule => "\f7df", Icon::CarFrontFill => "\f7e0", Icon::CarFront => "\f7e1", Icon::CassetteFill => "\f7e2", Icon::Cassette => "\f7e3", Icon::CcCircleFill => "\f7e6", Icon::CcCircle => "\f7e7", Icon::CcSquareFill => "\f7e8", Icon::CcSquare => "\f7e9", Icon::CupHotFill => "\f7ea", Icon::CupHot => "\f7eb", Icon::CurrencyRupee => "\f7ec", Icon::Dropbox => "\f7ed", Icon::Escape => "\f7ee", Icon::FastForwardBtnFill => "\f7ef", Icon::FastForwardBtn => "\f7f0", Icon::FastForwardCircleFill => "\f7f1", Icon::FastForwardCircle => "\f7f2", Icon::FastForwardFill => "\f7f3", Icon::FastForward => "\f7f4", Icon::FiletypeSql => "\f7f5", Icon::Fire => "\f7f6", Icon::GooglePlay => "\f7f7", Icon::HCircleFill => "\f7fa", Icon::HCircle => "\f7fb", Icon::HSquareFill => "\f7fc", Icon::HSquare => "\f7fd", Icon::Indent => "\f7fe", Icon::LungsFill => "\f7ff", Icon::Lungs => "\f800", Icon::MicrosoftTeams => "\f801", Icon::PCircleFill => "\f804", Icon::PCircle => "\f805", Icon::PSquareFill => "\f806", Icon::PSquare => "\f807", Icon::PassFill => "\f808", Icon::Pass => "\f809", Icon::Prescription => "\f80a", Icon::Prescription2 => "\f80b", Icon::RCircleFill => "\f80e", Icon::RCircle => "\f80f", Icon::RSquareFill => "\f810", Icon::RSquare => "\f811", Icon::Repeat1 => "\f812", Icon::Repeat => "\f813", Icon::RewindBtnFill => "\f814", Icon::RewindBtn => "\f815", Icon::RewindCircleFill => "\f816", Icon::RewindCircle => "\f817", Icon::RewindFill => "\f818", Icon::Rewind => "\f819", Icon::TrainFreightFrontFill => "\f81a", Icon::TrainFreightFront => "\f81b", Icon::TrainFrontFill => "\f81c", Icon::TrainFront => "\f81d", Icon::TrainLightrailFrontFill => "\f81e", Icon::TrainLightrailFront => "\f81f", Icon::TruckFrontFill => "\f820", Icon::TruckFront => "\f821", Icon::Ubuntu => "\f822", Icon::Unindent => "\f823", Icon::Unity => "\f824", Icon::UniversalAccessCircle => "\f825", Icon::UniversalAccess => "\f826", Icon::Virus => "\f827", Icon::Virus2 => "\f828", Icon::Wechat => "\f829", Icon::Yelp => "\f82a", Icon::SignStopFill => "\f82b", Icon::SignStopLightsFill => "\f82c", Icon::SignStopLights => "\f82d", Icon::SignStop => "\f82e", Icon::SignTurnLeftFill => "\f82f", Icon::SignTurnLeft => "\f830", Icon::SignTurnRightFill => "\f831", Icon::SignTurnRight => "\f832", Icon::SignTurnSlightLeftFill => "\f833", Icon::SignTurnSlightLeft => "\f834", Icon::SignTurnSlightRightFill => "\f835", Icon::SignTurnSlightRight => "\f836", Icon::SignYieldFill => "\f837", Icon::SignYield => "\f838", Icon::EvStationFill => "\f839", Icon::EvStation => "\f83a", Icon::FuelPumpDieselFill => "\f83b", Icon::FuelPumpDiesel => "\f83c", Icon::FuelPumpFill => "\f83d", Icon::FuelPump => "\f83e", Icon::Circle0Fill => "\f83f", Icon::Circle0 => "\f840", Icon::Square0Fill => "\f841", Icon::Square0 => "\f842", Icon::RocketFill => "\f843", Icon::RocketTakeoffFill => "\f844", Icon::RocketTakeoff => "\f845", Icon::Rocket => "\f846", Icon::Stripe => "\f847", Icon::Subscript => "\f848", Icon::Superscript => "\f849", Icon::Trello => "\f84a", Icon::EnvelopeAtFill => "\f84b", Icon::EnvelopeAt => "\f84c", Icon::Regex => "\f84d", Icon::TextWrap => "\f84e", Icon::SignDeadEndFill => "\f84f", Icon::SignDeadEnd => "\f850", Icon::SignDoNotEnterFill => "\f851", Icon::SignDoNotEnter => "\f852", Icon::SignIntersectionFill => "\f853", Icon::SignIntersectionSideFill => "\f854", Icon::SignIntersectionSide => "\f855", Icon::SignIntersectionTFill => "\f856", Icon::SignIntersectionT => "\f857", Icon::SignIntersectionYFill => "\f858", Icon::SignIntersectionY => "\f859", Icon::SignIntersection => "\f85a", Icon::SignMergeLeftFill => "\f85b", Icon::SignMergeLeft => "\f85c", Icon::SignMergeRightFill => "\f85d", Icon::SignMergeRight => "\f85e", Icon::SignNoLeftTurnFill => "\f85f", Icon::SignNoLeftTurn => "\f860", Icon::SignNoParkingFill => "\f861", Icon::SignNoParking => "\f862", Icon::SignNoRightTurnFill => "\f863", Icon::SignNoRightTurn => "\f864", Icon::SignRailroadFill => "\f865", Icon::SignRailroad => "\f866", Icon::BuildingAdd => "\f867", Icon::BuildingCheck => "\f868", Icon::BuildingDash => "\f869", Icon::BuildingDown => "\f86a", Icon::BuildingExclamation => "\f86b", Icon::BuildingFillAdd => "\f86c", Icon::BuildingFillCheck => "\f86d", Icon::BuildingFillDash => "\f86e", Icon::BuildingFillDown => "\f86f", Icon::BuildingFillExclamation => "\f870", Icon::BuildingFillGear => "\f871", Icon::BuildingFillLock => "\f872", Icon::BuildingFillSlash => "\f873", Icon::BuildingFillUp => "\f874", Icon::BuildingFillX => "\f875", Icon::BuildingFill => "\f876", Icon::BuildingGear => "\f877", Icon::BuildingLock => "\f878", Icon::BuildingSlash => "\f879", Icon::BuildingUp => "\f87a", Icon::BuildingX => "\f87b", Icon::BuildingsFill => "\f87c", Icon::Buildings => "\f87d", Icon::BusFrontFill => "\f87e", Icon::BusFront => "\f87f", Icon::EvFrontFill => "\f880", Icon::EvFront => "\f881", Icon::GlobeAmericas => "\f882", Icon::GlobeAsiaAustralia => "\f883", Icon::GlobeCentralSouthAsia => "\f884", Icon::GlobeEuropeAfrica => "\f885", Icon::HouseAddFill => "\f886", Icon::HouseAdd => "\f887", Icon::HouseCheckFill => "\f888", Icon::HouseCheck => "\f889", Icon::HouseDashFill => "\f88a", Icon::HouseDash => "\f88b", Icon::HouseDownFill => "\f88c", Icon::HouseDown => "\f88d", Icon::HouseExclamationFill => "\f88e", Icon::HouseExclamation => "\f88f", Icon::HouseGearFill => "\f890", Icon::HouseGear => "\f891", Icon::HouseLockFill => "\f892", Icon::HouseLock => "\f893", Icon::HouseSlashFill => "\f894", Icon::HouseSlash => "\f895", Icon::HouseUpFill => "\f896", Icon::HouseUp => "\f897", Icon::HouseXFill => "\f898", Icon::HouseX => "\f899", Icon::PersonAdd => "\f89a", Icon::PersonDown => "\f89b", Icon::PersonExclamation => "\f89c", Icon::PersonFillAdd => "\f89d", Icon::PersonFillCheck => "\f89e", Icon::PersonFillDash => "\f89f", Icon::PersonFillDown => "\f8a0", Icon::PersonFillExclamation => "\f8a1", Icon::PersonFillGear => "\f8a2", Icon::PersonFillLock => "\f8a3", Icon::PersonFillSlash => "\f8a4", Icon::PersonFillUp => "\f8a5", Icon::PersonFillX => "\f8a6", Icon::PersonGear => "\f8a7", Icon::PersonLock => "\f8a8", Icon::PersonSlash => "\f8a9", Icon::PersonUp => "\f8aa", Icon::Scooter => "\f8ab", Icon::TaxiFrontFill => "\f8ac", Icon::TaxiFront => "\f8ad", Icon::Amd => "\f8ae", Icon::DatabaseAdd => "\f8af", Icon::DatabaseCheck => "\f8b0", Icon::DatabaseDash => "\f8b1", Icon::DatabaseDown => "\f8b2", Icon::DatabaseExclamation => "\f8b3", Icon::DatabaseFillAdd => "\f8b4", Icon::DatabaseFillCheck => "\f8b5", Icon::DatabaseFillDash => "\f8b6", Icon::DatabaseFillDown => "\f8b7", Icon::DatabaseFillExclamation => "\f8b8", Icon::DatabaseFillGear => "\f8b9", Icon::DatabaseFillLock => "\f8ba", Icon::DatabaseFillSlash => "\f8bb", Icon::DatabaseFillUp => "\f8bc", Icon::DatabaseFillX => "\f8bd", Icon::DatabaseFill => "\f8be", Icon::DatabaseGear => "\f8bf", Icon::DatabaseLock => "\f8c0", Icon::DatabaseSlash => "\f8c1", Icon::DatabaseUp => "\f8c2", Icon::DatabaseX => "\f8c3", Icon::Database => "\f8c4", Icon::HousesFill => "\f8c5", Icon::Houses => "\f8c6", Icon::Nvidia => "\f8c7", Icon::PersonVcardFill => "\f8c8", Icon::PersonVcard => "\f8c9", Icon::SinaWeibo => "\f8ca", Icon::TencentQq => "\f8cb", Icon::Wikipedia => "\f8cc", Icon::AlphabetUppercase => "\f2a5", Icon::Alphabet => "\f68a", Icon::Amazon => "\f68d", Icon::ArrowsCollapseVertical => "\f690", Icon::ArrowsExpandVertical => "\f695", Icon::ArrowsVertical => "\f698", Icon::Arrows => "\f6a2", Icon::BanFill => "\f6a3", Icon::Ban => "\f6b6", Icon::Bing => "\f6c2", Icon::Cake => "\f6e0", Icon::Cake2 => "\f6ed", Icon::Cookie => "\f6ee", Icon::Copy => "\f759", Icon::Crosshair => "\f769", Icon::Crosshair2 => "\f794", Icon::EmojiAstonishedFill => "\f795", Icon::EmojiAstonished => "\f79a", Icon::EmojiGrimaceFill => "\f79b", Icon::EmojiGrimace => "\f7a0", Icon::EmojiGrinFill => "\f7a1", Icon::EmojiGrin => "\f7a6", Icon::EmojiSurpriseFill => "\f7a7", Icon::EmojiSurprise => "\f7ac", Icon::EmojiTearFill => "\f7ad", Icon::EmojiTear => "\f7b2", Icon::EnvelopeArrowDownFill => "\f7b3", Icon::EnvelopeArrowDown => "\f7b8", Icon::EnvelopeArrowUpFill => "\f7b9", Icon::EnvelopeArrowUp => "\f7be", Icon::Feather => "\f7bf", Icon::Feather2 => "\f7c4", Icon::FloppyFill => "\f7c5", Icon::Floppy => "\f7d8", Icon::Floppy2Fill => "\f7d9", Icon::Floppy2 => "\f7e4", Icon::Gitlab => "\f7e5", Icon::Highlighter => "\f7f8", Icon::MarkerTip => "\f802", Icon::NvmeFill => "\f803", Icon::Nvme => "\f80c", Icon::Opencollective => "\f80d", Icon::PciCardNetwork => "\f8cd", Icon::PciCardSound => "\f8ce", Icon::Radar => "\f8cf", Icon::SendArrowDownFill => "\f8d0", Icon::SendArrowDown => "\f8d1", Icon::SendArrowUpFill => "\f8d2", Icon::SendArrowUp => "\f8d3", Icon::SimSlashFill => "\f8d4", Icon::SimSlash => "\f8d5", Icon::Sourceforge => "\f8d6", Icon::Substack => "\f8d7", Icon::ThreadsFill => "\f8d8", Icon::Threads => "\f8d9", Icon::Transparency => "\f8da", Icon::TwitterX => "\f8db", Icon::TypeH4 => "\f8dc", Icon::TypeH5 => "\f8dd", Icon::TypeH6 => "\f8de", Icon::BackpackFill => "\f8df", Icon::Backpack => "\f8e0", Icon::Backpack2Fill => "\f8e1", Icon::Backpack2 => "\f8e2", Icon::Backpack3Fill => "\f8e3", Icon::Backpack3 => "\f8e4", Icon::Backpack4Fill => "\f8e5", Icon::Backpack4 => "\f8e6", Icon::Brilliance => "\f8e7", Icon::CakeFill => "\f8e8", Icon::Cake2Fill => "\f8e9", Icon::DuffleFill => "\f8ea", Icon::Duffle => "\f8eb", Icon::Exposure => "\f8ec", Icon::GenderNeuter => "\f8ed", Icon::Highlights => "\f8ee", Icon::LuggageFill => "\f8ef", Icon::Luggage => "\f8f0", Icon::MailboxFlag => "\f8f1", Icon::Mailbox2Flag => "\f8f2", Icon::NoiseReduction => "\f8f3", Icon::PassportFill => "\f8f4", Icon::Passport => "\f8f5", Icon::PersonArmsUp => "\f8f6", Icon::PersonRaisedHand => "\f8f7", Icon::PersonStandingDress => "\f8f8", Icon::PersonStanding => "\f8f9", Icon::PersonWalking => "\f8fa", Icon::PersonWheelchair => "\f8fb", Icon::Shadows => "\f8fc", Icon::SuitcaseFill => "\f8fd", Icon::SuitcaseLgFill => "\f8fe", Icon::SuitcaseLg => "\f8ff", Icon::Suitcase => "\f900", Icon::Suitcase2Fill => "\f901", Icon::Suitcase2 => "\f902", Icon::Vignette => "\f903", }; } public function emoji() { return match( $this ) { Icon::AlarmFill => "", Icon::Alarm => "", Icon::AlignBottom => "", Icon::AlignCenter => "", Icon::AlignEnd => "", Icon::AlignMiddle => "", Icon::AlignStart => "⭐", Icon::AlignTop => "", Icon::Alt => "", Icon::AppIndicator => "", Icon::App => "", Icon::ArchiveFill => "", Icon::Archive => "", Icon::Arrow90degDown => "➡️", Icon::Arrow90degLeft => "➡️", Icon::Arrow90degRight => "➡️", Icon::Arrow90degUp => "➡️", Icon::ArrowBarDown => "➡️", Icon::ArrowBarLeft => "➡️", Icon::ArrowBarRight => "➡️", Icon::ArrowBarUp => "➡️", Icon::ArrowClockwise => "🔄", Icon::ArrowCounterclockwise => "➡️", Icon::ArrowDownCircleFill => "➡️", Icon::ArrowDownCircle => "➡️", Icon::ArrowDownLeftCircleFill => "➡️", Icon::ArrowDownLeftCircle => "➡️", Icon::ArrowDownLeftSquareFill => "➡️", Icon::ArrowDownLeftSquare => "➡️", Icon::ArrowDownLeft => "➡️", Icon::ArrowDownRightCircleFill => "➡️", Icon::ArrowDownRightCircle => "➡️", Icon::ArrowDownRightSquareFill => "➡️", Icon::ArrowDownRightSquare => "➡️", Icon::ArrowDownRight => "➡️", Icon::ArrowDownShort => "➡️", Icon::ArrowDownSquareFill => "➡️", Icon::ArrowDownSquare => "➡️", Icon::ArrowDownUp => "➡️", Icon::ArrowDown => "➡️", Icon::ArrowLeftCircleFill => "➡️", Icon::ArrowLeftCircle => "➡️", Icon::ArrowLeftRight => "➡️", Icon::ArrowLeftShort => "➡️", Icon::ArrowLeftSquareFill => "➡️", Icon::ArrowLeftSquare => "➡️", Icon::ArrowLeft => "⬅️", Icon::ArrowRepeat => "🔁", Icon::ArrowReturnLeft => "➡️", Icon::ArrowReturnRight => "➡️", Icon::ArrowRightCircleFill => "➡️", Icon::ArrowRightCircle => "➡️", Icon::ArrowRightShort => "➡️", Icon::ArrowRightSquareFill => "➡️", Icon::ArrowRightSquare => "➡️", Icon::ArrowRight => "➡️", Icon::ArrowUpCircleFill => "➡️", Icon::ArrowUpCircle => "➡️", Icon::ArrowUpLeftCircleFill => "➡️", Icon::ArrowUpLeftCircle => "➡️", Icon::ArrowUpLeftSquareFill => "➡️", Icon::ArrowUpLeftSquare => "➡️", Icon::ArrowUpLeft => "➡️", Icon::ArrowUpRightCircleFill => "➡️", Icon::ArrowUpRightCircle => "➡️", Icon::ArrowUpRightSquareFill => "➡️", Icon::ArrowUpRightSquare => "➡️", Icon::ArrowUpRight => "➡️", Icon::ArrowUpShort => "➡️", Icon::ArrowUpSquareFill => "➡️", Icon::ArrowUpSquare => "➡️", Icon::ArrowUp => "➡️", Icon::ArrowsAngleContract => "➡️", Icon::ArrowsAngleExpand => "➡️", Icon::ArrowsCollapse => "➡️", Icon::ArrowsExpand => "➡️", Icon::ArrowsFullscreen => "➡️", Icon::ArrowsMove => "➡️", Icon::AspectRatioFill => "", Icon::AspectRatio => "", Icon::Asterisk => "", Icon::At => "", Icon::AwardFill => "", Icon::Award => "", Icon::Back => "", Icon::BackspaceFill => "", Icon::BackspaceReverseFill => "", Icon::BackspaceReverse => "", Icon::Backspace => "", Icon::Badge3dFill => "", Icon::Badge3d => "", Icon::Badge4kFill => "", Icon::Badge4k => "", Icon::Badge8kFill => "", Icon::Badge8k => "", Icon::BadgeAdFill => "", Icon::BadgeAd => "", Icon::BadgeArFill => "", Icon::BadgeAr => "", Icon::BadgeCcFill => "", Icon::BadgeCc => "", Icon::BadgeHdFill => "", Icon::BadgeHd => "", Icon::BadgeTmFill => "", Icon::BadgeTm => "", Icon::BadgeVoFill => "", Icon::BadgeVo => "", Icon::BadgeVrFill => "", Icon::BadgeVr => "", Icon::BadgeWcFill => "", Icon::BadgeWc => "", Icon::BagCheckFill => "✅", Icon::BagCheck => "✅", Icon::BagDashFill => "", Icon::BagDash => "", Icon::BagFill => "", Icon::BagPlusFill => "➕", Icon::BagPlus => "➕", Icon::BagXFill => "❌", Icon::BagX => "❌", Icon::Bag => "", Icon::BarChartFill => "", Icon::BarChartLineFill => "", Icon::BarChartLine => "", Icon::BarChartSteps => "", Icon::BarChart => "", Icon::BasketFill => "", Icon::Basket => "", Icon::Basket2Fill => "", Icon::Basket2 => "", Icon::Basket3Fill => "", Icon::Basket3 => "", Icon::BatteryCharging => "", Icon::BatteryFull => "", Icon::BatteryHalf => "", Icon::Battery => "", Icon::BellFill => "🔔", Icon::Bell => "🔔", Icon::Bezier => "", Icon::Bezier2 => "", Icon::Bicycle => "", Icon::BinocularsFill => "", Icon::Binoculars => "", Icon::BlockquoteLeft => "⬅️", Icon::BlockquoteRight => "➡️", Icon::BookFill => "", Icon::BookHalf => "", Icon::Book => "", Icon::BookmarkCheckFill => "✅", Icon::BookmarkCheck => "✅", Icon::BookmarkDashFill => "", Icon::BookmarkDash => "", Icon::BookmarkFill => "", Icon::BookmarkHeartFill => "❤️", Icon::BookmarkHeart => "❤️", Icon::BookmarkPlusFill => "➕", Icon::BookmarkPlus => "➕", Icon::BookmarkStarFill => "⭐", Icon::BookmarkStar => "⭐", Icon::BookmarkXFill => "❌", Icon::BookmarkX => "❌", Icon::Bookmark => "", Icon::BookmarksFill => "", Icon::Bookmarks => "", Icon::Bookshelf => "", Icon::BootstrapFill => "", Icon::BootstrapReboot => "", Icon::Bootstrap => "", Icon::BorderAll => "", Icon::BorderBottom => "", Icon::BorderCenter => "", Icon::BorderInner => "", Icon::BorderLeft => "⬅️", Icon::BorderMiddle => "", Icon::BorderOuter => "", Icon::BorderRight => "➡️", Icon::BorderStyle => "", Icon::BorderTop => "", Icon::BorderWidth => "", Icon::Border => "", Icon::BoundingBoxCircles => "❌", Icon::BoundingBox => "❌", Icon::BoxArrowDownLeft => "➡️", Icon::BoxArrowDownRight => "➡️", Icon::BoxArrowDown => "➡️", Icon::BoxArrowInDownLeft => "➡️", Icon::BoxArrowInDownRight => "➡️", Icon::BoxArrowInDown => "➡️", Icon::BoxArrowInLeft => "➡️", Icon::BoxArrowInRight => "➡️", Icon::BoxArrowInUpLeft => "➡️", Icon::BoxArrowInUpRight => "➡️", Icon::BoxArrowInUp => "➡️", Icon::BoxArrowLeft => "➡️", Icon::BoxArrowRight => "➡️", Icon::BoxArrowUpLeft => "➡️", Icon::BoxArrowUpRight => "➡️", Icon::BoxArrowUp => "➡️", Icon::BoxSeam => "❌", Icon::Box => "❌", Icon::Braces => "", Icon::Bricks => "", Icon::BriefcaseFill => "", Icon::Briefcase => "", Icon::BrightnessAltHighFill => "➡️", Icon::BrightnessAltHigh => "➡️", Icon::BrightnessAltLowFill => "➡️", Icon::BrightnessAltLow => "➡️", Icon::BrightnessHighFill => "➡️", Icon::BrightnessHigh => "➡️", Icon::BrightnessLowFill => "➡️", Icon::BrightnessLow => "➡️", Icon::BroadcastPin => "", Icon::Broadcast => "", Icon::BrushFill => "", Icon::Brush => "", Icon::BucketFill => "", Icon::Bucket => "", Icon::BugFill => "", Icon::Bug => "", Icon::Building => "", Icon::Bullseye => "", Icon::CalculatorFill => "", Icon::Calculator => "", Icon::CalendarCheckFill => "✅", Icon::CalendarCheck => "✅", Icon::CalendarDateFill => "📅", Icon::CalendarDate => "📅", Icon::CalendarDayFill => "📅", Icon::CalendarDay => "📅", Icon::CalendarEventFill => "📅", Icon::CalendarEvent => "📅", Icon::CalendarFill => "📅", Icon::CalendarMinusFill => "➖", Icon::CalendarMinus => "➖", Icon::CalendarMonthFill => "📅", Icon::CalendarMonth => "📅", Icon::CalendarPlusFill => "➕", Icon::CalendarPlus => "📅", Icon::CalendarRangeFill => "📅", Icon::CalendarRange => "📅", Icon::CalendarWeekFill => "📅", Icon::CalendarWeek => "📅", Icon::CalendarXFill => "❌", Icon::CalendarX => "❌", Icon::Calendar => "📅", Icon::Calendar2CheckFill => "✅", Icon::Calendar2Check => "✅", Icon::Calendar2DateFill => "📅", Icon::Calendar2Date => "📅", Icon::Calendar2DayFill => "📅", Icon::Calendar2Day => "📅", Icon::Calendar2EventFill => "📅", Icon::Calendar2Event => "📅", Icon::Calendar2Fill => "📅", Icon::Calendar2MinusFill => "➖", Icon::Calendar2Minus => "➖", Icon::Calendar2MonthFill => "📅", Icon::Calendar2Month => "📅", Icon::Calendar2PlusFill => "➕", Icon::Calendar2Plus => "➕", Icon::Calendar2RangeFill => "📅", Icon::Calendar2Range => "📅", Icon::Calendar2WeekFill => "📅", Icon::Calendar2Week => "📅", Icon::Calendar2XFill => "❌", Icon::Calendar2X => "❌", Icon::Calendar2 => "📅", Icon::Calendar3EventFill => "📅", Icon::Calendar3Event => "📅", Icon::Calendar3Fill => "📅", Icon::Calendar3RangeFill => "📅", Icon::Calendar3Range => "📅", Icon::Calendar3WeekFill => "📅", Icon::Calendar3Week => "📅", Icon::Calendar3 => "📅", Icon::Calendar4Event => "📅", Icon::Calendar4Range => "📅", Icon::Calendar4Week => "📅", Icon::Calendar4 => "📅", Icon::CameraFill => "📷", Icon::CameraReelsFill => "📷", Icon::CameraReels => "📷", Icon::CameraVideoFill => "📷", Icon::CameraVideoOffFill => "📷", Icon::CameraVideoOff => "📷", Icon::CameraVideo => "📷", Icon::Camera => "📷", Icon::Camera2 => "📷", Icon::CapslockFill => "🔒", Icon::Capslock => "🔒", Icon::CardChecklist => "✅", Icon::CardHeading => "", Icon::CardImage => "", Icon::CardList => "", Icon::CardText => "❌", Icon::CaretDownFill => "⬇️", Icon::CaretDownSquareFill => "⬇️", Icon::CaretDownSquare => "⬇️", Icon::CaretDown => "⬇️", Icon::CaretLeftFill => "⬅️", Icon::CaretLeftSquareFill => "⬅️", Icon::CaretLeftSquare => "⬅️", Icon::CaretLeft => "⬅️", Icon::CaretRightFill => "➡️", Icon::CaretRightSquareFill => "➡️", Icon::CaretRightSquare => "➡️", Icon::CaretRight => "➡️", Icon::CaretUpFill => "⬆️", Icon::CaretUpSquareFill => "⬆️", Icon::CaretUpSquare => "⬆️", Icon::CaretUp => "⬆️", Icon::CartCheckFill => "✅", Icon::CartCheck => "✅", Icon::CartDashFill => "", Icon::CartDash => "", Icon::CartFill => "", Icon::CartPlusFill => "➕", Icon::CartPlus => "➕", Icon::CartXFill => "❌", Icon::CartX => "❌", Icon::Cart => "🛒", Icon::Cart2 => "", Icon::Cart3 => "", Icon::Cart4 => "", Icon::CashStack => "", Icon::Cash => "", Icon::Cast => "", Icon::ChatDotsFill => "💬", Icon::ChatDots => "💬", Icon::ChatFill => "💬", Icon::ChatLeftDotsFill => "⬅️", Icon::ChatLeftDots => "⬅️", Icon::ChatLeftFill => "⬅️", Icon::ChatLeftQuoteFill => "⬅️", Icon::ChatLeftQuote => "⬅️", Icon::ChatLeftTextFill => "⬅️", Icon::ChatLeftText => "💬", Icon::ChatLeft => "⬅️", Icon::ChatQuoteFill => "💬", Icon::ChatQuote => "💬", Icon::ChatRightDotsFill => "➡️", Icon::ChatRightDots => "➡️", Icon::ChatRightFill => "➡️", Icon::ChatRightQuoteFill => "➡️", Icon::ChatRightQuote => "➡️", Icon::ChatRightTextFill => "➡️", Icon::ChatRightText => "➡️", Icon::ChatRight => "➡️", Icon::ChatSquareDotsFill => "💬", Icon::ChatSquareDots => "💬", Icon::ChatSquareFill => "💬", Icon::ChatSquareQuoteFill => "💬", Icon::ChatSquareQuote => "💬", Icon::ChatSquareTextFill => "❌", Icon::ChatSquareText => "❌", Icon::ChatSquare => "💬", Icon::ChatTextFill => "❌", Icon::ChatText => "❌", Icon::Chat => "💬", Icon::CheckAll => "✅", Icon::CheckCircleFill => "✅", Icon::CheckCircle => "✅", Icon::CheckSquareFill => "✅", Icon::CheckSquare => "✅", Icon::Check => "✅", Icon::Check2All => "✅", Icon::Check2Circle => "✅", Icon::Check2Square => "✅", Icon::Check2 => "✅", Icon::ChevronBarContract => "", Icon::ChevronBarDown => "⬇️", Icon::ChevronBarExpand => "❌", Icon::ChevronBarLeft => "⬅️", Icon::ChevronBarRight => "➡️", Icon::ChevronBarUp => "⬆️", Icon::ChevronCompactDown => "⬇️", Icon::ChevronCompactLeft => "⬅️", Icon::ChevronCompactRight => "➡️", Icon::ChevronCompactUp => "⬆️", Icon::ChevronContract => "", Icon::ChevronDoubleDown => "⬇️", Icon::ChevronDoubleLeft => "⬅️", Icon::ChevronDoubleRight => "➡️", Icon::ChevronDoubleUp => "⬆️", Icon::ChevronDown => "⬇️", Icon::ChevronExpand => "❌", Icon::ChevronLeft => "⬅️", Icon::ChevronRight => "➡️", Icon::ChevronUp => "⬆️", Icon::CircleFill => "", Icon::CircleHalf => "", Icon::CircleSquare => "", Icon::Circle => "", Icon::ClipboardCheck => "✅", Icon::ClipboardData => "", Icon::ClipboardMinus => "➖", Icon::ClipboardPlus => "➕", Icon::ClipboardX => "❌", Icon::Clipboard => "", Icon::ClockFill => "🔒", Icon::ClockHistory => "🕘", Icon::Clock => "🕒", Icon::CloudArrowDownFill => "➡️", Icon::CloudArrowDown => "➡️", Icon::CloudArrowUpFill => "➡️", Icon::CloudArrowUp => "➡️", Icon::CloudCheckFill => "✅", Icon::CloudCheck => "✅", Icon::CloudDownloadFill => "⬇️", Icon::CloudDownload => "⬇️", Icon::CloudDrizzleFill => "", Icon::CloudDrizzle => "", Icon::CloudFill => "", Icon::CloudFogFill => "", Icon::CloudFog => "", Icon::CloudFog2Fill => "", Icon::CloudFog2 => "", Icon::CloudHailFill => "", Icon::CloudHail => "", Icon::CloudHazeFill => "", Icon::CloudHaze => "", Icon::CloudHaze2Fill => "", Icon::CloudLightningFill => "", Icon::CloudLightningRainFill => "", Icon::CloudLightningRain => "", Icon::CloudLightning => "", Icon::CloudMinusFill => "➖", Icon::CloudMinus => "➖", Icon::CloudMoonFill => "", Icon::CloudMoon => "", Icon::CloudPlusFill => "➕", Icon::CloudPlus => "➕", Icon::CloudRainFill => "", Icon::CloudRainHeavyFill => "", Icon::CloudRainHeavy => "", Icon::CloudRain => "", Icon::CloudSlashFill => "", Icon::CloudSlash => "", Icon::CloudSleetFill => "", Icon::CloudSleet => "", Icon::CloudSnowFill => "", Icon::CloudSnow => "", Icon::CloudSunFill => "", Icon::CloudSun => "", Icon::CloudUploadFill => "⬆️", Icon::CloudUpload => "⬆️", Icon::Cloud => "", Icon::CloudsFill => "", Icon::Clouds => "", Icon::CloudyFill => "", Icon::Cloudy => "", Icon::CodeSlash => "💻", Icon::CodeSquare => "💻", Icon::Code => "💻", Icon::CollectionFill => "", Icon::CollectionPlayFill => "▶️", Icon::CollectionPlay => "▶️", Icon::Collection => "", Icon::ColumnsGap => "", Icon::Columns => "", Icon::Command => "", Icon::CompassFill => "", Icon::Compass => "", Icon::ConeStriped => "", Icon::Cone => "", Icon::Controller => "", Icon::CpuFill => "", Icon::Cpu => "🧠", Icon::CreditCard2BackFill => "", Icon::CreditCard2Back => "", Icon::CreditCard2FrontFill => "", Icon::CreditCard2Front => "", Icon::CreditCardFill => "", Icon::CreditCard => "", Icon::Crop => "", Icon::CupFill => "⬆️", Icon::CupStraw => "⬆️", Icon::Cup => "⬆️", Icon::CursorFill => "", Icon::CursorText => "❌", Icon::Cursor => "", Icon::DashCircleDotted => "", Icon::DashCircleFill => "", Icon::DashCircle => "➖", Icon::DashSquareDotted => "", Icon::DashSquareFill => "", Icon::DashSquare => "", Icon::Dash => "", Icon::Diagram2Fill => "", Icon::Diagram2 => "", Icon::Diagram3Fill => "", Icon::Diagram3 => "📊", Icon::DiamondFill => "", Icon::DiamondHalf => "", Icon::Diamond => "", Icon::Dice1Fill => "", Icon::Dice1 => "", Icon::Dice2Fill => "", Icon::Dice2 => "", Icon::Dice3Fill => "", Icon::Dice3 => "", Icon::Dice4Fill => "", Icon::Dice4 => "", Icon::Dice5Fill => "", Icon::Dice5 => "", Icon::Dice6Fill => "", Icon::Dice6 => "", Icon::DiscFill => "", Icon::Disc => "", Icon::Discord => "", Icon::DisplayFill => "▶️", Icon::Display => "▶️", Icon::DistributeHorizontal => "", Icon::DistributeVertical => "", Icon::DoorClosedFill => "", Icon::DoorClosed => "", Icon::DoorOpenFill => "", Icon::DoorOpen => "", Icon::Dot => "", Icon::Download => "⬇️", Icon::DropletFill => "", Icon::DropletHalf => "", Icon::Droplet => "", Icon::Earbuds => "", Icon::EaselFill => "", Icon::Easel => "", Icon::EggFill => "", Icon::EggFried => "", Icon::Egg => "", Icon::EjectFill => "", Icon::Eject => "", Icon::EmojiAngryFill => "", Icon::EmojiAngry => "", Icon::EmojiDizzyFill => "", Icon::EmojiDizzy => "", Icon::EmojiExpressionlessFill => "❌", Icon::EmojiExpressionless => "❌", Icon::EmojiFrownFill => "", Icon::EmojiFrown => "", Icon::EmojiHeartEyesFill => "❤️", Icon::EmojiHeartEyes => "❤️", Icon::EmojiLaughingFill => "", Icon::EmojiLaughing => "", Icon::EmojiNeutralFill => "", Icon::EmojiNeutral => "", Icon::EmojiSmileFill => "", Icon::EmojiSmileUpsideDownFill => "⬆️", Icon::EmojiSmileUpsideDown => "⬆️", Icon::EmojiSmile => "", Icon::EmojiSunglassesFill => "", Icon::EmojiSunglasses => "", Icon::EmojiWinkFill => "", Icon::EmojiWink => "", Icon::EnvelopeFill => "", Icon::EnvelopeOpenFill => "", Icon::EnvelopeOpen => "", Icon::Envelope => "", Icon::EraserFill => "", Icon::Eraser => "", Icon::ExclamationCircleFill => "❌", Icon::ExclamationCircle => "❌", Icon::ExclamationDiamondFill => "❌", Icon::ExclamationDiamond => "❌", Icon::ExclamationOctagonFill => "❌", Icon::ExclamationOctagon => "❌", Icon::ExclamationSquareFill => "❌", Icon::ExclamationSquare => "❌", Icon::ExclamationTriangleFill => "❌", Icon::ExclamationTriangle => "⚠️", Icon::Exclamation => "❌", Icon::Exclude => "❌", Icon::EyeFill => "", Icon::EyeSlashFill => "", Icon::EyeSlash => "🙈", Icon::Eye => "👁️", Icon::Eyedropper => "", Icon::Eyeglasses => "", Icon::Facebook => "", Icon::FileArrowDownFill => "➡️", Icon::FileArrowDown => "➡️", Icon::FileArrowUpFill => "➡️", Icon::FileArrowUp => "➡️", Icon::FileBarGraphFill => "📄", Icon::FileBarGraph => "📄", Icon::FileBinaryFill => "📄", Icon::FileBinary => "📄", Icon::FileBreakFill => "📄", Icon::FileBreak => "📄", Icon::FileCheckFill => "✅", Icon::FileCheck => "✅", Icon::FileCodeFill => "📄", Icon::FileCode => "📄", Icon::FileDiffFill => "📄", Icon::FileDiff => "📄", Icon::FileEarmarkArrowDownFill => "➡️", Icon::FileEarmarkArrowDown => "➡️", Icon::FileEarmarkArrowUpFill => "➡️", Icon::FileEarmarkArrowUp => "➡️", Icon::FileEarmarkBarGraphFill => "📄", Icon::FileEarmarkBarGraph => "📄", Icon::FileEarmarkBinaryFill => "📄", Icon::FileEarmarkBinary => "📄", Icon::FileEarmarkBreakFill => "📄", Icon::FileEarmarkBreak => "📄", Icon::FileEarmarkCheckFill => "✅", Icon::FileEarmarkCheck => "✅", Icon::FileEarmarkCodeFill => "📄", Icon::FileEarmarkCode => "📄", Icon::FileEarmarkDiffFill => "📄", Icon::FileEarmarkDiff => "📄", Icon::FileEarmarkEaselFill => "📄", Icon::FileEarmarkEasel => "📄", Icon::FileEarmarkExcelFill => "❌", Icon::FileEarmarkExcel => "❌", Icon::FileEarmarkFill => "📄", Icon::FileEarmarkFontFill => "📄", Icon::FileEarmarkFont => "📄", Icon::FileEarmarkImageFill => "📄", Icon::FileEarmarkImage => "📄", Icon::FileEarmarkLockFill => "📄", Icon::FileEarmarkLock => "📄", Icon::FileEarmarkLock2Fill => "📄", Icon::FileEarmarkLock2 => "📄", Icon::FileEarmarkMedicalFill => "📄", Icon::FileEarmarkMedical => "📄", Icon::FileEarmarkMinusFill => "➖", Icon::FileEarmarkMinus => "➖", Icon::FileEarmarkMusicFill => "📄", Icon::FileEarmarkMusic => "📄", Icon::FileEarmarkPersonFill => "📄", Icon::FileEarmarkPerson => "📄", Icon::FileEarmarkPlayFill => "📄", Icon::FileEarmarkPlay => "📄", Icon::FileEarmarkPlusFill => "➕", Icon::FileEarmarkPlus => "➕", Icon::FileEarmarkPostFill => "📄", Icon::FileEarmarkPost => "📄", Icon::FileEarmarkPptFill => "📄", Icon::FileEarmarkPpt => "📄", Icon::FileEarmarkRichtextFill => "❌", Icon::FileEarmarkRichtext => "❌", Icon::FileEarmarkRuledFill => "📄", Icon::FileEarmarkRuled => "📄", Icon::FileEarmarkSlidesFill => "📄", Icon::FileEarmarkSlides => "📄", Icon::FileEarmarkSpreadsheetFill => "📄", Icon::FileEarmarkSpreadsheet => "📄", Icon::FileEarmarkTextFill => "❌", Icon::FileEarmarkText => "❌", Icon::FileEarmarkWordFill => "📄", Icon::FileEarmarkWord => "📄", Icon::FileEarmarkXFill => "❌", Icon::FileEarmarkX => "❌", Icon::FileEarmarkZipFill => "📄", Icon::FileEarmarkZip => "📄", Icon::FileEarmark => "📄", Icon::FileEaselFill => "📄", Icon::FileEasel => "📄", Icon::FileExcelFill => "❌", Icon::FileExcel => "❌", Icon::FileFill => "📄", Icon::FileFontFill => "📄", Icon::FileFont => "📄", Icon::FileImageFill => "📄", Icon::FileImage => "📄", Icon::FileLockFill => "📄", Icon::FileLock => "📄", Icon::FileLock2Fill => "📄", Icon::FileLock2 => "📄", Icon::FileMedicalFill => "📄", Icon::FileMedical => "📄", Icon::FileMinusFill => "➖", Icon::FileMinus => "➖", Icon::FileMusicFill => "📄", Icon::FileMusic => "📄", Icon::FilePersonFill => "📄", Icon::FilePerson => "📄", Icon::FilePlayFill => "📄", Icon::FilePlay => "📄", Icon::FilePlusFill => "➕", Icon::FilePlus => "➕", Icon::FilePostFill => "📄", Icon::FilePost => "📄", Icon::FilePptFill => "📄", Icon::FilePpt => "📄", Icon::FileRichtextFill => "❌", Icon::FileRichtext => "❌", Icon::FileRuledFill => "📄", Icon::FileRuled => "📄", Icon::FileSlidesFill => "📄", Icon::FileSlides => "📄", Icon::FileSpreadsheetFill => "📄", Icon::FileSpreadsheet => "📄", Icon::FileTextFill => "❌", Icon::FileText => "❌", Icon::FileWordFill => "📄", Icon::FileWord => "📄", Icon::FileXFill => "❌", Icon::FileX => "❌", Icon::FileZipFill => "📄", Icon::FileZip => "📄", Icon::File => "📄", Icon::FilesAlt => "📄", Icon::Files => "📄", Icon::Film => "", Icon::FilterCircleFill => "", Icon::FilterCircle => "", Icon::FilterLeft => "⬅️", Icon::FilterRight => "➡️", Icon::FilterSquareFill => "", Icon::FilterSquare => "", Icon::Filter => "", Icon::FlagFill => "", Icon::Flag => "", Icon::Flower1 => "", Icon::Flower2 => "", Icon::Flower3 => "", Icon::FolderCheck => "✅", Icon::FolderFill => "📁", Icon::FolderMinus => "➖", Icon::FolderPlus => "➕", Icon::FolderSymlinkFill => "📁", Icon::FolderSymlink => "📁", Icon::FolderX => "❌", Icon::Folder => "📁", Icon::Folder2Open => "📁", Icon::Folder2 => "📁", Icon::Fonts => "", Icon::ForwardFill => "", Icon::Forward => "", Icon::Front => "", Icon::FullscreenExit => "❌", Icon::Fullscreen => "", Icon::FunnelFill => "", Icon::Funnel => "🔽", Icon::GearFill => "⚙️", Icon::GearWideConnected => "⚙️", Icon::GearWide => "⚙️", Icon::Gear => "⚙️", Icon::Gem => "", Icon::GeoAltFill => "", Icon::GeoAlt => "", Icon::GeoFill => "", Icon::Geo => "", Icon::GiftFill => "", Icon::Gift => "", Icon::Github => "", Icon::Globe => "🌐", Icon::Globe2 => "", Icon::Google => "", Icon::GraphDown => "⬇️", Icon::GraphUp => "⬆️", Icon::Grid1x2Fill => "❌", Icon::Grid1x2 => "❌", Icon::Grid3x2GapFill => "❌", Icon::Grid3x2Gap => "❌", Icon::Grid3x2 => "❌", Icon::Grid3x3GapFill => "❌", Icon::Grid3x3Gap => "❌", Icon::Grid3x3 => "❌", Icon::GridFill => "", Icon::Grid => "", Icon::GripHorizontal => "", Icon::GripVertical => "", Icon::Hammer => "", Icon::HandIndexFill => "❌", Icon::HandIndexThumbFill => "❌", Icon::HandIndexThumb => "❌", Icon::HandIndex => "❌", Icon::HandThumbsDownFill => "⬇️", Icon::HandThumbsDown => "⬇️", Icon::HandThumbsUpFill => "⬆️", Icon::HandThumbsUp => "⬆️", Icon::HandbagFill => "", Icon::Handbag => "", Icon::Hash => "", Icon::HddFill => "", Icon::HddNetworkFill => "", Icon::HddNetwork => "", Icon::HddRackFill => "", Icon::HddRack => "", Icon::HddStackFill => "", Icon::HddStack => "", Icon::Hdd => "", Icon::Headphones => "", Icon::Headset => "", Icon::HeartFill => "❤️", Icon::HeartHalf => "❤️", Icon::Heart => "❤️", Icon::HeptagonFill => "", Icon::HeptagonHalf => "", Icon::Heptagon => "", Icon::HexagonFill => "❌", Icon::HexagonHalf => "❌", Icon::Hexagon => "❌", Icon::HourglassBottom => "", Icon::HourglassSplit => "⏳", Icon::HourglassTop => "", Icon::Hourglass => "⏳", Icon::HouseDoorFill => "🏠", Icon::HouseDoor => "🚪", Icon::HouseFill => "🏠", Icon::House => "", Icon::Hr => "", Icon::Hurricane => "", Icon::ImageAlt => "", Icon::ImageFill => "", Icon::Image => "🖼️", Icon::Images => "🖼️", Icon::InboxFill => "❌", Icon::Inbox => "❌", Icon::InboxesFill => "❌", Icon::Inboxes => "❌", Icon::InfoCircleFill => "ℹ️", Icon::InfoCircle => "ℹ️", Icon::InfoSquareFill => "ℹ️", Icon::InfoSquare => "ℹ️", Icon::Info => "ℹ️", Icon::InputCursorText => "❌", Icon::InputCursor => "", Icon::Instagram => "", Icon::Intersect => "", Icon::JournalAlbum => "", Icon::JournalArrowDown => "➡️", Icon::JournalArrowUp => "➡️", Icon::JournalBookmarkFill => "", Icon::JournalBookmark => "", Icon::JournalCheck => "✅", Icon::JournalCode => "", Icon::JournalMedical => "", Icon::JournalMinus => "➖", Icon::JournalPlus => "➕", Icon::JournalRichtext => "❌", Icon::JournalText => "❌", Icon::JournalX => "❌", Icon::Journal => "", Icon::Journals => "", Icon::Joystick => "", Icon::JustifyLeft => "⬅️", Icon::JustifyRight => "➡️", Icon::Justify => "", Icon::KanbanFill => "", Icon::Kanban => "", Icon::KeyFill => "", Icon::Key => "🔑", Icon::KeyboardFill => "", Icon::Keyboard => "", Icon::Ladder => "", Icon::LampFill => "", Icon::Lamp => "", Icon::LaptopFill => "", Icon::Laptop => "", Icon::LayerBackward => "", Icon::LayerForward => "", Icon::LayersFill => "🧅", Icon::LayersHalf => "", Icon::Layers => "", Icon::LayoutSidebarInsetReverse => "", Icon::LayoutSidebarInset => "", Icon::LayoutSidebarReverse => "", Icon::LayoutSidebar => "", Icon::LayoutSplit => "", Icon::LayoutTextSidebarReverse => "❌", Icon::LayoutTextSidebar => "❌", Icon::LayoutTextWindowReverse => "❌", Icon::LayoutTextWindow => "❌", Icon::LayoutThreeColumns => "", Icon::LayoutWtf => "", Icon::LifePreserver => "", Icon::LightbulbFill => "", Icon::LightbulbOffFill => "", Icon::LightbulbOff => "", Icon::Lightbulb => "", Icon::LightningChargeFill => "⚡", Icon::LightningCharge => "⚡", Icon::LightningFill => "", Icon::Lightning => "", Icon::Link45deg => "🔗", Icon::Link => "🔗", Icon::Linkedin => "🔗", Icon::ListCheck => "✅", Icon::ListNested => "", Icon::ListOl => "", Icon::ListStars => "⭐", Icon::ListTask => "", Icon::ListUl => "📋", Icon::List => "📋", Icon::LockFill => "🔒", Icon::Lock => "🔒", Icon::Mailbox => "❌", Icon::Mailbox2 => "❌", Icon::MapFill => "", Icon::Map => "", Icon::MarkdownFill => "⬇️", Icon::Markdown => "⬇️", Icon::Mask => "", Icon::MegaphoneFill => "", Icon::Megaphone => "", Icon::MenuAppFill => "", Icon::MenuApp => "", Icon::MenuButtonFill => "", Icon::MenuButtonWideFill => "", Icon::MenuButtonWide => "", Icon::MenuButton => "", Icon::MenuDown => "⬇️", Icon::MenuUp => "⬆️", Icon::MicFill => "🎤", Icon::MicMuteFill => "🎤", Icon::MicMute => "🎤", Icon::Mic => "🎤", Icon::MinecartLoaded => "", Icon::Minecart => "", Icon::Moisture => "", Icon::MoonFill => "", Icon::MoonStarsFill => "⭐", Icon::MoonStars => "⭐", Icon::Moon => "", Icon::MouseFill => "", Icon::Mouse => "", Icon::Mouse2Fill => "", Icon::Mouse2 => "", Icon::Mouse3Fill => "", Icon::Mouse3 => "", Icon::MusicNoteBeamed => "", Icon::MusicNoteList => "", Icon::MusicNote => "", Icon::MusicPlayerFill => "▶️", Icon::MusicPlayer => "▶️", Icon::Newspaper => "", Icon::NodeMinusFill => "➖", Icon::NodeMinus => "➖", Icon::NodePlusFill => "➕", Icon::NodePlus => "➕", Icon::NutFill => "", Icon::Nut => "", Icon::OctagonFill => "", Icon::OctagonHalf => "", Icon::Octagon => "", Icon::Option => "", Icon::Outlet => "", Icon::PaintBucket => "", Icon::PaletteFill => "", Icon::Palette => "", Icon::Palette2 => "", Icon::Paperclip => "", Icon::Paragraph => "", Icon::PatchCheckFill => "✅", Icon::PatchCheck => "✅", Icon::PatchExclamationFill => "❌", Icon::PatchExclamation => "❌", Icon::PatchMinusFill => "➖", Icon::PatchMinus => "➖", Icon::PatchPlusFill => "➕", Icon::PatchPlus => "➕", Icon::PatchQuestionFill => "❓", Icon::PatchQuestion => "❓", Icon::PauseBtnFill => "⏸️", Icon::PauseBtn => "⏸️", Icon::PauseCircleFill => "⏸️", Icon::PauseCircle => "⏸️", Icon::PauseFill => "⏸️", Icon::Pause => "⏸️", Icon::PeaceFill => "", Icon::Peace => "", Icon::PenFill => "", Icon::Pen => "", Icon::PencilFill => "", Icon::PencilSquare => "📝", Icon::Pencil => "✏️", Icon::PentagonFill => "", Icon::PentagonHalf => "", Icon::Pentagon => "", Icon::PeopleFill => "👥", Icon::People => "👥", Icon::Percent => "", Icon::PersonBadgeFill => "", Icon::PersonBadge => "", Icon::PersonBoundingBox => "❌", Icon::PersonCheckFill => "✅", Icon::PersonCheck => "✅", Icon::PersonCircle => "", Icon::PersonDashFill => "", Icon::PersonDash => "", Icon::PersonFill => "👤", Icon::PersonLinesFill => "", Icon::PersonPlusFill => "➕", Icon::PersonPlus => "➕", Icon::PersonSquare => "", Icon::PersonXFill => "❌", Icon::PersonX => "🚫", Icon::Person => "👤", Icon::PhoneFill => "", Icon::PhoneLandscapeFill => "", Icon::PhoneLandscape => "", Icon::PhoneVibrateFill => "", Icon::PhoneVibrate => "📳", Icon::Phone => "", Icon::PieChartFill => "", Icon::PieChart => "", Icon::PinAngleFill => "", Icon::PinAngle => "", Icon::PinFill => "", Icon::Pin => "", Icon::PipFill => "", Icon::Pip => "", Icon::PlayBtnFill => "▶️", Icon::PlayBtn => "▶️", Icon::PlayCircleFill => "▶️", Icon::PlayCircle => "▶️", Icon::PlayFill => "▶️", Icon::Play => "▶️", Icon::PlugFill => "", Icon::Plug => "🔌", Icon::PlusCircleDotted => "➕", Icon::PlusCircleFill => "➕", Icon::PlusCircle => "➕", Icon::PlusSquareDotted => "➕", Icon::PlusSquareFill => "➕", Icon::PlusSquare => "➕", Icon::Plus => "➕", Icon::Power => "⚡", Icon::PrinterFill => "", Icon::Printer => "", Icon::PuzzleFill => "", Icon::Puzzle => "🧩", Icon::QuestionCircleFill => "❓", Icon::QuestionCircle => "❓", Icon::QuestionDiamondFill => "❓", Icon::QuestionDiamond => "❓", Icon::QuestionOctagonFill => "❓", Icon::QuestionOctagon => "❓", Icon::QuestionSquareFill => "❓", Icon::QuestionSquare => "❓", Icon::Question => "❓", Icon::Rainbow => "", Icon::ReceiptCutoff => "", Icon::Receipt => "", Icon::Reception0 => "", Icon::Reception1 => "", Icon::Reception2 => "", Icon::Reception3 => "", Icon::Reception4 => "", Icon::RecordBtnFill => "", Icon::RecordBtn => "", Icon::RecordCircleFill => "", Icon::RecordCircle => "", Icon::RecordFill => "", Icon::Record => "", Icon::Record2Fill => "", Icon::Record2 => "", Icon::ReplyAllFill => "", Icon::ReplyAll => "", Icon::ReplyFill => "", Icon::Reply => "", Icon::RssFill => "", Icon::Rss => "", Icon::Rulers => "", Icon::SaveFill => "", Icon::Save => "💾", Icon::Save2Fill => "", Icon::Save2 => "", Icon::Scissors => "", Icon::Screwdriver => "", Icon::Search => "🔍", Icon::SegmentedNav => "", Icon::Server => "", Icon::ShareFill => "", Icon::Share => "", Icon::ShieldCheck => "✅", Icon::ShieldExclamation => "❌", Icon::ShieldFillCheck => "✅", Icon::ShieldFillExclamation => "❌", Icon::ShieldFillMinus => "➖", Icon::ShieldFillPlus => "➕", Icon::ShieldFillX => "❌", Icon::ShieldFill => "", Icon::ShieldLockFill => "🛡️", Icon::ShieldLock => "🛡️", Icon::ShieldMinus => "➖", Icon::ShieldPlus => "➕", Icon::ShieldShaded => "", Icon::ShieldSlashFill => "", Icon::ShieldSlash => "", Icon::ShieldX => "❌", Icon::Shield => "", Icon::ShiftFill => "", Icon::Shift => "", Icon::ShopWindow => "", Icon::Shop => "", Icon::Shuffle => "", Icon::Signpost2Fill => "", Icon::Signpost2 => "", Icon::SignpostFill => "", Icon::SignpostSplitFill => "", Icon::SignpostSplit => "", Icon::Signpost => "", Icon::SimFill => "", Icon::Sim => "", Icon::SkipBackwardBtnFill => "", Icon::SkipBackwardBtn => "", Icon::SkipBackwardCircleFill => "", Icon::SkipBackwardCircle => "", Icon::SkipBackwardFill => "", Icon::SkipBackward => "", Icon::SkipEndBtnFill => "", Icon::SkipEndBtn => "", Icon::SkipEndCircleFill => "", Icon::SkipEndCircle => "", Icon::SkipEndFill => "", Icon::SkipEnd => "", Icon::SkipForwardBtnFill => "", Icon::SkipForwardBtn => "", Icon::SkipForwardCircleFill => "", Icon::SkipForwardCircle => "", Icon::SkipForwardFill => "", Icon::SkipForward => "", Icon::SkipStartBtnFill => "⭐", Icon::SkipStartBtn => "⭐", Icon::SkipStartCircleFill => "⭐", Icon::SkipStartCircle => "⭐", Icon::SkipStartFill => "⭐", Icon::SkipStart => "⭐", Icon::Slack => "", Icon::SlashCircleFill => "", Icon::SlashCircle => "🚫", Icon::SlashSquareFill => "", Icon::SlashSquare => "", Icon::Slash => "", Icon::Sliders => "", Icon::Smartwatch => "", Icon::Snow => "", Icon::Snow2 => "", Icon::Snow3 => "", Icon::SortAlphaDownAlt => "⬇️", Icon::SortAlphaDown => "⬇️", Icon::SortAlphaUpAlt => "⬆️", Icon::SortAlphaUp => "⬆️", Icon::SortDownAlt => "⬇️", Icon::SortDown => "⬇️", Icon::SortNumericDownAlt => "⬇️", Icon::SortNumericDown => "🔢", Icon::SortNumericUpAlt => "⬆️", Icon::SortNumericUp => "⬆️", Icon::SortUpAlt => "⬆️", Icon::SortUp => "⬆️", Icon::Soundwave => "", Icon::SpeakerFill => "", Icon::Speaker => "", Icon::Speedometer => "", Icon::Speedometer2 => "", Icon::Spellcheck => "✅", Icon::SquareFill => "", Icon::SquareHalf => "", Icon::Square => "", Icon::Stack => "", Icon::StarFill => "⭐", Icon::StarHalf => "⭐", Icon::Star => "⭐", Icon::Stars => "⭐", Icon::StickiesFill => "", Icon::Stickies => "", Icon::StickyFill => "", Icon::Sticky => "", Icon::StopBtnFill => "⏹️", Icon::StopBtn => "⏹️", Icon::StopCircleFill => "⏹️", Icon::StopCircle => "⏹️", Icon::StopFill => "⏹️", Icon::Stop => "⏹️", Icon::StoplightsFill => "⏹️", Icon::Stoplights => "⏹️", Icon::StopwatchFill => "⏹️", Icon::Stopwatch => "⏱️", Icon::Subtract => "", Icon::SuitClubFill => "", Icon::SuitClub => "", Icon::SuitDiamondFill => "", Icon::SuitDiamond => "", Icon::SuitHeartFill => "❤️", Icon::SuitHeart => "❤️", Icon::SuitSpadeFill => "", Icon::SuitSpade => "", Icon::SunFill => "", Icon::Sun => "", Icon::Sunglasses => "", Icon::SunriseFill => "", Icon::Sunrise => "", Icon::SunsetFill => "", Icon::Sunset => "", Icon::SymmetryHorizontal => "", Icon::SymmetryVertical => "", Icon::Table => "", Icon::TabletFill => "", Icon::TabletLandscapeFill => "", Icon::TabletLandscape => "", Icon::Tablet => "", Icon::TagFill => "", Icon::Tag => "🏷️", Icon::TagsFill => "", Icon::Tags => "🏷️", Icon::Telegram => "", Icon::TelephoneFill => "", Icon::TelephoneForwardFill => "", Icon::TelephoneForward => "📞", Icon::TelephoneInboundFill => "", Icon::TelephoneInbound => "", Icon::TelephoneMinusFill => "➖", Icon::TelephoneMinus => "➖", Icon::TelephoneOutboundFill => "", Icon::TelephoneOutbound => "", Icon::TelephonePlusFill => "➕", Icon::TelephonePlus => "➕", Icon::TelephoneXFill => "❌", Icon::TelephoneX => "❌", Icon::Telephone => "📞", Icon::TerminalFill => "", Icon::Terminal => "💻", Icon::TextCenter => "❌", Icon::TextIndentLeft => "⬅️", Icon::TextIndentRight => "➡️", Icon::TextLeft => "⬅️", Icon::TextParagraph => "❌", Icon::TextRight => "➡️", Icon::TextareaResize => "❌", Icon::TextareaT => "❌", Icon::Textarea => "❌", Icon::ThermometerHalf => "", Icon::ThermometerHigh => "", Icon::ThermometerLow => "", Icon::ThermometerSnow => "", Icon::ThermometerSun => "", Icon::Thermometer => "", Icon::ThreeDotsVertical => "", Icon::ThreeDots => "", Icon::ToggleOff => "", Icon::ToggleOn => "", Icon::Toggle2Off => "", Icon::Toggle2On => "", Icon::Toggles => "🎚️", Icon::Toggles2 => "", Icon::Tools => "🛠️", Icon::Tornado => "", Icon::TrashFill => "🗑️", Icon::Trash => "🗑️", Icon::Trash2Fill => "🗑️", Icon::Trash2 => "🗑️", Icon::TreeFill => "", Icon::Tree => "", Icon::TriangleFill => "", Icon::TriangleHalf => "", Icon::Triangle => "", Icon::TrophyFill => "", Icon::Trophy => "", Icon::TropicalStorm => "", Icon::TruckFlatbed => "", Icon::Truck => "", Icon::Tsunami => "", Icon::TvFill => "", Icon::Tv => "", Icon::Twitch => "", Icon::Twitter => "", Icon::TypeBold => "", Icon::TypeH1 => "", Icon::TypeH2 => "", Icon::TypeH3 => "", Icon::TypeItalic => "", Icon::TypeStrikethrough => "", Icon::TypeUnderline => "", Icon::Type => "", Icon::UiChecksGrid => "✅", Icon::UiChecks => "✅", Icon::UiRadiosGrid => "", Icon::UiRadios => "", Icon::UmbrellaFill => "", Icon::Umbrella => "", Icon::Union => "", Icon::UnlockFill => "🔒", Icon::Unlock => "🔒", Icon::UpcScan => "⬆️", Icon::Upc => "⬆️", Icon::Upload => "⬆️", Icon::VectorPen => "", Icon::ViewList => "", Icon::ViewStacked => "", Icon::VinylFill => "", Icon::Vinyl => "", Icon::Voicemail => "", Icon::VolumeDownFill => "⬇️", Icon::VolumeDown => "⬇️", Icon::VolumeMuteFill => "", Icon::VolumeMute => "", Icon::VolumeOffFill => "", Icon::VolumeOff => "", Icon::VolumeUpFill => "⬆️", Icon::VolumeUp => "🔊", Icon::Vr => "", Icon::WalletFill => "", Icon::Wallet => "", Icon::Wallet2 => "", Icon::Watch => "", Icon::Water => "", Icon::Whatsapp => "", Icon::Wifi1 => "", Icon::Wifi2 => "", Icon::WifiOff => "📡", Icon::Wifi => "📶", Icon::Wind => "", Icon::WindowDock => "", Icon::WindowSidebar => "", Icon::Window => "", Icon::Wrench => "🔧", Icon::XCircleFill => "❌", Icon::XCircle => "❌", Icon::XDiamondFill => "❌", Icon::XDiamond => "❌", Icon::XOctagonFill => "❌", Icon::XOctagon => "🛑", Icon::XSquareFill => "❌", Icon::XSquare => "❌", Icon::X => "❌", Icon::Youtube => "", Icon::ZoomIn => "", Icon::ZoomOut => "", Icon::Bank => "", Icon::Bank2 => "", Icon::BellSlashFill => "🔔", Icon::BellSlash => "🔔", Icon::CashCoin => "", Icon::CheckLg => "✅", Icon::Coin => "", Icon::CurrencyBitcoin => "", Icon::CurrencyDollar => "", Icon::CurrencyEuro => "", Icon::CurrencyExchange => "❌", Icon::CurrencyPound => "", Icon::CurrencyYen => "", Icon::DashLg => "", Icon::ExclamationLg => "❗", Icon::FileEarmarkPdfFill => "📄", Icon::FileEarmarkPdf => "📄", Icon::FilePdfFill => "📄", Icon::FilePdf => "📄", Icon::GenderAmbiguous => "", Icon::GenderFemale => "", Icon::GenderMale => "", Icon::GenderTrans => "", Icon::HeadsetVr => "", Icon::InfoLg => "ℹ️", Icon::Mastodon => "", Icon::Messenger => "", Icon::PiggyBankFill => "", Icon::PiggyBank => "", Icon::PinMapFill => "", Icon::PinMap => "", Icon::PlusLg => "➕", Icon::QuestionLg => "❓", Icon::Recycle => "", Icon::Reddit => "", Icon::SafeFill => "", Icon::Safe2Fill => "", Icon::Safe2 => "", Icon::SdCardFill => "", Icon::SdCard => "", Icon::Skype => "", Icon::SlashLg => "", Icon::Translate => "", Icon::XLg => "❌", Icon::Safe => "", Icon::Apple => "", Icon::Microsoft => "🎤", Icon::Windows => "", Icon::Behance => "", Icon::Dribbble => "", Icon::Line => "", Icon::Medium => "", Icon::Paypal => "", Icon::Pinterest => "", Icon::Signal => "", Icon::Snapchat => "💬", Icon::Spotify => "", Icon::StackOverflow => "", Icon::Strava => "", Icon::Wordpress => "", Icon::Vimeo => "", Icon::Activity => "", Icon::Easel2Fill => "", Icon::Easel2 => "", Icon::Easel3Fill => "", Icon::Easel3 => "", Icon::Fan => "", Icon::Fingerprint => "", Icon::GraphDownArrow => "➡️", Icon::GraphUpArrow => "➡️", Icon::Hypnotize => "", Icon::Magic => "", Icon::PersonRolodex => "❌", Icon::PersonVideo => "", Icon::PersonVideo2 => "", Icon::PersonVideo3 => "", Icon::PersonWorkspace => "", Icon::Radioactive => "", Icon::WebcamFill => "", Icon::Webcam => "", Icon::YinYang => "", Icon::BandaidFill => "", Icon::Bandaid => "", Icon::Bluetooth => "", Icon::BodyText => "❌", Icon::Boombox => "❌", Icon::Boxes => "📦", Icon::DpadFill => "", Icon::Dpad => "", Icon::EarFill => "", Icon::Ear => "", Icon::EnvelopeCheckFill => "✅", Icon::EnvelopeCheck => "✅", Icon::EnvelopeDashFill => "", Icon::EnvelopeDash => "", Icon::EnvelopeExclamationFill => "❌", Icon::EnvelopeExclamation => "❌", Icon::EnvelopePlusFill => "➕", Icon::EnvelopePlus => "➕", Icon::EnvelopeSlashFill => "", Icon::EnvelopeSlash => "", Icon::EnvelopeXFill => "❌", Icon::EnvelopeX => "❌", Icon::ExplicitFill => "❌", Icon::Explicit => "❌", Icon::Git => "", Icon::Infinity => "", Icon::ListColumnsReverse => "", Icon::ListColumns => "📑", Icon::Meta => "", Icon::NintendoSwitch => "", Icon::PcDisplayHorizontal => "▶️", Icon::PcDisplay => "▶️", Icon::PcHorizontal => "", Icon::Pc => "", Icon::Playstation => "▶️", Icon::PlusSlashMinus => "➕", Icon::ProjectorFill => "", Icon::Projector => "", Icon::QrCodeScan => "", Icon::QrCode => "🔳", Icon::Quora => "", Icon::Quote => "", Icon::Robot => "🤖", Icon::SendCheckFill => "✅", Icon::SendCheck => "✅", Icon::SendDashFill => "", Icon::SendDash => "", Icon::SendExclamationFill => "❌", Icon::SendExclamation => "❌", Icon::SendFill => "📤", Icon::SendPlusFill => "➕", Icon::SendPlus => "➕", Icon::SendSlashFill => "", Icon::SendSlash => "", Icon::SendXFill => "❌", Icon::SendX => "❌", Icon::Send => "📤", Icon::Steam => "", Icon::TerminalDash => "", Icon::TerminalPlus => "➕", Icon::TerminalSplit => "", Icon::TicketDetailedFill => "", Icon::TicketDetailed => "🎟️", Icon::TicketFill => "", Icon::TicketPerforatedFill => "", Icon::TicketPerforated => "", Icon::Ticket => "", Icon::Tiktok => "", Icon::WindowDash => "", Icon::WindowDesktop => "", Icon::WindowFullscreen => "", Icon::WindowPlus => "➕", Icon::WindowSplit => "", Icon::WindowStack => "🗂️", Icon::WindowX => "❌", Icon::Xbox => "❌", Icon::Ethernet => "", Icon::HdmiFill => "", Icon::Hdmi => "", Icon::UsbCFill => "", Icon::UsbC => "", Icon::UsbFill => "", Icon::UsbPlugFill => "", Icon::UsbPlug => "", Icon::UsbSymbol => "", Icon::Usb => "", Icon::BoomboxFill => "❌", Icon::Displayport => "▶️", Icon::GpuCard => "", Icon::Memory => "", Icon::ModemFill => "", Icon::Modem => "", Icon::MotherboardFill => "", Icon::Motherboard => "", Icon::OpticalAudioFill => "", Icon::OpticalAudio => "", Icon::PciCard => "", Icon::RouterFill => "", Icon::Router => "", Icon::ThunderboltFill => "", Icon::Thunderbolt => "", Icon::UsbDriveFill => "", Icon::UsbDrive => "", Icon::UsbMicroFill => "🎤", Icon::UsbMicro => "🎤", Icon::UsbMiniFill => "", Icon::UsbMini => "", Icon::CloudHaze2 => "", Icon::DeviceHddFill => "", Icon::DeviceHdd => "", Icon::DeviceSsdFill => "", Icon::DeviceSsd => "", Icon::DisplayportFill => "▶️", Icon::MortarboardFill => "", Icon::Mortarboard => "", Icon::TerminalX => "❌", Icon::ArrowThroughHeartFill => "➡️", Icon::ArrowThroughHeart => "➡️", Icon::BadgeSdFill => "", Icon::BadgeSd => "", Icon::BagHeartFill => "❤️", Icon::BagHeart => "❤️", Icon::BalloonFill => "", Icon::BalloonHeartFill => "❤️", Icon::BalloonHeart => "❤️", Icon::Balloon => "", Icon::Box2Fill => "❌", Icon::Box2HeartFill => "❌", Icon::Box2Heart => "❌", Icon::Box2 => "❌", Icon::BracesAsterisk => "", Icon::CalendarHeartFill => "❤️", Icon::CalendarHeart => "❤️", Icon::Calendar2HeartFill => "❤️", Icon::Calendar2Heart => "❤️", Icon::ChatHeartFill => "❤️", Icon::ChatHeart => "❤️", Icon::ChatLeftHeartFill => "⬅️", Icon::ChatLeftHeart => "⬅️", Icon::ChatRightHeartFill => "➡️", Icon::ChatRightHeart => "➡️", Icon::ChatSquareHeartFill => "❤️", Icon::ChatSquareHeart => "❤️", Icon::ClipboardCheckFill => "✅", Icon::ClipboardDataFill => "", Icon::ClipboardFill => "", Icon::ClipboardHeartFill => "❤️", Icon::ClipboardHeart => "❤️", Icon::ClipboardMinusFill => "➖", Icon::ClipboardPlusFill => "➕", Icon::ClipboardPulse => "", Icon::ClipboardXFill => "❌", Icon::Clipboard2CheckFill => "✅", Icon::Clipboard2Check => "✅", Icon::Clipboard2DataFill => "", Icon::Clipboard2Data => "", Icon::Clipboard2Fill => "", Icon::Clipboard2HeartFill => "❤️", Icon::Clipboard2Heart => "❤️", Icon::Clipboard2MinusFill => "➖", Icon::Clipboard2Minus => "➖", Icon::Clipboard2PlusFill => "➕", Icon::Clipboard2Plus => "➕", Icon::Clipboard2PulseFill => "", Icon::Clipboard2Pulse => "📈", Icon::Clipboard2XFill => "❌", Icon::Clipboard2X => "📋", Icon::Clipboard2 => "", Icon::EmojiKissFill => "", Icon::EmojiKiss => "", Icon::EnvelopeHeartFill => "❤️", Icon::EnvelopeHeart => "❤️", Icon::EnvelopeOpenHeartFill => "❤️", Icon::EnvelopeOpenHeart => "❤️", Icon::EnvelopePaperFill => "", Icon::EnvelopePaperHeartFill => "❤️", Icon::EnvelopePaperHeart => "❤️", Icon::EnvelopePaper => "", Icon::FiletypeAac => "📄", Icon::FiletypeAi => "📄", Icon::FiletypeBmp => "📄", Icon::FiletypeCs => "📄", Icon::FiletypeCss => "📄", Icon::FiletypeCsv => "📄", Icon::FiletypeDoc => "📄", Icon::FiletypeDocx => "❌", Icon::FiletypeExe => "❌", Icon::FiletypeGif => "📄", Icon::FiletypeHeic => "📄", Icon::FiletypeHtml => "📄", Icon::FiletypeJava => "📄", Icon::FiletypeJpg => "📄", Icon::FiletypeJs => "📄", Icon::FiletypeJsx => "❌", Icon::FiletypeKey => "📄", Icon::FiletypeM4p => "📄", Icon::FiletypeMd => "📄", Icon::FiletypeMdx => "❌", Icon::FiletypeMov => "📄", Icon::FiletypeMp3 => "📄", Icon::FiletypeMp4 => "📄", Icon::FiletypeOtf => "📄", Icon::FiletypePdf => "📄", Icon::FiletypePhp => "📄", Icon::FiletypePng => "📄", Icon::FiletypePpt => "📄", Icon::FiletypePsd => "📄", Icon::FiletypePy => "📄", Icon::FiletypeRaw => "📄", Icon::FiletypeRb => "📄", Icon::FiletypeSass => "📄", Icon::FiletypeScss => "📄", Icon::FiletypeSh => "📄", Icon::FiletypeSvg => "📄", Icon::FiletypeTiff => "📄", Icon::FiletypeTsx => "❌", Icon::FiletypeTtf => "📄", Icon::FiletypeTxt => "❌", Icon::FiletypeWav => "📄", Icon::FiletypeWoff => "📄", Icon::FiletypeXls => "❌", Icon::FiletypeXml => "❌", Icon::FiletypeYml => "📄", Icon::HeartArrow => "➡️", Icon::HeartPulseFill => "❤️", Icon::HeartPulse => "❤️", Icon::HeartbreakFill => "❤️", Icon::Heartbreak => "❤️", Icon::Hearts => "❤️", Icon::HospitalFill => "", Icon::Hospital => "", Icon::HouseHeartFill => "❤️", Icon::HouseHeart => "❤️", Icon::Incognito => "", Icon::MagnetFill => "", Icon::Magnet => "", Icon::PersonHeart => "❤️", Icon::PersonHearts => "❤️", Icon::PhoneFlip => "", Icon::Plugin => "🔌", Icon::PostageFill => "", Icon::PostageHeartFill => "❤️", Icon::PostageHeart => "❤️", Icon::Postage => "", Icon::PostcardFill => "", Icon::PostcardHeartFill => "❤️", Icon::PostcardHeart => "❤️", Icon::Postcard => "", Icon::SearchHeartFill => "❤️", Icon::SearchHeart => "❤️", Icon::Sliders2Vertical => "", Icon::Sliders2 => "", Icon::Trash3Fill => "🗑️", Icon::Trash3 => "🗑️", Icon::Valentine => "", Icon::Valentine2 => "", Icon::WrenchAdjustableCircleFill => "", Icon::WrenchAdjustableCircle => "", Icon::WrenchAdjustable => "", Icon::FiletypeJson => "📄", Icon::FiletypePptx => "❌", Icon::FiletypeXlsx => "❌", Icon::Circle1Fill => "", Icon::Circle1 => "", Icon::Square1Fill => "", Icon::Square1 => "", Icon::Circle2Fill => "", Icon::Circle2 => "", Icon::Square2Fill => "", Icon::Square2 => "", Icon::Circle3Fill => "", Icon::Circle3 => "", Icon::Square3Fill => "", Icon::Square3 => "", Icon::Circle4Fill => "", Icon::Circle4 => "", Icon::Square4Fill => "", Icon::Square4 => "", Icon::Circle5Fill => "", Icon::Circle5 => "", Icon::Square5Fill => "", Icon::Square5 => "", Icon::Circle6Fill => "", Icon::Circle6 => "", Icon::Square6Fill => "", Icon::Square6 => "", Icon::Circle7Fill => "", Icon::Circle7 => "", Icon::Square7Fill => "", Icon::Square7 => "", Icon::Circle8Fill => "", Icon::Circle8 => "", Icon::Square8Fill => "", Icon::Square8 => "", Icon::Circle9Fill => "", Icon::Circle9 => "", Icon::Square9Fill => "", Icon::Square9 => "", Icon::AirplaneEnginesFill => "", Icon::AirplaneEngines => "", Icon::AirplaneFill => "", Icon::Airplane => "", Icon::Alexa => "❌", Icon::Alipay => "", Icon::Android => "", Icon::Android2 => "", Icon::BoxFill => "❌", Icon::BoxSeamFill => "❌", Icon::BrowserChrome => "", Icon::BrowserEdge => "", Icon::BrowserFirefox => "❌", Icon::BrowserSafari => "", Icon::CCircleFill => "", Icon::CCircle => "", Icon::CSquareFill => "", Icon::CSquare => "", Icon::CapsulePill => "", Icon::Capsule => "", Icon::CarFrontFill => "", Icon::CarFront => "", Icon::CassetteFill => "", Icon::Cassette => "", Icon::CcCircleFill => "", Icon::CcCircle => "", Icon::CcSquareFill => "", Icon::CcSquare => "", Icon::CupHotFill => "⬆️", Icon::CupHot => "⬆️", Icon::CurrencyRupee => "⬆️", Icon::Dropbox => "❌", Icon::Escape => "", Icon::FastForwardBtnFill => "", Icon::FastForwardBtn => "", Icon::FastForwardCircleFill => "", Icon::FastForwardCircle => "", Icon::FastForwardFill => "", Icon::FastForward => "", Icon::FiletypeSql => "📄", Icon::Fire => "", Icon::GooglePlay => "▶️", Icon::HCircleFill => "", Icon::HCircle => "", Icon::HSquareFill => "", Icon::HSquare => "", Icon::Indent => "", Icon::LungsFill => "", Icon::Lungs => "", Icon::MicrosoftTeams => "🎤", Icon::PCircleFill => "", Icon::PCircle => "", Icon::PSquareFill => "", Icon::PSquare => "", Icon::PassFill => "", Icon::Pass => "", Icon::Prescription => "", Icon::Prescription2 => "", Icon::RCircleFill => "", Icon::RCircle => "", Icon::RSquareFill => "", Icon::RSquare => "", Icon::Repeat1 => "", Icon::Repeat => "", Icon::RewindBtnFill => "", Icon::RewindBtn => "", Icon::RewindCircleFill => "", Icon::RewindCircle => "", Icon::RewindFill => "", Icon::Rewind => "", Icon::TrainFreightFrontFill => "", Icon::TrainFreightFront => "", Icon::TrainFrontFill => "", Icon::TrainFront => "", Icon::TrainLightrailFrontFill => "", Icon::TrainLightrailFront => "", Icon::TruckFrontFill => "", Icon::TruckFront => "", Icon::Ubuntu => "", Icon::Unindent => "", Icon::Unity => "", Icon::UniversalAccessCircle => "", Icon::UniversalAccess => "", Icon::Virus => "", Icon::Virus2 => "", Icon::Wechat => "💬", Icon::Yelp => "", Icon::SignStopFill => "⏹️", Icon::SignStopLightsFill => "⏹️", Icon::SignStopLights => "⏹️", Icon::SignStop => "⏹️", Icon::SignTurnLeftFill => "⬅️", Icon::SignTurnLeft => "⬅️", Icon::SignTurnRightFill => "➡️", Icon::SignTurnRight => "➡️", Icon::SignTurnSlightLeftFill => "⬅️", Icon::SignTurnSlightLeft => "⬅️", Icon::SignTurnSlightRightFill => "➡️", Icon::SignTurnSlightRight => "➡️", Icon::SignYieldFill => "", Icon::SignYield => "", Icon::EvStationFill => "", Icon::EvStation => "", Icon::FuelPumpDieselFill => "", Icon::FuelPumpDiesel => "", Icon::FuelPumpFill => "", Icon::FuelPump => "", Icon::Circle0Fill => "", Icon::Circle0 => "", Icon::Square0Fill => "", Icon::Square0 => "", Icon::RocketFill => "", Icon::RocketTakeoffFill => "", Icon::RocketTakeoff => "", Icon::Rocket => "", Icon::Stripe => "", Icon::Subscript => "", Icon::Superscript => "⬆️", Icon::Trello => "", Icon::EnvelopeAtFill => "", Icon::EnvelopeAt => "📧", Icon::Regex => "❌", Icon::TextWrap => "❌", Icon::SignDeadEndFill => "", Icon::SignDeadEnd => "", Icon::SignDoNotEnterFill => "", Icon::SignDoNotEnter => "", Icon::SignIntersectionFill => "", Icon::SignIntersectionSideFill => "", Icon::SignIntersectionSide => "", Icon::SignIntersectionTFill => "", Icon::SignIntersectionT => "", Icon::SignIntersectionYFill => "", Icon::SignIntersectionY => "", Icon::SignIntersection => "", Icon::SignMergeLeftFill => "⬅️", Icon::SignMergeLeft => "⬅️", Icon::SignMergeRightFill => "➡️", Icon::SignMergeRight => "➡️", Icon::SignNoLeftTurnFill => "⬅️", Icon::SignNoLeftTurn => "⬅️", Icon::SignNoParkingFill => "", Icon::SignNoParking => "", Icon::SignNoRightTurnFill => "➡️", Icon::SignNoRightTurn => "➡️", Icon::SignRailroadFill => "", Icon::SignRailroad => "", Icon::BuildingAdd => "", Icon::BuildingCheck => "✅", Icon::BuildingDash => "", Icon::BuildingDown => "⬇️", Icon::BuildingExclamation => "❌", Icon::BuildingFillAdd => "", Icon::BuildingFillCheck => "✅", Icon::BuildingFillDash => "", Icon::BuildingFillDown => "⬇️", Icon::BuildingFillExclamation => "❌", Icon::BuildingFillGear => "⚙️", Icon::BuildingFillLock => "🔒", Icon::BuildingFillSlash => "", Icon::BuildingFillUp => "⬆️", Icon::BuildingFillX => "❌", Icon::BuildingFill => "", Icon::BuildingGear => "⚙️", Icon::BuildingLock => "🔒", Icon::BuildingSlash => "", Icon::BuildingUp => "⬆️", Icon::BuildingX => "❌", Icon::BuildingsFill => "", Icon::Buildings => "", Icon::BusFrontFill => "", Icon::BusFront => "", Icon::EvFrontFill => "", Icon::EvFront => "", Icon::GlobeAmericas => "🌎", Icon::GlobeAsiaAustralia => "", Icon::GlobeCentralSouthAsia => "", Icon::GlobeEuropeAfrica => "", Icon::HouseAddFill => "", Icon::HouseAdd => "", Icon::HouseCheckFill => "✅", Icon::HouseCheck => "✅", Icon::HouseDashFill => "", Icon::HouseDash => "", Icon::HouseDownFill => "⬇️", Icon::HouseDown => "⬇️", Icon::HouseExclamationFill => "❌", Icon::HouseExclamation => "❌", Icon::HouseGearFill => "⚙️", Icon::HouseGear => "⚙️", Icon::HouseLockFill => "🔒", Icon::HouseLock => "🔒", Icon::HouseSlashFill => "", Icon::HouseSlash => "", Icon::HouseUpFill => "⬆️", Icon::HouseUp => "⬆️", Icon::HouseXFill => "❌", Icon::HouseX => "❌", Icon::PersonAdd => "", Icon::PersonDown => "⬇️", Icon::PersonExclamation => "❌", Icon::PersonFillAdd => "", Icon::PersonFillCheck => "✅", Icon::PersonFillDash => "", Icon::PersonFillDown => "⬇️", Icon::PersonFillExclamation => "❌", Icon::PersonFillGear => "🛠️", Icon::PersonFillLock => "🔒", Icon::PersonFillSlash => "", Icon::PersonFillUp => "⬆️", Icon::PersonFillX => "❌", Icon::PersonGear => "👤", Icon::PersonLock => "🔒", Icon::PersonSlash => "🚫", Icon::PersonUp => "⬆️", Icon::Scooter => "", Icon::TaxiFrontFill => "❌", Icon::TaxiFront => "❌", Icon::Amd => "", Icon::DatabaseAdd => "", Icon::DatabaseCheck => "✅", Icon::DatabaseDash => "", Icon::DatabaseDown => "⬇️", Icon::DatabaseExclamation => "❌", Icon::DatabaseFillAdd => "", Icon::DatabaseFillCheck => "✅", Icon::DatabaseFillDash => "", Icon::DatabaseFillDown => "⬇️", Icon::DatabaseFillExclamation => "❌", Icon::DatabaseFillGear => "⚙️", Icon::DatabaseFillLock => "🔒", Icon::DatabaseFillSlash => "", Icon::DatabaseFillUp => "⬆️", Icon::DatabaseFillX => "❌", Icon::DatabaseFill => "", Icon::DatabaseGear => "💾", Icon::DatabaseLock => "🔒", Icon::DatabaseSlash => "", Icon::DatabaseUp => "⬆️", Icon::DatabaseX => "💾", Icon::Database => "", Icon::HousesFill => "", Icon::Houses => "", Icon::Nvidia => "", Icon::PersonVcardFill => "", Icon::PersonVcard => "", Icon::SinaWeibo => "", Icon::TencentQq => "", Icon::Wikipedia => "", Icon::AlphabetUppercase => "⬆️", Icon::Alphabet => "", Icon::Amazon => "", Icon::ArrowsCollapseVertical => "➡️", Icon::ArrowsExpandVertical => "➡️", Icon::ArrowsVertical => "➡️", Icon::Arrows => "➡️", Icon::BanFill => "", Icon::Ban => "", Icon::Bing => "", Icon::Cake => "", Icon::Cake2 => "", Icon::Cookie => "", Icon::Copy => "", Icon::Crosshair => "", Icon::Crosshair2 => "", Icon::EmojiAstonishedFill => "", Icon::EmojiAstonished => "", Icon::EmojiGrimaceFill => "", Icon::EmojiGrimace => "", Icon::EmojiGrinFill => "", Icon::EmojiGrin => "", Icon::EmojiSurpriseFill => "", Icon::EmojiSurprise => "", Icon::EmojiTearFill => "", Icon::EmojiTear => "", Icon::EnvelopeArrowDownFill => "➡️", Icon::EnvelopeArrowDown => "➡️", Icon::EnvelopeArrowUpFill => "➡️", Icon::EnvelopeArrowUp => "➡️", Icon::Feather => "", Icon::Feather2 => "", Icon::FloppyFill => "", Icon::Floppy => "", Icon::Floppy2Fill => "", Icon::Floppy2 => "", Icon::Gitlab => "", Icon::Highlighter => "", Icon::MarkerTip => "", Icon::NvmeFill => "", Icon::Nvme => "", Icon::Opencollective => "", Icon::PciCardNetwork => "", Icon::PciCardSound => "", Icon::Radar => "", Icon::SendArrowDownFill => "➡️", Icon::SendArrowDown => "➡️", Icon::SendArrowUpFill => "➡️", Icon::SendArrowUp => "➡️", Icon::SimSlashFill => "", Icon::SimSlash => "", Icon::Sourceforge => "", Icon::Substack => "", Icon::ThreadsFill => "", Icon::Threads => "", Icon::Transparency => "", Icon::TwitterX => "❌", Icon::TypeH4 => "", Icon::TypeH5 => "", Icon::TypeH6 => "", Icon::BackpackFill => "", Icon::Backpack => "", Icon::Backpack2Fill => "", Icon::Backpack2 => "", Icon::Backpack3Fill => "", Icon::Backpack3 => "", Icon::Backpack4Fill => "", Icon::Backpack4 => "", Icon::Brilliance => "", Icon::CakeFill => "", Icon::Cake2Fill => "", Icon::DuffleFill => "", Icon::Duffle => "", Icon::Exposure => "❌", Icon::GenderNeuter => "", Icon::Highlights => "", Icon::LuggageFill => "", Icon::Luggage => "", Icon::MailboxFlag => "❌", Icon::Mailbox2Flag => "❌", Icon::NoiseReduction => "", Icon::PassportFill => "", Icon::Passport => "", Icon::PersonArmsUp => "⬆️", Icon::PersonRaisedHand => "", Icon::PersonStandingDress => "", Icon::PersonStanding => "", Icon::PersonWalking => "", Icon::PersonWheelchair => "", Icon::Shadows => "", Icon::SuitcaseFill => "", Icon::SuitcaseLgFill => "", Icon::SuitcaseLg => "💼", Icon::Suitcase => "", Icon::Suitcase2Fill => "", Icon::Suitcase2 => "", Icon::Vignette => "", }; } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Base/BaseGenerator.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Base; use UnitEnum; use JsonSerializable; use ARUNOX\Plugins\REST\Generator; use ARUNOX\Plugins\REST\Generator\Config; abstract class BaseGenerator implements JsonSerializable { /* ### VARIABLES ################################################ # */ protected static string $TYPE = "generator"; protected static ?string $PRIMARYKEY = NULL; protected ?string $GENERATOR = NULL; protected string $RETURN_OBJECT = "self"; // e.g. self, content protected bool $INCLUDE_ALLOWED_CONTENT = TRUE; protected bool $INCLUDE_ALLOWED_CONFIG = TRUE; protected bool $INCLUDE_ALLOWED_DATA = TRUE; protected array $ALLOWED_CONTENT = []; protected array $ALLOWED_CONFIG = []; protected array $ALLOWED_DATA = []; protected string $CHILDREN_NAME = "content"; protected ?object $parent_node = NULL; protected ?self $lastelement = NULL; protected array $data = []; protected array $config = []; protected array $content = []; /* ### PUBLIC STATIC FUNCTIONS ################################## # */ public static function create( ...$args ) : ?BaseGenerator { return Generator::create( ...$args ); } public static function hasGenerator() : bool { return !!static::$GENERATOR; } public static function getGenerator() : ?string { return static::$GENERATOR; } /* ### PUBLIC FUNCTIONS ######################################### # */ public function getReturnObject() { return match( $this->RETURN_OBJECT ) { "self" => $this, "content" => end( $this->content ), }; } public function getType() { return static::$TYPE; } public function getPrimaryKey() { return $this->get( static::$PRIMARYKEY ); } public function setParentNode( object $parentNode ) : self { $this->parent_node = $parentNode; return $this; } public function parent( int $steps = 1 ) : ?self { $parent = $this->parent_node; for( $x = 1; $x < $steps; $x++ ) { if( !$parent->parent() ) break; $parent = $parent->parent(); } return $parent; } public function top() : self { $parent = $this; while( $parent->parent() ) { $parent = $parent->parent(); } return $parent; } public function addCallable( string|array $callable, ...$arguments ) { if( is_callable( $callable ) ) { return call_user_func( $callable, $this, ...$arguments ) ?? $this; } return $this; } /* ### JSON FUNCTIONS ########################################### # */ public function jsonSerialize() : array { $config = $this->config; foreach( $config as &$value ) { if( $value instanceof \UnitEnum ) { $value = $value->value; } } $config["content"] = $this->content; if( $this->GENERATOR ) { $config = [ "type" => $this->TYPE, "generator" => $this->GENERATOR, "config" => $config, ]; if( $this->TYPE == "wrapper" && $this->HISTORY ) { $config["history"] = $this->HISTORY; } foreach( $this->data as $name => $data ) { $config[ $name ] = $data; } } return $config; } /* ### PROTECTED FUNCTIONS ###################################### # */ protected function set( $name, ...$args ) { if( FALSE !== $this->checkAllowedData( $name, ...$args ) ) { $this->data[ $name ] = current( $args ); return $this; } if( FALSE !== $this->checkAllowedConfig( $name, ...$args ) ) { // $this->config[ $name ] = is_multi( $args ) ? $args : current( $args ); $this->config[ $name ] = current( $args ); return $this; } // if( FALSE !== $addClass = $this->checkAllowedContent( $name, ...$args ) ) { // $lastkey = array_key_last( $this->content ); // unset( $this->content[ $lastkey ] ); // show( $this->content ); // return $this->addToAllowed( $name, $addClass, "content", $args ); // } show( "SET", $name, $args ); } protected function get( ?string $name = NULL, ...$args ) { // show( $name, $args, FALSE ); if( in_array( $name, [ "content", NULL ] ) ) { if( isset( $args[0] ) ) return $this->content[ $args[0] ]; if( $this->lastelement ) return $this->lastelement; } if( in_array( $name, [ "children" ] ) ) { return $this->{$this->CHILDREN_NAME}; } if( in_array( $name, [ "config", "content" ] ) ) { $value = $this->{$name}; foreach( $args as $arg ) { $value = $value[ $arg ]; } return $value; } if( FALSE !== $class = $this->getAllowedConfig( $name ) ) { if( !is_array( $this->config[ $name ] ) ) { return $this->config[ $name ]; } if( isset( $args[0] ) ) return $this->config[ $name ][ $args[0] ]; return end( $this->config[ $name ] ); } if( FALSE !== $class = $this->checkAllowedContent( $name ) ) { foreach( $this->content as $content ) { show( $class, false ); if( is_a( $content, $class ) ) { $origin = $content->getPrimaryKey(); $origin = strtolower( $origin ); $needle = strtolower( $args[0] ); if( $needle === $origin ) { return $content; } } } } show( "GET", [ "name" => $name, "args" => $args, "config" => $this->config ] ); } /* ### PROTECTED ADD FUNCTIONS ################################## # */ protected function add( string $addName, ...$args ) { // if( $addName == "children" ) show( $addName, $args, FALSE ); foreach( $args as &$arg ) { if( $arg instanceof BaseGenerator ) { $arg = $arg->top(); } } if( $addName == "children" ) { if( !is_array( $args[0] ) ) $args[0] = [ $args[0] ]; foreach( $args[0] as $child ) { if( FALSE !== $addClass = $this->checkAllowedContent( "content", $child ) ) { $last = $this->addToAllowed( "content", $addClass, "content", [$child] ); } } return $last ?? NULL; } if( FALSE !== $addClass = $this->checkAllowedConfig( $addName ) ) { return $this->addToAllowed( $addName, $addClass, "config", $args ); } if( FALSE !== $addClass = $this->checkAllowedContent( $addName, ...$args ) ) { return $this->addToAllowed( $addName, $addClass, "content", $args ); } show( "ADD", $addName, $addClass, $args, $this->ALLOWED_CONTENT, get_called_class() ); } protected function insert( string $insertPosition, int $insertIndex, string $insertName, ...$args ) { if( FALSE !== $insertClass = $this->checkAllowedConfig( $insertName, ...$args ) ) { return $this->{"insert{$insertPosition}Config"}( $this->config[ $insertName ][ $insertIndex ], $insertName, $insertClass, $args ); } if( FALSE !== $insertClass = $this->checkAllowedContent( $insertName, ...$args ) ) { return $this->{"insert{$insertPosition}Content"}( $this->content[ $insertIndex ], $insertName, $insertClass, $args ); } show( "INSERT", $insertPosition, $insertName, $insertClass, $args, $this->ALLOWED_CONTENT, get_called_class() ); } protected function before( string $addName, ...$args ) { $parent = $this->parent(); foreach( $args as &$arg ) { if( $arg instanceof BaseGenerator ) { $arg = $arg->top(); } } if( FALSE !== $addClass = $parent->checkAllowedConfig( $addName, ...$args ) ) { return $parent->insertBeforeConfig( $this, $addName, $addClass, $args ); } if( FALSE !== $addClass = $parent->checkAllowedContent( $addName, ...$args ) ) { return $parent->insertBeforeContent( $this, $addName, $addClass, $args ); } show( "before", $addName, $addClass, $args, $parent->ALLOWED_CONTENT, get_called_class() ); } protected function after( string $addName, ...$args ) { $parent = $this->parent(); foreach( $args as &$arg ) { if( $arg instanceof BaseGenerator ) { $arg = $arg->top(); } } if( FALSE !== $addClass = $parent->checkAllowedConfig( $addName, ...$args ) ) { return $parent->insertAfterConfig( $this, $addName, $addClass, $args ); } if( FALSE !== $addClass = $parent->checkAllowedContent( $addName, ...$args ) ) { return $parent->insertAfterContent( $this, $addName, $addClass, $args ); } show( "AFTER", $addName, $addClass, $args, $parent->ALLOWED_CONTENT, get_called_class() ); } /* ### PROTECTED REMOVE FUNCTIONS ############################### # */ protected function remove( ?string $removeName = NULL ) { $parent = $this->parent(); // show( $removeName, FALSE ); if( FALSE !== $parent->checkAllowedConfig( $removeName ) ) { // show( 1 ); return $parent->removeConfig( $this, $removeName ); } if( FALSE !== $parent->checkAllowedContent( $removeName ) ) { // show( 2 ); return $parent->removeContent( $this ); } show( "remove", $removeName, $parent->ALLOWED_CONTENT, get_called_class() ); } /* ### PROTECTED REMPLACE FUNCTIONS ############################### # */ protected function replace( ?string $addName = NULL, ...$args ) { // show( $addName ); $child = $this->get( $addName ); // show( $addName, $child, $this->{$addName}, "X"); if( FALSE !== $addClass = $this->checkAllowedConfig( $addName, ...$args ) ) { $child->before( $addName, $addClass, ...$args ); return $this->removeConfig( $child, $addName ); } // show($child, "X"); if( FALSE !== $addClass = $this->checkAllowedContent( $addName, ...$args ) ) { $child->before( $addName, $addClass, ...$args ); return $this->removeContent( $child ); } show( "replace", $addName, $this->ALLOWED_CONTENT, get_called_class() ); } /* ### ALLOWED DATA FUNCTIONS ################################### # */ protected function isAllowedData( $name ) : bool { return key_exists( $name, $this->ALLOWED_DATA ); } protected function getAllowedData( ?string $name = NULL ) : mixed { if( !$name ) return $this->ALLOWED_DATA; if( !$this->isAllowedData( $name ) ) return FALSE; return $this->ALLOWED_DATA[ $name ]; } protected function addAllowedData( string $name, string|array|UnitEnum $types ) : void { $this->ALLOWED_DATA[ $name ] = $this->parseAllowedTypes( $types ); } protected function checkAllowedData( $name, ...$args ) : null|bool|string { if( !$this->isAllowedData( $name ) ) return FALSE; $types = $this->getAllowedData( $name ); return $this->checkAllowedTypes( $name, $types, ...$args ); } /* ### ALLOWED CONFIG FUNCTIONS ################################# # */ protected function isAllowedConfig( $name ) : bool { return key_exists( $name, $this->ALLOWED_CONFIG ); } protected function getAllowedConfig( ?string $name = NULL ) : mixed { if( !$name ) return $this->ALLOWED_CONFIG; if( !$this->isAllowedConfig( $name ) ) return FALSE; return $this->ALLOWED_CONFIG[ $name ]; } protected function addAllowedConfig( string $name, string|array|UnitEnum $types ) : void { $this->ALLOWED_CONFIG[ $name ] = $this->parseAllowedTypes( $types ); } protected function checkAllowedConfig( ?string $name = NULL , ...$args ) : null|bool|string { if( !$this->isAllowedConfig( $name ) ) return FALSE; $types = $this->getAllowedConfig( $name ); return $this->checkAllowedTypes( $name, $types, ...$args ); } protected function removeConfig( self $remove, string $name ) { $index = array_search( $remove, $this->config[ $name ] ); $before = array_slice( $this->config[ $name ],0, $index ); $after = array_slice( $this->config[ $name ], $index + 1 ); // show( $index, $remove, $before, $after, $this->config[ $name ] ); $this->config[ $name ] = array_merge( $before, $after ); return $this; } protected function insertBeforeConfig( self $before, string $name, $class, $args ) { $this->addToAllowed( $name, $class, "config", $args ); $index = array_search( $before, $this->config[ $name ] ); $before = array_slice( $this->config[ $name ],0, $index ); $insert = array_slice( $this->config[ $name ], -1 ); $after = array_slice( $this->config[ $name ], $index, -1 ); $this->config[ $name ] = array_merge( $before, $insert, $after ); return $this; } protected function insertAfterConfig( self $after, string $name, $class, $args ) { $this->addToAllowed( $name, $class, "config", $args ); $index = array_search( $after, $this->config[ $name ] ); $before = array_slice( $this->config[ $name ],0, $index + 1 ); $insert = array_slice( $this->config[ $name ], -1 ); $after = array_slice( $this->config[ $name ], $index + 1, -1 ); $this->config[ $name ] = array_merge( $before, $insert, $after ); return $this; } /* ### ALLOWED CONTENT FUNCTIONS ################################ # */ protected function isAllowedContent( $name ) : bool { return key_exists( $name, $this->ALLOWED_CONTENT ); } protected function getAllowedContent( ?string $name = NULL ) : mixed { if( !$name ) return $this->ALLOWED_CONTENT; if( !$this->isAllowedContent( $name ) ) return FALSE; return $this->ALLOWED_CONTENT[ $name ]; } protected function addAllowedContent( string $name, string|array|UnitEnum $types ) : void { $this->ALLOWED_CONTENT[ $name ] = $this->parseAllowedTypes( $types ); } protected function checkAllowedContent( ?string $name = NULL, ...$args ) : null|bool|string { if( !$this->isAllowedContent( $name ) ) { if( !$this->isAllowedContent( $name = "*" ) ) return FALSE; } $types = $this->getAllowedContent( $name ); return $this->checkAllowedTypes( $name, $types, ...$args ); } protected function removeContent( self $remove ) { $index = array_search( $remove, $this->content ); $before = array_slice( $this->content,0, $index ); $after = array_slice( $this->content, $index, -1 ); $this->content = array_merge( $before, $after ); return $this; } protected function insertBeforeContent( self $before, string $name, $class, $args ) { $this->addToAllowed( $name, $class, "content", $args ); $index = array_search( $before, $this->content ); $before = array_slice( $this->content,0, $index ); $insert = array_slice( $this->content, -1 ); $after = array_slice( $this->content, $index, -1 ); $this->content = array_merge( $before, $insert, $after ); return $this; } protected function insertAfterContent( self $after, string $name, $class, $args ) { $this->addToAllowed( $name, $class, "content", $args ); $index = array_search( $after, $this->content ); $before = array_slice( $this->content,0, $index + 1 ); $insert = array_slice( $this->content, -1 ); $after = array_slice( $this->content, $index + 1, -1 ); $this->content = array_merge( $before, $insert, $after ); return $this; } /* ### PRIVATE FUNCTIONS ######################################## # */ private function addToAllowed( string $name, $class, $type, $args ) { // if( $name == "dropdowns" ) show( $name, $class, $type, $args, FALSE ); if( !isset( $args[0] ) || ! $args[0] instanceof BaseGenerator ) { if( $name == $type ) { $class = array_shift( $args ); } // if( $name == "dropdowns" ) show( $name, FALSE ); if( $class ) $generator = Generator::create( $class, ...$args ); // if( $name == "dropdowns" ) show( $generator, FALSE ); } else $generator = $args[0]; if( $class ) $generator->setParentNode( $this ); else $generator = $args[0]; // if( $name == "dropdowns" ) show( $this, $generator, FALSE ); if( $type === 'config' ) { $this->{$type}[ $name ][] = $this->lastelement = $generator; } else $this->{$type}[] = $this->lastelement = $generator; return $this; } private function loadAllowedTypes() : bool { $class = get_class( $this ); foreach( $this->getAllowedData() as $name => $types ) { $this->addAllowedData( $name, $types ); } foreach( $this->getAllowedConfig() as $name => $types ) { $this->addAllowedConfig( $name, $types ); } foreach( $this->getAllowedContent() as $name => $types ) { $this->addAllowedContent( $name, $types ); } while( $class ) { $vars = get_class_vars( $class ); if( key_exists( "ALLOWED_DATA", $vars ) ) { foreach( $vars["ALLOWED_DATA"] as $name => $types ) { $this->addAllowedData( $name, $types ); } } if( key_exists( "ALLOWED_CONFIG", $vars ) ) { foreach( $vars["ALLOWED_CONFIG"] as $name => $types ) { $this->addAllowedConfig( $name, $types ); } } if( key_exists( "ALLOWED_CONTENT", $vars ) ) { foreach( $vars["ALLOWED_CONTENT"] as $name => $types ) { $this->addAllowedContent( $name, $types ); } } $class = get_parent_class( $class ); } return TRUE; } private function parseAllowedTypes( string|array|UnitEnum $types ) : array { if( key_exists( "generator", (array)$types ) ) { $generator = $types["generator"]; } if( key_exists( "args", (array)$types ) ) { $types = $types["args"]; } if( !is_array( $types ) ) $types = [ $types ]; if( !is_multi( $types ) ) $types = [ $types ]; $types = [ "generator" => $generator ?? NULL, "args" => $types, ]; return $types; } private function checkAllowedTypes( string $name, array $types, ...$args ) : null|bool|string { $class = $types["generator"]; $types = $types["args"] ?? $types; $last = end( $types ); $last = $last instanceof UnitEnum ? $last->value : $last; $index = 0; // show( $name, $types, $args, FALSE ); foreach( $args as $arg ) { $index++; if( $index + 1 >= count( $types ) && $last == Config\Type::Traversable->value ) { $index = count( $types ) - 2; } if( !key_exists( $index, $types ) ) return $class; foreach( $types[ $index ] as $type ) { if( $type instanceof UnitEnum ) { $type = $type->value; } if( is_a( $arg, $type ) ) continue 2; if( function_exists( "is_{$type}" ) ) { $func = "is_{$type}"; if( $func( $arg ) ) continue 2; } } return FALSE; } return $class; } private function loadTraits() { if( class_uses( $this ) ) { $traits = get_declared_traits(); $traits = array_intersect( class_uses($this), $traits ); $traits = array_values( $traits ); foreach( $traits as $trait ) { $trait = explode( "\\", $trait ); $trait = end( $trait ); if( method_exists( $this, "{$trait}Init" ) ) { $this->{"{$trait}Init"}(); } } } } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( ...$args ) { // if( get_called_class() == "ARUNOX\Plugins\REST\Generator\Components\Button" ) show( get_called_class(), $args, FALSE ); $this->loadAllowedTypes(); $this->loadTraits(); foreach( $args as $name => $arg ) { $this->{"set{$name}"}( $arg ); } } public function __get( $name ) { if( method_exists( $this, "get{$name}" ) ) { return $this->{"get{$name}"}(); } return NULL; } public function __call( $name, $args ) { if( preg_match( "/^(insert)(.+)(before|after)$/", strtolower( $name ), $matches ) ) { $insertIndex = array_shift( $args ); return $this->insert( $matches[3], $insertIndex, $matches[2], ...$args ); } if( preg_match( "/^(add|set|get|remove|replace|insert|before|after)(.*)$/", strtolower( $name ), $matches ) ) { if( !$matches[2] ) $matches[2] = NULL; array_unshift( $args, $matches[2] ); return $this->{$matches[1]}( ...$args ); } } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Generator/Base/BaseWrapper.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Generator\Base; use ARUNOX\Plugins\REST\Response; use ARUNOX\Plugins\REST\Generator\Config; class BaseWrapper extends BaseGenerator { /* ### VARIABLES ################################################ # */ protected static ?string $PRIMARYKEY = "title"; protected ?string $HISTORY = REQUEST_ROUTE; protected ?string $GENERATOR = "dynamic"; protected array $ALLOWED_CONTENT = [ "*" => [ BaseGenerator::class, Config\Type::String ], ]; protected array $ALLOWED_CONFIG = [ "title" => Config\Type::String, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function setHistory( null|string $history = NULL ) { $this->HISTORY = $history; return $this; } /* ### PUBLIC STATIC FUNCTIONS ################################# # */ public static function create( ...$args ) : ?BaseGenerator { return parent::create( get_called_class(), ...$args ); } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( ?string $title = NULL, ...$args ) { parent::__construct(); if( $title ) $this->setTitle( $title ); foreach( $args as $arg ) { $content = $this->addContent( $arg ); if( $title ) $content->get()->setTitle( $title ); } } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Notifications/Type.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Notifications; /* ### ENUMS ######################################################## # */ enum Type : string { case Default = "default"; case Info = "info"; case Danger = "danger"; case Success = "success"; case Warning = "warning"; /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Notifications/Icon.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Notifications; /* ### ENUMS ######################################################## # */ enum Icon : string { case Default = "bi bi-bell-fill"; case Info = "bi bi-info-lg"; case Danger = "bi bi-x-lg"; case Success = "bi bi-check-lg"; case Warning = "bi bi-exclamation-lg"; /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Notifications/Notification.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Notifications; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; class Notification extends BaseGenerator { /* ### VARIABLES ################################################ # */ protected static ?string $PRIMARYKEY = "message"; protected ?string $GENERATOR = "notification"; protected array $ALLOWED_CONFIG = [ "type" => [ Config\Type::String, Type::class ], "message" => Config\Type::String, "title" => Config\Type::String, "duration" => Config\Type::Int, "icon" => [ Config\Type::String, Icon::class ], ]; /* ### PUBLIC STATIC FUNCTIONS ################################## # */ /* ### PUBLIC FUNCTIONS ######################################### # */ public function setIcon( ?string $icon = NULL ) { if( !$icon ) { $type = $this->config["type"]; if( $type && $icon = Icon::{$type->name} ) { return $this->set( "icon", $icon ); } } return $this->set( "icon", $icon ); } /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Notifications/Notifications.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST; use ARUNOX\Plugins\REST\Notifications\Type; use ARUNOX\Plugins\REST\Notifications\Notification; use ARUNOX\Plugins\REST\Generator\Base\BaseWrapper; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; class Notifications extends BaseWrapper { /* ### VARIABLES ################################################ # */ protected static string $TYPE = "wrapper"; protected ?string $HISTORY = NULL; protected static ?BaseGenerator $GENERATED = NULL; protected ?string $GENERATOR = "notifications"; protected array $ALLOWED_CONTENT = []; protected array $ALLOWED_CONFIG = []; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### PUBLIC STATIC FUNCTIONS ################################## # */ public static function create( ...$args ) : ?BaseGenerator { $type = array_shift( $args ); if( !$type = Type::tryFrom( strtolower( $type ) ) ) { $type = Type::Default; } if( ! static::$GENERATED ) { static::$GENERATED = parent::create(); } $notify = Generator::create( Notification::class ); static::$GENERATED->addContent( $notify ); $notify->setType( $type ); $notify->setIcon( $args[1] ?? NULL ); $notify->setDuration( $args[2] ?? NULL ); $notify->setTitle( $args[3] ?? NULL ); $notify->setParentNode( static::$GENERATED ); if( isset( $args[0] ) ) $notify->setMessage( $args[0] ); return $notify; } /* ### PRIVATE FUNCTIONS ######################################## # */ /* ### MAGIC FUNCTIONS ########################################## # */ public static function __callStatic( string $name, $args ) { return static::create( $name, ...$args ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Router.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST; use ARUNOX\Core; class Router extends Core\Router { /* ### PUBLIC STATIC FUNCTIONS ################################## # */ public static function get( string $path, callable | array $action ) : void { parent::get( "/rest{$path}", $action ); } public static function post( string $path, callable | array $action ) : void { parent::post( "/rest{$path}", $action ); } public static function put( string $path, callable | array $action ) : void { parent::put( "/rest{$path}", $action ); } public static function patch( string $path, callable | array $action ) : void { parent::patch( "/rest{$path}", $action ); } public static function delete( string $path, callable | array $action ) : void { parent::delete( "/rest{$path}", $action ); } /* ### PROTECTED FUNCTIONS ###################################### # */ /* ### PRIVATE FUNCTIONS ######################################## # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Wrapper/Modal/Modal.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Wrapper; use ARUNOX\Database\BaseEntity; use ARUNOX\Plugins\REST\Response; use ARUNOX\Plugins\REST\Generator; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseWrapper; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; use ARUNOX\Plugins\REST\Generator\Helpers\ActionHelpers; class Modal extends BaseWrapper { /* ### TRAITS ################################################### # */ use ActionHelpers; /* ### VARIABLES ################################################ # */ protected static string $TYPE = "wrapper"; protected ?string $HISTORY = NULL; protected ?string $GENERATOR = "dialog"; protected array $ALLOWED_CONTENT = []; protected array $ALLOWED_CONFIG = [ "id" => Config\Type::String, "size" => Modal\Size::class, "width" => Modal\Size::class, "close" => Config\Type::Bool, "backdrop" => Config\Type::Bool, "icon" => Config\Type::String, "color" => Config\Color::class, ]; protected array $ALLOWED_DATA = [ "list" => Config\Type::Array, "data" => BaseEntity::class, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function getContent() { return $this->get(); } /* ### PUBLIC STATIC FUNCTIONS ################################## # */ public static function close( string $name ) { return [ "type" => "dialog", "action" => "close", "name" => $name ]; } public static function details( ...$args ) : BaseGenerator { return parent::create( "data/form", ...$args ); } public static function overview( ...$args ) : BaseGenerator { return parent::create( "data/table", ...$args ); } /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( ?string $template = NULL, ?string $title = "Modal", ...$args ) { parent::__construct( $title, $template, ...$args ); $this->setSize( Modal\Size::md ); $this->setClose( TRUE ); $this->setBackdrop( TRUE ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Wrapper/Modal/Size.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Wrapper\Modal; /* ### ENUMS ######################################################## # */ enum Size : string { case sm = "sm"; case md = "md"; case lg = "lg"; case xl = "xl"; case full = "full"; /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Wrapper/Overview.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Wrapper; use ARUNOX\Database\BaseEntity; use ARUNOX\Plugins\REST\Generator; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseWrapper; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; class Overview extends BaseWrapper { /* ### VARIABLES ################################################ # */ protected static string $TYPE = "wrapper"; // protected string $RETURN_OBJECT = "content"; protected array $ALLOWED_CONTENT = []; protected array $ALLOWED_CONFIG = []; protected array $ALLOWED_DATA = [ "list" => Config\Type::Array, "data" => BaseEntity::class, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### PUBLIC STATIC FUNCTIONS ################################## # */ /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( ?string $title = "Overview", ...$args ) { parent::__construct( $title, "data/table", ...$args ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/Wrapper/Details.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\REST\Wrapper; use ARUNOX\Database\BaseEntity; use ARUNOX\Plugins\REST\Generator; use ARUNOX\Plugins\REST\Generator\Config; use ARUNOX\Plugins\REST\Generator\Base\BaseWrapper; use ARUNOX\Plugins\REST\Generator\Base\BaseGenerator; use ARUNOX\Plugins\REST\Generator\Helpers\ActionHelpers; class Details extends BaseWrapper { /* ### TRAITS ################################################### # */ use ActionHelpers; /* ### VARIABLES ################################################ # */ protected static string $TYPE = "wrapper"; // protected string $RETURN_OBJECT = "content"; protected array $ALLOWED_CONTENT = []; protected array $ALLOWED_CONFIG = [ "backlink" => Config\Type::String, ]; protected array $ALLOWED_DATA = [ "data" => BaseEntity::class, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function setBacklink( ...$args ) { return $this->setNamedAction( "backlink", ...$args ); } /* ### PUBLIC STATIC FUNCTIONS ################################## # */ /* ### MAGIC FUNCTIONS ########################################## # */ public function __construct( ?string $title = "Details", ...$args ) { parent::__construct( $title, "data/form", ...$args ); } } /** Datei: plugins/rest/library/ARUNOX/Plugins/REST/REST.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ namespace ARUNOX\Plugins; class REST { /* ### VARIABLES ################################################ # */ protected static array $RESPONSE = []; /* ### PUBLIC STATIC FUNCTIONS ################################## # */ public static function findCoreFiles() : array { $names = []; $root = realpath( ROOT_DIR ); $path = realpath( $root ); $paths = glob_recursive( "{$path}/*/core" ); foreach( $paths as $path ) { $files = glob_recursive( "{$path}/*.js" ); foreach( $files as $file ) { $name = basename( $file, ".js" ); $names[ "core/{$name}" ] = str_replace( $root, "", $file ); } } return $names; } public static function findExtensions( string $path = PLUGINS_DIR . "/rest", null|string $type = NULL ) : array { $type = strtolower( $type ?? "[a-z]+" ); $names = []; $root = realpath( ROOT_DIR ); $path = realpath( $path ); $files = glob_recursive( "{$path}/*.js" ); foreach( $files as $file ) { $pattern = "({$type})\.extend\(\s*['\"]([^'\"]+)['\"]"; $content = file_get_contents( $file ); if( preg_match_all( "/{$pattern}/", $content, $matches ) ) { foreach( $matches[2] as $name ) { $names[ "{$matches[1][0]}/{$name}" ] = str_replace( $root, "", $file ); } } } // show( $files, $names ); return $names; } /* ### PROTECTED FUNCTIONS ###################################### # */ /* ### PRIVATE FUNCTIONS ######################################## # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/rest/hooks/generator/controls.php **/ default", function( array $config, string $name, bool $required ) { return array_replace_recursive([ "key" => $name, "label" => ucfirst( $name ), "type" => REST\Generator\Data\Form\Control\Type::Text, "required" => $required ? "Pflichtfeld" : NULL, "validate" => [ "required" => "Bitte füllen Sie dieses Feld aus!", ], ], $config ); }); /* ### CONTROL TYPES ################################################ # */ Hooks::add("rest.generator::control.type->custom", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Custom, ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->tel", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Tel, "validate" => [ "tel" => "Bitte geben Sie eine korrekte Telefonnummer ein!", ], ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->url", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::URL, "validate" => [ "url" => "Bitte geben Sie eine korrekte URL ein!", ], ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->text", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", $config, $name, $required ) ); }); Hooks::add("rest.generator::control.type->date", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Date, ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->datetime", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::DateTime, ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->time", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Time, ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->file", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::File, ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->range", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Range, ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->email", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Email, "validate" => [ "email" => "Bitte geben Sie eine korrekte E-Mail-Adresse ein!", ], ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->radio", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Radio, "options" => [], ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->select", function( array $config, string $name, bool $required, string|bool $choose = FALSE, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Select, "choose" => $choose == TRUE ? "-- Bitte wählen --" : $choose, "options" => [], ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->hidden", function( array $config, string $name, string $value, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "label" => FALSE, "type" => REST\Generator\Data\Form\Control\Type::Hidden, "value" => $value, ], $config ), $name, FALSE ) ); }); Hooks::add("rest.generator::control.type->textarea", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Textarea, ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->checkbox", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Checkbox, "options" => [], // "validate" => [ // "minChecked" => [ // "value" => 1, // "message" => "Mindestens 2 Optionen auswählen!", // ], // "maxChecked" => [ // "value" => 2, // "message" => "Maximal 2 Optionen auswählen!" // ], // ], ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->password", function( array $config, string $name, bool $confirm, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Password, "confirm" => $confirm ? "Password wiederholen" : FALSE, "validate" => [ "confirm" =>$confirm ? "Die Passwörter stimmen nicht überein!" : FALSE, ], ], $config ), $name, TRUE ) ); }); Hooks::add("rest.generator::control.type->number", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Number, ], $config ), $name, $required ) ); }); Hooks::add("rest.generator::control.type->progress", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Progress, ], $config ), $name, $required ) ); }); /* ### SPECIAL CONTROL TYPES ######################################## # */ Hooks::add("rest.generator::control.type->code", function( array $config, string $name, bool $required, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::control.name->{$name}.{$hook}", Hooks::filter("rest.generator::control->default", array_replace_recursive([ "type" => REST\Generator\Data\Form\Control\Type::Code, ], $config ), $name, $required ) ); }); /* ### CONTROL NAMES ################################################ # */ Hooks::add("rest.generator::control.name->email.*", function( array $config ) { return array_replace_recursive( $config, [ "label" => "E-Mail-Adresse", ]); }); Hooks::add("rest.generator::control.name->status.*", function( array $config ) { return array_replace_recursive( $config, [ "options" => Attribute\Status::getAll(), ]); }); /** Datei: plugins/rest/hooks/generator/actions.php **/ *", function( ...$args ) { $matches = array_pop( $args ); $config = array_shift( $args ); $plugin = array_shift( $args ); $route_id = array_shift( $args ) ?? "id"; $config = Hooks::filter("rest.generator::button.{$matches[0]}->{$matches[1]}", $config, $plugin, $route_id ); $config["title"] = $config["label"]; $config["label"] = NULL; return $config; }); /** Datei: plugins/rest/hooks/generator/buttons.php **/ default", function( array $config, ?string $label ) { if( isset( $config["action"] ) ) $config["action"] = preg_replace( '/\/+/', "/", $config["action"] ); return array_replace_recursive([ "label" => $label, "color" => REST\Generator\Config\Color::Primary, "type" => REST\Generator\Components\Button\Type::Button, ], $config ); }); /* ### BUTTON TYPES ################################################# # */ Hooks::add("rest.generator::button.type->submit", function( array $config, ?string $label, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::button.type->submit.{$hook}", Hooks::filter("rest.generator::button->default", array_replace_recursive([ "icon" => "bi bi-send-fill", "type" => REST\Generator\Components\Button\Type::Submit, ], $config ), $label ?? "Absenden" ) ); }); Hooks::add("rest.generator::button.type->delete", function( array $config, ?string $label, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::button.type->delete.{$hook}", Hooks::filter("rest.generator::button->default", array_replace_recursive([ "icon" => "bi bi-trash", "color" => REST\Generator\Config\Color::Danger, "type" => REST\Generator\Components\Button\Type::Submit, ], $config ), $label ?? "Löschen" ) ); }); Hooks::add("rest.generator::button.type->cancel", function( array $config, ?string $label, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::button.type->cancel.{$hook}", Hooks::filter("rest.generator::button->default", array_replace_recursive([ "icon" => "bi bi-x-lg", "color" => REST\Generator\Config\Color::Primary, "type" => REST\Generator\Components\Button\Type::Cancel, ], $config ), $label ?? "Abbrechen" ) ); }); Hooks::add("rest.generator::button.type->sort", function( array $config, ?string $column, string $order = "asc", ?string $label, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::button.type->sort.{$hook}", Hooks::filter("rest.generator::button->default", array_replace_recursive([ "icon" => "bi bi-sort-down-alt", "color" => REST\Generator\Config\Color::Default, "type" => REST\Generator\Components\Button\Type::Button, "title" => $label ?? "Sortieren", "order" => [ "column" => strtolower( $column ), "order" => strtolower( $order ), ], ], $config ), "" ) ); }); /* ### BUTTON NAMES ################################################# # */ Hooks::add("rest.generator::button.name->create", function( array $config, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::button.name->create.{$hook}", Hooks::filter("rest.generator::button->default", array_replace_recursive([ "name" => "create", "label" => "Hinzufügen", "color" => REST\Generator\Config\Color::Primary, "icon" => "bi bi-plus-lg", "action" => "/{$plugin}/create", ], $config ), NULL ) ); }); Hooks::add("rest.generator::button.name->edit", function( array $config, string $plugin, string $route_id = "id" ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::button.name->edit.{$hook}", Hooks::filter("rest.generator::button->default", array_replace_recursive([ "name" => "edit", "label" => "Bearbeiten", "color" => REST\Generator\Config\Color::Primary, "icon" => "bi bi-pencil", "action" => "/{$plugin}/{{$route_id}}", ], $config ), NULL ) ); }); Hooks::add("rest.generator::button.name->list", function( array $config, string $plugin, string $route_id = "id" ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::button.name->list.{$hook}", Hooks::filter("rest.generator::button->default", array_replace_recursive([ "name" => "list", "label" => "Übersicht", "color" => REST\Generator\Config\Color::Secondary, "icon" => "bi bi-list", "action" => "/{$plugin}/{{$route_id}}", ], $config ), NULL ) ); }); Hooks::add("rest.generator::button.name->link", function( array $config, string $plugin, string $route_id = "id" ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::button.name->link.{$hook}", Hooks::filter("rest.generator::button->default", array_replace_recursive([ "name" => "link", "label" => "Öffnen", "color" => REST\Generator\Config\Color::Default, "icon" => REST\Generator\Config\Icon::BoxArrowUpRight, "href" => $config["action"], "target" => "_blank", ], $config ), NULL ) ); }); Hooks::add("rest.generator::button.name->delete", function( array $config, string $plugin, string $route_id = "id" ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::button.name->delete.{$hook}", Hooks::filter("rest.generator::button->default", array_replace_recursive([ "name" => "delete", "label" => "Löschen", "color" => REST\Generator\Config\Color::Danger, "icon" => "bi bi-trash", "action" => "/{$plugin}/{{$route_id}}/delete", ], $config ), NULL ) ); }); /** Datei: plugins/rest/hooks/generator/enum/icon.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * * @hook config::loaded * * @since 1.0.0 File is created */ $pluginDir = realpath( PLUGINS_DIR ); $hashFile = "{$pluginDir}/rest/assets/bootstrap-icons.hash"; $emojiFile = "{$pluginDir}/rest/assets/icon_emoji.json"; $inputFile = "{$pluginDir}/rest/assets/bootstrap-icons.json"; $outputFile = "{$pluginDir}/rest/library/ARUNOX/Plugins/REST/Generator/Config/Icon.php"; if( !file_exists( $emojiFile ) ) return; if( !file_exists( $inputFile ) ) return; $currentHash = implode( ".", [ md5_file( $inputFile ), md5_file( $emojiFile ), ]); $previousHash = file_exists( $hashFile ) ? file_get_contents( $hashFile ) : ""; if( $currentHash === $previousHash ) return; $emojis = file_get_contents( $emojiFile ); $emojis = json_decode( $emojis, TRUE ); $input = file_get_contents( $inputFile ); $input = json_decode( $input, TRUE ); $output = []; if( !is_array( $input ) ) return; function toReadableEnumCase( string $iconName ): string { $parts = explode("-", $iconName ); if( is_numeric( $parts[0] ) ) { $number = array_shift($parts ); $prev = array_shift($parts ); $parts = array_merge( [ $prev, $number ], $parts ); $parts = array_filter( $parts, fn( $item ) => $item !== NULL ); } return implode("", array_map("ucfirst", $parts ) ); } $input = array_map( fn( $key, $code ) => [ "icon" => "bi-{$key}", "name" => toReadableEnumCase( $key ), "code" => sprintf( '\\%04x', $code ), "emoji" => $emojis["bi-{$key}"] ?? NULL, ], array_keys( $input ), array_values( $input ) ); $input = array_filter( $input, fn( $item ) => !is_numeric( $item["name"] ) ); // show( $input ); $output[] = " \"{$icon["code"]}\","; } $output[] = " };"; $output[] = " }"; $output[] = " public function emoji() {"; $output[] = " return match( \$this ) {"; foreach( $input as $icon ) { $output[] = " Icon::{$icon["name"]} => \"{$icon["emoji"]}\","; } $output[] = " };"; $output[] = " }"; $output[] = " }"; $output = implode( "\n", $output ); // show( htmlentities( $output ) ); file_put_contents( $outputFile, $output ); file_put_contents( $hashFile, $currentHash ); /** Datei: plugins/rest/hooks/generator/columns.php **/ default", function( array $config, string $name, bool $sortable, bool $searchable, bool $visible ) { return array_replace_recursive([ "key" => $name, "label" => ucfirst( $name ), "type" => REST\Generator\Data\Table\Column\Type::Text, "visible" => $visible, "sortable" => $sortable, "searchable" => $searchable, "color" => [ "color" => REST\Generator\Config\Color::Default, "target" => "icon" ], ], $config ); }); /* ### COLUMN TYPES ################################################# # */ Hooks::add("rest.generator::column.type->date", function( array $config, string $name, bool $sortable, bool $searchable, bool $visible, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::column.name->{$name}.{$hook}", Hooks::filter("rest.generator::column->default", array_replace_recursive([ "type" => REST\Generator\Data\Table\Column\Type::Date, ], $config ), $name, $sortable, $searchable, $visible ) ); }); Hooks::add("rest.generator::column.type->tel", function( array $config, string $name, bool $sortable, bool $searchable, bool $visible, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::column.name->{$name}.{$hook}", Hooks::filter("rest.generator::column->default", array_replace_recursive([ "type" => REST\Generator\Data\Table\Column\Type::Tel, ], $config ), $name, $sortable, $searchable, $visible ) ); }); Hooks::add("rest.generator::column.type->url", function( array $config, string $name, bool $sortable, bool $searchable, bool $visible, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::column.name->{$name}.{$hook}", Hooks::filter("rest.generator::column->default", array_replace_recursive([ "type" => REST\Generator\Data\Table\Column\Type::URL, ], $config ), $name, $sortable, $searchable, $visible ) ); }); Hooks::add("rest.generator::column.type->text", function( array $config, string $name, bool $sortable, bool $searchable, bool $visible, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::column.name->{$name}.{$hook}", Hooks::filter("rest.generator::column->default", $config, $name, $sortable, $searchable, $visible ) ); }); Hooks::add("rest.generator::column.type->amount", function( array $config, string $name, bool $sortable, bool $searchable, bool $visible, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::column.name->{$name}.{$hook}", Hooks::filter("rest.generator::column->default", array_replace_recursive([ "type" => REST\Generator\Data\Table\Column\Type::Amount, ], $config ), $name, $sortable, $searchable, $visible ) ); }); Hooks::add("rest.generator::column.type->time", function( array $config, string $name, bool $sortable, bool $searchable, bool $visible, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::column.name->{$name}.{$hook}", Hooks::filter("rest.generator::column->default", array_replace_recursive([ "type" => REST\Generator\Data\Table\Column\Type::Time, ], $config ), $name, $sortable, $searchable, $visible ) ); }); Hooks::add("rest.generator::column.type->email", function( array $config, string $name, bool $sortable, bool $searchable, bool $visible, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::column.name->{$name}.{$hook}", Hooks::filter("rest.generator::column->default", array_replace_recursive([ "type" => REST\Generator\Data\Table\Column\Type::Email, ], $config ), $name, $sortable, $searchable, $visible ) ); }); Hooks::add("rest.generator::column.type->actions", function( array $config, array $buttons, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::column.name->actions.{$hook}", Hooks::filter("rest.generator::column->default", array_replace_recursive([ "type" => REST\Generator\Data\Table\Column\Type::Actions, "buttons" => $buttons, ], $config ), "actions", FALSE, FALSE, TRUE ) ); }); Hooks::add("rest.generator::column.type->datetime", function( array $config, string $name, bool $sortable, bool $searchable, bool $visible, string $plugin ) { $hook = Hooks::parseName( $plugin ); return Hooks::filter("rest.generator::column.name->{$name}.{$hook}", Hooks::filter("rest.generator::column->default", array_replace_recursive([ "type" => REST\Generator\Data\Table\Column\Type::DateTime, ], $config ), $name, $sortable, $searchable, $visible ) ); }); Hooks::add("rest.generator::column.type->icon", function( array $config, string $name, array $options, string $plugin ) { $hook = Hooks::parseName( $plugin ); $config = Hooks::filter("rest.generator::column->default", array_replace_recursive([ "icon" => "bi-toggles", "type" => REST\Generator\Data\Table\Column\Type::Icon, "color" => [ "color" => REST\Generator\Config\Color::Default, "target" => "icon" ], "options" => $options, ], $config ), $name, FALSE, FALSE, TRUE ); return Hooks::filter("rest.generator::column.name->{$name}.{$hook}", array_replace_recursive( $config, [ "title" => $config["label"], "label" => NULL, ])); }); Hooks::add("rest.generator::column.type->status", function( array $config, string $name, array $options, string $plugin, string $route_id = "id" ) { return Hooks::filter("rest.generator::column.type->icon", array_replace_recursive([ "type" => REST\Generator\Data\Table\Column\Type::Status, "action" => [ "method" => "PATCH", "route" => "/api/v1/{$plugin}/{{$route_id}}/{$name}", ], ], $config ), $name, $options, $plugin ); }); /* ### COLUMN NAMES ################################################# # */ Hooks::add("rest.generator::column.name->status.*", function( array $config ) { return array_replace_recursive([ "options" => Attribute\Status::getAll(), ], $config ); }); Hooks::add("rest.generator::column.name->email.*", function( array $config ) { return array_replace_recursive( $config, [ "label" => "E-Mail-Adresse", ]); }); Hooks::add("rest.generator::column.name->date_create.*", function( array $config ) { return array_replace_recursive( $config, [ "label" => "Erstellt am", ]); }); Hooks::add("rest.generator::column.name->date_edit.*", function( array $config ) { return array_replace_recursive( $config, [ "label" => "Bearbeitet am", ]); }); Hooks::add("rest.generator::column.name->actions.*", function( array $config ) { return array_replace_recursive( $config, [ "label" => "Aktionen", ]); }); /** Datei: plugins/rest/hooks/theme.php **/ before", function() { ?>before", function() { $hooks = Filesystem::getDir( PLUGINS_DIR . "/rest/assets/js/hooks" ); $hooks = array_map( fn( $file ) => $file->filename, $hooks->files ); $components = Filesystem::getDir( PLUGINS_DIR . "/rest/assets/js/components" ); $components = array_map( fn( $file ) => $file->filename, $components->files ); ?> $item->path, ARUNOX\Core\Plugins::getPreload() ), array_map( fn( $item ) => $item->path, ARUNOX\Core\Plugins::getActive() ), [ "CURRENT THEME" => THEME->dir->path ], ); $dirs = array_values( $dirs ); $dirs = array_unique( $dirs ); $dirs = array_map( fn( $dir ) => realpath( $dir ), $dirs ); $regex = implode( "|", $dirs ); $files = glob_recursive( "{$root}/*/assets/js/arunox/*.js", GLOB_BRACE ); $files = array_filter( $files, fn( $file ) => preg_match( "#^({$regex})#", $file )); $files = array_merge( glob_recursive( "{$root}/assets/js/arunox/*.js", GLOB_BRACE ), $files ); $files = array_map( fn( $file ) => str_replace( $root, "", "{$file}?_=" . filemtime( $file ) ), $files ); $import = array_merge( $import, $files ); // show( $import ); return $import; }); /** Datei: plugins/rest/hooks/public.php **/ template', function( string $template, string $theme ) { return PLUGINS_DIR . "/rest/templates/index.php"; }); Hooks::add('public::api->init', function() { Router::get( "/api/v1/rest/importmap", function() { return [ "list" => Hooks::filter('router::get->api.v1.rest.importmap', [] ) ]; }); Hooks::add('router::resolve->before', function( $route ) { if( !in_array( $route[0], [ "/rest/routes", "/rest/importmap", ])) return $route; Hooks::remove( "LOGIN_REDIRECT" ); return $route; }, 5 ); Hooks::add('router::resolve->before', function( $route ) { if( isset( $_SERVER["HTTP_AUTHORIZATION"] ) ) { $bearer = preg_replace( "/\s*Bearer\s*/", "", $_SERVER["HTTP_AUTHORIZATION"] ?? "" ); if( $bearer != ARUNOX_API_KEY ) return $route; // if( $bearer != "YXJpLmFydW5veC5kZXY" ) return $route; } else return $route; Hooks::remove( "LOGIN_REDIRECT" ); return $route; }, 5 ); }); /** Datei: plugins/rest/hooks/database/attribute/options/get.php **/ * @copyright 2025 ARUNOX * @version 1.0.0 * * @hook database.attribute.options::get * @param array $option Option * * @since 1.0.0 File is created */ if( isset( $option["emoji"] ) && !empty( $option["emoji"] ) ) return $option; if( !defined( "REST_ICON_EMOJIS_MAP" ) ) { $filename = PLUGINS_DIR . "/rest/assets/icon_emoji.json"; $filename = realpath( $filename ); if( file_exists( $filename ) ) { $iconmap = file_get_contents( $filename ); $iconmap = json_decode( $iconmap, TRUE ); if( $iconmap ) { define( "REST_ICON_EMOJIS_MAP", $iconmap ); } } } if( defined( "REST_ICON_EMOJIS_MAP" ) ) { if( isset( $option["icon"] ) ) { if( is_a( $option["icon"], ARUNOX\Plugins\REST\Generator\Config\Icon::class ) ) { $option["emoji"] = $option["icon"]->emoji(); } else if( preg_match('/\b(bi-[a-z0-9-]+)/i', $option["icon"], $matches ) ) { if( key_exists( $matches[1], REST_ICON_EMOJIS_MAP ) ) { $option["emoji"] = REST_ICON_EMOJIS_MAP[ $matches[1] ]; } } } } return $option; /** Datei: plugins/rest/hooks/rest.php **/ api.v1.rest.importmap', function( array $import = [] ) { $root = ROOT_DIR; $root = realpath( $root ); $dirs = array_merge( array_map( fn( $item ) => $item->path, ARUNOX\Core\Plugins::getPreload() ), array_map( fn( $item ) => $item->path, ARUNOX\Core\Plugins::getActive() ), [ "CUTTENT THEME" => THEME->dir->path ], ); foreach( $dirs as $dir ) { $files = glob_recursive( "{$dir}/*.js" ); $files = array_map( fn( $item ) => str_replace( $root, "", $item ), $files ); $names = array_map( fn( $item ) => preg_replace( '#(themes|plugins)/([^/]+)/\2(?:/?.*)#', '$1/$2', preg_replace( '#.*(themes|plugins)/([^/]+)/(.*/js)/(.*)\.js#', '$1/$2/$4', $item ) ), $files ); $import = array_merge( $import, array_combine( $names, $files ) ); } return $import; }); Hooks::add('router::get->api.v1.rest.routes', function( $routes ) { $routes[] = [ "name" => "content", "label" => Users\Model::isSuperadmin() ? "Content" : "Verwaltung", "menu" => "sidebar", ]; return $routes; }, 10 ); Hooks::add('router::get->api.v1.rest.routes', function( $routes ) { $routes[] = [ "name" => "management", "label" => Users\Model::isSuperadmin() ? "Verwaltung" : "Administration", "menu" => "sidebar", ]; return $routes; }, 80 ); Hooks::add('router::get->api.v1.rest.routes', function( $routes ) { $routes[] = [ "name" => "administration", "label" => "Administration", "menu" => "sidebar", ]; return $routes; }, 100 ); Hooks::add( 'rest::response->data', function( $response ) { $request = new ARUNOX\HTTP\Request(); $columns = $request->getQueryParams( "columns" ); $columns = explode( ",", $columns ?? "" ); $columns = array_filter( $columns ); if( empty( $response ) ) return $response; if( is_a( $response, BaseEntity::class ) ) { $response->hashid; if( empty( $columns ) ) return $response; $data = []; foreach( $columns as $column ) { $data[ $column ] = $response->{$column}; } return $data; } if( !is_array( $response ) ) return $response; if( !is_list( $response ) ) return $response; if( !is_a( $response[0], BaseEntity::class ) ) return $response; foreach( $response as &$entity ) { $entity->hashid; if( empty( $columns ) ) continue; $data = []; foreach( $columns as $column ) { $data[ $column ] = $entity->{$column}; } $entity = $data; } // show($columns, $response); return $response; }); Hooks::add( 'response::data', function( $response ) { if( isset( $response["data"] ) ) { $response["data"] = Hooks::filter( 'rest::response->data', $response["data"] ); } return $response; }); Hooks::add( 'router::dispatch->after', function( $response, $route, $data, ...$args ) { return Hooks::filter( 'rest::response->data', $response ); }); Hooks::add( 'router::resolve->before', function( $route ) { [ $uri, $method ] = $route; if( $uri === "/rest" ) { // REST\Response::addWrapper( "overview" ); REST\Response::jsonResponse()->send(); } return $route; }, 200 ); /** Datei: plugins/rest/assets/scss/colors/_variables.scss **/ $theme-bg-blur : 5px !default; $theme-color-red : #ee1501 !default; $theme-color-orange : #ee6c01 !default; $theme-color-yellow : #eece01 !default; $theme-color-green : #01ee21 !default; $theme-color-cyan : #01c3ee !default; $theme-color-blue : #014cee !default; $theme-color-pink : #ee01e2 !default; $theme-color-purple : #8301ee !default; $theme-color-white : #ffffff !default; $theme-color-light : #bebebe !default; $theme-color-medium : #818181 !default; $theme-color-dark : #595959 !default; $theme-color-black : #000000 !default; $theme-colors : ( 'background': $theme-color-white, 'color' : $theme-color-black, 'content' : $theme-color-white, 'default' : $theme-color-medium, 'inverted' : $theme-color-light, 'primary' : $theme-color-cyan, 'secondary' : $theme-color-yellow, 'tertiary' : $theme-color-pink, 'success' : $theme-color-green, 'danger' : $theme-color-red, 'warning' : $theme-color-yellow, 'red' : $theme-color-red, 'orange' : $theme-color-orange, 'yellow' : $theme-color-yellow, 'green' : $theme-color-green, 'cyan' : $theme-color-cyan, 'blue' : $theme-color-blue, 'pink' : $theme-color-pink, 'purple' : $theme-color-purple, 'white' : $theme-color-white, 'light' : $theme-color-light, 'medium' : $theme-color-medium, 'dark' : $theme-color-dark, 'black' : $theme-color-black, ) !default; $theme-colorize-alpha : .3 !default; $theme-colorize-bg-alpha : .4 !default; $theme-colorize-border-alpha : .5 !default; $theme-colorize-active-alpha : .1 !default; $theme-colorize-hover-alpha : .2 !default; $theme-colorize-light : $theme-color-white !default; $theme-colorize-dark : $theme-color-black !default; $theme-brightness: ( 50 : 5%, 100 : 0%, 200 : -2.5%, 300 : -5%, 400 : -7.5%, 500 : -10%, ) !default; /** Datei: plugins/rest/assets/scss/colors/_functions.scss **/ @function get_color( $color, $alpha : 1, $brightness : 0 ) { @if type-of( $color ) != color { $color : map-get( $theme-colors, $color ); } @if( $brightness < 0 ) { $color : darken( $color, $brightness * -1); } @if( $brightness > 0 ) { $color : lighten( $color, $brightness ); } @if( $alpha != 1 ) { $color : rgba( red( $color ), green( $color ), blue( $color ), $alpha ); } @return $color } @function get_color_hover( $color, $alpha : 1 ) { @return get_color( $color, $alpha, 5% ); } @function get_color_active( $color, $alpha : 1 ) { @return get_color( $color, $alpha, -5% ); } @function get_color_tone( $color, $level : 100, $tint: null, $tint-strength: 15% ) { @if( $level > 0 ) { $base: get_color( $color, 1, get_brightness( $level ) ); } @else { $base: get_color( $color, 1, get_brightness( $level * -1 ) * -1 ); } @if $tint == null { @return $base; } $accent: get_color( $tint ); @return mix( $accent, $base, $tint-strength ); } @function get_brightness( $level : 100 ) { @return map-get( $theme-brightness, $level ); } @function contrast( $color, $color1, $color2, $contrast-threshold : 55% ) { @if type-of( $color ) == color and type-of( $color1 ) == color and type-of( $color2 ) == color { $light : if( lightness( $color1 ) > lightness( $color2 ), $color1, $color2 ); $dark : if( lightness( $color1 ) > lightness( $color2 ), $color2, $color1 ); @return if( lightness( $color ) < $contrast-threshold, $light, $dark ); } @else { @return $color1; } } /** Datei: plugins/rest/assets/scss/colors/_styles.scss **/ @import "variables.scss"; @import "mixins.scss"; @import "functions.scss"; @mixin colorize_btn_all() { @each $name, $color in $theme-colors { &.colored-#{$name} { @include colorize_btn( $name ); } } } @mixin colorize_btn( $color: inverted ) { @include colorize_this( $color ); &.focus { @include colorize_this( $color, $theme-active-opacity ); } &.active { @include colorize_this( $color, $theme-active-opacity ); } &.current { @include colorize_this( $color, $theme-active-opacity ); } &:focus { @include colorize_this( $color, $theme-hover-opacity ); } &:hover { @include colorize_this( $color, $theme-hover-opacity ); } } @each $name, $color in $theme-colors { .text--colored-#{$name} { color: #{get_color( $color )}; } .theme--colored-#{$name} { @include colorize( $color ); } } /** Datei: plugins/rest/assets/scss/colors/_mixins.scss **/ @mixin colorize( $color, $light: $theme-colorize-light, $dark: $theme-colorize-dark ) { // @debug 'theme-colorize-alpha: #{$theme-colorize-alpha}'; // @debug 'theme-colorize-bg-alpha: #{$theme-colorize-bg-alpha}'; // @debug 'theme-colorize-border-alpha: #{$theme-colorize-border-alpha}'; // @debug 'theme-colorize-hover-alpha: #{$theme-colorize-hover-alpha}'; // @debug 'theme-colorize-active-alpha: #{$theme-colorize-active-alpha}'; $normal : get_colorize_bg( $color ); $hover : get_colorize_bg( $color, $theme-colorize-hover-alpha ); $active : get_colorize_bg( $color, $theme-colorize-active-alpha ); --color-background: #{$normal}; --color-background-hover: #{$hover}; --color-background-active: #{$active}; --color-border: #{get_colorize_border( $color )}; --color-border-hover: #{get_colorize_border( $color, $theme-colorize-hover-alpha )}; --color-border-active: #{get_colorize_border( $color, $theme-colorize-active-alpha )}; --color-text: #{get_colorize_text( $normal, $light, $dark )}; --color-text-hover: #{get_colorize_text( $hover, $light, $dark )}; --color-text-active: #{get_colorize_text( $active, $light, $dark )}; } @function get_colorize_bg( $color, $alpha: 0 ) { @return get_color( $color, $theme-colorize-alpha + $theme-colorize-bg-alpha + $alpha ); } @function get_colorize_border( $color, $alpha: 0 ) { @return get_color( $color, #{$theme-colorize-alpha + $theme-colorize-border-alpha + $alpha } ); } @function get_colorize_text( $color, $light: $theme-colorize-light, $dark: $theme-colorize-dark ) { @return contrast( $color, $light, $dark ); } /** Datei: plugins/rest/assets/scss/_functions.scss **/ /** Datei: plugins/rest/assets/scss/_mixins.scss **/ @mixin transition( $property: all, $duration: 200ms, $timing-function: ease-in-out, $behavior: normal, $delay: 0 ) { transition-property: $property; transition-duration: $duration; transition-timing-function: $timing-function; transition-behavior: $behavior; transition-delay: $delay; } /** Datei: plugins/rest/assets/scss/layout/_table.scss **/ @import "../mixins.scss"; .cursor--pointer, .js--click-bound, .js--click-ownbound { cursor : pointer; }; .table { $table-padding : 5px; $table-gap-small : 10px !default; $table-gap-default : 15px !default; $table-gap-wide : 20px !default; $table-font-family : sans-serif !default; $table-border-radius : 0 !default; $table-border-spacing : 0 !default; $table-header-font-family : $table-font-family !default; $table-header-border-width : 0 1px 1px 0 !default; $table-header-bg-color : get_color_tone( content, 400 ) !default; $table-header-bg-color-hover : get_color_tone( content, 500 ) !default; $table-header-bg-color-active : get_color_tone( content, 400, primary ) !default; $table-header-bg-color-active-hover : get_color_tone( content, 500, primary ) !default; $table-header-border-color : get_color_tone( border, 400 ) !default; $table-header-border-color-hover : $table-header-border-color !default; $table-header-border-color-active : get_color_tone( content, 500, primary ) !default; $table-header-border-color-active-hover : $table-header-border-color-active !default; $table-cell-font-family : $table-header-font-family !default; $table-cell-border-width : $table-header-border-width !default; $table-cell-border-color : get_color( border ) !default; $table-cell-bg-color-hover : $table-header-bg-color-hover !default; $table-cell-border-color-hover : $table-header-border-color !default; $table-row-bg-color : get_color_tone( content, 100 ) !default; $table-row-bg-color-even : get_color_tone( content, 200 ) !default; $table-row-bg-color-hover : get_color_tone( content, 300, primary ) !default; $table-row-bg-color-hover-even : get_color_tone( content, 400, primary ) !default; $table-row-bg-color-active-hover : get_color_tone( content, 300, secondary ) !default; $table-row-bg-color-active-hover-even : get_color_tone( content, 400, secondary ) !default; $table-row-bg-color-success-even : get_color_tone( content, 200, success ) !default; $table-row-bg-color-success-hover-even : get_color_tone( content, 300, success ) !default; $table-row-bg-color-success-active-hover-even : get_color_tone( content, 400, success ) !default; $table-row-bg-color-danger-even : get_color_tone( content, 200, danger ) !default; $table-row-bg-color-danger-hover-even : get_color_tone( content, 300, danger ) !default; $table-row-bg-color-danger-active-hover-even : get_color_tone( content, 400, danger ) !default; width: calc( 100% + var( --gap ) * 2 ); margin: var( --gap-negate ); font-family: #{$table-font-family}; @if( $table-border-spacing == 0 ) { border-collapse: collapse; border: 1px solid #{get_color( border )}; } @else { border-spacing: #{$table-border-spacing}; border-collapse: separate; } &__ { &wrapper { display: flex; flex-direction: column; gap: #{$table-gap-wide}; grid-column: 1/-1; overflow: hidden; } &toolbar { display: grid; grid-template-areas: 'search buttons'; grid-template-rows: 2rem; gap: #{$table-border-spacing}; } &search { flex: 1; display: flex; gap: #{$table-border-spacing}; grid-area: search; &__input { max-width: 250px; padding: 0.35rem; border: 1px solid #{get_color( border )}; border-radius: #{$table-border-radius}; font-size: 0.9rem; min-height: 2rem; outline: none; &:hover { border-color: #{get_color_hover(primary)}; box-shadow: 0 0 5px #{get_color(primary, 0.5)}; outline: none; } &:focus { border-color: #{get_color(primary)}; box-shadow: 0 0 5px #{get_color(primary, 0.5)}; outline: none; } } } &buttons { display: flex; gap: #{$table-border-spacing}; grid-area: buttons; justify-content: end; height: 100%; } &body { &__ { &row { @include transition( 'background-color' ); &:nth-child(odd) { background-color: #{$table-row-bg-color}; &.js--click-bound:hover { background-color: #{$table-row-bg-color-hover}; } .js--click-ownbound:hover { background-color: #{$table-row-bg-color-active-hover}; } } &:nth-child(even) { background-color: #{$table-row-bg-color-even}; &.js--click-bound:hover { background-color: #{$table-row-bg-color-hover-even}; } .js--click-ownbound:hover { background-color: #{$table-row-bg-color-active-hover-even}; } } &--failed { background-color: #{get_color_tone(content, 100, danger)} !important; &.js--click-bound:hover { background-color: #{get_color_tone(content, 200, danger)} !important; } &.js--click-ownbound:hover { background-color: #{get_color_tone(content, 200, danger)} !important; } } &--failed { background-color: #{$table-row-bg-color-danger-even} !important; &.js--click-bound:hover { background-color: #{$table-row-bg-color-danger-hover-even} !important; } .js--click-ownbound:hover { background-color: #{$table-row-bg-color-danger-active-hover-even} !important; } } &--updated { background-color: #{$table-row-bg-color-success-even} !important; &.js--click-bound:hover { background-color: #{$table-row-bg-color-success-hover-even} !important; } .js--click-ownbound:hover { background-color: #{$table-row-bg-color-success-active-hover-even} !important; } } } } } &cell { padding: 0.3rem 0.5rem; height: 42px; // Grundhöhe vertical-align: middle; text-align: left; border-style: solid; border-color: #{$table-cell-border-color}; border-width: #{$table-cell-border-width}; .icon:not(.button__icon) { margin-right: #{$table-gap-small}; } &__ { &inner { $table-cell-inner: & !global; height: 100%; &__ { &text { flex: 1 0 auto; display: flex; align-items: center; } } } } &:is( .table__body__cell ) { padding: 0.3rem; > #{$table-cell-inner} { gap: 0.3rem; display: flex; justify-content: end; } } &--type { &-email { a { color: inherit; } } &-text { } &-amount { $table-cell-type-amount: & !global; text-align: right; } &-actions { text-align: right; } &-icon, &-status { width: 42px; min-width: 42px; text-align: center; padding: 0; &-icon { max-width: 42px; } .icon { display: flex; justify-content: center; align-items: center; width: 28px; height: 28px; font-size: 1.4rem; border-radius: 50%; margin: auto !important; } } } } &header { &__ { &cell { background-color: #{$table-header-bg-color}; border-color: #{$table-header-border-color}; border-width: #{$table-header-border-width}; font-weight: bold; text-transform: uppercase; &.sortable { position: relative; cursor: pointer; padding-right: 1.2rem; &::after { content: "▲▼"; position: absolute; font-size: 0.8rem; opacity: 0.5; right: 0.5rem; } &:hover { background-color: #{$table-header-bg-color-hover}; border-color: #{$table-header-border-color-hover}; } &#{$table-cell-type-amount} { padding-right: 0.5rem; padding-left: 1.2rem; &::after { right: auto; left: 0.5rem; } } &.sorting--asc::after { content: "▲"; } &.sorting--desc::after { content: "▼"; } &.sorting--current { background-color: #{$table-header-bg-color-active}; border-color: #{$table-header-border-color-active}; font-weight: bold; &:hover { background-color: #{$table-header-bg-color-active-hover}; border-color: #{$table-header-border-color-active-hover}; } // @include highlightBorder(); } } } } } } } /** Datei: plugins/rest/assets/scss/layout/_form.scss **/ .form { $form-padding : 5px; $form-font-size-label : 8px; $form-control-padding : $form-padding; $form-checked-size : 16px; $form-checked-inner : 10px; $tabs-gap-tiny : 5px !default;; $form-gap-small : 10px !default; $form-gap-default : 15px !default; $form-gap-wide : 20px !default; $form-icon-font-family : bootstrap-icons !default; $form-border-radius : 0 !default; font-size: 1rem; &__ { &container { display: flex; flex-direction: column; gap: $form-gap-default; > * { margin: 0; } } &fieldset { display: flex; flex-direction: column; gap: #{$form-gap-wide}; padding: 0; border: 0; &.has-legend { flex: 1 1 20%; background-color: #{get_color( white )}; padding: #{$form-gap-wide} 0 0; &.has-border { padding: #{$form-gap-wide}; border: 1px solid #{get_color( border )}; border-radius: #{$form-border-radius}; } } } &legend { font-weight: 600; font-size: 1.17em; color: #{get_color(black)}; } &info { font-size: #{$form-font-size-label}; padding: #{$form-padding}; position: absolute; display: none; flex-direction: row-reverse; align-items: start; gap: #{$form-padding}; top: 0; right: 0; &-- { &required { $form-info-required: & !global; color: get_color( danger ); } &feedback { $form-info-feedback: & !global; &::before { font-size: #{$form-font-size-label *1.5}; font-family: #{$icon-font-family}; translate: 0 -#{$form-font-size-label * 0.5 / 2}; } } } } &control { $form-control: & !global; display: flex; align-items: center; position: relative; &:is( input, select, textarea, .is-fake-control ) { position: relative; font-size: 1em; border-radius: #{$form-border-radius}; border: 0; outline: 0; padding: #{$form-control-padding}; transition: border-color 0.3s, box-shadow 0.3s; background: transparent; width: 100%; font-family: inherit; } &:is( pre ) { margin: 0; } &__{ &group { display: flex; flex-direction: column; gap: #{$form-control-padding}; } &icon { margin-right: #{$form-gap-small}; } &label { display: block; width: 100%; padding: #{$form-control-padding}; padding-left: #{$form-checked-size + $form-control-padding * 3}; position: relative; &:has( input[type=radio] ) { &::after, &::before { border-radius: 50%; } } &::before { content: ""; display: inline-block; aspect-ratio: 1; height: #{$form-checked-size}; border: 1px solid get_color( border ); margin-right: 0.5em; position: absolute; top: #{$form-control-padding}; left: #{$form-control-padding}; } &:has( input:checked ) { &::after { content: ''; aspect-ratio: 1; display: block; top: #{$form-control-padding + $form-checked-size / 2 - $form-checked-inner / 2}; left: #{$form-control-padding + $form-checked-size / 2 - $form-checked-inner / 2}; position: absolute; background-color: #{get_color_hover(primary)}; height: #{$form-checked-inner}; } } &:hover { &::before { border-color: #{get_color_hover(primary)}; box-shadow: 0 0 5px #{get_color(primary, 0.5)}; outline: none; } } } } &-- { @mixin form-control-icon { font-family: #{$form-icon-font-family}; position: absolute; right: #{$form-padding}; top: #{$form-padding + $form-padding + $form-font-size-label}; bottom: #{$form-padding}; aspect-ratio: 1; display: flex; justify-content: center; align-items: center; } &progress { .progress { &__ { &wrapper { position: relative; height: 17px; font-size: 0.8rem; margin: #{$form-control-padding}; overflow: hidden; border-radius: #{$form-border-radius}; background-color: #{get_color( border )}; } &bar, &label { position: absolute; top: 0; left: 0; height: 100%; } &bar { background-color: var( --progress-color, #{get_color( primary )} ); transition: background-color 300ms ease-in-out, width 500ms cubic-bezier(.4,0,.2,1), } &label { color: #{get_color( white )}; width: 100%; display: flex; align-items: center; justify-content: center;; } } } } &radio, &checkbox { // border-color: transparent !important; box-shadow: none !important; flex-direction: column; width: 100%; input { visibility: hidden; position: fixed; top: 100vh; left: 100vw; } } &file { cursor: pointer; input { position: absolute !important; visibility: hidden; } &::after { content: '\F3D1'; @include form-control-icon; } } &textarea { textarea { height: calc( 2em + var( --lines, 2 ) * ( 2.8125em + #{$form-gap-wide} ) ); height: calc( ( var(--lines) * #{$form-gap-wide} ) + ( 45px * var(--lines) ) + 1.625em ) } } &select { select { appearance: none; } &::after { content: '\F229'; @include form-control-icon; } } } } &group { display: flex; flex-direction: column; position: relative; border: 1px solid #{get_color(border)}; > label { font-weight: 500; color: #{get_color(text)}; font-size: #{$form-font-size-label}; padding: #{$form-padding} #{$form-padding} 0; } &:hover { border-color: #{get_color_hover(primary)}; box-shadow: 0 0 5px #{get_color(primary, 0.5)}; outline: none; } &.has-focus { border-color: #{get_color(primary)}; box-shadow: 0 0 5px #{get_color(primary, 0.5)}; outline: none; } &.is-required { #{$form-info-required} { display: flex; } } &.is-valid, &.is-invalid, &.on-validate { #{$form-info-required} { display: none; } #{$form-info-feedback} { display: flex; } } &.is-valid { border-color: #{get_color(success)}; background-color: #{get_color(success, 0.05)}; &:hover { box-shadow: 0 0 5px #{get_color(success, 0.5)}; } #{$form-info-feedback} { color: #{get_color(success)}; &::before { content: '\F26A'; } } } &.is-invalid { border-color: #{get_color(danger)}; background-color: #{get_color(danger, 0.05)}; &:hover { box-shadow: 0 0 5px #{get_color(danger, 0.5)}; } #{$form-info-feedback} { color: #{get_color(danger)}; &::before { content: '\F622'; } } } &.on-validate { border-color: #{get_color(warning)}; background-color: #{get_color(warning, 0.05)}; &:hover { box-shadow: 0 0 5px #{get_color(warning, 0.5)}; } #{$form-info-feedback} { color: #{get_color(warning)}; &::before { content: '\F3E2'; animation-name: animate-rotate; animation-duration: 3s; animation-iteration-count: infinite; animation-timing-function: linear; } } } } &buttons { display: flex; justify-content: end; gap: $form-gap-default; } &actions { display: flex; justify-content: flex-end; gap: 0.75rem; .action-button { padding: 0.75rem 1.5rem; font-size: 1rem; border-radius: #{$form-border-radius}; cursor: pointer; border: none; transition: background-color 0.3s, transform 0.2s; &.primary { background-color: #{get_color(primary)}; color: #{get_color(white)}; &:hover { background-color: #{get_color_hover(primary)}; transform: scale(1.05); } &:active { transform: scale(0.95); } } &.secondary { background-color: #{get_color(secondary)}; color: #{get_color(white)}; &:hover { background-color: #{get_color_hover(secondary)}; transform: scale(1.05); } &:active { transform: scale(0.95); } } } } } } /** Datei: plugins/rest/assets/scss/layout/_tabs.scss **/ .tabs { $tabs-gap-tiny : 5px !default; $tabs-gap-small : 10px !default; $tabs-gap-default : 15px !default; $tabs-gap-wide : 20px !default; $tabs-icon-font-family : bootstrap-icons !default; $tabs-border-radius : 0 !default; display: flex; flex-direction: column; gap: #{$tabs-gap-wide}; width: 100%; .headline, .paragraph { grid-column: 1/-1; } &__ { &list { display: flex; list-style: none; gap: #{$tabs-gap-tiny}; padding: 0; margin: 0; border-bottom: 2px solid #{getCoreColor( gray )}; } &item { // padding: 0.2em 0.6em; font-size: 1rem; gap: #{$tabs-gap-small}; // min-height: 2rem; border-radius: #{$tabs-border-radius}; // border: none; background: transparent; cursor: pointer; transition: background 0.2s ease; display: flex; align-items: center; user-select: none; &:hover { background: rgba(0, 0, 0, 0.05); } &--active { // font-weight: bold; // @include highlightBorder(); } &--action { margin-left: auto; // Action-Tabs immer rechts background: rgba(0, 0, 0, 0.05); border-radius: #{$tabs-border-radius}; & + & { margin-left: 0; } } // @include buttonColors(); } &content { // padding: 15px; // border: 1px solid #{getThemeColor( border )}; // background: #fff; &__item { display: flex; flex-direction: column; gap: #{$tabs-gap-default}; display: grid; grid-template-columns: repeat( 2, 1fr ); > * { margin-block: 0; } > .form__group { grid-column: 1/-1; } } } &icon { font-size: 16px; } } } /** Datei: plugins/rest/assets/scss/layout/_button.scss **/ .button { &__ { &dropdown { $button-dropdown: & !global; display: none; z-index: 10; position: absolute; top: 100%; padding-top: var( --gap ); left: 0; flex-direction: column; gap: var( --gap ); } &wrapper { $button-wrapper: & !global; position: relative; display: flex; flex-direction: column-reverse; gap: var( --gap ); &.is--open { #{$button-dropdown} { display: flex; } } } } } /** Datei: plugins/rest/assets/js/components/button.js **/ /** * /arunox/components/button.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'button', { hooks : { config : function( config ) { if( config.type ) config.attributes.type ??= config.type; if( config.name ) config.attributes.name ??= config.name; if( config.value ) config.attributes.value ??= config.value; if( config.title ) config.attributes.title ??= config.title; if( config.target ) config.attributes.target ??= config.target; return config; }, wrapper : function( root ) { const config = this.instance.config; if( !config.dropdowns ) return root; const wrapper = ARUNOX.Templates.render( 'div', { classes : 'button__wrapper', }) this.instance.nodes.wrapper = wrapper; this.instance.apply('dropdown'); wrapper.root.append( root ); return wrapper.root; }, dropdown : function() { const config = this.instance.config; const wrapper = this.instance.nodes.wrapper; if( !wrapper || !config.dropdowns ) return; const menu = wrapper.render('div', { nodeName : 'menu', classes : 'button__dropdown' }); for( const item of config.dropdowns ) { const itemContainer = menu.render( 'div', { classes : 'button__dropdown__item' }); itemContainer.render( 'button', Object.assign({ color : 'secondary', type : 'button', block : 'button__dropdown' }, item.config ), this.instance.data); } this.instance.root.addEventListener( 'click', ( event ) => { event.stopPropagation(); wrapper.root.classList.toggle('is--open'); }); wrapper.root.addEventListener( 'mouseleave', () => { wrapper.root.classList.remove( 'is--open'); const focusedElement = wrapper.root.querySelector(':focus'); if( focusedElement ) focusedElement.blur(); }); }, }, htmlTag : function( config, data, ctx ) { return config.href ? 'a' : 'button'; }, builder : function( config, data ) { const className = config?.name || 'button'; const block = config.block ? `${className} ${config.block}__${className}` : className; this.root.classList.add( ...block.split(' ') ); if( config.label ) { this.root.classList.add( 'has--label' ); this.render( 'span', { classes : `${block}__label label`, content : config.label, }); } if( config.href ) { this.root.href = ARUNOX.Router.parseUrl( config.href, data ); this.root.addEventListener( 'click', ( event ) => { event.stopPropagation(); }); return; } if( config.type ) { const form = this.root.closest( 'form' ); if( form ) { if( !form.id ) form.id = crypto.randomUUID(); this.root.setAttribute( 'form', form.id ); } } if( config.action && !config.dropdowns ) { this.root.addEventListener( 'click', ( event ) => { event.preventDefault(); event.stopPropagation(); if( this.root.name && this.root.value !== '' && this.root.value !== 'undefined' ) { ARUNOX.Router.handle( config.action, { [`${this.name}`] : this.value }, data, 'rest' ); } else ARUNOX.Router.handle( config.action, {}, data, 'rest' ); }); } }, }); ARUNOX.Templates.register( 'buttons', { htmlTag : 'div', hooks : { config : function( config ) { config.datasets.arx.node = 'buttons'; config.content ??= config.buttons || []; return config; }, }, builder : function( config, data ) {} }); /** Datei: plugins/rest/assets/js/components/tabs.js **/ /** * /arunox/components/tabs.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'tabs', { htmlTag : 'div', hooks : { config : function( config, data ) { config.classes.push( 'tabs' ); return config; }, buildRootAfter : function() { const config = this.instance.config; config.query = window.location.hash.match( /\?(.*)/ ); config.hash = window.location.hash.substring(1).replace( /\?.*/, '' ); config.filter = new URLSearchParams( config.query ? config.query[1] : '' ); config.isNested = this.instance.root.closest( '.dialog, .tabs__content' ) !== null; if( config.isNested ) { let match = ARUNOX.Router.history.match( /.*(?:#([a-zA-Z0-9]+)).*/ ); config.hash = this.instance.root.dataset.activeTab || match ? match[1] : ''; config.filter = new URLSearchParams(); } }, }, builder : function( config, data ) { this.render( 'tabs.buttons', { nodeName : 'buttons', tabs : config.tabs, }); this.render( 'tabs.contents', { nodeName : 'contents', tabs : config.tabs, }); }, }); ARUNOX.Templates.register( 'tabs.buttons', { extends : 'buttons', hooks : { config : function( config, data ) { config.classes.push( 'tabs__list' ); return config; }, content : function( content, config, data ) { for( let [ index, tabConfig ] of config.tabs.entries() ) { tabConfig = Object.assign( { index }, tabConfig ); delete tabConfig.content; content.push( ARUNOX.Templates.config( 'tabs.button', tabConfig, data ) ); } return content; }, }, }); ARUNOX.Templates.register( 'tabs.button', { extends : 'button', hooks : { config : function( config, data ) { config.classes.push( 'tabs__item' ); config.color = 'secondary'; config.arx.tab = config.id; return config; }, select : function() { const tabs = this.instance.closest( 'tabs' ); const tabsConfig = tabs.config; const config = this.instance.config; const buttons = tabs.nodes.buttons; const contents = tabs.nodes.contents; for( const button of buttons.root.childNodes ) { button.classList.remove('tabs__item--active' ); if( button.dataset.arxTab == config.arx.tab ) { this.instance.root.classList.add( 'tabs__item--active' ); } } for( const content of Object.values( contents.root.childNodes ) ) { if( content.dataset.arxTab == config.arx.tab ) { content.classList.add('tabs__content--active' ); content.style.display = ''; content.ARX.apply('show'); } else { content.classList.remove('tabs__content--active' ); content.style.display = 'none'; content.ARX.apply('hide'); } } if( tabsConfig.isNested ) return; if( this.instance.root.dataset.tabFilter ) history.pushState(null, null, `#${config.arx.tab}?${config.arx.filter}` ); else if( config.arx.tab ) history.pushState(null, null, `#${config.arx.tab}` ); else history.pushState(null, null, '' ); }, afterRender : function( config, data ) { const tabs = this.instance.closest( 'tabs' ); const tabsConfig = tabs.config; if( config.action ) { this.instance.root.classList.add( 'tabs__item--action' ); this.instance.root.addEventListener( 'click', () => ARUNOX.Router.handle( config.action, {}, data, 'rest' ) ); return; } if( config.type === 'submit' ) { this.instance.root.classList.add( 'tabs__item--action' ); return; } this.instance.root.addEventListener( 'click', () => this.instance.apply('select') ); if( ( tabsConfig.hash && tabsConfig.hash === config.arx.tab ) || ( !tabsConfig.hash && ( config.active || ( config.index === 0 && !tabsConfig.tabs.some( t => t.active ) ) ) ) ) { queueMicrotask( () => this.instance.apply('select') ); } }, }, }); ARUNOX.Templates.register( 'tabs.contents', { htmlTag : 'div', hooks : { config : function( config, data ) { config.classes.push( 'tabs__content', 'scroll__viewport' ); config.arx.tab = config.id; return config; }, }, builder : function( config, data ) { for( const tabConfig of config.tabs ) { if( tabConfig.action || tabConfig.type === 'submit' ) continue; this.render( 'tabs.content', tabConfig ); } }, }); ARUNOX.Templates.register( 'tabs.content', { htmlTag : 'div', hooks : { config : function( config, data ) { config.classes.push( 'tabs__content__item' ); config.arx.tab = config.id; config.styles.display = 'none'; delete config.icon; delete config.label; return config; }, }, builder : function( config, data ) {}, }); /** Datei: plugins/rest/assets/js/components/table.js **/ /** * /arunox/components/table.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'table', { hooks : { config : function( config, data ) { config.classes.push( 'table' ); return config; }, wrapper : function( root ) { const wrapper = ARUNOX.Templates.render( 'div', { classes : 'table__wrapper', }) this.instance.nodes.wrapper = wrapper; this.instance.apply('toolbar'); wrapper.root.append( root ); return wrapper.root; }, toolbar : function() { const config = this.instance.config; const filters = this.instance.filter( 'filters', config.filter, config.columns ); const wrapper = this.instance.nodes.wrapper; const toolbar = wrapper.render( 'div', { nodeName : 'toolbar', classes : 'table__toolbar', }); if( config.search ) { toolbar.render( 'div', { nodeName : 'searchContainer', classes : 'table__search', content : [ config.search ? ARUNOX.Templates.config( 'input', { nodeName : 'searchInput', type : 'text', classes : 'table__search__input', attributes : { placeholder : 'Suchen...', }, onInput : () => this.instance.apply( 'search', toolbar.nodes.searchInput.root.value ), }) : null, ARUNOX.Templates.config( 'button', { nodeName : 'searchReset', color : 'danger', type : 'button', block : 'table__search', title : 'Suche zurücksetzen', icon : 'bi bi-funnel', apply : [ 'active', false ], onClick : () => this.instance.apply( 'reset', 'search', config ), }), ], }); } if( filters ) { toolbar.render( 'buttons', { nodeName : 'filterContainer', classes : 'table__filter', content : [ ...filters, ARUNOX.Templates.config( 'button', { nodeName : 'filterReset', color : 'danger', type : 'button', block : 'table__filter', label : 'Filter zurücksetzen', icon : 'bi bi-funnel', apply : [ 'active', false ], onClick : () => this.instance.apply( 'reset', 'filter', config ), }) ], }); } if( config.buttons ) { const buttons = toolbar.render( 'buttons', { content : config.buttons, nodeName : 'buttons', classes : 'table__buttons', }); } }, filters : function( filters, columns ) { const buttons = []; if( !Array.isArray( filters ) ) filters = [ filters ]; for( const column of filters ) { const config = columns.find( item => item.key == column ); if( !config?.filterable || !config?.options ) continue; for( const option of config.options ) { buttons.push( ARUNOX.Templates.config( 'button', { nodeName : `filter${config.key.toUpperCaseFirst()}-${option.value}`, color : option?.color, type : 'button', block : 'table__filter', label : option?.label, icon : option?.icon, apply : [ 'active', false ], onClick : () => this.instance.apply( 'filter', config.key, option?.value ), }) ); } } return buttons; }, filter : function( column, filterValue ) { // console.log( 'FILTER', column, filterValue ); const toolbar = this.instance.nodes.toolbar; const tableBody = this.instance.nodes.body; const tableRows = tableBody.root.querySelectorAll('tr'); filterValue = filterValue.toString().toLowerCase(); let visibleRows = []; tableRows.forEach(row => { const filterData = row.dataset?.[`arxFilter${column.toUpperCaseFirst()}`] || ''; const isVisible = filterData == filterValue; row.style.display = isVisible ? null : 'none'; if( isVisible ) { visibleRows.push( row ); } }); toolbar.nodes.filterContainer.root.childNodes.forEach( element => { element.ARX.apply( 'active', element.ARX.config.nodeName == `filter${column.toUpperCaseFirst()}-${filterValue}` ); }); toolbar.nodes.filterReset.apply( 'active', visibleRows.length != tableRows.length ); visibleRows.forEach( row => tableBody.root.append( row ) ); }, search : function( searchValue ) { // console.log( 'SEARCH', searchValue ); const toolbar = this.instance.nodes.toolbar; const tableBody = this.instance.nodes.body; const tableRows = tableBody.root.querySelectorAll('tr'); searchValue = searchValue.toLowerCase(); let visibleRows = []; tableRows.forEach(row => { const searchData = row.dataset?.arxSearch || ''; const isVisible = searchData.includes( searchValue ); row.style.display = isVisible ? null : 'none'; if( isVisible ) { visibleRows.push( row ); } }); toolbar.nodes.searchReset.apply( 'active', visibleRows.length != tableRows.length ); visibleRows.forEach( row => tableBody.root.append( row ) ); }, reset : function( config, type ) { // console.log( 'RESET', type ); const toolbar = this.instance.nodes.toolbar; const tableBody = this.instance.nodes.body; const tableRows = tableBody.root.querySelectorAll('tr'); tableRows.forEach(row => { row.style.display = null; }); const query = window.location.hash.match( /\?(.*)/ ); const hash = window.location.hash.substring(1).replace( /\?.*/, '' ); const filter = new URLSearchParams( query ? query[1] : '' ); if( config?.query ) { for( const [ index, value ] of config.query.entries() ) { if( filter.has( index ) && filter.get( index ) == value ) { filter.delete( index ); } } } if( hash && filter?.size ) history.pushState(null, null, `#${hash}?${filter.toString()}`); else if( filter?.size ) history.pushState(null, null, `?${filter.toString()}`); else if( hash ) history.pushState(null, null, `#${hash}`); else history.pushState(null, null, ); toolbar.nodes.filterContainer.root.childNodes.forEach( element => { element.ARX.apply( 'active', false ); }); if( toolbar.nodes.filterReset ) toolbar.nodes.filterReset.apply( 'active', false ); if( toolbar.nodes.searchReset ) toolbar.nodes.searchReset.apply( 'active', false ); if( toolbar.nodes.searchInput ) toolbar.nodes.searchInput.root.value = ''; }, sort : function( column, order ) { // console.log( 'SORT', column, order ); const config = this.instance.config; const thead = this.instance.nodes.header; const tbody = this.instance.nodes.body; const tabelCell = thead.root.querySelector( `[data-arx-column="${column}"]` ); const tableRows = Array.from( tbody.root.querySelectorAll( 'tr' ) ); if( !tabelCell || !tableRows ) return; if( !order ) order = tabelCell.dataset.arxOrder === 'asc' ? 'desc' : 'asc'; const columns = config.columns || []; const rules = config.order.rules || {}; if( rules[ column ]?.replace ) { for( const [ oldCol, newCol ] of Object.entries( rules[ column ].replace ) ) { this.instance.root.querySelectorAll(`td[data-arx-column="${oldCol}"]`).forEach(td => { const rowData = td.closest('tr').rowData; td.innerHTML = rowData[ newCol ] || ''-''; // td.dataset.arxColumn = newCol; // Key anpassen }); } } if( rules[ column ]?.show ) { for( const showCol of rules[ column ].show ) { this.instance.root.querySelectorAll(`td[data-arx-column="${showCol}"], th[data-arx-column="${showCol}"]`).forEach(el => { el.style.display = ''; // Wieder anzeigen }); } } if( rules[ column ]?.hide ) { for( const hideCol of rules[ column ].hide ) { this.instance.root.querySelectorAll(`td[data-arx-column="${hideCol}"], th[data-arx-column="${hideCol}"]`).forEach(el => { el.style.display = 'none'; // Wieder anzeigen }); } } // Entferne vorherige Sortierklassen this.instance.root.querySelectorAll('.table__header__cell').forEach(cell => { cell.classList.remove('sorting--asc', 'sorting--desc', 'sorting--current'); }); // Setze neue Sortierklasse if( tabelCell ) { tabelCell.dataset.arxOrder = order; tabelCell.classList.add( `sorting--${order}`, 'sorting--current' ); } this.instance.root.dataset.arxSortingColumn = column; this.instance.root.dataset.arxSortingOrder = order; // Reihen sortieren tableRows.sort( (rowA, rowB) => { let a = rowA.ARX.data[ column ]?.trim() || ''; let b = rowB.ARX.data[ column ]?.trim() || ''; // Versuche als Zahl zu interpretieren, falls möglich if (!isNaN(a) && !isNaN(b)) { a = parseFloat(a); b = parseFloat(b); } return order === 'asc' ? String(a).localeCompare(String(b)) : String(b).localeCompare(String(a)); }); // Sortierte Reihen neu einfügen tbody.root.innerHTML = ''; tableRows.forEach( row => tbody.root.append( row ) ); }, loaded : function() { const config = this.instance.config; if( config.order && config.order.column ) { this.instance.apply( 'sort', config.order.column, config.order.order ); } }, }, builder : function( config, data ) { this.render( 'thead', { nodeName : 'header', columns : config.columns, }); const body = this.render( 'tbody', { nodeName : 'body', columns : config.columns, apply : [ 'generate', data, true ], }, data ); if( config.api ) { if( !config.api.resolved ) { config.api = ARUNOX.Router.parseRoute( config.api, data ); } ARUNOX.Router.fetch( config.api ).then( data => body.apply( 'generate', data, false ) ); } }, }); ARUNOX.Templates.register( 'thead', { hooks : { config : function( config, data ) { config.classes.push( 'table__header' ); return config; }, }, builder : function( config, data ) { const headerRow = this.render( 'tr', { classes : [ 'table__header__row' ], columns : config.columns, }); } }); ARUNOX.Templates.register( 'tbody', { hooks : { config : function( config, data ) { config.classes.push( 'table__body' ); return config; }, generate : async function( data, loading ) { const table = this.instance.closest( 'table' ); this.instance.root.innerHTML = ''; if( data && data.length ) { for( const rowData of data ) { this.instance.render( 'tr', { classes : [ 'table__body__row' ], columns : this.instance.config.columns, }, rowData ); } table.apply( 'loaded' ); return; } this.instance.render( 'tr', { classes : [ 'table__body__row' ], columns : [ { key : 'hint', attributes : { colSpan : this.instance.config.columns.length, }, innerText : loading ? 'Daten werden geladen ...' : 'Keine Datensätze gefunden', } ], }); }, }, }); ARUNOX.Templates.register( 'tr', { hooks : { config : function( config, data ) { config.arx.filter ??= {}; config.arx.search ??= []; config.classes.push( 'table_row' ); return config; }, afterRender : function( config, data ) { const table = this.instance.closest( 'table' ); this.instance.setUUID( data?.hashid ); if( table.config.action && data ) { this.instance.root.classList.add('cursor--pointer'); this.instance.root.addEventListener('click', async ( event ) => { if( event.target.tagName.toLowerCase() === 'a' ) return; await ARUNOX.Router.handle( table.config.action, {}, data, 'rest' ); }); } }, afterContent : function( config, data ) { this.instance.config.arx.search = this.instance.config.arx.search.join( ' ' ).toLowerCase(); }, update : function( data, status ) { const updatedRow = this.instance.replace( 'tr', { columns : this.instance.config.columns, classes : status ? 'table__body__row--updated' : 'table__body__row--failed', }, data ); setTimeout(() => updatedRow.root.classList.remove( 'table__body__row--updated', 'table__body__row--failed' ), 200 ); }, }, builder : function( config, data ) { const cellTag = this.parent.template == 'thead' ? 'th' : 'td'; for( const column of config.columns ) { this.render( cellTag, column, data ); } }, }); ARUNOX.Templates.register( 'th', { htmlTag : 'th', hooks : { config : function( config, data ) { config.type ??= 'text'; config.innerText ??= config?.label || null; config.arx.column ??= config?.key || null; config.classes.push( 'table__header__cell' ); config.classes.push( `table__header__cell--type-${config.type}` ); config.classes.push( 'table__cell' ); config.classes.push( `table__cell--type-${config.type}` ); if( config.sortable ) { config.classes.push( 'sortable' ); config.arx.column = config.key; config.arx.order = config.order?.order || 'asc'; config.events.click = () => this.instance.apply( 'sort', null ); } if( !config.visible ) { config.styles.display = 'none'; } return config; }, sort : function( order ) { const table = this.instance.closest( 'table' ); const column = this.instance.root.dataset?.arxColumn || null; table.apply( 'sort', column, order ); }, }, builder : function( config, data ) {} }); ARUNOX.Templates.register( 'td', { hooks : { config : function( config, data ) { config.type ??= 'text'; config.arx.column ??= config?.key || null; config.classes.push( 'table__cell' ); config.classes.push( `table__cell--type-${config.type}` ); if( !config.visible ) { config.styles.display = 'none'; } return config; }, data : function( data, config ) { if( data === undefined ) return data; let value = '-'; if( typeof config.output === 'object' ) { let keys = Array.isArray( config.output.keys ) ? config.output.keys : [ config.output.keys ]; let values = []; for( const key of keys ) { values.push( data[ key ] || null ); } if( config.output.format == 'string' ) { value = values.join( config.output.seperator || ' ' ); } else value = values.join(' '); data[ config.key ] = value; } if( data[ config.key ] !== undefined ) { value = data[ config.key ]; } else if( config.key.indexOf('.') > -1 ) { let current = data; for( const key of config.key.split('.') ) { current = current[ key ]; } if( current !== undefined ) { value = current; } } return value; }, beforeRender : function( config, data ) { this.instance.config.innerText ??= data || '-'; if( data !== null && data !== undefined ) { if( config?.filterable ) this.instance.parent.config.arx.filter[ config.key ] = data.toString().toLowerCase(); if( config?.searchable ) this.instance.parent.config.arx.search.push( data.trim() ); } }, afterRender : function( config, data ) { this.instance.apply( `type.${config.type}` ); }, }, }); ARUNOX.Hooks.register( 'td->type.icon', function() { const data = this.instance.data; const config = this.instance.config; const option = config.options[ data ] || {}; this.instance.root.textContent = null; const icon = this.instance.render( 'icon', { name : option?.icon || 'bi bi-question-diamond', color : { target : 'text', prefix : null, color : option?.color || 'default', }, attributes : { title : option?.label || `${config.title} nicht definiert`, }, }); }); ARUNOX.Hooks.register( 'td->type.status', function() { this.instance.apply( 'type.icon' ); const table = this.instance.closest( 'table' ); const config = this.instance.config; const data = this.instance.parent.data; const action = Object.assign( {}, { query : table.config.api?.query || {}, }, config.action ); this.instance.root.classList.add( 'js--click-ownbound' ); this.instance.root.addEventListener( 'click', async event => { event.preventDefault(); event.stopPropagation(); await ARUNOX.Router.handle( action, {}, data, 'rest' ); }); }); /** Datei: plugins/rest/assets/js/components/headline.js **/ /** * /components/headline.js * * Headline-Component mit Icon-Support * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File created */ ARUNOX.Templates.register( 'headline', { htmlTag : function( config, data, ctx ) { return `h${config.size || 2}`; }, builder : function( config, data ) { const className = 'headline'; const block = config.block ? `${className} ${config.block}__${className}` : className; this.root.classList.add( ...block.split(' ') ); // Title als Text-Node if( config.title ) { const textNode = document.createTextNode( config.title ); this.root.appendChild( textNode ); } } }); /** Datei: plugins/rest/assets/js/components/icon.js **/ /** * /arunox/components/icon.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'icon', { htmlTag : 'i', builder : function( config, data ) { const className = 'icon'; const block = config.block ? `${className} ${config.block}__${className}` : className; this.root.classList.add( ...config.name.split(' ') ); this.root.classList.add( ...block.split(' ') ); this.parent.root?.classList.add( 'has--icon' ); }, }); /** Datei: plugins/rest/assets/js/components/form.js **/ /** * rest/arunox/templates/form.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'form', { htmlTag : 'form', hooks : { config : function( config ) { config.attributes.id = crypto.randomUUID(); config.datasets.arx.node = this.instance.htmlTag; config.classes.push( 'form__container' ); return config; }, content : function( content, config, data ) { if( config.buttons ) { content.push( ARUNOX.Templates.config( 'buttons', { classes : [ 'form__buttons' ], content : config.buttons.map( button => { button.config.attributes ??= {}; button.config.attributes.form = this.instance.config.attributes.id; return button; }), }, data ) ); } return content; }, afterRender : function( config, data ) { if( !config.action ) return; let lastSearchParams = ARUNOX.Router.history?.match( /\?(.*)/) | {}; let formSearchParams = config.action.route.match( /\?(.*)/); if( lastSearchParams ) lastSearchParams = new URLSearchParams( lastSearchParams[1] ); if( formSearchParams ) formSearchParams = new URLSearchParams( formSearchParams[1] ); if( lastSearchParams ) { if( lastSearchParams.has('columns') && ( !formSearchParams || !formSearchParams.has('columns') ) ) { if( formSearchParams ) lastSearchParams = '&columns=' + lastSearchParams.get('columns'); else lastSearchParams = '?columns=' + lastSearchParams.get('columns'); config.action.route += lastSearchParams; } } this.instance.noValidate = true; this.instance.actionConfig = ARUNOX.Router.parseRoute( config.action, data, {}, 'rest' ); } }, }); ARUNOX.Templates.register( 'control', { htmlTag : 'div', hooks : { config : function( config ) { config.input = Object.assignRecursive( {}, config || {} ); config.template = `input/${config.type}`; config.datasets.arx.node = 'control'; config.datasets.arx.control = config.type; if( !ARUNOX.Templates.exists( config.template ) ) { if( config.type == 'select' ) config.template = 'select' else if( config.type == 'textarea' ) config.template = 'textarea' else if( config.type == 'datalist' ) config.template = 'datalist' else config.template = 'input'; } config.classes.push( 'form__group', `form__control--${config.type}` ); return config; }, }, builder : function( config, data ) { if( config.label ) { const label = this.render( 'label', { classes : [ 'form__label' ], content : config.label, attributes : { for : `control_${config.key}`, }, }); } const element = this.render( config.template, Object.assignRecursive( { classes : [ 'form__control' ], datasets : { arx : { node : 'input', }, }, }, config.input || {} ), data ); }, }); ARUNOX.Hooks.register( 'input->beforeRender', function( config, data ) { config.attributes = { name : config.key, type : config.type, required : !!config.required, }; }); ARUNOX.Hooks.register( 'select->beforeRender', function( config, data ) { config.content = []; config.attributes = { name : config.key, required : !!config.required, multiple : !!config.multiple, }; // console.log( config, data ); if( config.choose ) { config.content.push( ARUNOX.Templates.config( 'option', { content : config.choose, attributes : { selected : true, }, } ) ); } for( const option of config.options ) { config.content.push( ARUNOX.Templates.config( 'option', { content : option.label, attributes : { value : option.value, selected : data?.[ config.key ] == option.value, }, } ) ); } }); ARUNOX.Hooks.register( 'textarea->beforeRender', function( config, data ) { config.attributes = { name : config.key, required : !!config.required, }; }); if( !window.formSubmitListener ) { document.addEventListener('submit', async event => { if( !event.target.matches('form') ) return; event.preventDefault(); const form = event.target; const inputs = form.querySelectorAll('input, select, textarea'); const promises = []; console.log( inputs ); inputs.forEach(input => { if( typeof input.ARX.validate === 'function' ) { promises.push(new Promise((resolve) => { input.addEventListener('validationComplete', () => { resolve( !!input.closest('.is-valid') ); }, { once: true }); input.dispatchEvent( new Event('validate', { bubbles: true } ) ); })); } }); const results = await Promise.all( promises ); const isValid = results.every( result => result ); if( isValid ) { ARUNOX.Router.handle( form.ARX.actionConfig, new FormData( form ), {}, 'rest' ); } }); window.formSubmitListener = true; } /** Datei: plugins/rest/assets/js/components/notifications.js **/ /** * /arunox/components/notifications.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'notifications', { htmlTag : 'div', hooks : { shouldRebuild : function( shouldRebuild, config, data ) { return document.querySelector('.notification__wrapper'); }, config : function( config, data ) { config.classes.push( 'notification__wrapper', 'notification--show-on-hover', 'notification--stacked' ); return config }, }, addNotification : function( config, data ) { this.render( 'notification', config, data ); } }); ARUNOX.Templates.register( 'notification', { htmlTag : 'div', hooks : { config : function( config, data ) { return { type : config.type, color : config.color || config.type, duration : config.duration, message : config.message || config.title, classes : [ 'notification' ], content : [ config.icon ? ARUNOX.Templates.config( 'icon', { name: config.icon, block: 'notification' }) : null, ARUNOX.Templates.config( 'div', { classes : [ 'notification__inner' ], content : [ config.title ? ARUNOX.Templates.config( 'div', { innerText : config.title, classes : 'notification__title', }) : null, config.message ? ARUNOX.Templates.config( 'div', { innerText : config.message, classes : 'notification__message', }) : null, ], }), ARUNOX.Templates.config( 'button', { classes : [ 'notification__close' ], innerHTML : '×', onClick : () => this.apply( 'close' ), }), ], }; }, close : function() { this.instance.root.classList.add( 'is-closing' ); setTimeout(() => this.instance.root.remove(), 300 ); }, afterRender : function( config, data ) { if( config.duration === 0 ) return; setTimeout(() => this.instance.apply( 'close' ), config.duration || 5000 ); } }, }); /** Datei: plugins/rest/assets/js/hooks/colorize.js **/ /** * rest/arunox/hooks/colorize.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Adaptation to new ctx method * @since 1.0.0 File is created */ ARUNOX.Hooks.register( 'colorize', function( cellColorConfig, optionColorConfig, iconContainer ) { if( typeof cellColorConfig === 'string' ) cellColorConfig = { color: cellColorConfig } if( typeof optionColorConfig === 'string' ) optionColorConfig = { color: optionColorConfig } let colorConfig = Object.assign( { target : 'background', prefix : false, }, optionColorConfig, cellColorConfig ); // console.log( colorConfig ); if( colorConfig.prefix ) { this.instance.root.classList.removeRexEx( /.*--colored-.*/ ); this.instance.root.classList.add( `${colorConfig.prefix}--colored-${colorConfig.color || 'default'}` ); return ; } if( this.instance.root && ['button'].includes( colorConfig.target ) ) { this.instance.root.classList.removeRexEx( /button--colored-.*/ ); this.instance.root.classList.add( `button--colored-${colorConfig.color || 'default'}` ); this.instance.root.style.backgroundColor = colorConfig.value; } if( this.instance.root && ['background'].includes( colorConfig.target ) ) { this.instance.root.classList.removeRexEx( /bg--colored-.*/ ); this.instance.root.classList.add( `bg--colored-${colorConfig.color || 'default'}` ); this.instance.root.style.backgroundColor = colorConfig.value; } if( iconContainer && ['icon','both'].includes( colorConfig.target ) ) { iconContainer.classList.removeRexEx( /text--colored-.*/ ); iconContainer.classList.add( `text--colored-${colorConfig.color || 'default'}` ); iconContainer.style.color = colorConfig.value; } if( this.instance.root && ['text','both'].includes( colorConfig.target ) ) { this.instance.root.classList.removeRexEx( /text--colored-.*/ ); this.instance.root.classList.add( `text--colored-${colorConfig.color || 'default'}` ); this.instance.root.style.color = colorConfig.value; } }); /** Datei: plugins/rest/assets/js/hooks/_general.js **/ /** * /arunox/hooks/_general.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ import '/plugins/rest/assets/js/hooks/colorize.js'; import '/plugins/rest/assets/js/components/icon.js'; ARUNOX.Hooks.register( 'applyConfigAfter', function( element, config ) { if( config.color ) this.instance.apply( 'colorize', typeof config.color === 'object' ? config.color : { color: config.color }, { prefix: 'theme' } ); if( config.icon ) this.instance.render( 'icon', { name: config.icon } ); }); ARUNOX.Hooks.register( 'config', function( config, data ) { config.arx ??= {}; config.events ??= []; config.styles ??= {}; config.classes ??= []; config.datasets ??= {}; config.attributes ??= {}; config.datasets.arx ??= config.arx; if( typeof config.color === 'string' ) config.color = { color: config.color }; if( typeof config.classes === 'string' ) config.classes = config.classes.split(' '); return config; }, 2 ); ARUNOX.Hooks.register( 'config', function( config, data ) { if( config.nodeName ) { config = this.instance.filter( `config.node->${config.nodeName}`, config, data ); } return config; }); /** Datei: plugins/rest/assets/js/core/templates.js **/ /** * /arunox/core/templates.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX ??= {}; ARUNOX.Templates = { _type : 'templates', _componentData : { content : undefined, config : undefined, data : undefined, htmlTag : 'div', template : null, container : null, // DOM-Object root : null, // DOM-Object parent : null, // ARX-Object children : [], // ARX-Object nodes : () => Object.create(null), hooks : () => Object.create(null), keepOnReplace : () => ({ classes : [], // CSS-Klassen die erhalten bleiben sollen attributes : [], // Attribute die erhalten bleiben sollen datasets : [], // Data-Attribute die erhalten bleiben sollen structure : null, // DOM-Struktur die erhalten werden soll }), takeOnReplace : () => ({ classes : [], // CSS-Klassen die übernommen werden sollen attributes : [], // Attribute die übernommen werden sollen datasets : [], // Data-Attribute die übernommen werden sollen }), mounted : false, destroyed : false, }, _componentProto : { apply : function( hook, ...args ) { ARUNOX.Hooks.apply.call( this, hook, ...args ); if( this.extends ) ARUNOX.Hooks.apply.call( this, `${this.extends}->${hook}`, ...args ); ARUNOX.Hooks.apply.call( this, `${this.template}->${hook}`, ...args ); // if( this.nodeName ) console.log( 'apply', `${this.template}->${hook}`, this.nodeName ); }, filter : function( hook, value, ...args ) { value = ARUNOX.Hooks.filter.call( this, hook, value, ...args ); if( this.extends ) value = ARUNOX.Hooks.filter.call( this, `${this.extends}->${hook}`, value, ...args ); // if( this.nodeName ) console.log( 'filter', `${this.template}->${hook}`, this.nodeName ); return ARUNOX.Hooks.filter.call( this, `${this.template}->${hook}`, value, ...args ); }, render : function( templateName, config, data, container ) { return ARUNOX.Templates.render( templateName, config, data, container || this.root, this ); }, replace : function( template, config, data ) { const newCtx = typeof template === 'object' ? template : ARUNOX.Templates.render( template, config, data, null, this.parent ); const newRoot = newCtx && newCtx.root instanceof HTMLElement ? newCtx.root : null; if( !newRoot ) return this.root; const preserveData = this._collectPreservables( this, newCtx ); if( this.mounted && this.root instanceof HTMLElement ) { this.root.replaceWith( newRoot ); } this._applyPreserved( newRoot, preserveData ); this.root = newRoot; this.nodes = newCtx.nodes || Object.create(null); newCtx.mounted = true; return this; }, node : function( nodeName, nodeConfig = {}, placement = 'append', refElement = null ) { if( !this.nodes ) this.nodes = {}; if( !this.nodes[ nodeName ] ) { nodeConfig = typeof nodeConfig === 'object' ? nodeConfig : { textNode: nodeConfig }; nodeConfig = Object.assign( { htmlTag : 'div', datasets : { arx : { node : nodeName, }, }, }, nodeConfig ); nodeConfig = this.filter(`node`, nodeConfig ); nodeConfig = this.filter(`node${nodeName.toUpperCaseFirst()}`, nodeConfig ); refElement = refElement instanceof HTMLElement ? refElement : refElement?.root || null; const nodeCtx = ARUNOX.Templates._createContext( nodeName, nodeConfig, this, this.root, { htmlTag: nodeConfig.htmlTag }); const ctxRender = nodeCtx.render; const ctxReplace = nodeCtx.replace; nodeCtx.render = function( template, config, data, container ) { if( !this.root ) { const tmp = ARUNOX.Templates.render( this.config.htmlTag, this.config, {}, this.parent?.root, this.parent ); this.root = tmp.root; this.nodes = tmp.nodes; this.nodeName = nodeName; this.root.ARX = this; if( placement === 'prepend' ) this.parent.root.prepend( this.root ); else if( placement === 'append' ) this.parent.root.append( this.root ); else if( placement === 'before' && refElement ) refElement.before( this.root ); else if( placement === 'after' && refElement ) refElement.after( this.root ); this.mounted = true; } return ctxRender.call( this, template, config, data, container || this.root ) || this; }; nodeCtx.replace = function( template, config, data ) { if( !this.root ) this.render(); return ctxReplace.call( this, template, config, data ); } nodeCtx.nodeName = nodeName; // console.log(nodeCtx); this.nodes[ nodeName ] = nodeCtx; } return this.nodes[ nodeName ]; }, closest : function( template ) { if( this.template == template || this.extends == template ) { return this; } return this.parent.closest( template ); }, dataset : function( dataset, namespace = 'arx' ) { dataset = typeof dataset === 'string' ? { [dataset] : true } : dataset; for( const [ key, value ] of Object.entries( dataset ) ) { this.root.setAttribute( `data-${namespace}-${key}`, Array.isArray( value ) ? value.join(',') : value ); } }, setUUID : function( hashid, context = '') { this.dataset({ 'uuid': btoa( [ this.template, hashid, context ].filter( Boolean ).join(':') ).replace( /[^a-zA-Z0-9]/g, '' ) }); }, _collectPreservables : function( oldCtx, newCtx ) { const preserveData = { classes : new Set(), attributes : new Map(), datasets : new Map(), structure : null }; if( oldCtx.keepOnReplace ) this._collectPreserveData( oldCtx.root, oldCtx.keepOnReplace, preserveData ); if( newCtx.takeOnReplace ) this._collectPreserveData( oldCtx.root, newCtx.takeOnReplace, preserveData ); return preserveData; }, _collectPreserveData : function( element, rules, preserveData ) { if( !element ) return; // CSS Classes if( rules.classes ) { rules.classes.forEach( pattern => { const matchingClasses = this._matchClasses( element, pattern ); matchingClasses.forEach( cls => preserveData.classes.add( cls ) ); }); } // Attributes if( rules.attributes ) { rules.attributes.forEach( pattern => { const matchingAttrs = this._matchAttributes( element, pattern ); matchingAttrs.forEach( ([name, value]) => { preserveData.attributes.set( name, value ); }); }); } // Datasets if( rules.datasets ) { rules.datasets.forEach( pattern => { const matchingData = this._matchDatasets( element, pattern ); matchingData.forEach( ([name, value]) => { preserveData.datasets.set( name, value ); }); }); } // Structure (komplexerer Fall - erstmal placeholder) if( rules.structure ) { preserveData.structure = rules.structure; } }, _applyPreserved : function( newRoot, preserveData ) { preserveData.classes.forEach( cls => { newRoot.classList.add( cls ); }); preserveData.attributes.forEach( (value, name) => { newRoot.setAttribute( name, value ); }); preserveData.datasets.forEach( (value, name) => { newRoot.setAttribute( `data-${name}`, value ); }); // Structure handling würde hier kommen (komplexer) if( preserveData.structure ) { // TODO: Implementierung für Structure-Preservation console.log( 'Structure preservation not yet implemented:', preserveData.structure ); } }, _matchClasses : function( element, pattern ) { const classes = Array.from( element.classList ); if( pattern.includes('*') ) { // Wildcard matching: "lcars-*" matches "lcars-panel", "lcars-frame", etc. const regex = new RegExp( '^' + pattern.replace(/\*/g, '.*') + '$' ); return classes.filter( cls => regex.test( cls ) ); } else { // Exact match return classes.includes( pattern ) ? [pattern] : []; } }, _matchAttributes : function( element, pattern ) { const attributes = Array.from( element.attributes ); const matching = []; attributes.forEach( attr => { if( pattern.includes('*') ) { const regex = new RegExp( '^' + pattern.replace(/\*/g, '.*') + '$' ); if( regex.test( attr.name ) ) { matching.push( [attr.name, attr.value] ); } } else if( attr.name === pattern ) { matching.push( [attr.name, attr.value] ); } }); return matching; }, _matchDatasets : function( element, pattern ) { const matching = []; // Durchsuche alle data-* Attribute Array.from( element.attributes ).forEach( attr => { if( attr.name.startsWith('data-') ) { const dataName = attr.name.substring(5); // Remove 'data-' prefix if( pattern.includes('*') ) { const regex = new RegExp( '^' + pattern.replace(/\*/g, '.*') + '$' ); if( regex.test( dataName ) ) { matching.push( [dataName, attr.value] ); } } else if( dataName === pattern ) { matching.push( [dataName, attr.value] ); } } }); return matching; }, }, register : function( template, callback, priority ) { return ARUNOX.Registry.register( this._type, template, callback, priority ); }, once : function( template, callback, priority = 10 ) { return ARUNOX.Registry.once( this._type, template, callback, priority ); }, remove : function( template, callback ) { return ARUNOX.Registry.remove( this._type , template, callback ); }, resolve : function( template ) { const mergeHooks = (baseHooks={}, childHooks={}) => { const out = {}; for( const [ key, fn ] of Object.entries( baseHooks ) ) { out[ key ] = Array.isArray( fn ) ? fn : [ fn ]; } for( const [ key, fn ] of Object.entries( childHooks ) ) { const hooks = Array.isArray( fn ) ? fn : [ fn ]; out[ key ] = out[ key ] ? [ ...out[ key ], ...hooks ] : hooks; } return out; }; const extendsSupport = function( childName ) { const base = ARUNOX.Templates.resolve( this.extends ); const merged = { ...base, ...this }; merged.hooks = mergeHooks( base.hooks, this.hooks ); // merged.extends = base; // delete merged.extends; return merged; }; let def = ARUNOX.Registry.resolve( this._type, template, extendsSupport ); if( !def ) { const source = ARUNOX.Loader.findSource(template); if( source ) { if( ARUNOX.Loader.loadSync( source.path ) ) { // Hooks laden (nur registrierte) if( source.source.hooks.has( template ) ) { ARUNOX.Loader.loadSync( source.source.hooks.get( template ) ); } // Plugin-Hooks for( const [ name, plugin ] of ARUNOX.Loader._sources.plugins ) { if( plugin.hooks.has( template ) ) { ARUNOX.Loader.loadSync( plugin.hooks.get( template ) ); } } // Theme-Hooks if( ARUNOX.Loader._sources.theme.hooks.has( template ) ) { ARUNOX.Loader.loadSync( ARUNOX.Loader._sources.theme.hooks.get( template ) ); } def = ARUNOX.Registry.resolve( this._type, template, extendsSupport ); } } } if( !def ) { if( template.match(/\//) ) return false; if( !( document.createElement( template ) instanceof HTMLUnknownElement ) ) { return { htmlTag: template }; } return false } if( typeof def === 'function' ) return { builder: def }; if( typeof def === 'object') { if( !def.htmlTag ) { if( !( document.createElement( template ) instanceof HTMLUnknownElement ) ) { return { ...def, htmlTag: template }; } throw new Error(`[ARUNOX.Templates] Invalid definition for: ${template}`); } if( def.hooks ) { // Normalisiere alle hooks zu Arrays for (const [key, fn] of Object.entries(def.hooks)) { if (!Array.isArray(fn)) { def.hooks[key] = [fn]; } } } return def; } throw new Error(`[Templates] Invalid definition for: ${template}`); }, exists : function( template ) { return ARUNOX.Registry.exists( this._type, template ); }, all : function( template ) { return [ ...ARUNOX.Registry.all( this._type, template ) ].reverse(); }, config : function( template, config, data ) { return { template, config, data }; }, render : function( template, config, data, container, parentCtx ) { if( !template ) return; const definition = this.resolve( template ); if( !definition ) return; config = Object.assign( {}, config ); const ctx = this._createContext( template, config, parentCtx || null, container, definition ); this._registerHooks( template, ctx ); config = ctx.config = ctx.filter( 'config', config, data ); data = ctx.data = ctx.filter( 'data', data, config ); const doRebuild = ctx.filter( 'shouldRebuild', null, config, data ); const doRender = ctx.filter( 'shouldRender', true, config, data ); if( doRebuild ) { const rebuildCtx = doRebuild?.ARX ? doRebuild.ARX : doRebuild; if( rebuildCtx ) { rebuildCtx.apply( 'beforeRebuild', config, data ); this._applyConfig( rebuildCtx, config ); // Config kann sich geändert haben this._renderContent( rebuildCtx, config, data ); // Neuer Content queueMicrotask( ()=> { this._collectNodes( rebuildCtx ); rebuildCtx.apply( 'afterRebuild', config, data ); }); } return rebuildCtx; } if( doRender ) { ctx.apply( 'beforeRender', config, data ); this._buildRoot( definition, ctx, config, data ); this._collectNodes( ctx ); this._renderContent( ctx, config, data ); this._applyConfig( ctx, config ); ctx.root = ctx.filter( 'wrapper', ctx.root ); this._mount( ctx, container ); queueMicrotask( ()=>{ this._collectNodes( ctx ); ctx.apply( 'afterRender', config, data ); }); return ctx; } return ctx; }, _registerHooks : function( template, ctx ) { for( const [ hookKey, hookDef ] of Object.entries( ctx.hooks ) ) { const hookArray = Array.isArray( hookDef ) ? hookDef : [ hookDef ]; hookArray.forEach( def => { // Parse Hook-Definition const hooks = this._parseHookDefinition( hookKey, def, template ); // Registriere alle geparsten Hooks hooks.forEach( hookConfig => { const originalCallback = hookConfig.callback; const wrappedCallback = function( ...args ) { this.owner = ctx; return originalCallback.call( this, ...args ); }; wrappedCallback._originalCallback = originalCallback; if( !ARUNOX.Hooks.exists( `${hookConfig.template}->${hookConfig.hook}`, originalCallback ) ) { if( hookConfig.once ) { ARUNOX.Hooks.once( `${hookConfig.template}->${hookConfig.hook}`, wrappedCallback, hookConfig.priority ); } else { ARUNOX.Hooks.register( `${hookConfig.template}->${hookConfig.hook}`, wrappedCallback, hookConfig.priority ); } } }); }); } }, _parseHookDefinition : function( hookKey, hookDef, currentTemplate ) { // Function → Standard Hook für eigenes Template if( typeof hookDef === 'function' ) { return [{ template : currentTemplate, hook : hookKey, callback : hookDef, priority : 9, once : false }]; } // Object → Vollständige Definition let templates, hookName; // Templates ermitteln if( hookDef.templates ) { templates = hookDef.templates; } else if( hookDef.template ) { templates = [ hookDef.template ]; } else if( hookKey.includes('->') ) { templates = [ hookKey.split('->')[0] ]; } else { templates = [ currentTemplate ]; } // Hook-Name ermitteln if( hookDef.hook ) { hookName = hookDef.hook; } else if( hookKey.includes('->') ) { hookName = hookKey.split('->')[1]; } else { hookName = hookKey; } // Für jedes Template eine Hook-Config erstellen return templates.map( templateName => ({ template : templateName, hook : hookName, callback : hookDef.callback, priority : hookDef.priority || 9, once : hookDef.once || false })); }, _createContext : function( template, config, parent = null, container = null, definition ) { const prototype = Object.create( this._componentProto ); const context = Object.assign( prototype, definition ); for( const key in this._componentData ) { if( typeof context[ key ] !== 'undefined' ) continue; const val = this._componentData[ key ]; context[ key ] = typeof val === 'function' ? val() : val; } if( definition.keepOnReplace || config.keepOnReplace ) { context.keepOnReplace = this._mergeReplaceRules( definition.keepOnReplace || {}, config.keepOnReplace || {} ); } if( definition.takeOnReplace || config.takeOnReplace ) { context.takeOnReplace = this._mergeReplaceRules( definition.takeOnReplace || {}, config.takeOnReplace || {} ); } context.extends = definition.extends; context.template = template; context.parent = parent; context.container = container || null; context.config = config || {}; // console.log( definition ); // console.log( template, context ); return context; }, _mergeReplaceRules : function( templateRules, configRules ) { return { classes: [ ...(templateRules.classes || []), ...(configRules.classes || []) ], attributes: [ ...(templateRules.attributes || []), ...(configRules.attributes || []) ], datasets: [ ...(templateRules.datasets || []), ...(configRules.datasets || []) ], structure: configRules.structure || templateRules.structure || null }; }, _createElement : function( tagResolver, config, data, ctx ) { // Tag ermitteln (String oder Function(config, data, ctx) -> String) const tag = ( typeof tagResolver === 'function' ) ? tagResolver( config, data, ctx ) : ( tagResolver || 'div' ); return document.createElement( String(tag || 'div') ); }, _buildRoot : function( definition, ctx, config, data ) { ctx.apply( 'buildRootBefore' ); // 1) Root vorab erstellen (Definition darf optional `tag` liefern: string | fn(cfg,data,ctx) -> string) if( !ctx.root ) { ctx.root = this._createElement( definition.htmlTag, config, data, ctx ); } // 2) Definition ausführen – nur wenn render existiert (permissive) if( typeof definition.builder === 'function' ) { const out = definition.builder.call( ctx, config, data ); if( out instanceof HTMLElement ) { ctx.root = out; } else if( typeof out === 'string' ) { const tpl = document.createElement('template'); tpl.innerHTML = out.trim(); ctx.root = tpl.content.firstElementChild || ctx.root; } } // 3) Inputs persistieren ctx.config = config; ctx.data = data; ctx.root.ARX = ctx; ctx.apply( 'buildRootAfter' ); return ctx.root; }, _collectNodes : function( ctx ) { ctx.apply( 'collectNodesBefore' ); if( !ctx.root ) return; // console.log( ctx.root.querySelectorAll('[data-arx-node]') ); ctx.root.querySelectorAll('[data-arx-node]').forEach( element => { const nodeName = element.getAttribute('data-arx-node'); if( nodeName && element.ARX && !ctx.nodes[ nodeName ] ) { element.ARX.nodeName = nodeName; ctx.nodes[ nodeName ] = element.ARX; } }); ctx.apply( 'collectNodesAfter' ); }, _applyConfig : function( ctx, config ) { if( !ctx.root || !config ) return; ctx.apply( 'applyConfigBefore', ctx.root, config ); if( config.classes ) { if( typeof config.classes === 'string' ) ctx.root.classList.add( ...config.classes.split(/\s+/).filter( Boolean ) ); else if( Array.isArray( config.classes ) ) ctx.root.classList.add( ...config.classes ); else if( typeof config.classes === 'object' ) { const { add = [], remove = [] } = config.classes; if( add.length ) ctx.root.classList.add( ...add ); if( remove.length ) ctx.root.classList.remove( ...remove ); } } if( typeof config === 'object' ) { for( const key of Object.keys( config ) ) { const match = key.match( /^on([a-z].*)/i ); if( !match ) continue; config.events ??= {}; config.events[ match[1].toLowerCase() ] = config[ key ]; } } if( typeof config.events === 'object' ) { for( const [ name, callback ] of Object.entries( config.events ) ) ctx.root.addEventListener( name, callback ); } if( typeof config.styles === 'object' ) { for (const [k, v] of Object.entries( config.styles )) ctx.root.style[k] = v; } if( typeof config.nodeName === 'string' ) { config.datasets.arx.node = config.nodeName; if( ctx.parent ) { ctx.parent.nodes[ config.nodeName ] = ctx; } } if( typeof config.datasets === 'object' ) { const applyDataset = ( obj, prefix = '' ) => { for( const [ k, v ] of Object.entries(obj)) { const key = prefix ? `${prefix}-${k}` : k; if( v && typeof v === 'object' && !Array.isArray( v ) ) { applyDataset( v, key ); // rekursiv } else { ctx.root.setAttribute( `data-${key}`, String( v ) ); } } }; applyDataset( config.datasets ); } if( typeof config.attributes === 'object' ) { for (const [k, v] of Object.entries( config.attributes )) { if (v === false || v == null) ctx.root.removeAttribute(k); else ctx.root.setAttribute(k, String(v)); } } if( typeof config.apply === 'object' ) ctx.apply( ...config.apply ); if( typeof config.innerHTML === 'string' ) ctx.root.innerHTML = config.innerHTML; if( typeof config.innerText === 'string' ) ctx.root.innerText = config.innerText; ctx.apply( 'applyConfigAfter', ctx.root, config ); }, _renderContent : function( ctx, config, data ) { const content = ctx.filter( 'content', config?.content || [], config, data ); ctx.apply( 'beforeContent', config, data ); if( typeof content === 'string' ) { ctx.root.innerText = content; } else if( Array.isArray( content ) ) { content.filter( Boolean ).forEach( item => { if( item.history ) { if( location.pathname != item.history ) { window.history.pushState({}, "", item.history ); } } ctx.render( item.template || item.generator, item.config, item.data ); }); } else if( typeof content === 'object' ) { if( content.history ) { if( location.pathname != content.history ) { window.history.pushState({}, "", content.history ); } } ctx.render( content.template || content.generator, content.config, content.data ); } ctx.apply( 'afterContent', config, data ); }, _mount : function( ctx, container ) { if( !ctx || !ctx.root ) return ctx; ctx.apply( 'mount' ); // Ziel ermitteln: explizites container-Arg → sonst ctx.container let target = ( container !== undefined ? container : ctx.container ); if( !target ) return ctx; // Selector-String erlauben if( typeof target === 'string' ) target = document.querySelector( target ); // ARX als Container erlauben else if( target && typeof target === 'object' && target.root instanceof HTMLElement ) target = target.root; if( !( target instanceof HTMLElement ) ) return ctx; target.appendChild( ctx.root ); ctx.container = target; ctx.mounted = true; ctx.apply( 'mounted' ); return ctx; }, }; /** Datei: plugins/rest/assets/js/core/utilities.js **/ /** * /arunox/core/utilities.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX ??= {}; ARUNOX.Utils = { device: { get isRotatable() { return window.orientation > -1; }, get isMobile() { return this.isRotatable || screen.availHeight <= 800 || screen.availWidth <= 800; }, get screenSize() { return { width: screen.availWidth, height: screen.availHeight }; } }, // Weitere Utils können hier hinzugefügt werden dom: { ready(callback) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', callback); } else { callback(); } } } }; /** Datei: plugins/rest/assets/js/core/prototypes.js **/ /** * /arunox/core/prototypes.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ Object.defineProperty( DOMTokenList.prototype, 'removeRexEx', { value: function( regex ) { if( !( regex instanceof RegExp ) ) { throw new TypeError('DOMTokenList.removeRegex erwartet ein RegExp-Objekt.'); } const toRemove = [ ...this ].filter( cls => regex.test( cls ) ); if( toRemove.length ) this.remove( ...toRemove ); }, enumerable : false, writable : true, configurable: true }); Object.defineProperty( DOMTokenList.prototype, 'match', { value: function( regex, returnMatchIndex = 1 ) { if( !( regex instanceof RegExp ) ) { throw new TypeError('DOMTokenList.match erwartet ein RegExp-Objekt.'); } return [ ...this ] .map( cls => regex.exec( cls ) ) .filter( Boolean ) .map( m => m[ returnMatchIndex ] ); }, enumerable : false, writable : true, configurable: true }); Object.defineProperty( Object.prototype, 'assignRecursive', { value: function( target, ...sources ) { for( const source of sources ) { for( const [ key, item ] of Object.entries( source ) ) { if( item === null ) { delete target[ key ]; continue; } if( Array.isArray( item ) ) { target[ key ] = [].concat( item, target[ key ] || [] ); continue; } if( typeof item === 'object' && item !== null ) { if( typeof target[ key ] !== 'object' || target[ key ] === null ) { target[ key ] = target[ key ] === undefined ? {} : { [key] : target[ key ] }; } target[ key ] = Object.assignRecursive( target[ key ] || {}, item ); continue; } target[ key ] = item; } } return target; }, enumerable : false, writable : true, configurable: true }); Object.defineProperty( String.prototype, 'toUpperCaseFirst', { value: function() { return String( this ).charAt(0).toUpperCase() + String( this ).slice(1); }, enumerable : false, writable : true, configurable: true }); /** Datei: plugins/rest/assets/js/core/loader.js **/ /** * /arunox/core/loader.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX ??= {}; ARUNOX.Loader = { _loaded : new Set(), _loading : new Map(), _sources : { core : { basePath: '/plugins/rest/assets/js', templates: new Map(), hooks: new Map() }, theme : { basePath: '/themes/lcars/assets/js', templates: new Map(), hooks: new Map() }, plugins : new Map() }, load : async function( path, options = {} ) { const { force = false, cache = true, type = 'module' } = options; if( !force && this._loaded.has( path ) ) return Promise.resolve(); // Already loaded? if( this._loading.has( path ) ) return this._loading.get( path ); // Currently loading? const loadPromise = this._loadModule( path, type, cache ); this._loading.set( path, loadPromise ); try { await loadPromise; this._loaded.add( path ); } catch( error ) { console.error( `Failed to load module: ${path}`, error ); throw error; } finally { this._loading.delete( path ); } return loadPromise; }, loadAll : async function( modules ) { const promises = modules.map( module => { if( typeof module === 'string' ) { return this.load( module ); } return this.load( module.path, module.options || {} ); }); return Promise.all( promises ); }, loadIf : async function( condition, path, options = {} ) { if( typeof condition === 'function' ) { condition = condition(); } if( condition ) return this.load( path, options ); return Promise.resolve(); }, loadSync : function( path ) { if( this._loaded.has( path ) || this._loaded.has(path + ':404' ) ) return this._loaded.has( path ); try { const xhr = new XMLHttpRequest(); xhr.open('GET', path, false); // SYNCHRON! xhr.send(); if( xhr.status === 200 ) { // Extrahiere und entferne import Statements const importRegex = /import\s+['"](.*?)['"]\s*;?/g; const imports = []; let code = xhr.responseText; let match; while ((match = importRegex.exec(code)) !== null) { imports.push(match[1]); } // Entferne imports aus dem Code code = code.replace(importRegex, ''); // Lade alle imports zuerst (rekursiv) imports.forEach(importPath => { this.loadSync(importPath); }); const script = document.createElement('script'); // script.type = 'module'; script.textContent = code; document.head.appendChild( script ); document.head.removeChild( script ); this._loaded.add( path ); return true; } if( xhr.status === 404 ) { // 404 merken, damit wir es nicht nochmal versuchen this._loaded.add( path + ':404' ); return false; } return false; } catch( e ) { return false; } }, isLoaded : function( path ) { return this._loaded.has( path ); }, unload : function( path ) { this._loaded.delete( path ); }, registerSource : function( type, name, config ) { if( type === 'core' ) { this._sources.core.basePath = config.basePath || this._sources.core.basePath; this._sources.core.templates = this._parseTemplates( config.templates, this._sources.core.basePath ); this._sources.core.hooks = this._parseHooks( config.hooks, this._sources.core.basePath ); } else if( type === 'theme' ) { this._sources.theme.basePath = config.basePath || this._sources.theme.basePath; this._sources.theme.templates = this._parseTemplates( config.templates, this._sources.theme.basePath ); this._sources.theme.hooks = this._parseHooks( config.hooks, this._sources.theme.basePath ); } else if( type === 'plugin' ) { this._sources.plugins.set( name, { basePath : config.basePath, templates : this._parseTemplates( config.templates, config.basePath ), hooks : this._parseHooks( config.hooks, config.basePath ) }); } }, findSource : function( template ) { // Theme (höchste Priorität) if( this._sources.theme.templates.has( template ) ) { return { type : 'theme', path : this._sources.theme.templates.get( template ), source : this._sources.theme }; } // Plugins for( const [ name, plugin ] of this._sources.plugins ) { if( plugin.templates.has( template ) ) { return { type : 'plugin', name, path : plugin.templates.get( template ), source : plugin }; } } // Core (Fallback) if( this._sources.core.templates.has( template ) ) { return { type : 'core', path : this._sources.core.templates.get( template), source : this._sources.core }; } return null; }, _loadModule : function( path, type, cache ) { return new Promise(( resolve, reject ) => { const script = document.createElement('script'); script.onload = () => { document.head.removeChild( script ); resolve(); }; script.onerror = () => { document.head.removeChild( script ); reject( new Error( `Failed to load script: ${path}` ) ); }; if( type === 'module' ) { script.type = 'module'; } if( !cache ) { const url = new URL( path, window.location.origin ); url.searchParams.set( '_t', Date.now() ); script.src = url.toString(); } else { script.src = path; } document.head.appendChild( script ); }); }, _parseTemplates : function( templates, basePath ) { if( Array.isArray( templates ) ) { // Einfache Liste → nutzt basePath const map = new Map(); templates.forEach( t => map.set( t, `${basePath}/components/${t}.js` ) ); return map; } else if( typeof templates === 'object' ) { // Explizite Pfade return new Map( Object.entries( templates ) ); } return new Map(); }, _parseHooks : function( hooks, basePath ) { if( Array.isArray( hooks ) ) { const map = new Map(); hooks.forEach( h => map.set( h, `${basePath}/hooks/${h}.js` ) ); return map; } else if( typeof hooks === 'object' ) { return new Map( Object.entries( hooks ) ); } return new Map(); }, }; /** Datei: plugins/rest/assets/js/core/hooks.js **/ /** * /arunox/core/hooks.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX = window.ARUNOX || {}; ARUNOX.Hooks = { _type : 'hooks', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority ); }, once : function( name, callback, priority = 10 ) { return ARUNOX.Registry.once( this._type, name, callback, priority ); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type , name, callback ); }, exists : function( name, callback ) { return ARUNOX.Registry.exists( this._type, name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, all : function( name ) { return [ ...ARUNOX.Registry.all( this._type, name ) ].reverse(); }, filter : function( name, value, ...args ) { let hookNames = Array.isArray( name ) ? name : name.split(' '); let callbacks = hookNames.map( n => ARUNOX.Hooks.all( n ) ).flat(2).filter(Boolean); let stopped = false; let context = { stop : function() { stopped = true; }, instance : this }; // console.log( hookNames ); if( this.hooks?.hooks ) { callbacks = callbacks.concat( hookNames.map( n => this.hooks.hooks[ n ] ).filter(Boolean).flat() ); } // if( callbacks.length ) console.log( callbacks, context.instance ); for( const fn of callbacks ) { if( stopped ) break; if( fn.ARX?.once && fn.ARX?.done ) continue; value = fn.call( context, value, ...args ); if( fn.ARX?.once ) fn.ARX.done = true; } return value; }, apply : function( name, ...args ) { let hookNames = Array.isArray( name ) ? name : name.split(' '); let callbacks = hookNames.map( n => ARUNOX.Hooks.all( n ) ).flat(2).filter(Boolean); let stopped = false; let context = { stop : function() { stopped = true; }, instance : this }; // console.log( hookNames ); if( this.hooks?.hooks ) { callbacks = callbacks.concat( hookNames.map( n => this.hooks.hooks[ n ] ).filter(Boolean).flat() ); } for( const fn of callbacks ) { if( stopped ) break; if( fn.ARX?.once && fn.ARX?.done ) continue; fn.call( context, ...args ); if( fn.ARX?.once ) fn.ARX.done = true; } }, Instance : class { constructor( owner, hooks ) { this.owner = owner; this.hooks = hooks || {}; } _hooks( name ) { return [...new Set([ name, this.owner.constructor?.extends ? `${this.owner.constructor.extends.type}->${name}` : null, `${this.owner.type}->${name}`, this.owner.constructor?.extends ? `${this.owner.constructor.extends.hook}->${name}` : null, `${this.owner.hook}->${name}`, ].filter(Boolean))]; } register( name, callback ) { this.hooks[ name ] ??= []; this.hooks[ name ].push( callback ); return this; } exists( name ) { return ARUNOX.Hooks.exists( name ); } filter( name, value, ...args ) { return ARUNOX.Hooks.filter.call( this.owner, this._hooks( name ), value, ...args ); } apply( name, ...args ) { ARUNOX.Hooks.apply.call( this.owner, this._hooks( name ), ...args ); } } }; /** Datei: plugins/rest/assets/js/core/router.js **/ /** * /arunox/core/router.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX = ARUNOX || {}; ARUNOX.Router = new ( class { constructor() { this._routes = {}; this._cache = new Map(); this._lastCacheKey = null; this._isReloaded = false; this._CACHE_TIMEOUT = 500; } async handle( config, sendData = {}, parseData = {}, mode = 'data' ) { const debug = false; if( !config ) { console.warn("⚠️ Ungültige Konfiguration für handleAction()."); return; } if( !config.resolved ) { config = this.parseRoute( config, parseData, true, mode ); } if( debug ) console.groupCollapsed( config.resolved ); // console.log( config ); let response = { click : [], route : [], dialog : [], updater : [], wrapper : [], template : [], generator : [], javascript : [], }; for( const item of await this.fetch( config, sendData, {}, mode ) ) { response[ item.type ].push( item ); } // console.log( config, response ); ARUNOX.Router.history = config.history; if( response.dialog ) { for( const item of response.dialog ) { let dialog = null; if( item.name === '#ALL' ) { document.querySelectorAll('dialog').forEach( dialog => dialog.closeDialog() ); continue; } if( dialog = document.querySelector(`dialog[data-id="${item.name}"]`) ) { dialog.closeDialog(); continue; } if( dialog = document.querySelector('dialog') ) { dialog.closeDialog(); } } } if( response.route.length ) { for( const item of response.route ) { await ARUNOX.Router.handle( item, {}, {}, 'rest' ); } ARUNOX.Router.history = config.history; } if( response.template.length && !response.route.length ) { document.body.innerHTML = ''; ARUNOX.Router.template = ARUNOX.Templates.render( response.template[0].template, response.template[0], Object.assign( {}, config ), document.body ); } // if( response.template.length && !response.route.length ) { // document.body.innerHTML = ''; // template = ARUNOX.Theme.template.render( response.template[0].template, Object.assign( {}, config ), response.template[0], document.body ); // } if( response.wrapper.length && !response.route.length ) { for( const item of response.wrapper ) { ARUNOX.Router.template.nodes.content.render( item.generator, item.config, item.list || item.data || {} ); if( item.history ) { if( location.pathname != item.history ) { window.history.pushState({}, "", item.history ); } } } } // if( response.generator.length && !response.route.length ) { // for( const item of response.generator ) { // ARUNOX.Theme.generator.render( item.generator, config, item, template ); // } // } // if( response.updater.length && !response.route.length ) { for( const item of response.updater ) { document.querySelectorAll(`[data-arx-uuid="${item.updater}"]`).forEach( element => element.ARX.apply( 'update', item.data, item.status ) ); } } // // if( response.click.length ) { // for( const item of response.click ) { // const element = document.querySelector(`[data-uuid="${item.parent}"] ${item.selector ?? ''}` ); // // element.click(); // } // } if( response.javascript.length ) { for( const item of response.javascript ) { eval( item.script ); } } if( debug ) console.groupEnd(); ARUNOX.Router.history = config.history; } async routes() { if( !this._isLoaded() ) { await this._loadList(); } return this._routes; } async fetch(config, params = {}, options = {}, mode = 'data') { try { let cached = this.getCache(!config, {}, mode); if (cached !== false) return cached; cached = this.getCache(config, params, mode); if (cached !== false) return cached; if (!config.resolved) config = this.parseRoute(config, params, true, mode); if (!config.resolved) throw new Error(`❌ Fehlerhafte API-URL: ${config.resloved.data}`); const cacheKey = `${config.method}:${config.cachekey}`; const defaultOptions = { method: config.method, headers: {}, ...options }; if (config.method === "GET" || config.method === "DELETE") { const queryString = new URLSearchParams(params).toString(); config.resolved += queryString ? `?${queryString}` : ""; } else { if (params instanceof FormData) { defaultOptions.body = params; } else { defaultOptions.body = JSON.stringify(params); defaultOptions.headers["Content-Type"] = "application/json"; } } const response = await fetch(config.resolved, defaultOptions); const data = await response.json(); const fullcache = data.caching || false; delete data.caching; this._cache.set(cacheKey, { response: data, fullcache: false, timestamp: Date.now() }); this._lastCacheKey = cacheKey; return data; } catch (error) { console.error('❌ Fehler beim Abrufen der API-Daten:', error); return null; } } async fetchData(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.data; } async fetchList(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.list; } async fetchConfig(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.config; } async fetchGenerator(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.generator; } async fetchStream(config, params = {}, options = {}, onChunk) { try { if (!config.resolved) config = this.parseRoute(config, params, true); if (!config.resolved) throw new Error(`❌ Fehlerhafte API-URL: ${config.resloved.data}`); const defaultOptions = { method: config.method, headers: {}, ...options }; const response = await fetch(config.resolved, defaultOptions); if (!response.body) return; const decoder = new TextDecoder(); const reader = response.body.getReader(); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; if (onChunk) { buffer += decoder.decode(value, { stream: true }); let lines = buffer.split('\n'); buffer = lines.pop(); for (const line of lines) { if (!line.trim()) continue; try { onChunk(JSON.parse(line)); } catch { onChunk(line); } } } } if (onChunk && buffer.trim()) { try { onChunk(JSON.parse(buffer)); } catch { onChunk(buffer); } } } catch (error) { console.error('❌ Fehler beim Abrufen des API-Stream:', error); return null; } } async find(path, includeHidden = false) { await this._loadList(); return this._routes.find(route => { if (!includeHidden && route.visibility === "hidden") return false; const routePattern = new RegExp(`^${route.path.replace(/{\w+}/g, "([^/]+)")}$`); return routePattern.test(path); }); } getCache(config, params = {}, mode = 'data') { if (config === true && this._lastCacheKey) { const cached = this._cache.get(this._lastCacheKey); if (cached && Date.now() - cached.timestamp < this._CACHE_TIMEOUT) { cached.timestamp = Date.now(); return cached.response; } return null; } if (typeof config === 'object' || typeof config === 'string') { if (!config.resolved) config = this.parseRoute(config, params, {}, mode); const cacheKey = `${config.method}:${config.cachekey}`; const cached = this._cache.get(cacheKey); if (cached && Date.now() - cached.timestamp < this._CACHE_TIMEOUT) { this._lastCacheKey = cacheKey; return cached.response; } if (cached && cached.fullcache && location.pathname == config.cachekey) { return cached.response; } } return false; } getBreadcrumbSegments(path) { let segments = path.split('/').filter(Boolean); let breadcrumbs = []; let currentPath = ""; if (!segments.length) { segments = ['']; } segments.forEach(segment => { currentPath += `/${segment}`; const route = this._routes.find(r => r.path && new RegExp(`^${r.path.replace(/{\w+}/g, "([^/]+)")}$`).test(currentPath)); const group = this._routes.find(r => r.name && (new RegExp(`^${r.name.replace(/{\w+}/g, "([^/]+)")}$`).test(currentPath) || r.name == segment)); if (route) { breadcrumbs.push({ label: route.breadcrumb || route.label || segment, path: currentPath, }); } else if (group) { breadcrumbs.push({ label: group.breadcrumb || group.label || segment, path: false, }); } }); return breadcrumbs; } getDataRoute(config, params = {}, queryIncludes = true) { config = this.parseRoute(config ?? location.pathname, params, queryIncludes, 'data'); return config.resolved; } parseRoute( config, params = {}, queryIncludes = true, mode = 'data' ) { let route = typeof config === "string" ? config : config.route; let query = new URLSearchParams( config?.query || [] ); let connector = /\?/.test( route ) ? '&' : '?'; let resolved = null; let cachekey = null; if( query.size ) route += connector + query; if( !queryIncludes ) route = route.replace(/\?.*/, ''); switch (mode) { case 'rest': resolved = this._resolveApiUrl(route, params, '/rest'); cachekey = this._resolveApiUrl(route, params, '/rest', false); break; case 'data': resolved = this._resolveApiUrl(route, params, true); cachekey = this._resolveApiUrl(route, params, false, false); break; } return { route : route, resolved: resolved, cachekey: cachekey, history : this._resolveApiUrl(route, params, false), method : config?.method || "GET", query : config?.query || {}, }; } parseUrl(route, params) { const missingKeys = []; const resolvedUrl = route.replace(/{(\w+)}/g, (_, key) => { if (params[key] === 'undefined') { missingKeys.push(key); return `{${key}}`; } return params[key]; }).replace(/\/$/, ''); if (missingKeys.length > 0) { console.error(`Nicht ersetzte Platzhalter: ${missingKeys.join(', ')}`, `URL: ${route}`); return null; } return resolvedUrl; } _isLoaded() { return !!Object.entries( this._routes ).length; } async _loadList() { this._routes = await this.fetchList("/rest/routes"); } _resolveApiUrl(route, params = {}, prefix = true, hash = true) { const API_PREFIX = '/api/v1'; if (typeof prefix === 'string') { route = this._stripApiPrefix(route, API_PREFIX); route = route.startsWith(prefix) ? route : `${prefix}${route}`; } const mergedParams = params; const prefixedUrl = route.startsWith(API_PREFIX) ? route : `${API_PREFIX}${route}`; const resolvedUrl = this.parseUrl(prefixedUrl, params); if (!hash) { const withoutHash = resolvedUrl.replace(/#.*/, ''); return prefix ? withoutHash : this._stripApiPrefix(withoutHash, API_PREFIX); } return prefix ? resolvedUrl : this._stripApiPrefix(resolvedUrl, API_PREFIX); } _stripApiPrefix(route, api_prefix = '/api/v1') { if (route.startsWith(api_prefix)) { return route.replace(api_prefix, ''); } return route; } })(); ARUNOX.Router.history = null; /** Datei: plugins/rest/assets/js/core/registry.js **/ /** * /arunox/core/regestry.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX = window.ARUNOX || {}; ARUNOX.Registry = { _store : {}, register : function( type, name, callback, priority = 10, once = false ) { if( !this._store[ type ] ) this._store[ type ] = {}; if( !this._store[ type ][ name ]) this._store[ type ][ name ] = []; // callback.name = name; callback.once = once; callback.priority = priority; this._store[ type ][ name ].unshift( callback ); this._store[ type ][ name ].sort( ( a, b ) => b.priority - a.priority ); }, once : function( type, name, callback, priority = 10 ) { return this.register( type, name, callback, priority, true ); }, remove : function( type, name, callback ) { const arr = this._store[ type ]?.[ name ]; if (!arr) return; this._store[type][name] = arr.filter(e => e !== callback); }, resolve : function( type, name, extendsSupportCallback ) { const resolved = this._store[ type ]?.[ name ]?.[0]; if( !resolved ) return null; if( resolved?.extends && typeof resolved?.extends === 'string' ) { return extendsSupportCallback.call( resolved, name ); } return resolved; }, exists : function( type, name, callback, originCallback ) { if( this._store[ type ]?.[ name ] ) { return !!( this._store[ type ]?.[ name ]?.filter( item => item == callback || item._originalCallback == callback ).length ); } return false; }, all : function( type, name ) { if( !type ) return this._store; if( !name ) return Object.keys(this._store[ type ] || {} ); return this._store[ type ]?.[ name ] || []; }, template : { _type : 'template', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type, name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Registry.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Registry.all( this._type, name ); }, }, wrapper : { _type : 'wrapper', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type, name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Registry.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Registry.all( this._type, name ); }, }, generator : { _type : 'generator', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type, name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Registry.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Registry.all( this._type, name ); }, }, htmlTag : { _type : 'htmltag', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type, name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Registry.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Registry.all( this._type, name ); }, }, _mergeHooks : function( baseHooks = {}, childHooks = {} ) { const merged = {...baseHooks}; for (const [name, hookFn] of Object.entries(childHooks)) { if (name.startsWith('!')) { // explizites Überschreiben: "!config" const realName = name.slice(1); merged[realName] = hookFn; } else { // beide behalten → Liste draus machen if (merged[name]) { merged[name] = [].concat(merged[name], hookFn); } else { merged[name] = hookFn; } } } return merged; } }; /** Datei: plugins/rest/templates/index.php **/ name ); ?> <?php echo ARUNOX\Core\Hooks::filter("template::title", "ARUNOX FRAMEWORK"); ?> before" ); ?> after" ); ?> before" ); ?> after" ); ?> /** Datei: plugins/rest/assets.OLD/scss/colors/_variables.scss **/ $theme-bg-blur : 5px !default; $theme-color-red : #ee1501 !default; $theme-color-orange : #ee6c01 !default; $theme-color-yellow : #eece01 !default; $theme-color-green : #01ee21 !default; $theme-color-cyan : #01c3ee !default; $theme-color-blue : #014cee !default; $theme-color-pink : #ee01e2 !default; $theme-color-purple : #8301ee !default; $theme-color-white : #ffffff !default; $theme-color-light : #bebebe !default; $theme-color-medium : #818181 !default; $theme-color-dark : #595959 !default; $theme-color-black : #000000 !default; $theme-colors : ( 'background': $theme-color-white, 'color' : $theme-color-black, 'content' : $theme-color-white, 'default' : $theme-color-medium, 'inverted' : $theme-color-light, 'primary' : $theme-color-cyan, 'secondary' : $theme-color-yellow, 'tertiary' : $theme-color-pink, 'success' : $theme-color-green, 'danger' : $theme-color-red, 'warning' : $theme-color-yellow, 'red' : $theme-color-red, 'orange' : $theme-color-orange, 'yellow' : $theme-color-yellow, 'green' : $theme-color-green, 'cyan' : $theme-color-cyan, 'blue' : $theme-color-blue, 'pink' : $theme-color-pink, 'purple' : $theme-color-purple, 'white' : $theme-color-white, 'light' : $theme-color-light, 'medium' : $theme-color-medium, 'dark' : $theme-color-dark, 'black' : $theme-color-black, ) !default; $theme-colorize-alpha : .3 !default; $theme-colorize-bg-alpha : .4 !default; $theme-colorize-border-alpha : .5 !default; $theme-colorize-active-alpha : .1 !default; $theme-colorize-hover-alpha : .2 !default; $theme-colorize-light : $theme-color-white !default; $theme-colorize-dark : $theme-color-black !default; $theme-brightness: ( 50 : 5%, 100 : 0%, 200 : -2.5%, 300 : -5%, 400 : -7.5%, 500 : -10%, ) !default; /** Datei: plugins/rest/assets.OLD/scss/colors/_functions.scss **/ @function get_color( $color, $alpha : 1, $brightness : 0 ) { @if type-of( $color ) != color { $color : map-get( $theme-colors, $color ); } @if( $brightness < 0 ) { $color : darken( $color, $brightness * -1); } @if( $brightness > 0 ) { $color : lighten( $color, $brightness ); } @if( $alpha != 1 ) { $color : rgba( red( $color ), green( $color ), blue( $color ), $alpha ); } @return $color } @function get_color_hover( $color, $alpha : 1 ) { @return get_color( $color, $alpha, 5% ); } @function get_color_active( $color, $alpha : 1 ) { @return get_color( $color, $alpha, -5% ); } @function get_color_tone( $color, $level : 100, $tint: null, $tint-strength: 15% ) { @if( $level > 0 ) { $base: get_color( $color, 1, get_brightness( $level ) ); } @else { $base: get_color( $color, 1, get_brightness( $level * -1 ) * -1 ); } @if $tint == null { @return $base; } $accent: get_color( $tint ); @return mix( $accent, $base, $tint-strength ); } @function get_brightness( $level : 100 ) { @return map-get( $theme-brightness, $level ); } @function contrast( $color, $color1, $color2, $contrast-threshold : 55% ) { @if type-of( $color ) == color and type-of( $color1 ) == color and type-of( $color2 ) == color { $light : if( lightness( $color1 ) > lightness( $color2 ), $color1, $color2 ); $dark : if( lightness( $color1 ) > lightness( $color2 ), $color2, $color1 ); @return if( lightness( $color ) < $contrast-threshold, $light, $dark ); } @else { @return $color1; } } /** Datei: plugins/rest/assets.OLD/scss/colors/_styles.scss **/ @import "variables.scss"; @import "mixins.scss"; @import "functions.scss"; @mixin colorize_btn_all() { @each $name, $color in $theme-colors { &.colored-#{$name} { @include colorize_btn( $name ); } } } @mixin colorize_btn( $color: inverted ) { @include colorize_this( $color ); &.focus { @include colorize_this( $color, $theme-active-opacity ); } &.active { @include colorize_this( $color, $theme-active-opacity ); } &.current { @include colorize_this( $color, $theme-active-opacity ); } &:focus { @include colorize_this( $color, $theme-hover-opacity ); } &:hover { @include colorize_this( $color, $theme-hover-opacity ); } } @each $name, $color in $theme-colors { .text--colored-#{$name} { color: #{get_color( $color )}; } .theme--colored-#{$name} { @include colorize( $color ); } } /** Datei: plugins/rest/assets.OLD/scss/colors/_mixins.scss **/ @mixin colorize( $color, $light: $theme-colorize-light, $dark: $theme-colorize-dark ) { // @debug 'theme-colorize-alpha: #{$theme-colorize-alpha}'; // @debug 'theme-colorize-bg-alpha: #{$theme-colorize-bg-alpha}'; // @debug 'theme-colorize-border-alpha: #{$theme-colorize-border-alpha}'; // @debug 'theme-colorize-hover-alpha: #{$theme-colorize-hover-alpha}'; // @debug 'theme-colorize-active-alpha: #{$theme-colorize-active-alpha}'; $normal : get_colorize_bg( $color ); $hover : get_colorize_bg( $color, $theme-colorize-hover-alpha ); $active : get_colorize_bg( $color, $theme-colorize-active-alpha ); --color-background: #{$normal}; --color-background-hover: #{$hover}; --color-background-active: #{$active}; --color-border: #{get_colorize_border( $color )}; --color-border-hover: #{get_colorize_border( $color, $theme-colorize-hover-alpha )}; --color-border-active: #{get_colorize_border( $color, $theme-colorize-active-alpha )}; --color-text: #{get_colorize_text( $normal, $light, $dark )}; --color-text-hover: #{get_colorize_text( $hover, $light, $dark )}; --color-text-active: #{get_colorize_text( $active, $light, $dark )}; } @function get_colorize_bg( $color, $alpha: 0 ) { @return get_color( $color, $theme-colorize-alpha + $theme-colorize-bg-alpha + $alpha ); } @function get_colorize_border( $color, $alpha: 0 ) { @return get_color( $color, #{$theme-colorize-alpha + $theme-colorize-border-alpha + $alpha } ); } @function get_colorize_text( $color, $light: $theme-colorize-light, $dark: $theme-colorize-dark ) { @return contrast( $color, $light, $dark ); } /** Datei: plugins/rest/assets.OLD/scss/_functions.scss **/ /** Datei: plugins/rest/assets.OLD/scss/_mixins.scss **/ @mixin transition( $property: all, $duration: 200ms, $timing-function: ease-in-out, $behavior: normal, $delay: 0 ) { transition-property: $property; transition-duration: $duration; transition-timing-function: $timing-function; transition-behavior: $behavior; transition-delay: $delay; } /** Datei: plugins/rest/assets.OLD/scss/wrapper/_dialog.scss **/ // ===================================== // Dialog-Styles // ===================================== .dialog { $dialog: & !global; position: absolute; width: 100%; height: 100%; border: none; z-index: 1000; transition: opacity 300ms ease, transform 0.3s ease; backdrop-filter: blur(3px); opacity: 0; overflow: hidden; padding: 0; top: 0; left: 0; display: flex; justify-content: center; align-items: center; &.is- { &open { transform: scale(1); opacity: 1; } &closing { transform: scale(0.95); opacity: 0; } } &__{ &wrapper { $dialog-wrapper: & !global; width: 90%; background-color: var(--color-background, #fff); border-radius: 8px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); max-width: 600px; max-height: calc( 100vh - 6rem ); z-index: 1; padding: 2rem; } &close { $dialog-close: & !global; position: absolute; top: 0rem; right: 0rem; background: transparent; border: none; font-size: 1.25rem; cursor: pointer; color: var(--color-text, #666); transition: color 0.2s ease; outline: 0; padding: 0; line-height: 1em; &:hover { color: var(--color-danger, #d00); } } &header { $dialog-header: & !global; flex-shrink: 0; display: flex; justify-content: space-between; align-items: center; padding: 0 0 1rem 0; border-bottom: 1px solid #eee; background: var(--color-background, #fff); position: sticky; top: 0; z-index: 2; } &content { $dialog-content: & !global; flex-grow: 1; overflow-y: auto; padding: 0 1.5rem; margin: 0 -1.5rem; &:has(.tabs__content) { overflow: visible; padding: 0; margin: 0; .tabs__content { max-height: calc( 100vh - 6rem - var( --maxHeight ) - 1.5rem + 10px ); overflow-y: auto; border: 0; margin-inline: -1.5rem; padding-inline: 1.5rem; padding-bottom: 1.5rem; padding-block: 5px; margin-blok: -5px; } } } &backdrop { $dialog-backdrop: & !global; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(3px); opacity: 0; transition: opacity 300ms ease-in-out; z-index: 0; } } &-- { &sm { #{$dialog-wrapper} { max-width: 400px; } } &md { #{$dialog-wrapper} { max-width: 600px; // Standard } } &lg { #{$dialog-wrapper} { max-width: 800px; } } &xl { #{$dialog-wrapper} { max-width: 1200px; } } &full { #{$dialog-wrapper} { width: 100%; height: 100%; max-width: none; max-height: none; border-radius: 0; } } &max { #{$dialog-wrapper} { margin-top: 3rem; height: 100%; .scroll__viewport { height : 100vh; } } } &equal { #{$dialog-wrapper} { .scroll__viewport { height : var( --height ); } } } &top { #{$dialog-wrapper} { margin-top: 3rem; } } } } /** Datei: plugins/rest/assets.OLD/scss/layout/_table.scss **/ @import "../mixins.scss"; .cursor--pointer { cursor : pointer; }; .table { $table-padding : 5px; $table-gap-small : 10px !default; $table-gap-default : 15px !default; $table-gap-wide : 20px !default; $table-font-family : sans-serif !default; $table-border-radius : 0 !default; $table-border-spacing : 0 !default; $table-header-font-family : $table-font-family !default; $table-header-border-width : 0 1px 1px 0 !default; $table-header-bg-color : get_color_tone( content, 400 ) !default; $table-header-bg-color-hover : get_color_tone( content, 500 ) !default; $table-header-bg-color-active : get_color_tone( content, 400, primary ) !default; $table-header-bg-color-active-hover : get_color_tone( content, 500, primary ) !default; $table-header-border-color : get_color_tone( border, 400 ) !default; $table-header-border-color-hover : $table-header-border-color !default; $table-header-border-color-active : get_color_tone( content, 500, primary ) !default; $table-header-border-color-active-hover : $table-header-border-color-active !default; $table-cell-font-family : $table-header-font-family !default; $table-cell-border-width : $table-header-border-width !default; $table-cell-border-color : get_color( border ) !default; $table-cell-bg-color-hover : $table-header-bg-color-hover !default; $table-cell-border-color-hover : $table-header-border-color !default; $table-row-bg-color : get_color_tone( content, 100 ) !default; $table-row-bg-color-even : get_color_tone( content, 200 ) !default; $table-row-bg-color-hover : get_color_tone( content, 300, primary ) !default; $table-row-bg-color-hover-even : get_color_tone( content, 400, primary ) !default; $table-row-bg-color-active-hover : get_color_tone( content, 300, secondary ) !default; $table-row-bg-color-active-hover-even : get_color_tone( content, 400, secondary ) !default; $table-row-bg-color-success-even : get_color_tone( content, 200, success ) !default; $table-row-bg-color-success-hover-even : get_color_tone( content, 300, success ) !default; $table-row-bg-color-success-active-hover-even : get_color_tone( content, 400, success ) !default; $table-row-bg-color-danger-even : get_color_tone( content, 200, danger ) !default; $table-row-bg-color-danger-hover-even : get_color_tone( content, 300, danger ) !default; $table-row-bg-color-danger-active-hover-even : get_color_tone( content, 400, danger ) !default; width: 100%; font-family: #{$table-font-family}; @if( $table-border-spacing == 0 ) { border-collapse: collapse; border: 1px solid #{get_color( border )}; } @else { border-spacing: #{$table-border-spacing}; border-collapse: separate; } &__ { &container { display: flex; flex-direction: column; gap: #{$table-gap-wide}; grid-column: 1/-1; } &toolbar { display: grid; grid-template-areas: 'search buttons'; grid-template-rows: 2rem; gap: #{$table-border-spacing}; } &search { flex: 1; display: flex; gap: #{$table-border-spacing}; grid-area: search; &__input { max-width: 250px; padding: 0.35rem; border: 1px solid #{get_color( border )}; border-radius: #{$table-border-radius}; font-size: 0.9rem; min-height: 2rem; outline: none; &:hover { border-color: #{get_color_hover(primary)}; box-shadow: 0 0 5px #{get_color(primary, 0.5)}; outline: none; } &:focus { border-color: #{get_color(primary)}; box-shadow: 0 0 5px #{get_color(primary, 0.5)}; outline: none; } } } &buttons { display: flex; gap: #{$table-border-spacing}; grid-area: buttons; justify-content: end; height: 100%; } &body { &__ { &row { @include transition( 'background-color' ); &:nth-child(odd) { background-color: #{$table-row-bg-color}; &.js--click-bound:hover { background-color: #{$table-row-bg-color-hover}; } .js--click-ownbound:hover { background-color: #{$table-row-bg-color-active-hover}; } } &:nth-child(even) { background-color: #{$table-row-bg-color-even}; &.js--click-bound:hover { background-color: #{$table-row-bg-color-hover-even}; } .js--click-ownbound:hover { background-color: #{$table-row-bg-color-active-hover-even}; } } &--failed { background-color: #{get_color_tone(content, 100, danger)} !important; &.js--click-bound:hover { background-color: #{get_color_tone(content, 200, danger)} !important; } &.js--click-ownbound:hover { background-color: #{get_color_tone(content, 200, danger)} !important; } } &--failed { background-color: #{$table-row-bg-color-danger-even} !important; &.js--click-bound:hover { background-color: #{$table-row-bg-color-danger-hover-even} !important; } .js--click-ownbound:hover { background-color: #{$table-row-bg-color-danger-active-hover-even} !important; } } &--updated { background-color: #{$table-row-bg-color-success-even} !important; &.js--click-bound:hover { background-color: #{$table-row-bg-color-success-hover-even} !important; } .js--click-ownbound:hover { background-color: #{$table-row-bg-color-success-active-hover-even} !important; } } } } } &cell { padding: 0.3rem 0.5rem; height: 42px; // Grundhöhe vertical-align: middle; text-align: left; border-style: solid; border-color: #{$table-cell-border-color}; border-width: #{$table-cell-border-width}; .icon:not(.button__icon) { margin-right: #{$table-gap-small}; } &__ { &inner { $table-cell-inner: & !global; height: 100%; &__ { &text { flex: 1 0 auto; display: flex; align-items: center; } } } } &:is( .table__body__cell ) { padding: 0.3rem; > #{$table-cell-inner} { gap: 0.3rem; display: flex; justify-content: end; } } &--type { &-email { a { color: inherit; } } &-text { } &-amount { $table-cell-type-amount: & !global; text-align: right; } &-actions { text-align: right; } &-icon, &-status { width: 42px; min-width: 42px; text-align: center; padding: 0; &-icon { max-width: 42px; } .icon { display: flex; justify-content: center; align-items: center; width: 28px; height: 28px; font-size: 1.4rem; border-radius: 50%; margin: auto !important; } } } } &header { &__ { &cell { background-color: #{$table-header-bg-color}; border-color: #{$table-header-border-color}; border-width: #{$table-header-border-width}; font-weight: bold; text-transform: uppercase; &.sortable { position: relative; cursor: pointer; padding-right: 1.2rem; &::after { content: "▲▼"; position: absolute; font-size: 0.8rem; opacity: 0.5; right: 0.5rem; } &:hover { background-color: #{$table-header-bg-color-hover}; border-color: #{$table-header-border-color-hover}; } &#{$table-cell-type-amount} { padding-right: 0.5rem; padding-left: 1.2rem; &::after { right: auto; left: 0.5rem; } } &.sorting--asc::after { content: "▲"; } &.sorting--desc::after { content: "▼"; } &.sorting--current { background-color: #{$table-header-bg-color-active}; border-color: #{$table-header-border-color-active}; font-weight: bold; &:hover { background-color: #{$table-header-bg-color-active-hover}; border-color: #{$table-header-border-color-active-hover}; } // @include highlightBorder(); } } } } } } } /** Datei: plugins/rest/assets.OLD/scss/layout/_form.scss **/ .form { $form-padding : 5px; $form-font-size-label : 8px; $form-control-padding : $form-padding; $form-checked-size : 16px; $form-checked-inner : 10px; $tabs-gap-tiny : 5px !default;; $form-gap-small : 10px !default; $form-gap-default : 15px !default; $form-gap-wide : 20px !default; $form-icon-font-family : bootstrap-icons !default; $form-border-radius : 0 !default; font-size: 1rem; &__ { &container { display: flex; flex-direction: column; gap: $form-gap-default; > * { margin: 0; } } &fieldset { display: flex; flex-direction: column; gap: #{$form-gap-wide}; padding: 0; border: 0; &.has-legend { flex: 1 1 20%; background-color: #{get_color( white )}; padding: #{$form-gap-wide} 0 0; &.has-border { padding: #{$form-gap-wide}; border: 1px solid #{get_color( border )}; border-radius: #{$form-border-radius}; } } } &legend { font-weight: 600; font-size: 1.17em; color: #{get_color(black)}; } &info { font-size: #{$form-font-size-label}; padding: #{$form-padding}; position: absolute; display: none; flex-direction: row-reverse; align-items: start; gap: #{$form-padding}; top: 0; right: 0; &-- { &required { $form-info-required: & !global; color: get_color( danger ); } &feedback { $form-info-feedback: & !global; &::before { font-size: #{$form-font-size-label *1.5}; font-family: #{$icon-font-family}; translate: 0 -#{$form-font-size-label * 0.5 / 2}; } } } } &control { $form-control: & !global; display: flex; align-items: center; position: relative; &:is( input, select, textarea, .is-fake-control ) { position: relative; font-size: 1em; border-radius: #{$form-border-radius}; border: 0; outline: 0; padding: #{$form-control-padding}; transition: border-color 0.3s, box-shadow 0.3s; background: transparent; width: 100%; font-family: inherit; } &:is( pre ) { margin: 0; } &__{ &group { display: flex; flex-direction: column; gap: #{$form-control-padding}; } &icon { margin-right: #{$form-gap-small}; } &label { display: block; width: 100%; padding: #{$form-control-padding}; padding-left: #{$form-checked-size + $form-control-padding * 3}; position: relative; &:has( input[type=radio] ) { &::after, &::before { border-radius: 50%; } } &::before { content: ""; display: inline-block; aspect-ratio: 1; height: #{$form-checked-size}; border: 1px solid get_color( border ); margin-right: 0.5em; position: absolute; top: #{$form-control-padding}; left: #{$form-control-padding}; } &:has( input:checked ) { &::after { content: ''; aspect-ratio: 1; display: block; top: #{$form-control-padding + $form-checked-size / 2 - $form-checked-inner / 2}; left: #{$form-control-padding + $form-checked-size / 2 - $form-checked-inner / 2}; position: absolute; background-color: #{get_color_hover(primary)}; height: #{$form-checked-inner}; } } &:hover { &::before { border-color: #{get_color_hover(primary)}; box-shadow: 0 0 5px #{get_color(primary, 0.5)}; outline: none; } } } } &-- { @mixin form-control-icon { font-family: #{$form-icon-font-family}; position: absolute; right: #{$form-padding}; top: #{$form-padding + $form-padding + $form-font-size-label}; bottom: #{$form-padding}; aspect-ratio: 1; display: flex; justify-content: center; align-items: center; } &progress { .progress { &__ { &wrapper { position: relative; height: 17px; font-size: 0.8rem; margin: #{$form-control-padding}; overflow: hidden; border-radius: #{$form-border-radius}; background-color: #{get_color( border )}; } &bar, &label { position: absolute; top: 0; left: 0; height: 100%; } &bar { background-color: var( --progress-color, #{get_color( primary )} ); transition: background-color 300ms ease-in-out, width 500ms cubic-bezier(.4,0,.2,1), } &label { color: #{get_color( white )}; width: 100%; display: flex; align-items: center; justify-content: center;; } } } } &radio, &checkbox { // border-color: transparent !important; box-shadow: none !important; flex-direction: column; width: 100%; input { visibility: hidden; position: fixed; top: 100vh; left: 100vw; } } &file { cursor: pointer; input { position: absolute !important; visibility: hidden; } &::after { content: '\F3D1'; @include form-control-icon; } } &textarea { textarea { height: calc( 2em + var( --lines, 2 ) * ( 2.8125em + #{$form-gap-wide} ) ); height: calc( ( var(--lines) * #{$form-gap-wide} ) + ( 45px * var(--lines) ) + 1.625em ) } } &select { select { appearance: none; } &::after { content: '\F229'; @include form-control-icon; } } } } &group { display: flex; flex-direction: column; position: relative; border: 1px solid #{get_color(border)}; > label { font-weight: 500; color: #{get_color(text)}; font-size: #{$form-font-size-label}; padding: #{$form-padding} #{$form-padding} 0; } &:hover { border-color: #{get_color_hover(primary)}; box-shadow: 0 0 5px #{get_color(primary, 0.5)}; outline: none; } &.has-focus { border-color: #{get_color(primary)}; box-shadow: 0 0 5px #{get_color(primary, 0.5)}; outline: none; } &.is-required { #{$form-info-required} { display: flex; } } &.is-valid, &.is-invalid, &.on-validate { #{$form-info-required} { display: none; } #{$form-info-feedback} { display: flex; } } &.is-valid { border-color: #{get_color(success)}; background-color: #{get_color(success, 0.05)}; &:hover { box-shadow: 0 0 5px #{get_color(success, 0.5)}; } #{$form-info-feedback} { color: #{get_color(success)}; &::before { content: '\F26A'; } } } &.is-invalid { border-color: #{get_color(danger)}; background-color: #{get_color(danger, 0.05)}; &:hover { box-shadow: 0 0 5px #{get_color(danger, 0.5)}; } #{$form-info-feedback} { color: #{get_color(danger)}; &::before { content: '\F622'; } } } &.on-validate { border-color: #{get_color(warning)}; background-color: #{get_color(warning, 0.05)}; &:hover { box-shadow: 0 0 5px #{get_color(warning, 0.5)}; } #{$form-info-feedback} { color: #{get_color(warning)}; &::before { content: '\F3E2'; animation-name: animate-rotate; animation-duration: 3s; animation-iteration-count: infinite; animation-timing-function: linear; } } } } &buttons { display: flex; justify-content: end; gap: $form-gap-default; } &actions { display: flex; justify-content: flex-end; gap: 0.75rem; .action-button { padding: 0.75rem 1.5rem; font-size: 1rem; border-radius: #{$form-border-radius}; cursor: pointer; border: none; transition: background-color 0.3s, transform 0.2s; &.primary { background-color: #{get_color(primary)}; color: #{get_color(white)}; &:hover { background-color: #{get_color_hover(primary)}; transform: scale(1.05); } &:active { transform: scale(0.95); } } &.secondary { background-color: #{get_color(secondary)}; color: #{get_color(white)}; &:hover { background-color: #{get_color_hover(secondary)}; transform: scale(1.05); } &:active { transform: scale(0.95); } } } } } } /** Datei: plugins/rest/assets.OLD/scss/layout/_tabs.scss **/ .tabs { $tabs-gap-tiny : 5px !default; $tabs-gap-small : 10px !default; $tabs-gap-default : 15px !default; $tabs-gap-wide : 20px !default; $tabs-icon-font-family : bootstrap-icons !default; $tabs-border-radius : 0 !default; display: flex; flex-direction: column; gap: #{$tabs-gap-wide}; width: 100%; .headline, .paragraph { grid-column: 1/-1; } &__ { &list { display: flex; list-style: none; gap: #{$tabs-gap-tiny}; padding: 0; margin: 0; border-bottom: 2px solid #{getCoreColor( gray )}; } &item { padding: 0.2em 0.6em; font-size: 1rem; gap: #{$tabs-gap-small}; min-height: 2rem; border-radius: #{$tabs-border-radius}; border: none; background: transparent; cursor: pointer; transition: background 0.2s ease; display: flex; align-items: center; user-select: none; &:hover { background: rgba(0, 0, 0, 0.05); } &--active { // font-weight: bold; // @include highlightBorder(); } &--action { margin-left: auto; // Action-Tabs immer rechts background: rgba(0, 0, 0, 0.05); border-radius: #{$tabs-border-radius}; & + & { margin-left: 0; } } // @include buttonColors(); } &content { padding: 15px; border: 1px solid #{getThemeColor( border )}; background: #fff; &__item { display: flex; flex-direction: column; gap: #{$tabs-gap-default}; display: grid; grid-template-columns: repeat( 2, 1fr ); > * { margin-block: 0; } > .form__group { grid-column: 1/-1; } } } &icon { font-size: 16px; } } } /** Datei: plugins/rest/assets.OLD/js/arunox/hooks/colorize.js **/ /** * rest/arunox/hooks/colorize.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Adaptation to new ctx method * @since 1.0.0 File is created */ ARUNOX.Hooks.register( 'colorize', function( cellColorConfig, optionColorConfig, iconContainer ) { if( typeof cellColorConfig === 'string' ) cellColorConfig = { color: cellColorConfig } if( typeof optionColorConfig === 'string' ) optionColorConfig = { color: optionColorConfig } let colorConfig = Object.assign( { target : 'background', prefix : false, }, cellColorConfig, optionColorConfig ); if( colorConfig.prefix ) { this.instance.root.classList.removeRexEx( /.*--colored-.*/ ); this.instance.root.classList.add( `${colorConfig.prefix}--colored-${colorConfig.color || 'default'}` ); return ; } if( this.instance.root && ['button'].includes( colorConfig.target ) ) { this.instance.root.classList.removeRexEx( /button--colored-.*/ ); this.instance.root.classList.add( `button--colored-${colorConfig.color || 'default'}` ); this.instance.root.style.backgroundColor = colorConfig.value; } if( this.instance.root && ['background'].includes( colorConfig.target ) ) { this.instance.root.classList.removeRexEx( /bg--colored-.*/ ); this.instance.root.classList.add( `bg--colored-${colorConfig.color || 'default'}` ); this.instance.root.style.backgroundColor = colorConfig.value; } if( iconContainer && ['icon','both'].includes( colorConfig.target ) ) { iconContainer.classList.removeRexEx( /text--colored-.*/ ); iconContainer.classList.add( `text--colored-${colorConfig.color || 'default'}` ); iconContainer.style.color = colorConfig.value; } if( this.instance.root && ['text','both'].includes( colorConfig.target ) ) { this.instance.root.classList.removeRexEx( /text--colored-.*/ ); this.instance.root.classList.add( `text--colored-${colorConfig.color || 'default'}` ); this.instance.root.style.color = colorConfig.value; } }); /** Datei: plugins/rest/assets.OLD/js/arunox/templates/button.js **/ /** * rest/arunox/templates/button.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'button', { hooks : { config : function( config ) { if( config.type ) config.attributes.type ??= config.type; if( config.type ) config.attributes.type ??= config.type; if( config.name ) config.attributes.name ??= config.name; if( config.value ) config.attributes.value ??= config.value; if( config.value ) config.attributes.value ??= config.value; if( config.target ) config.attributes.target ??= config.target; return config; } }, htmlTag : function( config, data, ctx ) { return config.href ? 'a' : 'button'; }, builder : function( config, data ) { const className = config?.name || 'button'; const block = config.block ? `${className} ${config.block}__${className}` : className; this.root.classList.add( ...block.split(' ') ); if( config.label ) { this.root.classList.add( 'has--label' ); this.render( 'span', { classes : `${block}__label label`, content : config.label, }); } if( config.href ) { this.root.href = ARUNOX.Router.parseUrl( config.href, data ); // this.root.target = config.target || '_blank'; this.root.addEventListener( 'click', ( event ) => { event.stopPropagation(); }); return; } if( config.type ) { const form = this.root.closest( 'form' ); if( form ) { if( !form.id ) form.id = crypto.randomUUID(); this.root.setAttribute( 'form', form.id ); } } if( config.action ) { this.root.addEventListener( 'click', ( event ) => { event.preventDefault(); event.stopPropagation(); if( this.root.name && this.root.value !== '' && this.root.value !== 'undefined' ) { ARUNOX.Router.handle( config.action, { [`${this.name}`] : this.value }, data, 'rest' ); } else ARUNOX.Router.handle( config.action, {}, data, 'rest' ); }); } }, }); ARUNOX.Templates.register( 'buttons', { htmlTag : 'div', hooks : { config : function( config ) { config.datasets.arx.node = 'buttons'; return config; }, }, builder : function( config, data ) {} }); /** Datei: plugins/rest/assets.OLD/js/arunox/templates/_general.js **/ /** * rest/arunox/templates/_general.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Hooks.register( 'applyConfigAfter', function( element, config ) { if( config.color ) this.instance.apply( 'colorize', typeof config.color === 'object' ? config.color : { color: config.color }, { prefix: 'theme' } ); if( config.icon ) this.instance.render( 'icon', { name: config.icon } ); }); ARUNOX.Hooks.register( 'config', function( config, data ) { config.arx ??= {}; config.events ??= []; config.styles ??= {}; config.classes ??= []; config.datasets ??= {}; config.attributes ??= {}; config.datasets.arx ??= config.arx; return config; }, 2 ); /** Datei: plugins/rest/assets.OLD/js/arunox/templates/table.js **/ /** * /arunox/components/table.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'table', { hooks : { config : function( config, data ) { config.classes.push( 'table' ); return config; }, afterRender : function( config, data ) { this.instance.parent.render( 'div', { classes : 'table__container', }).root.append( this.instance.root ); }, search : function( searchValue ) { // console.log( 'SEARCH', searchValue ); const elements = this.instance.elements; const toolbar = elements.toolbar.elements; const tableRows = elements.tbody.element.querySelectorAll('tr'); searchValue = searchValue.toLowerCase(); let visibleRows = []; tableRows.forEach(row => { const searchData = row.dataset?.arxSearch || ''; const isVisible = searchData.includes( searchValue ); row.style.display = isVisible ? null : 'none'; if( isVisible ) { visibleRows.push( row ); } }); toolbar.resetBtn.element.style.display = visibleRows.length == tableRows.length ? 'none' : null; visibleRows.forEach( row => elements.tbody.append( row ) ); }, reset : function( config ) { // console.log( 'RESET' ); const elements = this.instance.elements; const toolbar = elements.toolbar.elements const tbody = elements.tbody; const tableRows = elements.tbody.element.querySelectorAll('tr'); tableRows.forEach(row => { row.style.display = null; }); const query = window.location.hash.match( /\?(.*)/ ); const hash = window.location.hash.substring(1).replace( /\?.*/, '' ); const filter = new URLSearchParams( query ? query[1] : '' ); if( config?.query ) { for( const [ index, value ] of config.query.entries() ) { if( filter.has( index ) && filter.get( index ) == value ) { filter.delete( index ); } } } if( filter?.size ) history.pushState(null, null, `#${hash}?${filter.toString()}`); else history.pushState(null, null, `#${hash}`); if( config?.button ) delete config.button.dataset.tabFilter; if( toolbar.resetBtn ) toolbar.resetBtn.element.style.display = 'none'; if( toolbar.searchInput ) toolbar.searchInput.element.value = ''; }, sort : function( column, order ) { // console.log( 'SORT', column, order ); const elements = this.instance.elements; const config = elements.table.storage.config; const tabelCell = elements.thead.element.querySelector( `[data-arx-column="${column}"]` ); const tableRows = Array.from( elements.tbody.element.querySelectorAll( 'tr' ) ); if( !tabelCell || !tableRows ) return; if( !order ) order = tabelCell.dataset.arxOrder === 'asc' ? 'desc' : 'asc'; const columns = config.columns || []; const rules = config.order.rules || {}; if( rules[ column ]?.replace ) { for( const [ oldCol, newCol ] of Object.entries( rules[ column ].replace ) ) { elements.table.querySelectorAll(`td[data-arx-column="${oldCol}"]`).forEach(td => { const rowData = td.closest('tr').rowData; td.innerHTML = rowData[ newCol ] || ''-''; // td.dataset.arxColumn = newCol; // Key anpassen }); } } if( rules[ column ]?.show ) { for( const showCol of rules[ column ].show ) { elements.table.querySelectorAll(`td[data-arx-column="${showCol}"], th[data-arx-column="${showCol}"]`).forEach(el => { el.style.display = ''; // Wieder anzeigen }); } } if( rules[ column ]?.hide ) { for( const hideCol of rules[ column ].hide ) { elements.table.querySelectorAll(`td[data-arx-column="${hideCol}"], th[data-arx-column="${hideCol}"]`).forEach(el => { el.style.display = 'none'; // Wieder anzeigen }); } } // Entferne vorherige Sortierklassen elements.table.element.querySelectorAll('.table__header__cell').forEach(cell => { cell.classList.remove('sorting--asc', 'sorting--desc', 'sorting--current'); }); // Setze neue Sortierklasse if( tabelCell ) { tabelCell.dataset.arxOrder = order; tabelCell.classList.add( `sorting--${order}`, 'sorting--current' ); } if( elements.container ) { elements.container.element.dataset.arxSortingColumn = column; elements.container.element.dataset.arxSortingOrder = order; } // Reihen sortieren tableRows.sort( (rowA, rowB) => { let a = rowA.ARX.storage.data[ column ]?.trim() || ''; let b = rowB.ARX.storage.data[ column ]?.trim() || ''; // Versuche als Zahl zu interpretieren, falls möglich if (!isNaN(a) && !isNaN(b)) { a = parseFloat(a); b = parseFloat(b); } return order === 'asc' ? String(a).localeCompare(String(b)) : String(b).localeCompare(String(a)); }); // Sortierte Reihen neu einfügen elements.tbody.innerHTML = ''; tableRows.forEach( row => elements.tbody.append( row ) ); }, }, builder : function( config, data ) { const thead = this.render( 'thead', { columns : config.columns, }); const tbody = this.render( 'tbody', { columns : config.columns, apply : [ 'generate', data, true ], }, data ); if( config.api ) { if( !config.api.resolved ) { config.api = ARUNOX.Router.parseRoute( config.api, data ); } ARUNOX.Router.fetch( config.api ).then( data => tbody.apply( 'generate', data, false ) ); } }, create( config, data ) { elements.container = this.render( 'div', { classes : 'table__container', elements : elements, }); elements.toolbar = this.addToolbar( config ); elements.container.wrap( this ); this.element.classList.add( 'table' ); if( config.columns ) { config.columns = config.columns.filter( item => Object.keys(item).length > 0 ); } if( !config.columns ) return; if( config.api ) { if( !config.api.resolved ) { config.api = ARUNOX.Router.parseRoute( config.api, data ); } ARUNOX.Router.fetch( config.api ).then( this.generateRows.bind( this ) ); } this.generateRows( data, true ); // Nach dem Generieren der Rows if( config.order && config.order.column ) { const headerCell = this.element.querySelector(`.table__header__cell[data-arx-column="${config.order.column}"]`); const container = this.element.closest( '.table__container' ); // sortTable( config.order.column, config.order.order, headerCell, this ); // Automatischer Sortieraufruf } elements.container.element.dispatchEvent( new Event('loaded', { bubbles: true }) ); }, addToolbar( config ) { const table = this.elements.table; const container = this.elements.container; const wrapper = container.render( 'div', { classes : 'table__toolbar', }); if( config.search || config.filter ) { wrapper.elements.searchContainer = wrapper.render( 'div', { classes : 'table__search', }); wrapper.elements.resetBtn = wrapper.elements.searchContainer.render( 'button', { color : 'danger', type : 'button', block : 'table__search', title : 'Filter zurücksetzen', icon : 'bi bi-funnel', style : { display : 'none' }, onClick : () => table.apply( 'reset', config.filter ), }); if( config.search ) { wrapper.elements.searchInput = wrapper.elements.searchContainer.render( 'input', { type : 'text', classes : 'table__search__input', placeholder : 'Suchen...', onInput : () => table.apply( 'search', wrapper.elements.searchInput.element.value ), }); } // if( config.order.buttons ) { // for( const btnConfig of config.order.buttons ) { // const button = this.elements.searchContainer.render( btnConfig.generator, btnConfig.config ); // // button.addEventListener( 'click', () => { // console.log( this.element ); // const table = tableContainer.querySelector('table'); // button.dataset.order = btnConfig.config.order.order == 'asc' ? 'desc' : 'asc'; // sortTable( btnConfig.config.order.column, null, button, table ); // }); // } // } if( config.filter ) { wrapper.addEventListener( 'loaded', () => table.apply( 'filter', config.filter ) ); } } if( config.buttons ) { wrapper.elements.buttonContainer = wrapper.render( 'components/buttons', { buttons : config.buttons, classes : 'table__buttons', }); } return wrapper; }, async generateRows1( data, loading = false ) { const elements = this.elements; const config = this.storage.config elements.tbody.element.innerHTML = ''; if( !loading && data && data.length ) { for( const rowData of data ) { elements.tbody.render('table.tbody.tr', { columns : config.columns, elements : this.elements, }, rowData ); } return; } elements.tbody.render( 'table.tbody.tr', { elements : this.elements, columns : [ { key : 'hint', colSpan : config.columns.length, textNode : loading ? 'Daten werden geladen ...' : 'Keine Datensätze gefunden', } ], }); } }); ARUNOX.Templates.register( 'thead', { hooks : { config : function( config, data ) { config.classes.push( 'table__header' ); return config; }, }, builder : function( config, data ) { const headerRow = this.render( 'tr', { classes : [ 'table__header__row' ], columns : config.columns, }); } }); ARUNOX.Templates.register( 'tbody', { hooks : { config : function( config, data ) { config.classes.push( 'table__body' ); return config; }, generate : async function( data, loading ) { this.instance.root.innerHTML = ''; if( data && data.length ) { for( const rowData of data ) { this.instance.render( 'tr', { classes : [ 'table__body__row' ], columns : this.instance.config.columns, }, rowData ); } return; } this.instance.render( 'tr', { classes : [ 'table__body__row' ], columns : [ { key : 'hint', attributes : { colSpan : this.instance.config.columns.length, }, innerText : loading ? 'Daten werden geladen ...' : 'Keine Datensätze gefunden', } ], }); }, }, }); ARUNOX.Templates.register( 'tr', { hooks : { config : function( config, data ) { config.classes.push( 'table_row' ); return config; }, }, builder : function( config, data ) { const cellTag = this.parent.template == 'thead' ? 'th' : 'td'; for( const column of config.columns ) { this.render( cellTag, column, data ); } }, }); ARUNOX.Templates.register( 'th', { htmlTag : 'th', hooks : { config : function( config, data ) { config.type ??= 'text'; config.innerText ??= config?.label || null; config.arx.column ??= config?.key || null; config.classes.push( 'table__header__cell' ); config.classes.push( `table__header__cell--type-${config.type}` ); config.classes.push( 'table__cell' ); config.classes.push( `table__cell--type-${config.type}` ); if( config.sortable ) { config.classes.push( 'sortable' ); config.arx.column = config.key; config.arx.order = config.order?.order || 'asc'; config.events.click = () => this.instance.apply( 'sort', null ); } if( !config.visible ) { config.styles.display = 'none'; } return config; }, sort( order ) { const elements = this.instance.elements; const column = this.instance.element.dataset?.arxColumn || null; elements.table.apply( 'sort', column, order ); }, }, builder : function( config, data ) {} }); ARUNOX.Templates.register( 'td', { hooks : { config : function( config, data ) { config.type ??= 'text'; config.arx.column ??= config?.key || null; config.classes.push( 'table__cell' ); config.classes.push( `table__cell--type-${config.type}` ); if( !config.visible ) { config.styles.display = 'none'; } return config; }, data : function( data, config ) { if( data === undefined ) return data; let value = '-'; if( data[ config.key ] !== undefined ) { value = data[ config.key ]; } else if( config.key.indexOf('.') > -1 ) { let current = data; for( const key of config.key.split('.') ) { current = current[ key ]; } if( current !== undefined ) { value = current; } } return value; }, beforeRender : function( config, data ) { this.instance.config.innerText ??= data || '-'; }, afterRender : function( config, data ) { if( data !== null && data !== undefined ) { this.instance.parent.dataset({ [`filter-${config.key}`] : data.toString().toLowerCase() }); } // if( config.searchable ) config.searchContent.push( this.element.textContent.trim() ); this.instance.apply( `type.${config.type}` ); }, }, }); // ARUNOX.Registry.htmlTag.register( 'table.tbody.tr', class extends ARUNOX.Theme.Component { // static htmlTag = 'tr'; // static hooks = { // config( config, type, name, data, storage ) { // config.classes.push( 'table_row' ); // config.classes.push( 'table__body__row' ); // // return config; // }, // // update( newData, status ) { // const updatedRow = this.instance.replace('table.tbody.tr', { // columns : this.instance.storage.config.columns, // elements : this.instance.elements, // classes : status ? 'table__body__row--updated' : 'table__body__row--failed', // }, newData ); // // setTimeout(() => updatedRow.element.classList.remove( 'table__body__row--updated', 'table__body__row--failed' ), 200 ); // } // }; // // create( config, data = {} ) { // const searchContent = []; // const elements = this.elements; // const storage = elements.table.storage.config // // for( const column of config.columns ) { // this.render( 'table.td', Object.assign( {}, column, { // elements : this.elements, // searchContent : searchContent, // }), data ); // } // // if( storage.action && !this.element.classList.contains( 'js--click-bound' ) ) { // this.element.classList.add( 'cursor--pointer' ); // // // if( this && this.RESTconfig.api.route && config.action.route ) { // // let rowSearchParams = this.RESTconfig.api.route.match( /\?(.*)/); // // let cellSearchParams = config.action.route.match( /\?(.*)/); // // // // if( rowSearchParams ) rowSearchParams = new URLSearchParams( rowSearchParams[1] ); // // if( cellSearchParams ) cellSearchParams = new URLSearchParams( cellSearchParams[1] ); // // // // // // if( rowSearchParams ) { // // if( rowSearchParams.has('columns') && ( !cellSearchParams || !cellSearchParams.has('columns') ) ) { // // if( cellSearchParams ) rowSearchParams = '&columns=' + rowSearchParams.get('columns'); // // else rowSearchParams = '?columns=' + rowSearchParams.get('columns'); // // // // config.action.route += rowSearchParams; // // // // } // // } // // } // // // // this.element.addEventListener( 'click', async ( event ) => { // if( ['a'].includes( event.target.tagName.toLowerCase() ) // || event.target.classList.contains('js--click-unbound') // || event.target.classList.contains('js--click-ownbound') // ) return false; // // await ARUNOX.Router.handle( storage.action, {}, data, 'rest' ); // }); // // this.element.classList.add( 'js--click-bound' ); // } // // this.setData( { 'search' : searchContent.join( ' ' ).toLowerCase() }); // } // }); // ARUNOX.Hooks.register( 'htmltag.table.td->type.OLD.email', function() { // const data = this.instance.storage.data; // const config = this.instance.storage.config; // const element = this.instance.origin || this.instance.element; // // if( !data ) { // element.textContent = '-'; // return; // } // // this.instance.element.classList.add( 'js--click-ownbound' ); // this.instance.element.addEventListener( 'click', event => { // event.preventDefault(); // window.location.href = `mailto:${data}`; // }); // }); ARUNOX.Hooks.register( 'td->type.icon', function() { const data = this.instance.data; const config = this.instance.config; const option = config.options[ data ] || {}; this.instance.root.textContent = null; const icon = this.instance.render( 'icon', { name : option?.icon || 'bi bi-question-diamond', color : { target : 'text', prefix : null, color : option?.color || 'default', }, title : option?.label || `${config.title} nicht definiert`, }); }); ARUNOX.Hooks.register( 'td->type.status', function() { this.instance.apply( 'type.icon' ); const table = this.instance.closest( 'table' ); const config = this.instance.config; const data = this.instance.parent.data; const action = Object.assign( {}, { query : table.config.api?.query || {}, }, config.action ); this.instance.root.classList.add( 'js--click-ownbound' ); this.instance.root.addEventListener( 'click', async event => { event.preventDefault(); event.stopPropagation(); await ARUNOX.Router.handle( action, {}, data, 'rest' ); }); }); /** Datei: plugins/rest/assets.OLD/js/arunox/templates/dynamic.js **/ /** * rest/arunox/templates/dynamic.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'dynamic', {}); ARUNOX.Registry.wrapper.register( 'dynamic.OLD', class extends ARUNOX.Theme.Component { create( route, response ) { const config = response.config ?? {}; const data = response.list ?? response.data ?? {}; const tooltip = document.querySelector('.has-tooltip--active'); if( tooltip ) tooltip.dispatchEvent( new Event('mouseleave') ); Array.from( this.parent.elements.content?.childNodes || [] ).filter( n => n != this.element ).forEach( e => e.remove() ); // this.parent.elements.content.innerHTML = ''; this.element.classList.add( 'main__inner', 'template__inner', 'template--dynamic' ); // if( route.history ) { // generator.generate( 'components/breadcrumbs', { path: route } ); // } if( config.title ) { const title = this.render( 'components/headline', { classes : [ 'headline', 'template__headline' ], }, config.title ); if( config.backlink ) { title.render( 'components/link', { path : config.backlink.route, icon : 'bi bi-arrow-left', }); } this.append( title ); } // for( const contentElement of config.content ) { // if( contentElement.generator ) { // contentElement.element = this.render( contentElement.generator, contentElement.config, data ); // } // } } }); /** Datei: plugins/rest/assets.OLD/js/arunox/templates/icon.js **/ /** * rest/arunox/templates/icon.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'icon', { htmlTag : 'i', builder : function( config, data ) { const className = 'icon'; const block = config.block ? `${className} ${config.block}__${className}` : className; this.root.classList.add( ...config.name.split(' ') ); this.root.classList.add( ...block.split(' ') ); this.parent.root?.classList.add( 'has--icon' ); }, }); /** Datei: plugins/rest/assets.OLD/js/arunox/templates/form.js **/ /** * /components/form.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'form', { htmlTag : 'form', hooks : { config : function( config ) { config.attributes.id = crypto.randomUUID(); config.datasets.arx.node = this.instance.htmlTag; config.classes.push( 'form__container' ); return config; }, content : function( content, config, data ) { if( config.buttons ) { content.push( ARUNOX.Templates.config( 'buttons', { classes : [ 'form__buttons' ], content : config.buttons.map( button => { button.config.attributes ??= {}; button.config.attributes.form = this.instance.config.attributes.id; return button; }), }, data ) ); } return content; }, afterRender : function( config, data ) { if( !config.action ) return; let lastSearchParams = ARUNOX.Router.history?.match( /\?(.*)/) | {}; let formSearchParams = config.action.route.match( /\?(.*)/); if( lastSearchParams ) lastSearchParams = new URLSearchParams( lastSearchParams[1] ); if( formSearchParams ) formSearchParams = new URLSearchParams( formSearchParams[1] ); if( lastSearchParams ) { if( lastSearchParams.has('columns') && ( !formSearchParams || !formSearchParams.has('columns') ) ) { if( formSearchParams ) lastSearchParams = '&columns=' + lastSearchParams.get('columns'); else lastSearchParams = '?columns=' + lastSearchParams.get('columns'); config.action.route += lastSearchParams; } } this.instance.noValidate = true; this.instance.actionConfig = ARUNOX.Router.parseRoute( config.action, data, {}, 'rest' ); } }, }); ARUNOX.Templates.register( 'control', { htmlTag : 'div', hooks : { config : function( config ) { console.log( config.type ); config.input = Object.assignRecursive( {}, config || {} ); config.template = `input/${config.type}`; config.datasets.arx.node = 'control'; config.datasets.arx.control = config.type; if( !ARUNOX.Templates.exists( config.template ) ) { if( config.type == 'select' ) config.template = 'select' else if( config.type == 'textarea' ) config.template = 'textarea' else if( config.type == 'datalist' ) config.template = 'datalist' else config.template = 'input'; } config.classes.push( 'form__group', `form__control--${config.type}` ); return config; }, }, builder : function( config, data ) { if( config.label ) { const label = this.render( 'label', { classes : [ 'form__label' ], content : config.label, attributes : { for : `control_${config.key}`, }, }); } const element = this.render( config.template, Object.assignRecursive( { classes : [ 'form__control' ], datasets : { arx : { node : 'input', }, }, }, config.input || {} ), data ); }, }); ARUNOX.Hooks.register( 'input->beforeRender', function( config, data ) { config.attributes = { name : config.key, type : config.type, required : !!config.required, }; }); ARUNOX.Hooks.register( 'select->beforeRender', function( config, data ) { config.content = []; config.attributes = { name : config.key, required : !!config.required, multiple : !!config.multiple, }; // console.log( config, data ); if( config.choose ) { config.content.push( ARUNOX.Templates.config( 'option', { content : config.choose, attributes : { selected : true, }, } ) ); } for( const option of config.options ) { config.content.push( ARUNOX.Templates.config( 'option', { content : option.label, attributes : { value : option.value, selected : data?.[ config.key ] == option.value, }, } ) ); } }); ARUNOX.Hooks.register( 'textarea->beforeRender', function( config, data ) { config.attributes = { name : config.key, required : !!config.required, }; }); if( !window.formSubmitListener ) { document.addEventListener('submit', async event => { if( !event.target.matches('form') ) return; event.preventDefault(); const form = event.target; const inputs = form.querySelectorAll('input, select, textarea'); const promises = []; console.log( inputs ); inputs.forEach(input => { if( typeof input.ARX.validate === 'function' ) { promises.push(new Promise((resolve) => { input.addEventListener('validationComplete', () => { resolve( !!input.closest('.is-valid') ); }, { once: true }); input.dispatchEvent( new Event('validate', { bubbles: true } ) ); })); } }); const results = await Promise.all( promises ); const isValid = results.every( result => result ); if( isValid ) { ARUNOX.Router.handle( form.ARX.actionConfig, new FormData( form ), {}, 'rest' ); } }); window.formSubmitListener = true; } /** Datei: plugins/rest/assets.OLD/js/arunox/templates/notifications.js **/ /** * rest/arunox/templates/notifications.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'notifications', { htmlTag : 'div', hooks : { shouldRender : function( shouldRender, config, data ) { return !document.querySelector('.notification__wrapper'); }, shouldRebuild : function( shouldRebuild, config, data ) { return document.querySelector('.notification__wrapper'); }, config : function( config, data ) { config.classes.push( 'notification__wrapper', 'notification--show-on-hover', 'notification--stacked' ); return config }, }, addNotification : function( config, data ) { this.render( 'notification', config, data ); } }); ARUNOX.Templates.register( 'notification', { htmlTag : 'div', hooks : { config : function( config, data ) { return { type : config.type, color : config.color || config.type, duration : config.duration, message : config.message || config.title, classes : [ 'notification' ], content : [ config.icon ? ARUNOX.Templates.config( 'icon', { name: config.icon, block: 'notification' }) : null, ARUNOX.Templates.config( 'div', { classes : [ 'notification__inner' ], content : [ config.title ? ARUNOX.Templates.config( 'div', { innerText : config.title, classes : 'notification__title', }) : null, config.message ? ARUNOX.Templates.config( 'div', { innerText : config.message, classes : 'notification__message', }) : null, ], }), ARUNOX.Templates.config( 'button', { classes : [ 'notification__close' ], innerHTML : '×', onClick : () => this.apply( 'close' ), }), ], }; }, close : function() { this.instance.root.classList.add( 'is-closing' ); setTimeout(() => this.instance.root.remove(), 300 ); }, afterRender : function( config, data ) { if( config.duration === 0 ) return; setTimeout(() => this.instance.apply( 'close' ), config.duration || 5000 ); } }, }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/components/paragraph.js **/ /** * rest/arunox/generators/components/paragraph.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Hooks.register( 'generator.components/paragraph->create.OLD', function( [ type, name, hook, config, data ] ) { type = 'htmltag'; name = 'p'; hook = `${type}.${name}`; ARUNOX.Theme.bindHooks( type, name, hook ); return [ type, name, hook, config, data ]; }, 0 ); ARUNOX.Registry.htmlTag.register( 'p', class RestTemplateDefault extends ARUNOX.Theme.Component { static htmlTag = 'p'; static hooks = { config( config, type, name, data, storage ) { config.textNode ??= config.text || null; return config; }, }; create( config, data ) { const className = 'paragraph'; const block = config.block ? `${className} ${config.block}__${className}` : className; this.element.classList.add( ...block.split(' ') ); if( config.icon ) this.elements.icon = this.render( 'components/icon', { name: config.icon, block: config.block } ); if( config.color ) this.apply( 'colorize', config.color, { target: 'icon' }, this.elements.icon ); } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/components/breadcrumbs.js **/ /** * rest/arunox/generators/components/breadcrumbs.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.generator.register( 'components/breadcrumbs', class RestTemplateDefault extends ARUNOX.Theme.Component { static htmlTag = 'h1'; create( config, data ) { const wrapper = document.querySelector('#breadcrumbs'); if( !wrapper) return; if( location.pathname != config.path.cachekey || config.path.cachekey != config.path.history ) { window.history.pushState({}, "", config.path.history ); } const home = wrapper.querySelector( '.breadcrumbs__item--home'); const items = wrapper.querySelectorAll( ':not(.breadcrumbs__item--home) '); if( !wrapper ) return; wrapper.classList.add( 'breadcrumbs' ); items.forEach( element => element.remove() ); const segments = router.getBreadcrumbSegments( config.path.history ); for( const [ index, segment ] of segments.entries() ) { if( ( home && index === 0 ) || ( index <= segments.length - 1 ) ) { generator.generate( 'components/breadcrumbs/separator', {}, {}, wrapper ); } generator.generate( 'components/link', { name : 'item', block : 'breadcrumbs', path : index === segments.length - 1 ? false : segment.path, label : segment.label, active : index === segments.length - 1, }, {}, wrapper ); } } }); ARUNOX.Registry.generator.register( 'components/breadcrumbs/separator', class RestTemplateDefault extends ARUNOX.Theme.Component { static htmlTag = 'h1'; create( config, data ) { const separator = document.createElement('div'); separator.classList.add('breadcrumbs__separator'); container.append( separator ); } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/components/headline.js **/ /** * rest/arunox/generators/components/headline.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.generator.register( 'components/headline', class extends ARUNOX.Theme.Component { static htmlTag = 'h1'; create( config, data ) { this.append( document.createTextNode( data || config.title ) ) } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/components/icon.js **/ /** * rest/arunox/generators/components/icon.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.generator.register( 'components/icon', class RestTemplateDefault extends ARUNOX.Theme.Component { static htmlTag = 'i'; create( config, data ) { const className = 'icon'; const block = config.block ? `${className} ${config.block}__${className}` : className; this.element.classList.add( ...config.name.split(' ') ); this.element.classList.add( ...block.split(' ') ); // if( config.color ) this.classList.add(`text--${config.color}`); if( config.color ) this.apply( 'colorize', config.color, { target: 'text' }); this.parent.element?.classList.add( 'has--icon' ); } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/components/link.js **/ /** * rest/arunox/generators/components/link.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.generator.register( 'components/link', class RestTemplateDefault extends ARUNOX.Theme.Component { static htmlTag = 'a'; create( config, data ) { const link = this.element; const label = document.createElement("span"); const name = config.name || 'link'; const block = config.block ? `${config.block}__${name}` : name; // Standardklasse setzen oder `block` nutzen (z. B. "menu") link.classList.add( block ); link.elements = {}; if( config.title ) link.title = config.title; if( config.active ) link.classList.add( `${block}--active` ); if( !config.path ) link.classList.add( `${block}--inactive` ); if( config.icon ) link.elements.icon = this.render("components/icon", { name: config.icon, block: config.block } ); if( config.label ) { label.innerHTML = config.label; label.classList.add( `${block}__label` ); link.append( label ); } if( config.path && config.path != '#' ) { link.addEventListener("click", (event) => { event.preventDefault(); ARUNOX.Router.handle( config.path, {}, {}, 'rest' ); }); } } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/hooks/menus.js **/ /** * rest/arunox/hooks/menus.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.generator.register( 'menus/category', class RestTemplateDefault extends ARUNOX.Theme.Component { create( config, data ) { const label = this.render( 'menus/category/label', { textNode : data.route.label, classes : 'menu__label', }); this.element.classList.add( 'menu__category' ); this.apply( 'addSubmenu', data.route, data.routes ); if( !this.element.querySelectorAll('.menu__item').length ) { this.remove(); } } }); ARUNOX.Hooks.register( 'routes.OLD', function( routes, name ) { if( !routes ) return []; const arxNames = new Set( ( Array.isArray(name) ? name : (typeof name === 'string' && name.trim() && name !== 'undefined') ? [name] : (this.instance.element?.dataset?.arxMenu ? this.instance.element.dataset.arxMenu.split(',') : []) ) .map(s => { if (typeof s !== 'string') return null; const val = s.trim().toLowerCase(); return val && val !== 'undefined' ? val : null; }) .filter(Boolean) ); return routes .map( r => Object.assign( r, { menu: Array.isArray( r.menu ) ? r.menu : r.menu?.split(' ' )} )) .filter( item => item.menu && item.menu.some( x => arxNames.has( String(x).toLowerCase() ) ) ); }); ARUNOX.Hooks.register( 'addMenus.OLD', function( routes ) { const arxNames = new Set( this.instance.element.dataset.arxMenu.split(',').map( s => s.toLowerCase() ) ); const arxRoutes = this.instance.filter( 'routes', routes ); if( !arxRoutes.length ) return; for( const arxRoute of arxRoutes ) { if( !arxRoute.path ) this.instance.apply( 'addCategory', arxRoute, routes ); else this.instance.apply( 'addButton', arxRoute, routes ); } }); ARUNOX.Hooks.register( 'addSubmenu.OLD', function( route, routes ) { const arxRoutes = ARUNOX.Hooks.filter( 'routes', routes, route.name ); if( !arxRoutes.length ) return; const submenu = this.instance.render( 'div' ); submenu.element.classList.add( 'menu__sub' ); submenu.setData( { menu: route.name }); submenu.apply( 'addMenus', routes ); submenu.parent.element.classList.add( 'has-children' ); route.element = submenu; }); ARUNOX.Hooks.register( 'addCategory.OLD', function( route, routes ) { route.element = this.instance.render( 'menus/category', {}, { route, routes } ); }); ARUNOX.Hooks.register( 'addButton.OLD', function( route, routes ) { const current = window.location.pathname; const wrapper = this.instance.render( 'div' ); const button = wrapper.render( 'button', Object.assign( {}, route, { name : 'button', block : 'menu', action : route.path, })); const icon = button.element.querySelector( '.icon' ); route.element = wrapper; wrapper.element.classList.add( 'menu__item' ); button.element.classList.add( 'menu__link' ); wrapper.element.querySelectorAll( '.button' ).forEach( e => e.classList.remove( 'button' ) ); if( route.path != '/' && current.startsWith( route.path ) ) { if( route.path == current || routes.filter( item => item.path && item.path.startsWith( route.path ) ).length == 1 ) { wrapper.element.classList.add('is-expanded'); wrapper.element.classList.add('menu__item--active'); button.element.classList.add('menu__link--active'); this.instance.element.closest('.menu__item')?.classList.add('is-expanded'); this.instance.element.closest('.menu__item')?.classList.add('has-active'); } } button.element.addEventListener( 'click', () => { const hasPath = route.path && route.path != '#'; const isExpanded = wrapper.element.classList.contains('is-expanded'); const hasChildren = wrapper.element.classList.contains('has-children'); document.querySelectorAll('.has-active').forEach(el => el.classList.remove('has-active')); document.querySelectorAll('.is-expanded').forEach(el => el.classList.remove('is-expanded')); document.querySelectorAll('.menu__item').forEach(el => el.classList.remove('menu__item--active')); document.querySelectorAll('.menu__link--active').forEach(el => el.classList.remove('menu__link--active')); if( hasPath ) { button.element.classList.add("menu__link--active"); } if( isExpanded && hasChildren ) return; wrapper.element.classList.add("is-expanded"); wrapper.element.classList.add("menu__item--active"); this.instance.element.closest('.menu__item')?.classList.add('is-expanded'); this.instance.element.closest('.menu__item')?.classList.add('has-active'); }); if( route.color ) button.apply( 'colorize', route.color, { prefix: 'theme' }, icon ); else button.element.classList.add( 'behavior-default' ); this.instance.append( wrapper ); wrapper.apply( 'addSubmenu', route, routes ); }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/hooks/validation.js **/ /** * rest/arunox/hooks/validation.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Hooks.register( 'validation.OLD', function( config, data ) { if( config.validate === false ) return; this.instance.element.validate = config.validate; if( typeof config.validate === 'undefined' ) { config.validate = true; } if( typeof config.validate === 'string' ) { const validateType = config.validate; config.validate = {}; config.validate[ validateType ] = true; } if( typeof config.validate === 'boolean' ) { config.validate = {}; } this.instance.parent.element.querySelectorAll( '.form__info--feedback' ).forEach( element => element.remove() ); if( config.validate?.onCreate ?? false ) this.instance.validators.validateAll( config, data ); if( config.validate?.onChange ?? true ) this.instance.validators.on( 'change', config, data ); if( config.validate?.onBlur ?? false ) this.instance.validators.on( 'blur', config, data ); this.instance.validators.on( 'validate', config, data ); }); ARUNOX.Hooks.register( 'validation.invalid.OLD', function( config, data ) { this.instance.render( 'data/form/feedback', config, data ); }); ARUNOX.Hooks.register( 'validation.valid.OLD', function( config, data ) { this.instance.render( 'data/form/feedback', config, data ); }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/hooks/inputs/password.js **/ /** * rest/arunox/hooks/inputs/password.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Hooks.register('customize/input/password.OLD', function( config, data ) { if( config.validate?.confirm ) { this.validators.register( 'confirm' ); } if( config.confirm ) { config.confirm = Object.assign( {}, config, { key : config.key + '_confirm', label : config.confirm, confirm : false, }); this.instance.parent.parent.render( 'data/form/control', config.confirm, data ); } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/data/tabs.js **/ /** * rest/arunox/htmltags/tabs.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.htmlTag.register( 'tabs', class extends ARUNOX.Theme.Component { static htmlTag = 'div'; static rootName = 'tabs'; static hooks = { config( config, type, name, data, storage ) { config.tabs ??= {}; return config; }, }; create( config, data ) { const tableUUID = ''; //context.getUUID(); const storage = this.storage.config const elements = this.elements; storage.query = window.location.hash.match( /\?(.*)/ ); storage.hash = window.location.hash.substring(1).replace( /\?.*/, '' ); storage.filter = new URLSearchParams( storage.query ? storage.query[1] : '' ); storage.isNested = this.element.closest( '.dialog, .tabs__content' ) !== null; if( storage.isNested ) { let match = ARUNOX.Router.history.match( /.*(?:#([a-zA-Z0-9]+)).*/ ); storage.hash = this.element.dataset.activeTab || match ? match[1] : ''; storage.filter = new URLSearchParams(); } storage.search = []; elements.tabs = this; elements.buttons = this.render( 'tabs.buttons', { tabs : config.tabs, }); elements.contents = this.render( 'tabs.contents', { tabs : config.tabs, }, data ); this.element.classList.add( 'tabs' ); } }); ARUNOX.Registry.htmlTag.register( 'tabs.buttons', class extends ARUNOX.Theme.Component { static htmlTag = 'div'; static hooks = { config( config, type, name, data, storage ) { config.classes.push( 'tabs__list' ); return config; } }; create( config, data ) { for( const [ index, tabConfig ] of config.tabs.entries() ) { this.elements[ tabConfig.id ] = this.render( 'tabs.button', Object.assign( {}, tabConfig, { index } ) ); } } }); ARUNOX.Registry.htmlTag.register( 'tabs.button', class extends ARUNOX.Theme.Component { static extends = 'htmltag.button'; static hooks = { config( config, type, name, data, storage ) { return Object.assignRecursive( {}, config, { id : null, content : null, classes : [ 'tabs__item' ], arx : { tab : config.id, }, }); }, select() { const elements = this.instance.parent.parent.elements; const storage = this.instance.parent.parent.storage.config; const element = this.instance.element; const config = this.instance.storage.config; for( const button of Object.values( elements.buttons.elements ) ) { button.element.classList.remove('tabs__item--active' ); if( button.element.dataset.arxTab == config.arx.tab ) { element.classList.add( 'tabs__item--active' ); } } for( const content of Object.values( elements.contents.elements ) ) { if( content.element.dataset.arxTab == config.arx.tab ) { content.element.classList.add('tabs__content--active' ); content.element.style.display = ''; content.apply('show'); } else { content.element.classList.remove('tabs__content--active' ); content.element.style.display = 'none'; content.apply('hide'); } } if( storage.isNested ) return; if( element.dataset.tabFilter ) { history.pushState(null, null, `#${config.arx.tab}?${config.arx.filter}`); } else history.pushState(null, null, `#${config.arx.tab}`); }, afterRender() { const elements = this.instance.parent.parent.elements; const storage = this.instance.parent.parent.storage.config; const element = this.instance.element; const config = this.instance.storage.config; const data = this.instance.storage.data; if( config.action ) { element.classList.add('tabs__item--action'); element.addEventListener( 'click', () => ARUNOX.Router.handle( config.action, {}, data, 'rest' ) ); return; } if( config.type === 'submit' ) { element.classList.add('tabs__item--action'); return; } if( ( storage.hash && storage.hash === config.arx.tab ) || ( !storage.hash && ( config.active || ( config.index === 0 && !storage.tabs.some( t => t.active ) ) ) ) ) { element.classList.add( 'tabs__item--active'); // contentContainer.style.display = ''; config.active = true; queueMicrotask( () => this.instance.apply('select') ); } else config.active = false; if( storage.filter && storage.hash && storage.hash === config.arx.tab ) { this.instance.setData({filter: storage.filter.toString() }); } element.addEventListener( 'click', () => this.instance.apply('select') ); }, }; }); ARUNOX.Registry.htmlTag.register( 'tabs.contents', class extends ARUNOX.Theme.Component { static htmlTag = 'div'; static hooks = { config( config, type, name, data, storage ) { config.classes.push( 'tabs__content', 'scroll__viewport' ); return config; } }; create( config, data ) { for( const [ index, tabConfig ] of config.tabs.entries() ) { if( tabConfig.action || config.type === 'submit' ) continue; this.elements[ tabConfig.id ] = this.render( 'tabs.content', Object.assign( {}, tabConfig, { index } ) ); } } }); ARUNOX.Registry.htmlTag.register( 'tabs.content', class extends ARUNOX.Theme.Component { static htmlTag = 'div'; static rootName = 'tab'; static hooks = { config( config, type, name, data, storage ) { return Object.assignRecursive( {}, config, { id : null, style : { display : 'none', }, classes : [ 'tabs__content__item' ], arx : { tab : config.id, }, }); }, afterRender() { const elements = this.instance.parent.parent.elements; const storage = this.instance.parent.parent.storage.config; const element = this.instance.element; const config = this.instance.storage.config; const data = this.instance.storage.data; }, }; }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/data/table.js **/ /** * rest/arunox/htmltags/table.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.htmlTag.register( 'table', class extends ARUNOX.Theme.Component { static htmlTag = 'table'; static rootName = 'table'; static hooks = { config( config, type, name, data, storage ) { config.columns ??= {}; return config; }, search( searchValue ) { // console.log( 'SEARCH', searchValue ); const elements = this.instance.elements; const toolbar = elements.toolbar.elements; const tableRows = elements.tbody.element.querySelectorAll('tr'); searchValue = searchValue.toLowerCase(); let visibleRows = []; tableRows.forEach(row => { const searchData = row.dataset?.arxSearch || ''; const isVisible = searchData.includes( searchValue ); row.style.display = isVisible ? null : 'none'; if( isVisible ) { visibleRows.push( row ); } }); toolbar.resetBtn.element.style.display = visibleRows.length == tableRows.length ? 'none' : null; visibleRows.forEach( row => elements.tbody.append( row ) ); }, reset( config ) { // console.log( 'RESET' ); const elements = this.instance.elements; const toolbar = elements.toolbar.elements const tbody = elements.tbody; const tableRows = elements.tbody.element.querySelectorAll('tr'); tableRows.forEach(row => { row.style.display = null; }); const query = window.location.hash.match( /\?(.*)/ ); const hash = window.location.hash.substring(1).replace( /\?.*/, '' ); const filter = new URLSearchParams( query ? query[1] : '' ); if( config?.query ) { for( const [ index, value ] of config.query.entries() ) { if( filter.has( index ) && filter.get( index ) == value ) { filter.delete( index ); } } } if( filter?.size ) history.pushState(null, null, `#${hash}?${filter.toString()}`); else history.pushState(null, null, `#${hash}`); if( config?.button ) delete config.button.dataset.tabFilter; if( toolbar.resetBtn ) toolbar.resetBtn.element.style.display = 'none'; if( toolbar.searchInput ) toolbar.searchInput.element.value = ''; }, sort( column, order ) { // console.log( 'SORT', column, order ); const elements = this.instance.elements; const config = elements.table.storage.config; const tabelCell = elements.thead.element.querySelector( `[data-arx-column="${column}"]` ); const tableRows = Array.from( elements.tbody.element.querySelectorAll( 'tr' ) ); if( !tabelCell || !tableRows ) return; if( !order ) order = tabelCell.dataset.arxOrder === 'asc' ? 'desc' : 'asc'; const columns = config.columns || []; const rules = config.order.rules || {}; if( rules[ column ]?.replace ) { for( const [ oldCol, newCol ] of Object.entries( rules[ column ].replace ) ) { elements.table.querySelectorAll(`td[data-arx-column="${oldCol}"]`).forEach(td => { const rowData = td.closest('tr').rowData; td.innerHTML = rowData[ newCol ] || ''-''; // td.dataset.arxColumn = newCol; // Key anpassen }); } } if( rules[ column ]?.show ) { for( const showCol of rules[ column ].show ) { elements.table.querySelectorAll(`td[data-arx-column="${showCol}"], th[data-arx-column="${showCol}"]`).forEach(el => { el.style.display = ''; // Wieder anzeigen }); } } if( rules[ column ]?.hide ) { for( const hideCol of rules[ column ].hide ) { elements.table.querySelectorAll(`td[data-arx-column="${hideCol}"], th[data-arx-column="${hideCol}"]`).forEach(el => { el.style.display = 'none'; // Wieder anzeigen }); } } // Entferne vorherige Sortierklassen elements.table.element.querySelectorAll('.table__header__cell').forEach(cell => { cell.classList.remove('sorting--asc', 'sorting--desc', 'sorting--current'); }); // Setze neue Sortierklasse if( tabelCell ) { tabelCell.dataset.arxOrder = order; tabelCell.classList.add( `sorting--${order}`, 'sorting--current' ); } if( elements.container ) { elements.container.element.dataset.arxSortingColumn = column; elements.container.element.dataset.arxSortingOrder = order; } // Reihen sortieren tableRows.sort( (rowA, rowB) => { let a = rowA.ARX.storage.data[ column ]?.trim() || ''; let b = rowB.ARX.storage.data[ column ]?.trim() || ''; // Versuche als Zahl zu interpretieren, falls möglich if (!isNaN(a) && !isNaN(b)) { a = parseFloat(a); b = parseFloat(b); } return order === 'asc' ? String(a).localeCompare(String(b)) : String(b).localeCompare(String(a)); }); // Sortierte Reihen neu einfügen elements.tbody.innerHTML = ''; tableRows.forEach( row => elements.tbody.append( row ) ); }, }; create( config, data ) { const tableUUID = ''; //context.getUUID(); const storage = this.storage.config const elements = this.elements; storage.search = []; elements.table = this; elements.thead = this.render( 'table.thead', { columns : config.columns, elements : elements, }); elements.tbody = this.render( 'table.tbody', { columns : config.columns, elements : elements, }, data ); elements.container = this.render( 'div', { classes : 'table__container', elements : elements, }); elements.toolbar = this.addToolbar( config ); elements.container.wrap( this ); this.element.classList.add( 'table' ); if( config.columns ) { config.columns = config.columns.filter( item => Object.keys(item).length > 0 ); } if( !config.columns ) return; if( config.api ) { if( !config.api.resolved ) { config.api = ARUNOX.Router.parseRoute( config.api, data ); } ARUNOX.Router.fetch( config.api ).then( this.generateRows.bind( this ) ); } this.generateRows( data, true ); // Nach dem Generieren der Rows if( config.order && config.order.column ) { const headerCell = this.element.querySelector(`.table__header__cell[data-arx-column="${config.order.column}"]`); const container = this.element.closest( '.table__container' ); // sortTable( config.order.column, config.order.order, headerCell, this ); // Automatischer Sortieraufruf } elements.container.element.dispatchEvent( new Event('loaded', { bubbles: true }) ); } addToolbar( config ) { const table = this.elements.table; const container = this.elements.container; const wrapper = container.render( 'div', { classes : 'table__toolbar', }); if( config.search || config.filter ) { wrapper.elements.searchContainer = wrapper.render( 'div', { classes : 'table__search', }); wrapper.elements.resetBtn = wrapper.elements.searchContainer.render( 'button', { color : 'danger', type : 'button', block : 'table__search', title : 'Filter zurücksetzen', icon : 'bi bi-funnel', style : { display : 'none' }, onClick : () => table.apply( 'reset', config.filter ), }); if( config.search ) { wrapper.elements.searchInput = wrapper.elements.searchContainer.render( 'input', { type : 'text', classes : 'table__search__input', placeholder : 'Suchen...', onInput : () => table.apply( 'search', wrapper.elements.searchInput.element.value ), }); } // if( config.order.buttons ) { // for( const btnConfig of config.order.buttons ) { // const button = this.elements.searchContainer.render( btnConfig.generator, btnConfig.config ); // // button.addEventListener( 'click', () => { // console.log( this.element ); // const table = tableContainer.querySelector('table'); // button.dataset.order = btnConfig.config.order.order == 'asc' ? 'desc' : 'asc'; // sortTable( btnConfig.config.order.column, null, button, table ); // }); // } // } if( config.filter ) { wrapper.addEventListener( 'loaded', () => table.apply( 'filter', config.filter ) ); } } if( config.buttons ) { wrapper.elements.buttonContainer = wrapper.render( 'components/buttons', { buttons : config.buttons, classes : 'table__buttons', }); } return wrapper; } async generateRows( data, loading = false ) { const elements = this.elements; const config = this.storage.config elements.tbody.element.innerHTML = ''; if( !loading && data && data.length ) { for( const rowData of data ) { elements.tbody.render('table.tbody.tr', { columns : config.columns, elements : this.elements, }, rowData ); } return; } elements.tbody.render( 'table.tbody.tr', { elements : this.elements, columns : [ { key : 'hint', colSpan : config.columns.length, textNode : loading ? 'Daten werden geladen ...' : 'Keine Datensätze gefunden', } ], }); } }); ARUNOX.Registry.htmlTag.register( 'table.thead', class extends ARUNOX.Theme.Component { static htmlTag = 'thead'; static hooks = {}; create( config, data ) { const headerRow = this.render('table.thead.tr', { columns : config.columns, elements : this.elements, }); } }); ARUNOX.Registry.htmlTag.register( 'table.thead.tr', class extends ARUNOX.Theme.Component { static htmlTag = 'tr'; static hooks = { config( config, type, name, data, storage ) { config.classes.push( 'table_row' ); config.classes.push( 'table__header__row' ); return config; } }; create( config, data ) { for( const column of config.columns ) { let cellConfig = Object.assign( {}, column, { elements : this.elements, }); let tableCell = this.render( 'table.th', cellConfig ); } } }); ARUNOX.Registry.htmlTag.register( 'table.th', class extends ARUNOX.Theme.Component { static htmlTag = 'th'; static hooks = { config( config, type, name, data, storage ) { config.type ??= 'text'; config.textNode ??= config?.label || null; config.arx.column ??= config?.key || null; config.classes.push( 'table__cell' ); config.classes.push( 'table__header__cell' ); config.classes.push( `table__cell--type-${config.type}` ); config.classes.push( `table__header__cell--type-${config.type}` ); return config; }, sort( order ) { const elements = this.instance.elements; const column = this.instance.element.dataset?.arxColumn || null; elements.table.apply( 'sort', column, order ); }, }; create( config, data ) { if( config.icon ) this.renderFirst( 'components/icon', { name: config.icon } ); if( config.sortable ) { this.element.classList.add( 'sortable' ); this.setData({ column : config.key, order : config.order?.order || 'asc', }); this.element.addEventListener( 'click', () => this.apply( 'sort', null ) ); } if( !config.visible ) { this.element.style.display = 'none'; } } }); ARUNOX.Registry.htmlTag.register( 'table.tbody', class extends ARUNOX.Theme.Component { static htmlTag = 'tbody'; static hooks = {}; create( config, data ) { for( const rowData of Object.entries( data || {} ) ) { this.render('table.tbody.tr', { columns : config.columns, elements : this.elements, }, rowData ); } } }); ARUNOX.Registry.htmlTag.register( 'table.tbody.tr', class extends ARUNOX.Theme.Component { static htmlTag = 'tr'; static hooks = { config( config, type, name, data, storage ) { config.classes.push( 'table_row' ); config.classes.push( 'table__body__row' ); return config; }, update( newData, status ) { const updatedRow = this.instance.replace('table.tbody.tr', { columns : this.instance.storage.config.columns, elements : this.instance.elements, classes : status ? 'table__body__row--updated' : 'table__body__row--failed', }, newData ); setTimeout(() => updatedRow.element.classList.remove( 'table__body__row--updated', 'table__body__row--failed' ), 200 ); } }; create( config, data = {} ) { const searchContent = []; const elements = this.elements; const storage = elements.table.storage.config for( const column of config.columns ) { this.render( 'table.td', Object.assign( {}, column, { elements : this.elements, searchContent : searchContent, }), data ); } if( storage.action && !this.element.classList.contains( 'js--click-bound' ) ) { this.element.classList.add( 'cursor--pointer' ); // if( this && this.RESTconfig.api.route && config.action.route ) { // let rowSearchParams = this.RESTconfig.api.route.match( /\?(.*)/); // let cellSearchParams = config.action.route.match( /\?(.*)/); // // if( rowSearchParams ) rowSearchParams = new URLSearchParams( rowSearchParams[1] ); // if( cellSearchParams ) cellSearchParams = new URLSearchParams( cellSearchParams[1] ); // // // if( rowSearchParams ) { // if( rowSearchParams.has('columns') && ( !cellSearchParams || !cellSearchParams.has('columns') ) ) { // if( cellSearchParams ) rowSearchParams = '&columns=' + rowSearchParams.get('columns'); // else rowSearchParams = '?columns=' + rowSearchParams.get('columns'); // // config.action.route += rowSearchParams; // // } // } // } // this.element.addEventListener( 'click', async ( event ) => { if( ['a'].includes( event.target.tagName.toLowerCase() ) || event.target.classList.contains('js--click-unbound') || event.target.classList.contains('js--click-ownbound') ) return false; await ARUNOX.Router.handle( storage.action, {}, data, 'rest' ); }); this.element.classList.add( 'js--click-bound' ); } this.setData( { 'search' : searchContent.join( ' ' ).toLowerCase() }); } }); ARUNOX.Registry.htmlTag.register( 'table.td', class extends ARUNOX.Theme.Component { static htmlTag = 'td'; static hooks = { config( config, type, name, data, storage ) { config.type ??= 'text'; config.textNode ??= data || '-'; config.visible ??= true; config.arx.column ??= config?.key || null; config.classes.push( 'table__cell' ); config.classes.push( 'table__body__cell' ); config.classes.push( `table__cell--type-${config.type}` ); config.classes.push( `table__body__cell--type-${config.type}` ); return config; }, data( data, config, type, name, storage ) { let value = '-'; if( data[ config.key ] !== undefined ) { value = data[ config.key ]; } else if( config.key.indexOf('.') > -1 ) { let current = data; for( const key of config.key.split('.') ) { current = current[ key ]; } if( current !== undefined ) { value = current; } } return value; } }; create( config, data ) { const tableRow = this.parent; if( data !== null ) tableRow.setData({ [`filter-${config.key}`] : data.toString().toLowerCase() }); if( config.searchable ) config.searchContent.push( this.element.textContent.trim() ); if( !config.visible ) this.style.display = 'none'; this.apply( `type.${config.type}` ); // console.log( config ); } }); ARUNOX.Hooks.register( 'htmltag.table.td->type.OLD.email', function() { const data = this.instance.storage.data; const config = this.instance.storage.config; const element = this.instance.origin || this.instance.element; if( !data ) { element.textContent = '-'; return; } this.instance.element.classList.add( 'js--click-ownbound' ); this.instance.element.addEventListener( 'click', event => { event.preventDefault(); window.location.href = `mailto:${data}`; }); }); ARUNOX.Hooks.register( 'htmltag.table.td->type.icon.OLD', function() { const data = this.instance.storage.data; const config = this.instance.storage.config; const element = this.instance.origin || this.instance.element; const option = config.options[ data ] || {}; element.textContent = null; const icon = this.instance.render( 'components/icon', { name : option?.icon || 'bi bi-question-diamond', color : option?.color || 'default', title : option?.label || `${config.title} nicht definiert`, }); }); ARUNOX.Hooks.register( 'htmltag.table.td->type.status.OLD', function() { this.instance.apply( 'type.icon' ); const rowData = this.instance.parent.storage.data; const tabelCnf = this.instance.elements.table.storage.config; const config = this.instance.storage.config; const element = this.instance.origin || this.instance.element; const action = Object.assign( {}, { query : tabelCnf.api?.query || {}, }, config.action ); this.instance.element.classList.add( 'js--click-ownbound' ); this.instance.element.addEventListener( 'click', async event => { event.preventDefault(); event.stopPropagation(); await ARUNOX.Router.handle( action, {}, rowData, 'rest' ); }); }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/data/form/input.js **/ /** * rest/arunox/generators/data/form/input.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.generator.register( 'data/form/input', class extends ARUNOX.Theme.Component { static htmlTag = 'input'; create( config, data ) { this.element.type = config.type; this.element.name = config.key; this.element.value = data?.[ config.key ] || config.value || ''; this.element.readOnly = config.readonly || false; if( config.type != 'hidden' ) { this.element.id = 'control_' + config.key; if( this.element.hasAttribute( 'readonly' ) ) this.element.readonly = true; if( this.element.hasAttribute( 'type' ) ) this.element.type = config.type; if( this.element.hasAttribute( 'name' ) ) this.element.name = config.key; this.element.classList.add( 'form__control' ); if( config.required ) { this.element.required = true; this.validators.register( 'required' ); } if( this.validators.exists( config.type ) ) { this.validators.register( config.type ); } if( config.validate?.request ) { this.validators.register( 'request' ); } if( config.readonly ) { this.parent.element.classList.add( 'is-readonly' ); } if( config.required ) { this.parent.element.classList.add( 'is-required' ); const required = document.createElement( 'span' ); required.classList.add( 'form__info', 'form__info--required' ); required.innerHTML = typeof config.required === 'string' ? config.required : 'required'; this.parent.append( required ); } this.element.addEventListener( 'focus', () => { this.parent.element.classList.add( 'has-focus' ); }); this.element.addEventListener( 'blur', () => { this.parent.element.classList.remove( 'has-focus' ); }); this.apply( 'validation', config, data ); } this.apply( `customize/input/${config.type}`, config, data ); } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/data/form/feedback.js **/ /** * rest/arunox/generators/data/form/control.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.generator.register( 'data/form/feedback', class extends ARUNOX.Theme.Component { static htmlTag = 'span'; create( config, type ) { const wrapper = this.parent.parent; const textField = type === null ? 'waiting' : 'message'; wrapper.element.classList.removeRexEx( /^(is)-(valid|invalid)/ ); wrapper.element.classList.removeRexEx( /^(on|validate)-/ ); if( type === null ) wrapper.element.classList.add( 'on-validate' ); else if( type === true ) wrapper.element.classList.add( 'is-valid' ); else if( type === false ) wrapper.element.classList.add( 'is-invalid' ); else wrapper.element.classList.add( `validate-${type}` ); wrapper.element.querySelector('.form__info--feedback')?.remove(); wrapper.append( this ); this.element.classList.add( 'form__info', 'form__info--feedback' ); this.element.innerHTML = ''; if( !config.validate ) return; if( typeof config.validate[ type ] === 'undefined' ) return; if( typeof config.validate[ type ] === 'string' ) { this.element.innerHTML = config.validate[ type ]; return; } if( typeof config.validate[ type ][ textField ] === 'string' ) { this.element.innerHTML = config.validate[ type ][ textField ]; return; } } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/data/table/row.js **/ /** * rest/arunox/generators/data/table/row.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ // updater.extend( 'data/table/row', async ( element, config, rowData ) => { // // if( !element || !rowData ) { // console.error('updateRow Fehler: Fehlende Daten', { element, rowData, config }); // return; // } // // if( !config.columns ) { // console.error('updateRow Fehler: `config.columns` nicht vorhanden', { config }); // return; // } // // element.classList.add( 'table__row--updated' ); // setTimeout(() => element.classList.remove('table__row--updated' ), 200 ); // // // console.log( 'UPDATER', 'data/table/row', config, rowData, element ); // // const newRow = await generator.generate( 'data/table/row', config, rowData, element ); // // const container = element.closest( '.table__container' ); // if( container ) { // const table = container.querySelector('table'); // const column = container.dataset.sortingColumn; // const order = container.dataset.sortingOrder; // // if( column && order && table ) { // sortTable( column, order, null, table ); // } // } // }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/data/table/table.js **/ /** * rest/arunox/generators/data/table/table.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Hooks.register( 'generator.data/table->create.OLD', function( [ type, name, hook, config, data ] ) { type = 'htmltag'; name = 'table'; hook = `${type}.${name}`; ARUNOX.Theme.bindHooks( type, name, hook ); return [ type, name, hook, config, data ]; }, 0 ); function filterTable( filter, elements ) { const tbody = elements.tableContainer.querySelector('tbody'); const rows = elements.tableContainer.querySelectorAll('.table__row'); let visibleRows = []; rows.forEach(row => { let isVisible = true; for( const [ column, value ] of filter.query.entries() ) { const filterable = row.hasAttribute(`data-filter-${column}`); if( !filterable ) { filter.query.delete( column ); continue; } const filterData = row.getAttribute(`data-filter-${column}`) || ''; const filterValue = value.toLowerCase(); if( isVisible ) isVisible = filterData == filterValue; } row.style.display = isVisible ? null : 'none'; if( isVisible ) visibleRows.push( row ); }); if( visibleRows.length != rows.length ) elements.resetBtn.style.display = null; visibleRows.forEach( row => tbody.appendChild( row ) ); } /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/data/table/column.js **/ /** * rest/arunox/generators/data/table/column.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.generator.register( 'data/table/column', class RestTemplateDefault extends ARUNOX.Theme.Component { static htmlTag = 'td'; create( config, data ) { const cell = this; const tableRow = this.parent; cell.classList.add('table__cell', 'table__body__column', `table__cell--type-${config.column.type || 'text'}`); cell.RESTconfig = config.column; if( cell.RESTconfig.title ) { cell.title = cell.RESTconfig.title; } if( cell.RESTconfig.action ) { cell.style.cursor = 'pointer'; if( tableRow && tableRow.RESTconfig.api.route && cell.RESTconfig.action.route ) { let rowSearchParams = tableRow.RESTconfig.api.route.match( /\?(.*)/); let cellSearchParams = cell.RESTconfig.action.route.match( /\?(.*)/); if( rowSearchParams ) rowSearchParams = new URLSearchParams( rowSearchParams[1] ); if( cellSearchParams ) cellSearchParams = new URLSearchParams( cellSearchParams[1] ); if( rowSearchParams ) { if( rowSearchParams.has('columns') && ( !cellSearchParams || !cellSearchParams.has('columns') ) ) { if( cellSearchParams ) rowSearchParams = '&columns=' + rowSearchParams.get('columns'); else rowSearchParams = '?columns=' + rowSearchParams.get('columns'); cell.RESTconfig.action.route += rowSearchParams; } // else console.log( cell.RESTconfig.action.route ); } // else console.log( tableRow.RESTconfig.api.route ); } cell.addEventListener('click', async (event) => { event.preventDefault(); event.stopPropagation(); ARUNOX.Router.handle( cell.RESTconfig.action, {}, data, 'rest' ); }); } // Korrekte Datei basierend auf dem Typ laden if( !this.hooks.exists( `customize/columns/${config.column.type || 'text'}` ) ) { config.column.type = 'text'; } this.apply( `customize/columns/${config.column.type || 'text'}`, config, data ); // generator.generate(`data/table/column/${cell.RESTconfig.type || 'text'}`, config, data, cell ); if( config.column.buttons ) { const children = cell.childNodes; const wrapper = document.createElement('div'); wrapper.classList.add( 'table__cell__inner' ); if( children.length ) { for( const child of children ) { if( child.nodeName === '#text' ) { const text = document.createElement('div'); text.classList.add( 'table__cell__inner__text' ); text.append( child ); wrapper.append( text ); } else wrapper.append( child ); } } for( const buttonConfig of config.column.buttons.values() ) { if( buttonConfig.config.visibility ) { let visible = true; for( let [ key, validValues ] of Object.entries( buttonConfig.config.visibility ) ) { if( !validValues.includes( data[ key ] ) ) { visible = false; break; } } if( !visible ) continue; } // await generator.generate( buttonConfig.generator, buttonConfig.config, data, wrapper ); } cell.append( wrapper ); } } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/templates/default.js **/ /** * rest/arunox/templates/default.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'default', class extends ARUNOX.Theme.Component { create( config, data ) { const breadcrumbs = document.createElement('div'); const homeLink = document.createElement('a'); const header = this.createHeader( config, data ); const sidebar = this.render( 'part/sidebar', config, data ); const navbar = this.createNavbar( config, data ); const logo = this.createLogo( config, data, sidebar ); const main = this.render('main', { name : 'main', classes : 'template__content', arx : { component: 'content' }, }); homeLink.href = '/'; homeLink.classList.add( 'breadcrumbs__item', 'breadcrumbs__item--home', 'bi', 'bi-house-door-fill' ); breadcrumbs.id = 'breadcrumbs'; breadcrumbs.append( homeLink ); header.append( breadcrumbs, navbar.element ); this.append( header, sidebar, main ); this.parent.element.classList.removeRexEx( /template--.*/ ); this.parent.element.classList.add( 'theme', 'template--default' ); this.element.classList.add( 'template__wrapper', 'template--default' ); this.content = main; sidebar.prepend( logo ); generateMenus(); } createNavbar( config, data ) { const wrapper = this.render('nav'); wrapper.element.id = 'navbar'; wrapper.element.classList.add( 'template__navbar', 'menu' ); wrapper.setData({ menu: 'navbar' }); return wrapper; } createHeader( config, data ) { const wrapper = document.createElement('div'); wrapper.id = 'header'; wrapper.classList.add( 'template__header' ); return wrapper; } createLogo( config, data ) { const wrapper = document.createElement('img'); wrapper.src = API_PREFIX + '/logo'; wrapper.classList.add( 'template__logo' ); return wrapper; } }); ARUNOX.Registry.generator.register( 'part/sidebar', class extends ARUNOX.Theme.Component { static htmlTag = 'aside'; create( config, data ) { const inner = this.render('div'); this.element.id = 'sidebar'; this.element.classList.add( 'template__sidebar', 'menu' ); inner.setData({ menu: 'sidebar' }); inner.element.classList.add( 'menu' ); this.append( inner ); } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/layouts/tabs.js **/ /** * rest/arunox/generators/layouts/tabs.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Hooks.register( 'generator.layouts/tabs->create.OLD', function( [ type, name, hook, config, data ] ) { type = 'htmltag'; name = 'tabs'; hook = `${type}.${name}`; ARUNOX.Theme.bindHooks( type, name, hook ); return [ type, name, hook, config, data ]; }, 0 ); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/wrapper/dialog.js **/ /** * rest/arunox/generators/wrapper/dialog.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.wrapper.register( 'dialog', class extends ARUNOX.Theme.Component { static htmlTag = 'div'; static rootName = 'dialog'; static hooks = { config( config, type, name, data, storage ) { config.classes.push( 'dialog' ); config.classes.push( `dialog--${config.size || 'md'}` ); config.classes.push( `dialog--${config.width || 'md'}` ); return config; }, }; create( config, data ) { const wrapper = this.render( 'div', { classes : [ 'dialog__wrapper' ], arx : { component: 'wrapper' }, }); const header = wrapper.render( 'div', { classes : [ 'dialog__header' ], arx : { component: 'header' }, }); const content = wrapper.render( 'div', { classes : [ 'dialog__content' ], arx : { component: 'content' }, }); this.elements.dialog = this; if( config.id ) this.element.dataset.id = config.id; if( config.title ) { header.render( 'components/headline', { size : 2, block : 'template', title : config.title, icon : config?.icon, color : config?.color, }); } if( config.close ) { const closeButton = header.render( 'button', { icon : 'bi-x-lg', classes : [ 'dialog__close' ], elements : this.elements, onClick : ( event ) => this.onClose( event ), arx : { component: 'close' }, }); if( config.backdrop ) { this.element.addEventListener( 'click', ( event ) => { if( event.target === this.element ) { this.onClose( event ); } }); } const cancelButtons = wrapper.element.querySelectorAll( '[type=cancel]' ); if( cancelButtons ) { cancelButtons.forEach( button => button.addEventListener('click', this.close ) ); } } this.element.addEventListener( 'close', this.onClose ); this.element.addEventListener( 'cancel', this._onCancel ); // this.element.show(); queueMicrotask( () => this.calcHeight( this, content ) ); } calcHeight( dialog, content ) { dialog.element.classList.add( 'is-open' ); let height = 0; content.element.querySelectorAll('.tabs__content__item').forEach( element => { const hidden = element.style.display === 'none'; if( hidden ) element.style.display = ''; if( height < element.clientHeight ) height = element.clientHeight; if( hidden ) element.style.display = 'none'; }); height += 10; const buttons = content.element.querySelector( '.form__buttons' ); const viewport = content.element.querySelector( '.scroll__viewport' ); if( viewport ) { const maxHeight = viewport.offsetTop + ( buttons ? buttons.clientHeight + 15 : 0 ); dialog.element.style.setProperty('--height', height + 'px' ); dialog.element.style.setProperty('--maxHeight', maxHeight + 'px' ); } } onClose( event ) { if( event ) event.preventDefault(); console.log( event.target ); event.target.ARX.elements.dialog.element.classList.add('is-closing'); // event.target.ARX.elements.backdrop.element.style.opacity = '0'; setTimeout(() => { event.target.ARX.elements.dialog.remove(); // event.target.ARX.elements.backdrop.remove(); }, 300 ); } _onCancel( event ) { if( !event.target.ARX.storage.config.close || !event.target.ARX.storage.config.backdrop ) { event.preventDefault(); } else event.target.ARX.onClose( event ); } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/header/link.js **/ /** * rest/arunox/generators/header/link.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Registry.generator.register( 'header/link', class extends ARUNOX.Theme.Component { static htmlTag = 'link'; create( route, response) { const config = response.config ?? {}; const data = response.list ?? response.data ?? {}; this.element.rel = data.rel; this.element.href = data.href; this.appendTo( document.head ); } }); /** Datei: plugins/rest/assets.OLD/js/arunox/_OLD/validators/required.js **/ /** * rest/arunox/validators/required.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Validator.register( 'required', ( instance, value ) => { if( !instance.element.value || instance.element.value === '' ) { return 'required'; } return true; }); /** Datei: plugins/rest/assets.OLD/js/arunox/router.js **/ window.ARUNOX = ARUNOX || {}; ARUNOX.Router = new ( class { constructor() { this._routes = {}; this._cache = new Map(); this._lastCacheKey = null; this._isReloaded = false; this._CACHE_TIMEOUT = 500; } async handle( config, sendData = {}, parseData = {}, mode = 'data' ) { const debug = false; let template = document.body; if( !config ) { console.warn("⚠️ Ungültige Konfiguration für handleAction()."); return; } if( !config.resolved ) { config = this.parseRoute( config, parseData, true, mode ); } if( debug ) console.groupCollapsed( config.resolved ); // console.log( config ); let response = { click : [], route : [], dialog : [], updater : [], wrapper : [], template : [], generator : [], }; for( const item of await this.fetch( config, sendData, {}, mode ) ) { response[ item.type ].push( item ); } // console.log( config, response ); ARUNOX.Router.history = config.history; if( response.dialog ) { for( const item of response.dialog ) { let dialog = null; if( item.name === '#ALL' ) { document.querySelectorAll('dialog').forEach( dialog => dialog.closeDialog() ); continue; } if( dialog = document.querySelector(`dialog[data-id="${item.name}"]`) ) { dialog.closeDialog(); continue; } if( dialog = document.querySelector('dialog') ) { dialog.closeDialog(); } } } if( response.route.length ) { for( const item of response.route ) { await ARUNOX.Router.handle( item, {}, {}, 'rest' ); } ARUNOX.Router.history = config.history; } if( response.template.length && !response.route.length ) { document.body.innerHTML = ''; // template = ARUNOX.Templates.render( response.template[0].template, Object.assign( {}, config ), response.template[0], document.body ); ARUNOX.Templates.render( response.template[0].template, response.template[0], Object.assign( {}, config ), document.body ); } // if( response.template.length && !response.route.length ) { // document.body.innerHTML = ''; // template = ARUNOX.Theme.template.render( response.template[0].template, Object.assign( {}, config ), response.template[0], document.body ); // } // // if( response.wrapper.length && !response.route.length ) { // for( const item of response.wrapper ) { // ARUNOX.Theme.wrapper.render( item.generator, item.config, item.list || item.data || {}, template ); // // if( item.history ) { // if( location.pathname != item.history ) { // window.history.pushState({}, "", item.history ); // } // } // } // } // // if( response.generator.length && !response.route.length ) { // for( const item of response.generator ) { // ARUNOX.Theme.generator.render( item.generator, config, item, template ); // } // } // // if( response.updater.length && !response.route.length ) { // for( const item of response.updater ) { // document.querySelectorAll(`[data-arx-uuid="${item.updater}"]`).forEach( element => element.ARX.apply( 'update', item.data, item.status ) ); // } // } // // if( response.click.length ) { // for( const item of response.click ) { // const element = document.querySelector(`[data-uuid="${item.parent}"] ${item.selector ?? ''}` ); // // element.click(); // } // } if( debug ) console.groupEnd(); ARUNOX.Router.history = config.history; } async routes() { if( !this._isLoaded() ) { await this._loadList(); } return this._routes; } async fetch(config, params = {}, options = {}, mode = 'data') { try { let cached = this.getCache(!config, {}, mode); if (cached !== false) return cached; cached = this.getCache(config, params, mode); if (cached !== false) return cached; if (!config.resolved) config = this.parseRoute(config, params, true, mode); if (!config.resolved) throw new Error(`❌ Fehlerhafte API-URL: ${config.resloved.data}`); const cacheKey = `${config.method}:${config.cachekey}`; const defaultOptions = { method: config.method, headers: {}, ...options }; if (config.method === "GET" || config.method === "DELETE") { const queryString = new URLSearchParams(params).toString(); config.resolved += queryString ? `?${queryString}` : ""; } else { if (params instanceof FormData) { defaultOptions.body = params; } else { defaultOptions.body = JSON.stringify(params); defaultOptions.headers["Content-Type"] = "application/json"; } } const response = await fetch(config.resolved, defaultOptions); const data = await response.json(); const fullcache = data.caching || false; delete data.caching; this._cache.set(cacheKey, { response: data, fullcache: false, timestamp: Date.now() }); this._lastCacheKey = cacheKey; return data; } catch (error) { console.error('❌ Fehler beim Abrufen der API-Daten:', error); return null; } } async fetchData(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.data; } async fetchList(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.list; } async fetchConfig(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.config; } async fetchGenerator(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.generator; } async fetchStream(config, params = {}, options = {}, onChunk) { try { if (!config.resolved) config = this.parseRoute(config, params, true); if (!config.resolved) throw new Error(`❌ Fehlerhafte API-URL: ${config.resloved.data}`); const defaultOptions = { method: config.method, headers: {}, ...options }; const response = await fetch(config.resolved, defaultOptions); if (!response.body) return; const decoder = new TextDecoder(); const reader = response.body.getReader(); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; if (onChunk) { buffer += decoder.decode(value, { stream: true }); let lines = buffer.split('\n'); buffer = lines.pop(); for (const line of lines) { if (!line.trim()) continue; try { onChunk(JSON.parse(line)); } catch { onChunk(line); } } } } if (onChunk && buffer.trim()) { try { onChunk(JSON.parse(buffer)); } catch { onChunk(buffer); } } } catch (error) { console.error('❌ Fehler beim Abrufen des API-Stream:', error); return null; } } async find(path, includeHidden = false) { await this._loadList(); return this._routes.find(route => { if (!includeHidden && route.visibility === "hidden") return false; const routePattern = new RegExp(`^${route.path.replace(/{\w+}/g, "([^/]+)")}$`); return routePattern.test(path); }); } getCache(config, params = {}, mode = 'data') { if (config === true && this._lastCacheKey) { const cached = this._cache.get(this._lastCacheKey); if (cached && Date.now() - cached.timestamp < this._CACHE_TIMEOUT) { cached.timestamp = Date.now(); return cached.response; } return null; } if (typeof config === 'object' || typeof config === 'string') { if (!config.resolved) config = this.parseRoute(config, params, {}, mode); const cacheKey = `${config.method}:${config.cachekey}`; const cached = this._cache.get(cacheKey); if (cached && Date.now() - cached.timestamp < this._CACHE_TIMEOUT) { this._lastCacheKey = cacheKey; return cached.response; } if (cached && cached.fullcache && location.pathname == config.cachekey) { return cached.response; } } return false; } getBreadcrumbSegments(path) { let segments = path.split('/').filter(Boolean); let breadcrumbs = []; let currentPath = ""; if (!segments.length) { segments = ['']; } segments.forEach(segment => { currentPath += `/${segment}`; const route = this._routes.find(r => r.path && new RegExp(`^${r.path.replace(/{\w+}/g, "([^/]+)")}$`).test(currentPath)); const group = this._routes.find(r => r.name && (new RegExp(`^${r.name.replace(/{\w+}/g, "([^/]+)")}$`).test(currentPath) || r.name == segment)); if (route) { breadcrumbs.push({ label: route.breadcrumb || route.label || segment, path: currentPath, }); } else if (group) { breadcrumbs.push({ label: group.breadcrumb || group.label || segment, path: false, }); } }); return breadcrumbs; } getDataRoute(config, params = {}, queryIncludes = true) { config = this.parseRoute(config ?? location.pathname, params, queryIncludes, 'data'); return config.resolved; } parseRoute( config, params = {}, queryIncludes = true, mode = 'data' ) { let route = typeof config === "string" ? config : config.route; let query = new URLSearchParams( config?.query || [] ); let connector = /\?/.test( route ) ? '&' : '?'; let resolved = null; let cachekey = null; if( query.size ) route += connector + query; if( !queryIncludes ) route = route.replace(/\?.*/, ''); switch (mode) { case 'rest': resolved = this._resolveApiUrl(route, params, '/rest'); cachekey = this._resolveApiUrl(route, params, '/rest', false); break; case 'data': resolved = this._resolveApiUrl(route, params, true); cachekey = this._resolveApiUrl(route, params, false, false); break; } return { route : route, resolved: resolved, cachekey: cachekey, history : this._resolveApiUrl(route, params, false), method : config?.method || "GET", query : config?.query || {}, }; } parseUrl(route, params) { const missingKeys = []; const resolvedUrl = route.replace(/{(\w+)}/g, (_, key) => { if (params[key] === 'undefined') { missingKeys.push(key); return `{${key}}`; } return params[key]; }).replace(/\/$/, ''); if (missingKeys.length > 0) { console.error(`Nicht ersetzte Platzhalter: ${missingKeys.join(', ')}`, `URL: ${route}`); return null; } return resolvedUrl; } _isLoaded() { return !!Object.entries( this._routes ).length; } async _loadList() { this._routes = await this.fetchList("/rest/routes"); } _resolveApiUrl(route, params = {}, prefix = true, hash = true) { const API_PREFIX = '/api/v1'; if (typeof prefix === 'string') { route = this._stripApiPrefix(route, API_PREFIX); route = route.startsWith(prefix) ? route : `${prefix}${route}`; } const mergedParams = params; const prefixedUrl = route.startsWith(API_PREFIX) ? route : `${API_PREFIX}${route}`; const resolvedUrl = this.parseUrl(prefixedUrl, params); if (!hash) { const withoutHash = resolvedUrl.replace(/#.*/, ''); return prefix ? withoutHash : this._stripApiPrefix(withoutHash, API_PREFIX); } return prefix ? resolvedUrl : this._stripApiPrefix(resolvedUrl, API_PREFIX); } _stripApiPrefix(route, api_prefix = '/api/v1') { if (route.startsWith(api_prefix)) { return route.replace(api_prefix, ''); } return route; } })(); ARUNOX.Router.history = null; /** Datei: plugins/rest/assets.OLD/js/generator/components/button.js **/ /** * generator/components/button.js * * Erstellt einen flexiblen Button basierend auf einer API-Config. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.2 * * @since 1.0.0 Datei erstellt * @since 1.0.1 Bootstrap-Icons + BEM-Syntax hinzugefügt * @since 1.0.2 BEM-Klasse auf `.button` umgestellt */ import { router } from 'plugins/rest/core/router'; import { generator } from 'plugins/rest/core/generator'; import { handleAction } from 'plugins/rest/core/helpers'; generator.extend( 'components/button', async ( config, data, container ) => { if( !config.label && !config.icon ) { console.warn("⚠ Button benötigt eine `label`- oder `icon`-Eigenschaft!"); return; } const dropdown = config.dropdowns && Array.isArray( config.dropdowns ); const wrapper = document.createElement('div' ); const button = document.createElement(config.href ? 'a' : 'button' ); const label = document.createElement('span' ); const className = 'button'; const block = config.block ? `${className} ${config.block}__${className}` : className; button.classList.add( ...block.split(' ') ); if( config.title ) button.title = config.title; if( config.type ) button.type = config.type; if( config.name ) button.name = config.name; if( config.value ) button.value = config.value; if( config.color ) await generator.generate( 'helpers/colorize', {}, config.color, button ); if( config.classes ) button.classList.add( ...config.classes ); if( config.icon ) { button.classList.add( 'has--icon' ); await generator.generate( 'components/icon', { name: config.icon, block: className }, {}, button ); } if( config.label ) { button.classList.add( 'has--label' ); label.classList.add( ...(`${block}__label label`.split(' ')) ); label.textContent = config.label; button.append( label ); } if( dropdown ) { const dropdownMenu = document.createElement('div'); wrapper.classList.add( 'button--wrapper' ); dropdownMenu.classList.add( 'button__dropdown' ); for( const item of config.dropdowns ) { const buttonWrapper = document.createElement('div'); // Wrapper für korrekte BEM-Struktur buttonWrapper.classList.add('button__dropdown__item'); const dropdownButton = await generator.generate( 'components/button', Object.assign({ color : 'secondary', type : 'button', block : 'button__dropdown' }, item.config ), data, buttonWrapper ); if( item.config.color ) { await generator.generate( 'helpers/colorize', {}, { target : 'icon', color : item.config.color, }, container || {}, dropdownButton.icon ); } dropdownMenu.appendChild( buttonWrapper ); } wrapper.appendChild( button ); wrapper.appendChild( dropdownMenu ); button.addEventListener('click', () => { dropdownMenu.classList.toggle('open'); }); wrapper.addEventListener('mouseleave', () => { dropdownMenu.classList.remove('open'); }); if( container ) container.appendChild( wrapper ); return wrapper; } if( container ) container.appendChild( button ); if( config.href ) { button.href = router.parseUrl( config.href, data ); button.target = config.target || '_blank'; button.addEventListener('click', ( event ) => { event.stopPropagation(); }); return button; } if( config.action ) { button.addEventListener('click', ( event ) => { event.preventDefault(); event.stopPropagation(); if( button.name && button.value !== '' && button.value !== 'undefined' ) { handleAction( config.action, { [`${button.name}`] : button.value }, data, 'rest' ); } else handleAction( config.action, {}, data, 'rest' ); }); } return button; }); /** Datei: plugins/rest/assets.OLD/js/generator/components/tooltips.js **/ /** * plugins/rest/generator/components/tooltips.js * * Erstellt dynamische Tooltips für Elemente mit `title`- oder `data-tooltip`-Attributen. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 Erste Implementierung für dynamische Tooltips */ export const enableTooltips = () => { let activeTooltip = null; // Referenz auf den aktuellen Tooltip const showTooltip = (event) => { const target = event.target.closest('[title], [data-tooltip]'); if (!target) return; const parent = target.closest('dialog, body'); const tooltipText = target.getAttribute('data-tooltip') || target.getAttribute('title'); if (!tooltipText) return; // Entferne native Tooltips if (target.hasAttribute('title')) { target.setAttribute('data-tooltip', target.getAttribute('title')); target.removeAttribute('title'); } // Tooltip erstellen oder aktualisieren if (!activeTooltip) { activeTooltip = document.createElement('div'); activeTooltip.className = 'tooltip'; parent.appendChild(activeTooltip); } activeTooltip.innerHTML = tooltipText; activeTooltip.style.display = 'block'; // Temporär sichtbar machen, um die Größe zu berechnen const { top: targetTop, left: targetLeft, width: targetWidth, height: targetHeight } = target.getBoundingClientRect(); const { top: parentTop, left: parentLeft, width: parentWidth, height: parentHeight } = parent.getBoundingClientRect(); const top = targetTop - parentTop; const left = targetLeft - parentLeft; const width = targetWidth; const height = targetHeight; // console.log( top, targetTop, parentTop, 'x', left, targetLeft, parentLeft ); // Tooltip-Größe aktualisieren const tooltipHeight = activeTooltip.offsetHeight; const tooltipWidth = activeTooltip.offsetWidth; const halfHeight = height / 2; const halfWidth = width / 2; const tooltipHalfH = tooltipHeight / 2; const tooltipHalfW = tooltipWidth / 2; // Abstände definieren const tooltipMargin = 8; const viewportSpace = 8; // Verfügbarer Platz ermitteln const spaceLeft = left - viewportSpace; const spaceRight = window.innerWidth - ( left + width ) - viewportSpace; const spaceAbove = top - viewportSpace; const spaceBelow = window.innerHeight - ( top + height ) - viewportSpace; const position = target.getAttribute('data-tooltip-position') || 'vertical'; const alignment = target.getAttribute('data-tooltip-alignment') || (position === 'vertical' ? 'top center' : 'right middle'); let verticalAlign = alignment.match( /(top|middle|bottom)/ )[0]; let horizontalAlign = alignment.match( /(left|center|right)/ )[0]; let tooltipTop = 0; let tooltipLeft = 0; let positionClass = ''; let alignmentClass = ''; if( position === 'vertical' ) { // Prüfen, ob bei default Position ausreichend Platz vorhanden ist switch( verticalAlign ) { case 'top' : verticalAlign = spaceAbove > tooltipWidth ? 'top' : 'bottom'; break; case 'bottom': verticalAlign = spaceBelow > tooltipWidth ? 'bottom' : 'top'; break; } if( verticalAlign === 'top' ) { tooltipTop = top - tooltipHeight - tooltipMargin; positionClass = 'tooltip-top'; } if( verticalAlign === 'bottom' ) { tooltipTop = top + height + tooltipMargin; positionClass = 'tooltip-bottom'; } // Prüfen, ob bei default Alignment ausreichend Platz links und rechts vorhanden ist if( horizontalAlign === 'center' ) { if( spaceLeft <= tooltipHalfW || spaceRight <= tooltipHalfW ) { horizontalAlign = "left"; } } // Prüfen, ob bei default Alignment ausreichend Platz vorhanden ist switch( horizontalAlign ) { case 'left' : horizontalAlign = spaceRight > tooltipWidth ? 'left' : 'right'; break; case 'right' : horizontalAlign = spaceLeft > tooltipWidth ? 'right' : 'left'; break; } if( horizontalAlign === 'left' ) { tooltipLeft = left; alignmentClass = 'tooltip-align-left'; } if( horizontalAlign === 'center' ) { tooltipLeft = left + halfWidth - tooltipHalfW; alignmentClass = 'tooltip-align-center'; } if( horizontalAlign === 'right' ) { tooltipLeft = left + width - tooltipWidth; alignmentClass = 'tooltip-align-right'; } } if( position === 'horizontal' ) { // Prüfen, ob bei default Position ausreichend Platz vorhanden ist switch( horizontalAlign ) { case 'left' : horizontalAlign = spaceLeft > tooltipWidth ? 'left' : 'right'; break; case 'right' : horizontalAlign = spaceRight > tooltipWidth ? 'right' : 'left'; break; } if( horizontalAlign === 'left' ) { tooltipLeft = left - tooltipWidth - tooltipMargin; positionClass = 'tooltip-left'; } if( horizontalAlign === 'right' ) { tooltipLeft = left + width + tooltipMargin; positionClass = 'tooltip-right'; } // Prüfen, ob bei default Alignment ausreichend Platz oben und unten vorhanden ist if( verticalAlign === 'middle' ) { if( spaceAbove <= tooltipHalfH || spaceBelow <= tooltipHalfH ) { verticalAlign = "top"; } } // Prüfen, ob bei default Alignment ausreichend Platz vorhanden ist switch( verticalAlign ) { case 'top' : verticalAlign = spaceBelow > tooltipHeight ? 'top' : 'bottom'; break; case 'bottom' : verticalAlign = spaceAbove > tooltipHeight ? 'bottom' : 'top'; break; } if( verticalAlign === 'top' ) { tooltipTop = top; alignmentClass = 'tooltip-align-top'; } if( verticalAlign === 'middle' ) { tooltipTop = top + halfHeight - tooltipHalfH; alignmentClass = 'tooltip-align-middle'; } if( verticalAlign === 'bottom' ) { tooltipTop = top + height - tooltipHeight; alignmentClass = 'tooltip-align-bottom'; } } activeTooltip.style.top = `${window.scrollY + tooltipTop}px`; activeTooltip.style.left = `${window.scrollX + tooltipLeft}px`; // CSS-Klassen basierend auf Position und Ausrichtung setzen activeTooltip.className = `tooltip ${positionClass} ${alignmentClass}`; // Tooltip anzeigen activeTooltip.style.display = 'block'; target.classList.add( 'has-tooltip--active' ); // Tooltip bei Mausverlassen entfernen const removeTooltip = () => { if( activeTooltip ) { activeTooltip.remove(); activeTooltip = null; } target.classList.remove( 'has-tooltip--active' ); target.removeEventListener('mouseleave', removeTooltip); }; target.addEventListener('mouseleave', removeTooltip, { once: true }); }; // Event-Listener für `mouseover` document.body.addEventListener('mouseover', showTooltip); }; /** Datei: plugins/rest/assets.OLD/js/generator/components/paragraph.js **/ /** * generator/components/paragraph.js * * Erstellt einen anklickbaren Link, der Route-Änderungen auslöst. * * @author Sparky * @copyright 2025 ARUNOX * @version 0.0.1 * * @since 1.0.0 Datei erstellt */ import { generator } from 'plugins/rest/core/generator'; import { handleAction } from 'plugins/rest/core/helpers'; generator.extend("components/paragraph", async ( config, data, container ) => { const wrapper = document.createElement("p" ); const className = 'paragraph'; const block = config.block ? `${className} ${config.block}__${className}` : className; if( !config.text ) return; wrapper.classList.add( ...block.split(' ') ); wrapper.elements = {}; if( config.icon ) { wrapper.elements.icon = await generator.generate("components/icon", { name: config.icon, block: config.block }, {}, wrapper ); } wrapper.innerHTML += config.text; if( config.color ) { generator.generate( 'helpers/colorize', config.color, { target: 'icon' }, wrapper, wrapper.elements.icon ); } container.append( wrapper ); return wrapper; }); /** Datei: plugins/rest/assets.OLD/js/generator/components/breadcrumbs.js **/ /** * generator/components/breadcrumbs.js * * Erstellt Breadcrumbs basierend auf der aktuellen Route. * * @author Sparky * @copyright 2025 ARUNOX * @version 0.0.1 * * @since 1.0.0 Datei erstellt */ import { router } from 'plugins/rest/core/router'; import { generator } from 'plugins/rest/core/generator'; generator.extend( 'components/breadcrumbs', async ( config, data, container ) => { const wrapper = document.querySelector('#breadcrumbs'); if( !wrapper) return; if( location.pathname != config.path.cachekey || config.path.cachekey != config.path.history ) { window.history.pushState({}, "", config.path.history ); } const home = wrapper.querySelector( '.breadcrumbs__item--home'); const items = wrapper.querySelectorAll( ':not(.breadcrumbs__item--home) '); if( !wrapper ) return; wrapper.classList.add( 'breadcrumbs' ); items.forEach( element => element.remove() ); const segments = router.getBreadcrumbSegments( config.path.history ); for( const [ index, segment ] of segments.entries() ) { if( ( home && index === 0 ) || ( index <= segments.length - 1 ) ) { await generator.generate( 'components/breadcrumbs/separator', {}, {}, wrapper ); } await generator.generate( 'components/link', { name : 'item', block : 'breadcrumbs', path : index === segments.length - 1 ? false : segment.path, label : segment.label, active : index === segments.length - 1, }, {}, wrapper ); } }); generator.extend( 'components/breadcrumbs/separator', ( config, data, container ) => { const separator = document.createElement('div'); separator.classList.add('breadcrumbs__separator'); container.append( separator ); }); /** Datei: plugins/rest/assets.OLD/js/generator/components/headline.js **/ /** * generator/components/headline.js * * Erstellt einen anklickbaren Link, der Route-Änderungen auslöst. * * @author Sparky * @copyright 2025 ARUNOX * @version 0.0.1 * * @since 1.0.0 Datei erstellt */ import { generator } from 'plugins/rest/core/generator'; import { handleAction } from 'plugins/rest/core/helpers'; generator.extend( 'components/headline', async ( config, data, container ) => { const wrapper = document.createElement(`h${config.size || 2}` ); const className = 'headline'; const block = config.block ? `${className} ${config.block}__${className}` : className; wrapper.classList.add( ...block.split(' ') ); wrapper.elements = {}; if( config.icon ) { wrapper.elements.icon = await generator.generate("components/icon", { name: config.icon, block: block }, {}, wrapper ); } if( config.title ) { wrapper.elements.title = document.createTextNode( config.title ); wrapper.append( wrapper.elements.title ); } if( config.color ) { generator.generate( 'helpers/colorize', config.color, { target: 'icon' }, wrapper, wrapper.elements.icon ); } container.append( wrapper ); return wrapper; }); /** Datei: plugins/rest/assets.OLD/js/generator/components/icon.js **/ /** * generator/components/icon.js * * Erstellt ein Icon-Element, unterstützt BEM-Klassen. * * @author Sparky * @copyright 2025 ARUNOX * @version 0.0.1 * * @since 1.0.0 Datei erstellt */ import { generator } from 'plugins/rest/core/generator'; generator.extend( 'components/icon', async ( config, data, container ) => { const wrapper = document.createElement("i"); const className = 'icon'; const block = config.block ? `${className} ${config.block}__${className}` : className; wrapper.classList.add( ...config.name.split(' ') ); wrapper.classList.add( ...block.split(' ') ); if( config.color ) wrapper.classList.add(`text--${config.color}`); if( container ) { container.classList.add( 'has--icon' ); container.append( wrapper ); } return wrapper; }); /** Datei: plugins/rest/assets.OLD/js/generator/components/link.js **/ /** * generator/components/link.js * * Erstellt einen anklickbaren Link, der Route-Änderungen auslöst. * * @author Sparky * @copyright 2025 ARUNOX * @version 0.0.1 * * @since 1.0.0 Datei erstellt */ import { generator } from 'plugins/rest/core/generator'; import { handleAction } from 'plugins/rest/core/helpers'; generator.extend("components/link", async ( config, data, container ) => { const link = document.createElement("a"); const label = document.createElement("span"); const name = config.name || 'link'; const block = config.block ? `${config.block}__${name}` : name; // Standardklasse setzen oder `block` nutzen (z. B. "menu") link.classList.add( block ); link.elements = {}; if( config.title ) link.title = config.title; if( config.active ) link.classList.add( `${block}--active` ); if( !config.path ) link.classList.add( `${block}--inactive` ); if( config.icon ) link.elements.icon = await generator.generate("components/icon", { name: config.icon, block: config.block }, {}, link ); if( config.label ) { label.innerHTML = config.label; label.classList.add( `${block}__label` ); link.append( label ); } if( config.path && config.path != '#' ) { link.addEventListener("click", (event) => { event.preventDefault(); handleAction( config.path, {}, {}, 'rest' ); }); } if( container ) container.append( link ); return link; }); /** Datei: plugins/rest/assets.OLD/js/generator/components/notification.js **/ /** * generator/components/notification.js * * Erstellt Breadcrumbs basierend auf der aktuellen Route. * * @author Sparky * @copyright 2025 ARUNOX * @version 0.0.1 * * @since 1.0.0 Datei erstellt */ import { generator } from 'core/generator'; generator.extend( 'components/notification', async ( config, data, container ) => { const wrapper = document.createElement('div'); // console.log( config, data, container ); wrapper.classList.add( 'notification', `notification--${config.type || 'default'}`); if( config.icon ) { if( config.icon ) { await generator.generate("components/icon", { name: config.icon, block: 'notification' }, {}, wrapper ); } } if( config.message ) { const message = document.createElement('div'); message.classList.add('notification__message'); message.textContent = config.message; wrapper.appendChild( message ); } const closeButton = document.createElement('button'); closeButton.classList.add('notification__close'); closeButton.innerHTML = '×'; closeButton.addEventListener('click', () => { wrapper.classList.add('is-closing'); setTimeout(() => wrapper.remove(), 300); }); wrapper.appendChild( closeButton ); container.appendChild( wrapper ); // Automatisches Entfernen nach der Dauer (Standard: 5 Sekunden) if( config.duration === 0 ) return; const duration = config.duration || 5000; setTimeout(() => { wrapper.classList.add('is-closing'); setTimeout(() => wrapper.remove(), 300); }, duration); }); /** Datei: plugins/rest/assets.OLD/js/generator/components/badge.js **/ /** * generator/components/badge.js * * Erstellt ein flexibles Badge-Element im BEM-Style. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 Datei erstellt */ import { generator } from 'plugins/rest/core/generator'; generator.extend('components/badge', ( config, data, container ) => { const badge = document.createElement('span' ); badge.classList.add( 'badge', `badge--${config.color || 'primary'}` ); const text = document.createElement('span' ); text.classList.add( 'badge__text' ); text.innerHTML = config.label; badge.appendChild(text); container.appendChild(badge); return badge; }); /** Datei: plugins/rest/assets.OLD/js/generator/helpers/colorize.js **/ /** * generator/helpers/colorize.js * * Spaltengenerator für Status-Anzeigen mit Farb- und Icon-Unterstützung. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * */ import { generator } from 'plugins/rest/core/generator'; generator.extend( 'helpers/colorize', ( cellColorConfig, optionColorConfig, container, iconContainer ) => { if( typeof cellColorConfig === 'string' ) cellColorConfig = { color: cellColorConfig } if( typeof optionColorConfig === 'string' ) optionColorConfig = { color: optionColorConfig } let colorConfig = Object.assign( { target : 'background', prefix : false, }, cellColorConfig, optionColorConfig ); if( container && ['button'].includes( colorConfig.target ) ) { container.classList.add( `${colorConfig.prefix || 'button'}--colored-${colorConfig.color || 'default'}` ); container.style.backgroundColor = colorConfig.value; } if( container && ['background'].includes( colorConfig.target ) ) { container.classList.add( `${colorConfig.prefix || 'bg'}--colored-${colorConfig.color || 'default'}` ); container.style.backgroundColor = colorConfig.value; } if( iconContainer && ['icon','both'].includes( colorConfig.target ) ) { iconContainer.classList.add( `${colorConfig.prefix || 'text'}--colored-${colorConfig.color || 'default'}` ); iconContainer.style.color = colorConfig.value; } if( container && ['text','both'].includes( colorConfig.target ) ) { container.classList.add( `${colorConfig.prefix || 'text'}--colored-${colorConfig.color || 'default'}` ); container.style.color = colorConfig.value; } }); /** Datei: plugins/rest/assets.OLD/js/generator/data/form/group.js **/ /** * /data/form/group.js * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * */ import { generator } from 'plugins/rest/core/generator'; import { router } from 'plugins/rest/core/router'; generator.extend( 'data/form/group', async ( config, data, container ) => { const wrapper = document.createElement( 'fieldset' ); container.append( wrapper ); wrapper.classList.add( 'form__fieldset' ); if( config.title ) { const legend = document.createElement( 'legend' ); legend.classList.add( 'form__legend' ); legend.innerHTML = config.title; wrapper.appendChild( legend ); wrapper.classList.add( 'has-legend' ); } if( config.border !== false ) { wrapper.classList.add( 'has-border' ); } for( const contentElement of config.content ) { if( contentElement.generator ) { await generator.generate( contentElement.generator, contentElement.config, data, wrapper ); } } }); /** Datei: plugins/rest/assets.OLD/js/generator/data/form/control/checkbox.js **/ /** * generator/data/form/control/checkbox.js * * Erstellt eine Checkbox-Gruppe mit mehreren auswählbaren Optionen. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 Erstellt als separater Generator */ import { generator } from 'plugins/rest/core/generator'; import { validator } from 'plugins/rest/core/validator'; generator.extend( 'data/form/control/checkbox', async ( config, data, wrapper ) => { const container = document.createElement( 'div' ); let value = config.value || data[ config.key ] || []; let elements = []; if( !Array.isArray( value ) ) { value = JSON.parse( value ); } wrapper.append( container ); container.classList.add( 'form__control__group' ); if( config.options ) { for( const option of config.options ) { const label = document.createElement( 'label' ); const element = document.createElement( 'input' ); element.type = 'checkbox' element.name = `${ config.key }[]`; element.value = option.value; element.checked = value.includes( option.value ); element.required = !!config.required; label.classList.add( 'form__control__label' ); label.innerText = option.label; label.prepend( element ); container.append( label ); elements.push( element ); } } if( typeof config?.validate?.minChecked === 'undefined' ) { if( config.required && config.validate?.required ) { config.validate.minChecked = config.validate.required; } } if( typeof config?.validate?.minChecked === 'string' ) { config.validate.minChecked = { value : 1, message : config.validate.minChecked, }; } validator.add( elements, 'checkbox' ); await generator.generate( 'data/form/additional/defaults', config, data, undefined, wrapper ); await generator.generate( 'data/form/additional/validate', config, data, elements, wrapper ); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/form/control/range.js **/ /** * generator/data/form/control/range.js * * Erstellt ein Passwortfeld mit optionaler Bestätigungsprüfung. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 Erstellt als separater Generator */ import { generator } from 'plugins/rest/core/generator'; import { validator } from 'plugins/rest/core/validator'; generator.extend( 'data/form/control/range', async ( config, data, wrapper ) => { const element = document.createElement( 'input' ); element.type = 'range'; element.name = config.key; element.value = data[ config.key ] ?? config.value ?? '50'; element.min = config.min ?? '0'; element.max = config.max ?? '100'; element.step = config.step ?? '10'; await generator.generate( 'data/form/additional/defaults', config, data, element, wrapper ); await generator.generate( 'data/form/additional/validate', config, data, element, wrapper ); return element; }); /** Datei: plugins/rest/assets.OLD/js/generator/data/form/control/radio.js **/ /** * generator/data/form/control/radio.js * * Erstellt eine Gruppe von Radio-Buttons für eine Einzelauswahl. * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * * @since 1.0.0 Erstellt als separater Generator */ import { generator } from 'plugins/rest/core/generator'; import { validator } from 'plugins/rest/core/validator'; generator.extend( 'data/form/control/radio', async ( config, data, wrapper ) => { const container = document.createElement( 'div' ); let elements = []; wrapper.append( container ); container.classList.add( 'form__control__group' ); if( config.options ) { for( const option of config.options ) { const label = document.createElement( 'label' ); const element = document.createElement( 'input' ); element.type = 'radio' element.name = config.key; element.value = option.value; element.checked = option.value == ( config.value || data[ config.key ] ); element.required = !!config.required; label.classList.add( 'form__control__label' ); label.innerText = option.label; if( option.icon ) { const icon = await generator.generate( 'components/icon', { name : option.icon, color : option.color, block : 'form__control' }, {} ); label.prepend( icon ); } label.prepend( element ); container.append( label ); elements.push( element ); } } validator.add( elements, 'radio' ); await generator.generate( 'data/form/additional/defaults', config, data, undefined, wrapper ); await generator.generate( 'data/form/additional/validate', config, data, elements, wrapper ); }); /** Datei: plugins/rest/assets.OLD/js/generator/data/form/control/textarea.js **/ /** * generator/data/form/control/textarea.js * * Erstellt ein mehrzeiliges Texteingabefeld (`