/** 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 (`