mirror of https://github.com/rancher/dashboard.git
Featured carousel (#5585)
* Created featured carousel * Created dummy data for Featured chart * Added style * Added toggle button * Added slider width dynamically * Fixed review comments * Removed not used code * Added link to Carousel slides * Created featured carousel * Created dummy data for Featured chart * Added style * Added toggle button * Added slider width dynamically * Fixed review comments * Removed not used code * Added link to Carousel slides * Merged master * Added featured annotation * Added auto scroll on page load * Fixed filtering of featured charts data * Removed duplicate keys
This commit is contained in:
parent
4861b1e490
commit
cd3a06269c
|
|
@ -0,0 +1,207 @@
|
||||||
|
.theme-dark {
|
||||||
|
// Local variables for reused colors
|
||||||
|
|
||||||
|
//dark sidebar
|
||||||
|
$darkest: #141419;
|
||||||
|
|
||||||
|
//dark body
|
||||||
|
$darker: #1b1c21;
|
||||||
|
|
||||||
|
//dark inputs
|
||||||
|
$dark: #27292e;
|
||||||
|
|
||||||
|
//dark borders and button
|
||||||
|
$medium: #4a4b52;
|
||||||
|
|
||||||
|
// dark disabled,
|
||||||
|
$light: #6c6c76;
|
||||||
|
|
||||||
|
//dark secondary
|
||||||
|
$lighter: #b6b6c2;
|
||||||
|
|
||||||
|
// dark main text
|
||||||
|
$lightest: #ffffff;
|
||||||
|
|
||||||
|
$secondary: $lighter;
|
||||||
|
$disabled: $light;
|
||||||
|
|
||||||
|
//Contrast colors
|
||||||
|
$contrasted-dark: $lightest !default;
|
||||||
|
$contrasted-light: $darkest !default;
|
||||||
|
|
||||||
|
|
||||||
|
--default : #{$dark};
|
||||||
|
--default-text : #{$light};
|
||||||
|
--default-hover-bg : #{darken($dark, 10%)};
|
||||||
|
--default-hover-text : #{saturate($lightest, 20%)};
|
||||||
|
--default-active-bg : #{darken($dark, 25%)};
|
||||||
|
--default-active-text : #{contrast-color(darken($dark, 25%))};
|
||||||
|
--default-border : #($dark);
|
||||||
|
--default-banner-bg : #{rgba($dark, 0.15)};
|
||||||
|
--default-light-bg : #{rgba($dark, 0.05)};
|
||||||
|
--slider-light-bg : #{rgba($darker, 1)};
|
||||||
|
--slider-light-bg-right : #{rgba($darker, 0)};
|
||||||
|
|
||||||
|
--muted : #{$disabled};
|
||||||
|
|
||||||
|
--body-bg : #{$darker};
|
||||||
|
--body-text : #{$lightest};
|
||||||
|
--scrollbar-thumb : #{$medium};
|
||||||
|
--scrollbar-thumb-dropdown : #{$medium};
|
||||||
|
|
||||||
|
--header-bg : #{$darker};
|
||||||
|
--header-border : #{$medium};
|
||||||
|
--header-btn-bg : transparent;
|
||||||
|
--header-btn-text : #{$lightest};
|
||||||
|
--header-input-text : #{$lightest};
|
||||||
|
|
||||||
|
// Header, Footer and Consent banner defaults
|
||||||
|
--banner-text-color : #{$lightest};
|
||||||
|
|
||||||
|
--nav-bg : #{$darkest};
|
||||||
|
--nav-active : var(--primary-active-bg);
|
||||||
|
--nav-border : #{$medium};
|
||||||
|
--nav-hover : var(--primary);
|
||||||
|
--nav-expander-hover : var(--primary-banner-bg);
|
||||||
|
|
||||||
|
--disabled-bg : #{darken($disabled, 10%)};
|
||||||
|
--disabled-text : #{$secondary};
|
||||||
|
--box-bg : #{$darkest};
|
||||||
|
--subtle-border : #{$darkest};
|
||||||
|
--border : #{$medium};
|
||||||
|
|
||||||
|
--topmenu-bg : #{$darkest};
|
||||||
|
--topmenu-text : #{$lightest};
|
||||||
|
--topmost-border : #{$medium};
|
||||||
|
--topmost-shadow : #{lighten($darkest, 5%)};
|
||||||
|
--topmost-light-hover : #{$medium};
|
||||||
|
|
||||||
|
--accent-btn : var(--primary-banner-bg);
|
||||||
|
--accent-btn-hover : var(--primary);
|
||||||
|
--accent-btn-hover-text : #{$lightest};
|
||||||
|
|
||||||
|
--modal-bg : #{$dark};
|
||||||
|
--modal-border : #{$medium};
|
||||||
|
--overlay-bg : #{rgba($darkest, 0.75)};
|
||||||
|
--shadow : #{rgba($darkest, 0.9)};
|
||||||
|
|
||||||
|
--checkbox-tick : #{$lightest};
|
||||||
|
--checkbox-border : #{$medium};
|
||||||
|
--checkbox-tick-disabled : #{lighten($disabled, 50%)};
|
||||||
|
--checkbox-disabled-bg : #{$disabled};
|
||||||
|
--checkbox-ticked-bg : var(--primary);
|
||||||
|
|
||||||
|
--dropdown-bg : #{mix($medium, $dark, 10%)};
|
||||||
|
--dropdown-border : #{$light};
|
||||||
|
--dropdown-divider : #{$light};
|
||||||
|
--dropdown-text : #{$link};
|
||||||
|
--dropdown-active-text : #{$lightest};
|
||||||
|
--dropdown-active-bg : #{$selected};
|
||||||
|
--dropdown-hover-text : #{$lightest};
|
||||||
|
--dropdown-hover-bg : #{$link};
|
||||||
|
--dropdown-disabled-bg : #{$disabled};
|
||||||
|
--dropdown-disabled-text : #{$disabled};
|
||||||
|
|
||||||
|
--input-text : #{$lightest};
|
||||||
|
--input-label : #{$lighter};
|
||||||
|
--input-placeholder : #{$disabled};
|
||||||
|
--input-border : var(--border);
|
||||||
|
--input-bg : var(--body-bg);
|
||||||
|
--input-bg-accent : #{darken($dark, 3%)};
|
||||||
|
--input-hover-bg : var(--box-bg);
|
||||||
|
--input-focus-bg : var(--box-bg);
|
||||||
|
--input-disabled-text : #{darken($lightest, 50%)};
|
||||||
|
--input-disabled-label : #{darken($lighter, 30%)};
|
||||||
|
--input-disabled-bg : #{darken($disabled, 30%)};
|
||||||
|
--input-disabled-border : #{darken($medium, 30%)};
|
||||||
|
--input-disabled-placeholder : #{darken($disabled, 10%)};
|
||||||
|
--input-addon-bg : #{$darker};
|
||||||
|
|
||||||
|
--progress-bg : #{$medium};
|
||||||
|
--progress-divider : #{$lightest};
|
||||||
|
|
||||||
|
--sortable-table-bg : #{lighten($darkest, 10%)};
|
||||||
|
--sortable-table-row-bg : #{$darker};;
|
||||||
|
--sortable-table-header-bg : #{$darkest};
|
||||||
|
--sortable-table-top-divider : var(--border);
|
||||||
|
--sortable-table-hover-bg : #{$darkest};
|
||||||
|
--sortable-table-selected-bg : var(--primary-light-bg);
|
||||||
|
--sortable-table-group-label : #{$lighter};
|
||||||
|
|
||||||
|
--tag-primary : #{$lightest};
|
||||||
|
--tag-bg : #{$medium};
|
||||||
|
|
||||||
|
--popover-bg : var(--body-bg);
|
||||||
|
--popover-border : var(--border);
|
||||||
|
--popover-text : var(--body-text);
|
||||||
|
|
||||||
|
--tooltip-bg : #{$medium};
|
||||||
|
--tooltip-border : var(--tag-primary);
|
||||||
|
--tooltip-text : var(--body-text);
|
||||||
|
--tooltip-text-warning : var(--body-text);
|
||||||
|
|
||||||
|
|
||||||
|
--tabbed-border : #{$medium};
|
||||||
|
--tabbed-sidebar-bg : #{$darkest};
|
||||||
|
--tabbed-container-bg : #{mix($medium, $dark, 20%)};
|
||||||
|
|
||||||
|
--yaml-editor-bg : #{$darkest};
|
||||||
|
|
||||||
|
--diff-border : var(--border);
|
||||||
|
--diff-header-bg : var(--nav-bg);
|
||||||
|
--diff-header-border : var(--border);
|
||||||
|
--diff-header : #{rgba($darkest, 0.3)};
|
||||||
|
--diff-linenum-bg : var(--nav-bg);
|
||||||
|
--diff-linenum : var(--muted);
|
||||||
|
--diff-linenum-border : var(--border);
|
||||||
|
--diff-line-ins-bg : $success;
|
||||||
|
--diff-line-del-bg : #{rgba($error, 0.75)};
|
||||||
|
--diff-del-bg : #{rgba($error, 0.3)};
|
||||||
|
--diff-del-border : #{$error};
|
||||||
|
--diff-ins-bg : #{rgba($success, 0.3)};
|
||||||
|
--diff-ins-border : #{rgba($success, 0.5)};
|
||||||
|
--diff-chg-ins : #{rgba($success, 0.25)};
|
||||||
|
--diff-chg-del : #{rgba($warning, 0.5)};
|
||||||
|
--diff-chg-dela : #{rgba($warning, 1)};
|
||||||
|
--diff-empty-placeholder : #{$darker};
|
||||||
|
|
||||||
|
|
||||||
|
--wm-tabs-bg : #{mix($medium, $dark, 10%)};
|
||||||
|
--wm-tab-bg : #{$darkest};
|
||||||
|
--wm-closer-hover-bg : #{$medium};
|
||||||
|
--wm-tab-active-bg : #{$darker};
|
||||||
|
--wm-title-bg : #{$darkest};
|
||||||
|
--wm-title-border : #{$medium};
|
||||||
|
--wm-body-bg : #{$darkest};
|
||||||
|
--wm-border : black;
|
||||||
|
|
||||||
|
--glance-divider : #{$medium};
|
||||||
|
|
||||||
|
--resource-gauge-back-circle : 74, 75, 82, 0.5;
|
||||||
|
|
||||||
|
--simple-box-bg : #{$darker};
|
||||||
|
--simple-box-border : #{$medium};
|
||||||
|
--simple-box-divider : #{$medium};
|
||||||
|
--simple-box-shadow : none;
|
||||||
|
|
||||||
|
--terminal-bg : var(--wm-body-bg);
|
||||||
|
--terminal-cursor : var(--warning);
|
||||||
|
--terminal-selection : #{$selected};
|
||||||
|
--terminal-text : var(--body-text);
|
||||||
|
|
||||||
|
--logs-bg : var(--wm-body-bg);
|
||||||
|
--logs-highlight : var(--wm-body-bg);
|
||||||
|
--logs-highlight-bg : var(--warning);
|
||||||
|
--logs-text : var(--body-text);
|
||||||
|
|
||||||
|
--gauge-divider : rgba(255, 255, 255, 0.3);
|
||||||
|
--gauge-success-primary : 75, 95, 64;
|
||||||
|
--gauge-success-secondary : 150, 189, 127;
|
||||||
|
--gauge-warning-primary : 218, 195, 66;
|
||||||
|
--gauge-warning-secondary : 109, 98, 33;
|
||||||
|
--gauge-error-primary : 239, 90, 83;
|
||||||
|
--gauge-error-secondary : 120, 45, 42;
|
||||||
|
|
||||||
|
--product-icon : #{$lighter};
|
||||||
|
--product-icon-active : #{$lightest};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,536 @@
|
||||||
|
// Local variables for reused colors
|
||||||
|
//light main text
|
||||||
|
$darkest : #141419;
|
||||||
|
|
||||||
|
//light secondary
|
||||||
|
$darker : #6C6C76;
|
||||||
|
|
||||||
|
//light disabled
|
||||||
|
$dark : #B6B6C2;
|
||||||
|
|
||||||
|
//light border and buttons
|
||||||
|
$medium : #DCDEE7;
|
||||||
|
|
||||||
|
//light inputs
|
||||||
|
$light : #EEEFF4;
|
||||||
|
|
||||||
|
//light sidebar and box
|
||||||
|
$lighter : #F4F5FA;
|
||||||
|
|
||||||
|
//light body bg
|
||||||
|
$lightest : #FFFFFF;
|
||||||
|
|
||||||
|
//color for items that are not enabled
|
||||||
|
$disabled : $medium;
|
||||||
|
|
||||||
|
$primary : #3D98D3;
|
||||||
|
$secondary : $darker;
|
||||||
|
$link : #3D98D3;
|
||||||
|
|
||||||
|
// Status colors
|
||||||
|
$success : #5D995D;
|
||||||
|
$warning : #DAC342;
|
||||||
|
$error : #F64747;
|
||||||
|
$info : #3D98D3;
|
||||||
|
|
||||||
|
$contrasted-dark: $darkest !default;
|
||||||
|
$contrasted-light: $lightest !default;
|
||||||
|
|
||||||
|
// Text selection color for terminal window (we don't want this to change with the primary color)
|
||||||
|
// The terminal alway uses a light background, so okay to use a fixed color
|
||||||
|
$selected: rgba(#3D98D3, .5);
|
||||||
|
|
||||||
|
BODY, .theme-light {
|
||||||
|
|
||||||
|
--primary : #{$primary};
|
||||||
|
--primary-text : #{contrast-color($primary)};
|
||||||
|
--primary-hover-bg : #{darken($primary, 10%)};
|
||||||
|
--primary-hover-text : #{saturate($lightest, 20%)};
|
||||||
|
--primary-active-bg : #{darken($primary, 25%)};
|
||||||
|
--primary-active-text : #{contrast-color(darken($primary, 25%))};
|
||||||
|
--primary-border : #{$primary};
|
||||||
|
--primary-banner-bg : #{rgba($primary, 0.15)};
|
||||||
|
--primary-light-bg : #{rgba($primary, 0.05)};
|
||||||
|
|
||||||
|
|
||||||
|
.text-primary {
|
||||||
|
color: var(--primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-primary {
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: var(--primary-text);
|
||||||
|
|
||||||
|
&.btn:hover {
|
||||||
|
color: var(--primary-hover-text);
|
||||||
|
background: var(--primary-hover-bg);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn:active {
|
||||||
|
color: var(--primary-active-text);
|
||||||
|
background: var(--primary-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--link : #{$link};
|
||||||
|
--link-text : #{contrast-color($link)};
|
||||||
|
--link-hover-bg : #{darken($link, 10%)};
|
||||||
|
--link-hover-text : #{saturate($lightest, 20%)};
|
||||||
|
--link-active-bg : #{darken($link, 25%)};
|
||||||
|
--link-active-text : #{contrast-color(darken($link, 25%))};
|
||||||
|
--link-border : #{$link};
|
||||||
|
--link-banner-bg : #{rgba($link, 0.15)};
|
||||||
|
--link-light-bg : #{rgba($link, 0.05)};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.text-link {
|
||||||
|
color: var(--link) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-link {
|
||||||
|
background-color: var(--link);
|
||||||
|
color: var(--link-text);
|
||||||
|
|
||||||
|
&.btn:hover {
|
||||||
|
color: var(--link-hover-text);
|
||||||
|
background: var(--link-hover-bg);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn:active {
|
||||||
|
color: var(--link-active-text);
|
||||||
|
background: var(--link-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--default : #{$light};
|
||||||
|
--default-text : #{contrast-color($light)};
|
||||||
|
--default-hover-bg : #{darken($light, 10%)};
|
||||||
|
--default-hover-text : #{saturate($lightest, 20%)};
|
||||||
|
--default-active-bg : #{darken($light, 25%)};
|
||||||
|
--default-active-text : #{contrast-color(darken($light, 25%))};
|
||||||
|
--default-border : #{$light};
|
||||||
|
--default-banner-bg : #{rgba($light, 0.15)};
|
||||||
|
--default-light-bg : #{rgba($light, 0.05)};
|
||||||
|
--slider-light-bg : #{rgba($lightest, 1)};
|
||||||
|
--slider-light-bg-right : #{rgba($lightest, 0)};
|
||||||
|
|
||||||
|
|
||||||
|
.text-default {
|
||||||
|
color: var(--default) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-default {
|
||||||
|
background-color: var(--default);
|
||||||
|
color: var(--default-text);
|
||||||
|
|
||||||
|
&.btn:hover {
|
||||||
|
color: var(--default-hover-text);
|
||||||
|
background: var(--default-hover-bg);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn:active {
|
||||||
|
color: var(--default-active-text);
|
||||||
|
background: var(--default-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--muted : #{$dark};
|
||||||
|
|
||||||
|
.text-muted {
|
||||||
|
color: var(--muted) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
--darker : #{$darker};
|
||||||
|
--darker-text : #{contrast-color($darker)};
|
||||||
|
--darker-hover-bg : #{darken($darker, 10%)};
|
||||||
|
--darker-hover-text : #{saturate($lightest, 20%)};
|
||||||
|
--darker-active-bg : #{darken($darker, 25%)};
|
||||||
|
--darker-active-text : #{contrast-color(darken($darker, 25%))};
|
||||||
|
--darker-border : #{$darker};
|
||||||
|
--darker-banner-bg : #{rgba($darker, 0.15)};
|
||||||
|
--darker-light-bg : #{rgba($darker, 0.05)};
|
||||||
|
|
||||||
|
.text-darker {
|
||||||
|
color: var(--default) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-darker {
|
||||||
|
background-color: var(--darker);
|
||||||
|
color: var(--darker-text);
|
||||||
|
|
||||||
|
&.btn:hover {
|
||||||
|
color: var(--darker-hover-text);
|
||||||
|
background: var(--darker-hover-bg);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn:active {
|
||||||
|
color: var(--darker-active-text);
|
||||||
|
background: var(--darker-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--success : #{$success};
|
||||||
|
--success-text : #{contrast-color($success)};
|
||||||
|
--success-hover-bg : #{darken($success, 10%)};
|
||||||
|
--success-hover-text : #{saturate($lightest, 20%)};
|
||||||
|
--success-active-bg : #{darken($success, 25%)};
|
||||||
|
--success-active-text : #{contrast-color(darken($success, 25%))};
|
||||||
|
--success-border : #{$success};
|
||||||
|
--success-banner-bg : #{rgba($success, 0.15)};
|
||||||
|
--success-light-bg : #{rgba($success, 0.05)};
|
||||||
|
|
||||||
|
.text-success {
|
||||||
|
color: var(--success) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-success {
|
||||||
|
background-color: var(--success);
|
||||||
|
color: var(--success-text);
|
||||||
|
|
||||||
|
&.btn:hover {
|
||||||
|
color: var(--success-hover-text);
|
||||||
|
background: var(--success-hover-bg);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn:active {
|
||||||
|
color: var(--success-active-text);
|
||||||
|
background: var(--success-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--info : #{$info};
|
||||||
|
--info-text : #{contrast-color($info)};
|
||||||
|
--info-hover-bg : #{darken($info, 10%)};
|
||||||
|
--info-hover-text : #{saturate($lightest, 20%)};
|
||||||
|
--info-active-bg : #{darken($info, 25%)};
|
||||||
|
--info-active-text : #{contrast-color(darken($info, 25%))};
|
||||||
|
--info-border : #{$info};
|
||||||
|
--info-banner-bg : #{rgba($info, 0.15)};
|
||||||
|
--info-light-bg : #{rgba($info, 0.05)};
|
||||||
|
|
||||||
|
.text-info {
|
||||||
|
color: var(--info) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-info {
|
||||||
|
background-color: var(--info);
|
||||||
|
color: var(--info-text);
|
||||||
|
|
||||||
|
&.btn:hover {
|
||||||
|
color: var(--info-hover-text);
|
||||||
|
background: var(--info-hover-bg);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn:active {
|
||||||
|
color: var(--info-active-text);
|
||||||
|
background: var(--info-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--warning : #{$warning};
|
||||||
|
--warning-text : #{contrast-color($warning)};
|
||||||
|
--warning-hover-bg : #{darken($warning, 10%)};
|
||||||
|
--warning-hover-text : #{saturate($lightest, 20%)};
|
||||||
|
--warning-active-bg : #{darken($warning, 25%)};
|
||||||
|
--warning-active-text : #{contrast-color(darken($warning, 25%))};
|
||||||
|
--warning-border : #{$warning};
|
||||||
|
--warning-banner-bg : #{rgba($warning, 0.15)};
|
||||||
|
--warning-light-bg : #{rgba($warning, 0.05)};
|
||||||
|
|
||||||
|
.text-warning {
|
||||||
|
color: var(--error) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-warning {
|
||||||
|
background-color: var(--warning);
|
||||||
|
color: var(--warning-text);
|
||||||
|
|
||||||
|
&.btn:hover {
|
||||||
|
color: var(--warning-hover-text);
|
||||||
|
background: var(--warning-hover-bg);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn:active {
|
||||||
|
color: var(--warning-active-text);
|
||||||
|
background: var(--warning-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--error : #{$error};
|
||||||
|
--error-text : #{contrast-color($error)};
|
||||||
|
--error-hover-bg : #{darken($error, 10%)};
|
||||||
|
--error-hover-text : #{saturate($lightest, 20%)};
|
||||||
|
--error-active-bg : #{darken($error, 25%)};
|
||||||
|
--error-active-text : #{contrast-color(darken($error, 25%))};
|
||||||
|
--error-border : #{$error};
|
||||||
|
--error-banner-bg : #{rgba($error, 0.15)};
|
||||||
|
--error-light-bg : #{rgba($error, 0.05)};
|
||||||
|
|
||||||
|
|
||||||
|
.text-error {
|
||||||
|
color: var(--error) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-error {
|
||||||
|
background-color: var(--error);
|
||||||
|
color: var(--error-text);
|
||||||
|
|
||||||
|
&.btn:hover {
|
||||||
|
color: var(--error-hover-text);
|
||||||
|
background: var(--error-hover-bg);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn:active {
|
||||||
|
color: var(--error-active-text);
|
||||||
|
background: var(--error-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--body-bg : #{$lightest};
|
||||||
|
--body-text : #{$darkest};
|
||||||
|
--scrollbar-thumb : #{$dark};
|
||||||
|
--scrollbar-thumb-dropdown : #{$lighter};
|
||||||
|
--scrollbar-track : transparent;
|
||||||
|
|
||||||
|
// Header, Footer and Consent banner defaults
|
||||||
|
--banner-text-color : #{$darkest};
|
||||||
|
|
||||||
|
--header-bg : #{$lightest};
|
||||||
|
--header-btn-bg : transparent;
|
||||||
|
--header-btn-text : #{$darkest};
|
||||||
|
--header-input-text : #{$darkest};
|
||||||
|
--header-height : 55px;
|
||||||
|
--header-border : #{$medium};
|
||||||
|
--header-border-size : 1px;
|
||||||
|
--nav-width : 230px;
|
||||||
|
--nav-bg : #{$lightest};
|
||||||
|
--nav-active : #{$light};
|
||||||
|
--nav-hover : #{$medium};
|
||||||
|
--nav-expander-hover : #{darken($medium, 10%)};
|
||||||
|
--nav-border : #{$medium};
|
||||||
|
--nav-border-size : 1px;
|
||||||
|
|
||||||
|
--footer-bg : transparent;
|
||||||
|
--footer-height : 0px;
|
||||||
|
|
||||||
|
--topmenu-bg : #{$lightest};
|
||||||
|
--topmenu-text : #{$darkest};
|
||||||
|
--topmost-border : #{$medium};
|
||||||
|
--topmost-shadow : #{lighten($lightest, 10%)};
|
||||||
|
--topmost-light-hover : #{$light};
|
||||||
|
|
||||||
|
--disabled-bg : #{$disabled};
|
||||||
|
--disabled-text : #{$secondary};
|
||||||
|
--box-bg : #{$lighter};
|
||||||
|
--subtle-border : #{$medium};
|
||||||
|
--border : #{$medium};
|
||||||
|
--border-width : 1px;
|
||||||
|
--border-radius : 4px;
|
||||||
|
--outline : var(--primary);
|
||||||
|
--outline-width : 1px;
|
||||||
|
|
||||||
|
--accent-btn : var(--primary-banner-bg);
|
||||||
|
--accent-btn-hover : var(--primary);
|
||||||
|
--accent-btn-hover-text : #{$lightest};
|
||||||
|
|
||||||
|
--modal-bg : #{$lightest};
|
||||||
|
--modal-border : #{$dark};
|
||||||
|
--overlay-bg : #{rgba($lighter, 0.75)};
|
||||||
|
--shadow : #{rgba($medium, 0.85)};
|
||||||
|
|
||||||
|
--checkbox-tick : #{$lightest};
|
||||||
|
--checkbox-border : #{$medium};
|
||||||
|
--checkbox-tick-disabled : #{darken($disabled, 40%)};
|
||||||
|
--checkbox-disabled-bg : #{$disabled};
|
||||||
|
--checkbox-ticked-bg : #{$link};
|
||||||
|
|
||||||
|
--dropdown-bg : #{$lightest};
|
||||||
|
--dropdown-border : #{$medium};
|
||||||
|
--dropdown-divider : #{$medium};
|
||||||
|
--dropdown-text : #{$link};
|
||||||
|
--dropdown-active-text : #{$lightest};
|
||||||
|
--dropdown-active-bg : #{$dark};
|
||||||
|
--dropdown-hover-text : #{$lightest};
|
||||||
|
--dropdown-hover-bg : var(--primary);
|
||||||
|
--dropdown-disabled-text : var(--muted);
|
||||||
|
--dropdown-disabled-bg : #{$disabled};
|
||||||
|
|
||||||
|
--card-badge-text : #ffffff;
|
||||||
|
|
||||||
|
// UNUSED?
|
||||||
|
--card-header : var(--primary-banner-bg);
|
||||||
|
|
||||||
|
--input-text : #{$darkest};
|
||||||
|
--input-label : #{$secondary};
|
||||||
|
--input-placeholder : #{darken($disabled, 10%)};
|
||||||
|
--input-border : var(--border);
|
||||||
|
--input-bg : var(--body-bg);
|
||||||
|
--input-bg-accent : #{darken($light, 2%)};
|
||||||
|
--input-hover-bg : var(--box-bg);
|
||||||
|
--input-focus-bg : var(--box-bg);
|
||||||
|
--input-disabled-text : #{darken($disabled, 60%)};
|
||||||
|
--input-disabled-label : #{darken($disabled, 40%)};
|
||||||
|
--input-disabled-bg : #{$disabled};
|
||||||
|
--input-disabled-border : #{darken($medium, 10%)};
|
||||||
|
--input-disabled-placeholder : #{darken($medium, 15%)};
|
||||||
|
--input-addon-bg : #{$darker};
|
||||||
|
--input-hover-border : #{darken($medium, 25%)};
|
||||||
|
|
||||||
|
--progress-bg : #{$medium};
|
||||||
|
--progress-divider : #{$medium};
|
||||||
|
|
||||||
|
--sortable-table-bg : #{darken($lightest, 5%)};
|
||||||
|
--sortable-table-row-bg : #{$lightest};
|
||||||
|
--sortable-table-header-bg : #{$lighter};
|
||||||
|
--sortable-table-top-divider : var(--border);
|
||||||
|
--sortable-table-body-divider : #{$medium};
|
||||||
|
|
||||||
|
--sortable-table-hover-bg : #{$lighter};
|
||||||
|
//--sortable-table-selected-bg : #{rgba($primary, 0.02)};
|
||||||
|
|
||||||
|
--sortable-table-selected-bg : var(--primary-light-bg);
|
||||||
|
--sortable-table-group-label : #{$secondary};
|
||||||
|
|
||||||
|
--tag-primary : #{$darkest};
|
||||||
|
--tag-bg : #{$medium};
|
||||||
|
|
||||||
|
--popover-bg : var(--body-bg);
|
||||||
|
--popover-border : var(--border);
|
||||||
|
--popover-text : var(--body-text);
|
||||||
|
--popover-border-radius : var(--border-radius);
|
||||||
|
--tooltip-bg : #{$medium};
|
||||||
|
--tooltip-border : var(--tag-primary);
|
||||||
|
--tooltip-text : var(--tag-primary);
|
||||||
|
--tooltip-bg-warning : #{rgba($warning, 0.8)};
|
||||||
|
--tooltip-text-warning : var(--body-text);
|
||||||
|
|
||||||
|
--tabbed-border : #{$medium};
|
||||||
|
--tabbed-sidebar-bg : #{$lighter};
|
||||||
|
--tabbed-container-bg : #{mix($light, $lighter, 15%)};
|
||||||
|
|
||||||
|
--yaml-editor-bg : #{$lighter};
|
||||||
|
|
||||||
|
--diff-border : var(--border);
|
||||||
|
--diff-header-bg : var(--nav-bg);
|
||||||
|
--diff-header-border : var(--border);
|
||||||
|
--diff-header : #{rgba($darkest, 0.3)};
|
||||||
|
--diff-linenum-bg : var(--nav-bg);
|
||||||
|
--diff-linenum : var(--muted);
|
||||||
|
--diff-linenum-border : var(--border);
|
||||||
|
--diff-line-ins-bg : $success;
|
||||||
|
--diff-line-del-bg : #{rgba($error, 0.75)};
|
||||||
|
--diff-del-bg : #{rgba($error, 0.3)};
|
||||||
|
--diff-del-border : #{$error};
|
||||||
|
--diff-ins-bg : #{rgba($success, 0.3)};
|
||||||
|
--diff-ins-border : #{rgba($success, 0.5)};
|
||||||
|
--diff-chg-ins : #{rgba($success, 0.25)};
|
||||||
|
--diff-chg-del : #{rgba($warning, 0.5)};
|
||||||
|
--diff-empty-placeholder : #{$lightest};
|
||||||
|
|
||||||
|
--wm-tabs-bg : #{$medium};
|
||||||
|
--wm-tab-bg : #{$light};
|
||||||
|
--wm-closer-hover-bg : #{$lighter};
|
||||||
|
--wm-tab-active-bg : #{$lighter};
|
||||||
|
--wm-title-bg : #{$lightest};
|
||||||
|
--wm-title-border : #{$medium};
|
||||||
|
--wm-body-bg : #{$lighter};
|
||||||
|
--wm-border : var(--border);
|
||||||
|
--wm-tab-height : 29px;
|
||||||
|
|
||||||
|
--glance-bg-rgb : 61, 152, 211;
|
||||||
|
--glance-divider : #{$medium};
|
||||||
|
|
||||||
|
--resource-gauge-back-circle : 255, 255, 255, 0.15;
|
||||||
|
|
||||||
|
--simple-box-bg : #{$lightest};
|
||||||
|
--simple-box-border : #{$medium};
|
||||||
|
--simple-box-divider : #{$medium};
|
||||||
|
--simple-box-shadow : none;
|
||||||
|
|
||||||
|
--terminal-bg : var(--body-bg);
|
||||||
|
--terminal-cursor : var(--warning);
|
||||||
|
--terminal-selection : #{$selected};
|
||||||
|
--terminal-text : var(--body-text);
|
||||||
|
|
||||||
|
--logs-bg : var(--wm-body-bg);
|
||||||
|
--logs-highlight : var(--wm-body-bg);
|
||||||
|
--logs-highlight-bg : var(--warning);
|
||||||
|
--logs-text : var(--body-text);
|
||||||
|
|
||||||
|
--gauge-divider : #{$lightest};
|
||||||
|
--gauge-zero : #{$medium};
|
||||||
|
--gauge-success-primary : 150, 189, 127;
|
||||||
|
--gauge-success-secondary : 190, 211, 172;
|
||||||
|
--gauge-warning-primary : 238, 226, 176;
|
||||||
|
--gauge-warning-secondary : 218, 195, 66;
|
||||||
|
--gauge-error-primary : 249, 186, 171;
|
||||||
|
--gauge-error-secondary : 239, 90, 83;
|
||||||
|
|
||||||
|
--sizzle-0 : 180, 210, 30;
|
||||||
|
--sizzle-1 : 225, 45, 74;
|
||||||
|
--sizzle-2 : 212, 66, 148;
|
||||||
|
--sizzle-3 : 0, 169, 217;
|
||||||
|
--sizzle-4 : 244, 136, 68;
|
||||||
|
--sizzle-5 : 0, 147, 128;
|
||||||
|
--sizzle-6 : 136, 81, 165;
|
||||||
|
--sizzle-7 : 45, 47, 149;
|
||||||
|
--sizzle-8 : 255, 235, 0;
|
||||||
|
|
||||||
|
--sizzle-success : #{red($success)}, #{green($success)}, #{blue($success)};
|
||||||
|
--sizzle-info : #{red($info)}, #{green($info)}, #{blue($info)};
|
||||||
|
--sizzle-warning : #{red($warning)}, #{green($warning)}, #{blue($warning)};
|
||||||
|
--sizzle-error : #{red($error)}, #{green($error)}, #{blue($error)};
|
||||||
|
--sizzle-unknown : #{red($disabled)},#{green($disabled)},#{blue($disabled)};
|
||||||
|
|
||||||
|
$rancher : $primary;
|
||||||
|
$partner : #FEA424;
|
||||||
|
$other : #614EA2;
|
||||||
|
|
||||||
|
--app-rancher-accent : #{$rancher};
|
||||||
|
--app-rancher-accent-text : #ffffff;
|
||||||
|
|
||||||
|
--app-partner-accent : #{$partner};
|
||||||
|
--app-partner-accent-text : black;
|
||||||
|
|
||||||
|
--app-color1-accent : rgba(var(--sizzle-1), 1);
|
||||||
|
--app-color1-accent-text : white;
|
||||||
|
|
||||||
|
--app-color2-accent : rgba(var(--sizzle-2), 1);
|
||||||
|
--app-color2-accent-text : white;
|
||||||
|
|
||||||
|
--app-color3-accent : rgba(var(--sizzle-3), 1);
|
||||||
|
--app-color3-accent-text : white;
|
||||||
|
|
||||||
|
--app-color4-accent : rgba(var(--sizzle-4), 1);
|
||||||
|
--app-color4-accent-text : white;
|
||||||
|
|
||||||
|
--app-color5-accent : rgba(var(--sizzle-5), 1);
|
||||||
|
--app-color5-accent-text : white;
|
||||||
|
|
||||||
|
--app-color6-accent : rgba(var(--sizzle-6), 1);
|
||||||
|
--app-color6-accent-text : white;
|
||||||
|
|
||||||
|
--app-color7-accent : rgba(var(--sizzle-7), 1);
|
||||||
|
--app-color7-accent-text : white;
|
||||||
|
|
||||||
|
--app-color8-accent : rgba(var(--sizzle-8), 1);
|
||||||
|
--app-color8-accent-text : white;
|
||||||
|
|
||||||
|
--product-icon : #{$darker};
|
||||||
|
--product-icon-active : #{$darkest};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,661 @@
|
||||||
|
<script>
|
||||||
|
import AsyncButton from '@/components/AsyncButton';
|
||||||
|
import Loading from '@/components/Loading';
|
||||||
|
import Banner from '@/components/Banner';
|
||||||
|
import Carousel from '@/components/Carousel';
|
||||||
|
import ButtonGroup from '@/components/ButtonGroup';
|
||||||
|
import SelectIconGrid from '@/components/SelectIconGrid';
|
||||||
|
import TypeDescription from '@/components/TypeDescription';
|
||||||
|
import {
|
||||||
|
REPO_TYPE, REPO, CHART, VERSION, SEARCH_QUERY, _FLAGGED, CATEGORY, DEPRECATED, HIDDEN, OPERATING_SYSTEM
|
||||||
|
} from '@/config/query-params';
|
||||||
|
import { lcFirst } from '@/utils/string';
|
||||||
|
import { sortBy } from '@/utils/sort';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import Checkbox from '@/components/form/Checkbox';
|
||||||
|
import Select from '@/components/form/Select';
|
||||||
|
import { mapPref, HIDE_REPOS, SHOW_PRE_RELEASE, SHOW_CHART_MODE } from '@/store/prefs';
|
||||||
|
import { removeObject, addObject, findBy } from '@/utils/array';
|
||||||
|
import { compatibleVersionsFor, filterAndArrangeCharts } from '@/store/catalog';
|
||||||
|
import { CATALOG } from '@/config/labels-annotations';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
AsyncButton,
|
||||||
|
Banner,
|
||||||
|
Carousel,
|
||||||
|
ButtonGroup,
|
||||||
|
Loading,
|
||||||
|
Checkbox,
|
||||||
|
Select,
|
||||||
|
SelectIconGrid,
|
||||||
|
TypeDescription
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
await this.$store.dispatch('catalog/load');
|
||||||
|
|
||||||
|
const query = this.$route.query;
|
||||||
|
|
||||||
|
this.searchQuery = query[SEARCH_QUERY] || '';
|
||||||
|
this.showDeprecated = query[DEPRECATED] === _FLAGGED;
|
||||||
|
this.showHidden = query[HIDDEN] === _FLAGGED;
|
||||||
|
this.category = query[CATEGORY] || '';
|
||||||
|
this.allRepos = this.areAllEnabled();
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
allRepos: null,
|
||||||
|
category: null,
|
||||||
|
operatingSystem: null,
|
||||||
|
searchQuery: null,
|
||||||
|
showDeprecated: null,
|
||||||
|
showHidden: null,
|
||||||
|
chartMode: this.$store.getters['prefs/get'](SHOW_CHART_MODE),
|
||||||
|
chartOptions: [
|
||||||
|
{
|
||||||
|
label: 'Browse',
|
||||||
|
value: 'browse',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Featured',
|
||||||
|
value: 'featured'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
...mapGetters(['currentCluster']),
|
||||||
|
...mapGetters({ allCharts: 'catalog/charts', loadingErrors: 'catalog/errors' }),
|
||||||
|
|
||||||
|
hideRepos: mapPref(HIDE_REPOS),
|
||||||
|
|
||||||
|
viewOptions() {
|
||||||
|
const out = [];
|
||||||
|
|
||||||
|
if ( this.hasDetail ) {
|
||||||
|
out.push({
|
||||||
|
labelKey: 'resourceDetail.masthead.detail',
|
||||||
|
value: 'detail',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.hasEdit ) {
|
||||||
|
out.push({
|
||||||
|
labelKey: 'resourceDetail.masthead.config',
|
||||||
|
value: 'config',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !out.length ) {
|
||||||
|
// If there's only YAML, return nothing and the button group will be hidden entirely
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push({
|
||||||
|
labelKey: 'resourceDetail.masthead.yaml',
|
||||||
|
value: 'yaml',
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
|
repoOptions() {
|
||||||
|
let nextColor = 0;
|
||||||
|
// Colors 3 and 4 match `rancher` and `partner` colors, so just avoid them
|
||||||
|
const colors = [1, 2, 5, 6, 7, 8];
|
||||||
|
|
||||||
|
let out = this.$store.getters['catalog/repos'].map((r) => {
|
||||||
|
return {
|
||||||
|
_key: r._key,
|
||||||
|
label: r.nameDisplay,
|
||||||
|
color: r.color,
|
||||||
|
weight: ( r.isRancher ? 1 : ( r.isPartner ? 2 : 3 ) ),
|
||||||
|
enabled: !this.hideRepos.includes(r._key),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
out = sortBy(out, ['weight', 'label']);
|
||||||
|
|
||||||
|
for ( const entry of out ) {
|
||||||
|
if ( !entry.color ) {
|
||||||
|
entry.color = `color${ colors[nextColor] }`;
|
||||||
|
nextColor++;
|
||||||
|
if ( nextColor >= colors.length ) {
|
||||||
|
nextColor = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
|
repoOptionsForDropdown() {
|
||||||
|
return [{
|
||||||
|
label: this.t('catalog.repo.all'), all: true, enabled: this.areAllEnabled()
|
||||||
|
}, ...this.repoOptions];
|
||||||
|
},
|
||||||
|
|
||||||
|
flattenedRepoNames() {
|
||||||
|
const allChecked = this.repoOptionsForDropdown.find(repo => repo.all && repo.enabled);
|
||||||
|
|
||||||
|
if (allChecked) {
|
||||||
|
return allChecked.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
// None checked
|
||||||
|
if (!this.repoOptionsForDropdown.find(repo => repo.enabled)) {
|
||||||
|
return this.t('generic.none');
|
||||||
|
}
|
||||||
|
|
||||||
|
const shownRepos = this.repoOptions.filter(repo => !this.hideRepos.includes(repo._key));
|
||||||
|
const reducedRepos = shownRepos.reduce((acc, c, i) => {
|
||||||
|
acc += c.label;
|
||||||
|
const length = shownRepos.length;
|
||||||
|
|
||||||
|
if (i < length - 1) {
|
||||||
|
acc += ', ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
return reducedRepos;
|
||||||
|
},
|
||||||
|
|
||||||
|
enabledCharts() {
|
||||||
|
return (this.allCharts || []).filter((c) => {
|
||||||
|
if ( c.deprecated && !this.showDeprecated ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( c.hidden && !this.showHidden ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.hideRepos.includes(c.repoKey) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
filteredCharts() {
|
||||||
|
const enabledCharts = (this.enabledCharts || []);
|
||||||
|
|
||||||
|
return filterAndArrangeCharts(enabledCharts, {
|
||||||
|
category: this.category,
|
||||||
|
searchQuery: this.searchQuery,
|
||||||
|
showDeprecated: this.showDeprecated,
|
||||||
|
showHidden: this.showHidden,
|
||||||
|
hideRepos: this.hideRepos,
|
||||||
|
hideTypes: [CATALOG._CLUSTER_TPL],
|
||||||
|
showPrerelease: this.$store.getters['prefs/get'](SHOW_PRE_RELEASE),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getFeaturedCharts() {
|
||||||
|
const newArray = (this.filteredCharts || []);
|
||||||
|
|
||||||
|
return newArray.slice(0, 5);
|
||||||
|
},
|
||||||
|
|
||||||
|
categories() {
|
||||||
|
const map = {};
|
||||||
|
|
||||||
|
for ( const chart of this.enabledCharts ) {
|
||||||
|
for ( const c of chart.categories ) {
|
||||||
|
if ( !map[c] ) {
|
||||||
|
const labelKey = `catalog.charts.categories.${ lcFirst(c) }`;
|
||||||
|
|
||||||
|
map[c] = {
|
||||||
|
label: this.$store.getters['i18n/withFallback'](labelKey, null, c),
|
||||||
|
value: c,
|
||||||
|
count: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
map[c].count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const out = Object.values(map);
|
||||||
|
|
||||||
|
out.unshift({
|
||||||
|
label: this.t('catalog.charts.categories.all'),
|
||||||
|
value: '',
|
||||||
|
count: this.enabledCharts.length
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
|
showCarousel() {
|
||||||
|
return this.chartMode === 'featured' && this.getFeaturedCharts.length;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
searchQuery(q) {
|
||||||
|
this.$router.applyQuery({ [SEARCH_QUERY]: q || undefined });
|
||||||
|
},
|
||||||
|
|
||||||
|
category(cat) {
|
||||||
|
this.$router.applyQuery({ [CATEGORY]: cat || undefined });
|
||||||
|
},
|
||||||
|
|
||||||
|
operatingSystem(os) {
|
||||||
|
this.$router.applyQuery({ [OPERATING_SYSTEM]: os || undefined });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if ( typeof window !== 'undefined' ) {
|
||||||
|
window.c = this;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
colorForChart(chart) {
|
||||||
|
const repos = this.repoOptions;
|
||||||
|
const repo = findBy(repos, '_key', chart.repoKey);
|
||||||
|
|
||||||
|
if ( repo ) {
|
||||||
|
return repo.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleAll(on) {
|
||||||
|
for ( const r of this.repoOptions ) {
|
||||||
|
this.toggleRepo(r, on, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.allRepos = this.areAllEnabled();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
areAllEnabled() {
|
||||||
|
const all = this.$store.getters['catalog/repos'];
|
||||||
|
|
||||||
|
for ( const r of all ) {
|
||||||
|
if ( this.hideRepos.includes(r._key) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleRepo(repo, on, updateAll = true) {
|
||||||
|
const hidden = this.hideRepos;
|
||||||
|
|
||||||
|
if ( on ) {
|
||||||
|
removeObject(hidden, repo._key);
|
||||||
|
} else {
|
||||||
|
addObject(hidden, repo._key);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hideRepos = hidden;
|
||||||
|
|
||||||
|
if ( updateAll ) {
|
||||||
|
this.allRepos = this.areAllEnabled();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
selectChart(chart) {
|
||||||
|
let version;
|
||||||
|
const OSs = this.currentCluster.workerOSs;
|
||||||
|
const showPrerelease = this.$store.getters['prefs/get'](SHOW_PRE_RELEASE);
|
||||||
|
const compatibleVersions = compatibleVersionsFor(chart, OSs, showPrerelease);
|
||||||
|
const versions = chart.versions;
|
||||||
|
|
||||||
|
if (compatibleVersions.length > 0) {
|
||||||
|
version = compatibleVersions[0].version;
|
||||||
|
} else {
|
||||||
|
version = versions[0].version;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$router.push({
|
||||||
|
name: 'c-cluster-apps-charts-chart',
|
||||||
|
params: {
|
||||||
|
cluster: this.$route.params.cluster,
|
||||||
|
product: this.$store.getters['productId'],
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
[REPO_TYPE]: chart.repoType,
|
||||||
|
[REPO]: chart.repoName,
|
||||||
|
[CHART]: chart.chartName,
|
||||||
|
[VERSION]: version,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
focusSearch() {
|
||||||
|
if ( this.$refs.searchQuery ) {
|
||||||
|
this.$refs.searchQuery.focus();
|
||||||
|
this.$refs.searchQuery.select();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async refresh(btnCb) {
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('catalog/refresh');
|
||||||
|
btnCb(true);
|
||||||
|
} catch (e) {
|
||||||
|
this.$store.dispatch('growl/fromError', e);
|
||||||
|
btnCb(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Loading v-if="$fetchState.pending" />
|
||||||
|
<div v-else>
|
||||||
|
<header>
|
||||||
|
<div class="title">
|
||||||
|
<h1 class="m-0">
|
||||||
|
{{ t('catalog.charts.header') }}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="actions-container">
|
||||||
|
<ButtonGroup
|
||||||
|
v-model="chartMode"
|
||||||
|
:options="chartOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<Carousel
|
||||||
|
v-if="showCarousel"
|
||||||
|
:sliders="getFeaturedCharts"
|
||||||
|
@clicked="(row) => selectChart(row)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TypeDescription resource="chart" />
|
||||||
|
<div class="left-right-split">
|
||||||
|
<Select
|
||||||
|
:searchable="false"
|
||||||
|
:options="repoOptionsForDropdown"
|
||||||
|
:value="flattenedRepoNames"
|
||||||
|
class="checkbox-select"
|
||||||
|
:close-on-select="false"
|
||||||
|
@option:selecting="$event.all ? toggleAll(!$event.enabled) : toggleRepo($event, !$event.enabled) "
|
||||||
|
>
|
||||||
|
<template #selected-option="selected">
|
||||||
|
{{ selected.label }}
|
||||||
|
</template>
|
||||||
|
<template #option="repo">
|
||||||
|
<Checkbox
|
||||||
|
:value="repo.enabled"
|
||||||
|
:label="repo.label"
|
||||||
|
class="pull-left repo in-select"
|
||||||
|
:class="{ [repo.color]: true}"
|
||||||
|
:color="repo.color"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<span>{{ repo.label }}</span><i v-if="!repo.all" class=" pl-5 icon icon-dot icon-sm" :class="{[repo.color]: true}" />
|
||||||
|
</template>
|
||||||
|
</Checkbox>
|
||||||
|
</template>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
v-model="category"
|
||||||
|
:clearable="false"
|
||||||
|
:searchable="false"
|
||||||
|
:options="categories"
|
||||||
|
placement="bottom"
|
||||||
|
label="label"
|
||||||
|
style="min-width: 200px;"
|
||||||
|
:reduce="opt => opt.value"
|
||||||
|
>
|
||||||
|
<template #option="opt">
|
||||||
|
{{ opt.label }} ({{ opt.count }})
|
||||||
|
</template>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<div class="filter-block">
|
||||||
|
<input
|
||||||
|
ref="searchQuery"
|
||||||
|
v-model="searchQuery"
|
||||||
|
type="search"
|
||||||
|
class="input-sm"
|
||||||
|
:placeholder="t('catalog.charts.search')"
|
||||||
|
>
|
||||||
|
|
||||||
|
<button v-shortkey.once="['/']" class="hide" @shortkey="focusSearch()" />
|
||||||
|
<AsyncButton class="refresh-btn" mode="refresh" size="sm" @click="refresh" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Banner v-for="err in loadingErrors" :key="err" color="error" :label="err" />
|
||||||
|
|
||||||
|
<div v-if="allCharts.length">
|
||||||
|
<div v-if="filteredCharts.length === 0" style="width: 100%;">
|
||||||
|
<div class="m-50 text-center">
|
||||||
|
<h1>{{ t('catalog.charts.noCharts') }}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SelectIconGrid
|
||||||
|
v-else
|
||||||
|
:rows="filteredCharts"
|
||||||
|
name-field="chartNameDisplay"
|
||||||
|
description-field="chartDescription"
|
||||||
|
:color-for="colorForChart"
|
||||||
|
@clicked="(row) => selectChart(row)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else class="m-50 text-center">
|
||||||
|
<h1>{{ t('catalog.charts.noCharts') }}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.left-right-split {
|
||||||
|
padding: 0 0 20px 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: z-index('fixedTableHeader');
|
||||||
|
background: transparent;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 40% auto auto;
|
||||||
|
align-content: center;
|
||||||
|
grid-column-gap: 10px;
|
||||||
|
|
||||||
|
.filter-block {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.refresh-btn {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.with-os-options {
|
||||||
|
grid-template-columns: 40% auto auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: map-get($breakpoints, '--viewport-12')) {
|
||||||
|
&{
|
||||||
|
grid-template-columns: auto auto !important;
|
||||||
|
grid-template-rows: 40px 40px;
|
||||||
|
grid-row-gap: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: map-get($breakpoints, '--viewport-7')) {
|
||||||
|
&{
|
||||||
|
&{
|
||||||
|
grid-template-columns: auto !important;
|
||||||
|
grid-template-rows: 40px 40px 40px !important;
|
||||||
|
|
||||||
|
&.with-os-options {
|
||||||
|
grid-template-rows: 40px 40px 40px 40px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-select {
|
||||||
|
.vs__search {
|
||||||
|
position: absolute;
|
||||||
|
right: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs__selected-options {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 2.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-outer-container.in-select {
|
||||||
|
transform: translateX(-5px);
|
||||||
|
padding: 7px 0 6px 13px;
|
||||||
|
width: calc(100% + 10px);
|
||||||
|
|
||||||
|
::v-deep.checkbox-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& i {
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
&:hover {
|
||||||
|
background: var(--input-hover-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--body-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.rancher {
|
||||||
|
&:hover {
|
||||||
|
background: var(--app-rancher-accent);
|
||||||
|
}
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--app-rancher-accent-text);
|
||||||
|
}
|
||||||
|
& i {
|
||||||
|
color: var(--app-rancher-accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.partner {
|
||||||
|
&:hover {
|
||||||
|
background: var(--app-partner-accent);
|
||||||
|
}
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--app-partner-accent-text);
|
||||||
|
}
|
||||||
|
& i {
|
||||||
|
color: var(--app-partner-accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.color1 {
|
||||||
|
&:hover {
|
||||||
|
background: var(--app-color1-accent);
|
||||||
|
}
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--app-color1-accent-text);
|
||||||
|
}
|
||||||
|
& i {
|
||||||
|
color: var(--app-color1-accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.color2 {
|
||||||
|
&:hover {
|
||||||
|
background: var(--app-color2-accent);
|
||||||
|
}
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--app-color2-accent-text);
|
||||||
|
}
|
||||||
|
& i {
|
||||||
|
color: var(--app-color2-accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.color3 {
|
||||||
|
&:hover {
|
||||||
|
background: var(--app-color3-accent);
|
||||||
|
}
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--app-color3-accent-text);
|
||||||
|
}
|
||||||
|
& i {
|
||||||
|
color: var(--app-color3-accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.color4 {
|
||||||
|
&:hover {
|
||||||
|
background: var(--app-color4-accent);
|
||||||
|
}
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--app-color4-accent-text);
|
||||||
|
}
|
||||||
|
& i {
|
||||||
|
color: var(--app-color4-accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.color5 {
|
||||||
|
&:hover {
|
||||||
|
background: var(--app-color5-accent);
|
||||||
|
}
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--app-color5-accent-text);
|
||||||
|
}
|
||||||
|
& i {
|
||||||
|
color: var(--app-color5-accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.color6 {
|
||||||
|
&:hover {
|
||||||
|
background: var(--app-color6-accent);
|
||||||
|
}
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--app-color6-accent-text);
|
||||||
|
}
|
||||||
|
& i {
|
||||||
|
color: var(--app-color6-accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.color7 {
|
||||||
|
&:hover {
|
||||||
|
background: var(--app-color7-accent);
|
||||||
|
}
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--app-color7-accent-text);
|
||||||
|
}
|
||||||
|
& i {
|
||||||
|
color: var(--app-color7-accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.color8 {
|
||||||
|
&:hover {
|
||||||
|
background: var(--app-color8-accent);
|
||||||
|
}
|
||||||
|
&:hover ::v-deep.checkbox-label {
|
||||||
|
color: var(--app-color8-accent-text);
|
||||||
|
}
|
||||||
|
& i {
|
||||||
|
color: var(--app-color8-accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
|
|
@ -39,6 +39,8 @@
|
||||||
--default-border : #($dark);
|
--default-border : #($dark);
|
||||||
--default-banner-bg : #{rgba($dark, 0.15)};
|
--default-banner-bg : #{rgba($dark, 0.15)};
|
||||||
--default-light-bg : #{rgba($dark, 0.05)};
|
--default-light-bg : #{rgba($dark, 0.05)};
|
||||||
|
--slider-light-bg : #{rgba($darker, 1)};
|
||||||
|
--slider-light-bg-right : #{rgba($darker, 0)};
|
||||||
|
|
||||||
--muted : #{$disabled};
|
--muted : #{$disabled};
|
||||||
|
|
||||||
|
|
@ -160,6 +162,7 @@
|
||||||
--diff-ins-border : #{rgba($success, 0.5)};
|
--diff-ins-border : #{rgba($success, 0.5)};
|
||||||
--diff-chg-ins : #{rgba($success, 0.25)};
|
--diff-chg-ins : #{rgba($success, 0.25)};
|
||||||
--diff-chg-del : #{rgba($warning, 0.5)};
|
--diff-chg-del : #{rgba($warning, 0.5)};
|
||||||
|
--diff-chg-dela : #{rgba($warning, 1)};
|
||||||
--diff-empty-placeholder : #{$darker};
|
--diff-empty-placeholder : #{$darker};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ BODY, .theme-light {
|
||||||
--link-border : #{$link};
|
--link-border : #{$link};
|
||||||
--link-banner-bg : #{rgba($link, 0.15)};
|
--link-banner-bg : #{rgba($link, 0.15)};
|
||||||
--link-light-bg : #{rgba($link, 0.05)};
|
--link-light-bg : #{rgba($link, 0.05)};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.text-link {
|
.text-link {
|
||||||
|
|
@ -113,6 +114,8 @@ BODY, .theme-light {
|
||||||
--default-border : #{$light};
|
--default-border : #{$light};
|
||||||
--default-banner-bg : #{rgba($light, 0.15)};
|
--default-banner-bg : #{rgba($light, 0.15)};
|
||||||
--default-light-bg : #{rgba($light, 0.05)};
|
--default-light-bg : #{rgba($light, 0.05)};
|
||||||
|
--slider-light-bg : #{rgba($lightest, 1)};
|
||||||
|
--slider-light-bg-right : #{rgba($lightest, 0)};
|
||||||
|
|
||||||
|
|
||||||
.text-default {
|
.text-default {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,291 @@
|
||||||
|
<script>
|
||||||
|
import { get } from '@shell/utils/object';
|
||||||
|
import { BadgeState } from '@components/BadgeState';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { BadgeState },
|
||||||
|
name: 'Carousel',
|
||||||
|
props: {
|
||||||
|
sliders: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
keyField: {
|
||||||
|
type: String,
|
||||||
|
default: 'key',
|
||||||
|
},
|
||||||
|
asLink: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
linkField: {
|
||||||
|
type: String,
|
||||||
|
default: 'link'
|
||||||
|
},
|
||||||
|
targetField: {
|
||||||
|
type: String,
|
||||||
|
default: 'target',
|
||||||
|
},
|
||||||
|
rel: {
|
||||||
|
type: String,
|
||||||
|
default: 'noopener noreferrer nofollow'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
slider: this.sliders,
|
||||||
|
activeItemId: 0,
|
||||||
|
autoScroll: true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
trackStyle() {
|
||||||
|
const sliderItem = this.activeItemId * 100 / this.slider.length;
|
||||||
|
const width = 60 * this.slider.length;
|
||||||
|
|
||||||
|
return `transform: translateX(-${ sliderItem }%); width: ${ width }%`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
get,
|
||||||
|
|
||||||
|
select(slide, i) {
|
||||||
|
this.$emit('clicked', slide, i);
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollSlide(i) {
|
||||||
|
this.autoScroll = false;
|
||||||
|
this.activeItemId = i;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.slidePosition();
|
||||||
|
}, 400);
|
||||||
|
},
|
||||||
|
|
||||||
|
nextPrev(item) {
|
||||||
|
this.autoScroll = false;
|
||||||
|
if (item === 'next' && this.activeItemId < this.slider.length - 1) {
|
||||||
|
this.activeItemId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item === 'prev' && this.activeItemId > 0) {
|
||||||
|
this.activeItemId--;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.slidePosition();
|
||||||
|
},
|
||||||
|
|
||||||
|
timer() {
|
||||||
|
setInterval(this.autoScrollSlide, 2000);
|
||||||
|
},
|
||||||
|
autoScrollSlide() {
|
||||||
|
if (this.activeItemId < this.slider.length && this.autoScroll ) {
|
||||||
|
this.activeItemId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.activeItemId > this.slider.length - 1) {
|
||||||
|
this.autoScroll = false;
|
||||||
|
this.activeItemId = 0;
|
||||||
|
}
|
||||||
|
this.slidePosition();
|
||||||
|
},
|
||||||
|
|
||||||
|
slidePosition() {
|
||||||
|
if (this.activeItemId <= 1) {
|
||||||
|
this.$refs.slide[this.slider.length - 1].style.left = '-93%';
|
||||||
|
this.$refs.slide[0].style.left = '7%';
|
||||||
|
} else {
|
||||||
|
this.$refs.slide[this.slider.length - 1].style.left = '7%';
|
||||||
|
this.$refs.slide[0].style.left = '107%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.timer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="slider">
|
||||||
|
<div id="slide-track" ref="slider" :style="trackStyle" class="slide-track">
|
||||||
|
<div
|
||||||
|
:is="asLink ? 'a' : 'div'"
|
||||||
|
v-for="(slide, i) in sliders"
|
||||||
|
:id="`slide` + i"
|
||||||
|
ref="slide"
|
||||||
|
:key="get(slide, keyField)"
|
||||||
|
class="slide"
|
||||||
|
:href="asLink ? get(slide, linkField) : null"
|
||||||
|
:target="get(slide, targetField)"
|
||||||
|
:rel="rel"
|
||||||
|
@click="select(slide, i)"
|
||||||
|
>
|
||||||
|
<div class="slide-content">
|
||||||
|
<div class="slide-img">
|
||||||
|
<img :src="slide.icon ? slide.icon : `/_nuxt/shell/assets/images/generic-catalog.svg`" />
|
||||||
|
</div>
|
||||||
|
<div class="slide-content-right">
|
||||||
|
<BadgeState :label="slide.repoName" color="slider-badge mb-20" />
|
||||||
|
<h1>{{ slide.chartNameDisplay }}</h1>
|
||||||
|
<p>{{ slide.chartDescription }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="controls">
|
||||||
|
<div
|
||||||
|
v-for="(slide, i) in slider"
|
||||||
|
:key="i"
|
||||||
|
class="control-item"
|
||||||
|
:class="{'active': activeItemId === i}"
|
||||||
|
@click="scrollSlide(i, slider.length)"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div ref="prev" class="prev" :class="[activeItemId === 0 ? 'disabled' : 'prev']" @click="nextPrev('prev')">
|
||||||
|
<i class="icon icon-chevron-left icon-4x"></i>
|
||||||
|
</div>
|
||||||
|
<div ref="next" class="next" :class="[activeItemId === slider.length - 1 ? 'disabled' : 'next']" @click="nextPrev('next')">
|
||||||
|
<i class="icon icon-chevron-right icon-4x"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
.slider {
|
||||||
|
margin: auto;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
place-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
min-width: 700px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.prev,
|
||||||
|
.next {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-track {
|
||||||
|
display: flex;
|
||||||
|
animation: scrolls 10s ;
|
||||||
|
position: relative;
|
||||||
|
transition: 1s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-badge {
|
||||||
|
background: var(--app-partner-accent);
|
||||||
|
color: var(--body-bg);
|
||||||
|
}
|
||||||
|
.slide {
|
||||||
|
min-height: 210px;
|
||||||
|
width: 60%;
|
||||||
|
max-width: 60%;
|
||||||
|
margin: 0 10px;
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid var(--tabbed-border);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
left: 7%;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
left: -93%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-header {
|
||||||
|
background: var(--default);
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
.slide-content {
|
||||||
|
display: flex;
|
||||||
|
padding: 30px;
|
||||||
|
|
||||||
|
.slide-img {
|
||||||
|
width: 150px;
|
||||||
|
background: var(--card-badge-text);
|
||||||
|
border-radius: calc(2 * var(--border-radius));
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-content-right {
|
||||||
|
border-left: 1px solid var(--tabbed-border);
|
||||||
|
margin-left: 30px;
|
||||||
|
padding-left: 30px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider::before,
|
||||||
|
.slider::after {
|
||||||
|
background: linear-gradient(to right, var(--slider-light-bg) 0%, var(--slider-light-bg-right) 100%);
|
||||||
|
content: "";
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
width: 15%;
|
||||||
|
z-index: z-index('overContent');
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider::before {
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.slider::after{
|
||||||
|
right: -1px;
|
||||||
|
top: 0;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
.control-item {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--scrollbar-thumb);
|
||||||
|
margin: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background:var(--body-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.prev,
|
||||||
|
.next {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 20;
|
||||||
|
top: 90px;
|
||||||
|
display: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.disabled .icon {
|
||||||
|
color: var(--disabled-bg);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.next {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -95,7 +95,7 @@ export const CATALOG = {
|
||||||
DEPLOYED_OS: 'catalog.cattle.io/deploys-on-os',
|
DEPLOYED_OS: 'catalog.cattle.io/deploys-on-os',
|
||||||
|
|
||||||
MIGRATED: 'apps.cattle.io/migrated',
|
MIGRATED: 'apps.cattle.io/migrated',
|
||||||
MANAGED: 'catalog.cattle.io/managed'
|
MANAGED: 'catalog.cattle.io/managed',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FLEET = {
|
export const FLEET = {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
import AsyncButton from '@shell/components/AsyncButton';
|
import AsyncButton from '@shell/components/AsyncButton';
|
||||||
import Loading from '@shell/components/Loading';
|
import Loading from '@shell/components/Loading';
|
||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
|
import Carousel from '@shell/components/Carousel';
|
||||||
|
import ButtonGroup from '@shell/components/ButtonGroup';
|
||||||
import SelectIconGrid from '@shell/components/SelectIconGrid';
|
import SelectIconGrid from '@shell/components/SelectIconGrid';
|
||||||
import TypeDescription from '@shell/components/TypeDescription';
|
import TypeDescription from '@shell/components/TypeDescription';
|
||||||
import {
|
import {
|
||||||
|
|
@ -12,7 +14,7 @@ import { sortBy } from '@shell/utils/sort';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { Checkbox } from '@components/Form/Checkbox';
|
import { Checkbox } from '@components/Form/Checkbox';
|
||||||
import Select from '@shell/components/form/Select';
|
import Select from '@shell/components/form/Select';
|
||||||
import { mapPref, HIDE_REPOS, SHOW_PRE_RELEASE } from '@shell/store/prefs';
|
import { mapPref, HIDE_REPOS, SHOW_PRE_RELEASE, SHOW_CHART_MODE } from '@shell/store/prefs';
|
||||||
import { removeObject, addObject, findBy } from '@shell/utils/array';
|
import { removeObject, addObject, findBy } from '@shell/utils/array';
|
||||||
import { compatibleVersionsFor, filterAndArrangeCharts } from '@shell/store/catalog';
|
import { compatibleVersionsFor, filterAndArrangeCharts } from '@shell/store/catalog';
|
||||||
import { CATALOG } from '@shell/config/labels-annotations';
|
import { CATALOG } from '@shell/config/labels-annotations';
|
||||||
|
|
@ -21,6 +23,8 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
AsyncButton,
|
AsyncButton,
|
||||||
Banner,
|
Banner,
|
||||||
|
Carousel,
|
||||||
|
ButtonGroup,
|
||||||
Loading,
|
Loading,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Select,
|
Select,
|
||||||
|
|
@ -48,6 +52,17 @@ export default {
|
||||||
searchQuery: null,
|
searchQuery: null,
|
||||||
showDeprecated: null,
|
showDeprecated: null,
|
||||||
showHidden: null,
|
showHidden: null,
|
||||||
|
chartMode: this.$store.getters['prefs/get'](SHOW_CHART_MODE),
|
||||||
|
chartOptions: [
|
||||||
|
{
|
||||||
|
label: 'Browse',
|
||||||
|
value: 'browse',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Featured',
|
||||||
|
value: 'featured'
|
||||||
|
}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -154,6 +169,14 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getFeaturedCharts() {
|
||||||
|
const allCharts = (this.filteredCharts || []);
|
||||||
|
|
||||||
|
const featuredCharts = allCharts.filter(value => value.featured).sort((a, b) => a.featured - b.featured);
|
||||||
|
|
||||||
|
return featuredCharts.slice(0, 5);
|
||||||
|
},
|
||||||
|
|
||||||
categories() {
|
categories() {
|
||||||
const map = {};
|
const map = {};
|
||||||
|
|
||||||
|
|
@ -184,6 +207,10 @@ export default {
|
||||||
return out;
|
return out;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showCarousel() {
|
||||||
|
return this.chartMode === 'featured' && this.getFeaturedCharts.length;
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
@ -313,7 +340,20 @@ export default {
|
||||||
{{ t('catalog.charts.header') }}
|
{{ t('catalog.charts.header') }}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="actions-container">
|
||||||
|
<ButtonGroup
|
||||||
|
v-model="chartMode"
|
||||||
|
:options="chartOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
<div v-if="showCarousel">
|
||||||
|
<h3>Featured Charts</h3>
|
||||||
|
<Carousel
|
||||||
|
:sliders="getFeaturedCharts"
|
||||||
|
@clicked="(row) => selectChart(row)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<TypeDescription resource="chart" />
|
<TypeDescription resource="chart" />
|
||||||
<div class="left-right-split">
|
<div class="left-right-split">
|
||||||
<Select
|
<Select
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ export const HIDE_REPOS = create('hide-repos', [], { parseJSON });
|
||||||
export const HIDE_DESC = create('hide-desc', [], { parseJSON });
|
export const HIDE_DESC = create('hide-desc', [], { parseJSON });
|
||||||
export const HIDE_SENSITIVE = create('hide-sensitive', true, { options: [true, false], parseJSON });
|
export const HIDE_SENSITIVE = create('hide-sensitive', true, { options: [true, false], parseJSON });
|
||||||
export const SHOW_PRE_RELEASE = create('show-pre-release', false, { options: [false, true], parseJSON });
|
export const SHOW_PRE_RELEASE = create('show-pre-release', false, { options: [false, true], parseJSON });
|
||||||
|
export const SHOW_CHART_MODE = create('chartMode', 'featured', { parseJSON });
|
||||||
|
|
||||||
export const DATE_FORMAT = create('date-format', 'ddd, MMM D YYYY', {
|
export const DATE_FORMAT = create('date-format', 'ddd, MMM D YYYY', {
|
||||||
options: [
|
options: [
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,485 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import { MANAGEMENT, STEVE } from '@/config/types';
|
||||||
|
import { clone } from '@/utils/object';
|
||||||
|
import { SETTING } from '@/config/settings';
|
||||||
|
|
||||||
|
const definitions = {};
|
||||||
|
|
||||||
|
export const create = function(name, def, opt = {}) {
|
||||||
|
const parseJSON = opt.parseJSON === true;
|
||||||
|
const asCookie = opt.asCookie === true;
|
||||||
|
const asUserPreference = opt.asUserPreference !== false;
|
||||||
|
const options = opt.options;
|
||||||
|
|
||||||
|
definitions[name] = {
|
||||||
|
def,
|
||||||
|
options,
|
||||||
|
parseJSON,
|
||||||
|
asCookie,
|
||||||
|
asUserPreference,
|
||||||
|
mangleRead: opt.mangleRead, // Alter the value read from the API (to match old Rancher expectations)
|
||||||
|
mangleWrite: opt.mangleWrite, // Alter the value written back to the API (ditto)
|
||||||
|
};
|
||||||
|
|
||||||
|
return name;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mapPref = function(name) {
|
||||||
|
return {
|
||||||
|
get() {
|
||||||
|
return this.$store.getters['prefs/get'](name);
|
||||||
|
},
|
||||||
|
|
||||||
|
set(value) {
|
||||||
|
this.$store.dispatch('prefs/set', { key: name, value });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
const parseJSON = true; // Shortcut for setting it below
|
||||||
|
const asCookie = true; // Store as a cookie so that it's available before auth + on server-side
|
||||||
|
|
||||||
|
// Keys must be lowercase and valid dns label (a-z 0-9 -)
|
||||||
|
export const CLUSTER = create('cluster', '');
|
||||||
|
export const LAST_NAMESPACE = create('last-namespace', '');
|
||||||
|
export const NAMESPACE_FILTERS = create('ns-by-cluster', {}, { parseJSON });
|
||||||
|
export const WORKSPACE = create('workspace', '');
|
||||||
|
export const EXPANDED_GROUPS = create('open-groups', ['cluster', 'rbac', 'serviceDiscovery', 'storage', 'workload'], { parseJSON });
|
||||||
|
export const FAVORITE_TYPES = create('fav-type', [], { parseJSON });
|
||||||
|
export const GROUP_RESOURCES = create('group-by', 'namespace');
|
||||||
|
export const DIFF = create('diff', 'unified', { options: ['unified', 'split'] });
|
||||||
|
export const THEME = create('theme', 'auto', {
|
||||||
|
options: ['light', 'auto', 'dark'],
|
||||||
|
asCookie,
|
||||||
|
parseJSON,
|
||||||
|
mangleRead: x => x.replace(/^ui-/, ''),
|
||||||
|
mangleWrite: x => `ui-${ x }`,
|
||||||
|
});
|
||||||
|
export const PREFERS_SCHEME = create('pcs', '', { asCookie, asUserPreference: false });
|
||||||
|
export const LOCALE = create('locale', 'en-us', { asCookie });
|
||||||
|
export const KEYMAP = create('keymap', 'sublime', { options: ['sublime', 'emacs', 'vim'] });
|
||||||
|
export const ROWS_PER_PAGE = create('per-page', 100, { options: [10, 25, 50, 100, 250, 500, 1000], parseJSON });
|
||||||
|
export const LOGS_WRAP = create('logs-wrap', true, { parseJSON });
|
||||||
|
export const LOGS_TIME = create('logs-time', true, { parseJSON });
|
||||||
|
export const LOGS_RANGE = create('logs-range', '30 minutes', { parseJSON });
|
||||||
|
export const HIDE_REPOS = create('hide-repos', [], { parseJSON });
|
||||||
|
export const HIDE_DESC = create('hide-desc', [], { parseJSON });
|
||||||
|
export const HIDE_SENSITIVE = create('hide-sensitive', true, { options: [true, false], parseJSON });
|
||||||
|
export const SHOW_PRE_RELEASE = create('show-pre-release', false, { options: [false, true], parseJSON });
|
||||||
|
export const SHOW_CHART_MODE = create('chartMode', 'featured', { parseJSON });
|
||||||
|
|
||||||
|
export const DATE_FORMAT = create('date-format', 'ddd, MMM D YYYY', {
|
||||||
|
options: [
|
||||||
|
'ddd, MMM D YYYY',
|
||||||
|
'ddd, D MMM YYYY',
|
||||||
|
'D/M/YYYY',
|
||||||
|
'M/D/YYYY',
|
||||||
|
'YYYY-MM-DD'
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export const TIME_FORMAT = create('time-format', 'h:mm:ss a', {
|
||||||
|
options: [
|
||||||
|
'h:mm:ss a',
|
||||||
|
'HH:mm:ss'
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export const TIME_ZONE = create('time-zone', 'local');
|
||||||
|
export const DEV = create('dev', false, { parseJSON });
|
||||||
|
export const LAST_VISITED = create('last-visited', 'home', { parseJSON });
|
||||||
|
export const SEEN_WHATS_NEW = create('seen-whatsnew', '', { parseJSON });
|
||||||
|
export const READ_WHATS_NEW = create('read-whatsnew', '', { parseJSON });
|
||||||
|
export const AFTER_LOGIN_ROUTE = create('after-login-route', 'home', { parseJSON } );
|
||||||
|
export const HIDE_HOME_PAGE_CARDS = create('home-page-cards', {}, { parseJSON } );
|
||||||
|
|
||||||
|
export const _RKE1 = 'rke1';
|
||||||
|
export const _RKE2 = 'rke2';
|
||||||
|
export const PROVISIONER = create('provisioner', _RKE1, { options: [_RKE1, _RKE2] });
|
||||||
|
|
||||||
|
// Promo for Cluster Tools feature on Cluster Dashboard page
|
||||||
|
export const CLUSTER_TOOLS_TIP = create('hide-cluster-tools-tip', false, { parseJSON });
|
||||||
|
|
||||||
|
// Maximum number of clusters to show in the slide-in menu
|
||||||
|
export const MENU_MAX_CLUSTERS = create('menu-max-clusters', 4, { options: [2, 3, 4, 5, 6, 7, 8, 9, 10], parseJSON });
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
const cookiePrefix = 'R_';
|
||||||
|
const cookieOptions = {
|
||||||
|
maxAge: 365 * 86400,
|
||||||
|
path: '/',
|
||||||
|
sameSite: true,
|
||||||
|
secure: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const state = function() {
|
||||||
|
return {
|
||||||
|
cookiesLoaded: false,
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getters = {
|
||||||
|
get: state => (key) => {
|
||||||
|
const definition = definitions[key];
|
||||||
|
|
||||||
|
if (!definition) {
|
||||||
|
throw new Error(`Unknown preference: ${ key }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = state.data[key];
|
||||||
|
|
||||||
|
if (user !== undefined) {
|
||||||
|
return clone(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
const def = clone(definition.def);
|
||||||
|
|
||||||
|
return def;
|
||||||
|
},
|
||||||
|
|
||||||
|
defaultValue: state => (key) => {
|
||||||
|
const definition = definitions[key];
|
||||||
|
|
||||||
|
if (!definition) {
|
||||||
|
throw new Error(`Unknown preference: ${ key }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone(definition.def);
|
||||||
|
},
|
||||||
|
|
||||||
|
options: state => (key) => {
|
||||||
|
const definition = definitions[key];
|
||||||
|
|
||||||
|
if (!definition) {
|
||||||
|
throw new Error(`Unknown preference: ${ key }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!definition.options) {
|
||||||
|
throw new Error(`Preference does not have options: ${ key }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return definition.options.slice();
|
||||||
|
},
|
||||||
|
|
||||||
|
theme: (state, getters) => {
|
||||||
|
let theme = getters['get'](THEME);
|
||||||
|
const pcs = getters['get'](PREFERS_SCHEME);
|
||||||
|
|
||||||
|
// console.log('Get Theme', theme, pcs);
|
||||||
|
|
||||||
|
// Ember UI uses this prefix
|
||||||
|
if ( theme.startsWith('ui-') ) {
|
||||||
|
theme = theme.substr(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( theme === 'auto' ) {
|
||||||
|
if ( pcs === 'light' || pcs === 'dark' ) {
|
||||||
|
return pcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
return theme;
|
||||||
|
},
|
||||||
|
|
||||||
|
afterLoginRoute: (state, getters) => {
|
||||||
|
const afterLoginRoutePref = getters['get'](AFTER_LOGIN_ROUTE);
|
||||||
|
|
||||||
|
if (typeof afterLoginRoutePref !== 'string') {
|
||||||
|
return afterLoginRoutePref;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case (afterLoginRoutePref === 'home'):
|
||||||
|
return { name: 'home' };
|
||||||
|
case (afterLoginRoutePref === 'last-visited'): {
|
||||||
|
const lastVisitedPref = getters['get'](LAST_VISITED);
|
||||||
|
|
||||||
|
if (lastVisitedPref) {
|
||||||
|
return lastVisitedPref;
|
||||||
|
}
|
||||||
|
const clusterPref = getters['get'](CLUSTER);
|
||||||
|
|
||||||
|
return { name: 'c-cluster-explorer', params: { product: 'explorer', cluster: clusterPref } };
|
||||||
|
}
|
||||||
|
case (!!afterLoginRoutePref.match(/.+-dashboard$/)):
|
||||||
|
{
|
||||||
|
const clusterId = afterLoginRoutePref.split('-dashboard')[0];
|
||||||
|
|
||||||
|
return { name: 'c-cluster-explorer', params: { product: 'explorer', cluster: clusterId } };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return { name: afterLoginRoutePref };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mutations = {
|
||||||
|
load(state, { key, value }) {
|
||||||
|
Vue.set(state.data, key, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
cookiesLoaded(state) {
|
||||||
|
state.cookiesLoaded = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
reset(state) {
|
||||||
|
for (const key in definitions) {
|
||||||
|
if ( definitions[key]?.asCookie ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
delete state.data[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
async set({ dispatch, commit }, opt) {
|
||||||
|
let { key, value } = opt; // eslint-disable-line prefer-const
|
||||||
|
const definition = definitions[key];
|
||||||
|
let server;
|
||||||
|
|
||||||
|
if ( opt.val ) {
|
||||||
|
throw new Error('Use value, not val');
|
||||||
|
}
|
||||||
|
|
||||||
|
commit('load', { key, value });
|
||||||
|
|
||||||
|
if ( definition.asCookie ) {
|
||||||
|
const opt = {
|
||||||
|
...cookieOptions,
|
||||||
|
parseJSON: definition.parseJSON === true
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$cookies.set(`${ cookiePrefix }${ key }`.toUpperCase(), value, opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( definition.asUserPreference ) {
|
||||||
|
try {
|
||||||
|
server = await dispatch('loadServer', key); // There's no watch on prefs, so get before set...
|
||||||
|
|
||||||
|
if ( server?.data ) {
|
||||||
|
if ( definition.mangleWrite ) {
|
||||||
|
value = definition.mangleWrite(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( definition.parseJSON ) {
|
||||||
|
Vue.set(server.data, key, JSON.stringify(value));
|
||||||
|
} else {
|
||||||
|
Vue.set(server.data, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.save({ redirectUnauthorized: false });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Well it failed, but not much to do about it...
|
||||||
|
|
||||||
|
// Return the error
|
||||||
|
return { type: e.type, status: e.status };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async setTheme({ dispatch }, val) {
|
||||||
|
await dispatch('set', { key: THEME, value: val });
|
||||||
|
},
|
||||||
|
|
||||||
|
loadCookies({ state, commit }) {
|
||||||
|
if ( state.cookiesLoaded ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in definitions) {
|
||||||
|
const definition = definitions[key];
|
||||||
|
|
||||||
|
if ( !definition.asCookie ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const opt = { parseJSON: definition.parseJSON === true };
|
||||||
|
const value = this.$cookies.get(`${ cookiePrefix }${ key }`.toUpperCase(), opt);
|
||||||
|
|
||||||
|
if (value !== undefined) {
|
||||||
|
commit('load', { key, value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commit('cookiesLoaded');
|
||||||
|
},
|
||||||
|
|
||||||
|
loadTheme({ state, dispatch }) {
|
||||||
|
if ( process.client ) {
|
||||||
|
const watchDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
const watchLight = window.matchMedia('(prefers-color-scheme: light)');
|
||||||
|
const watchNone = window.matchMedia('(prefers-color-scheme: no-preference)');
|
||||||
|
|
||||||
|
const interval = 30 * 60 * 1000;
|
||||||
|
const nextHalfHour = interval - Math.round(new Date().getTime()) % interval;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
dispatch('loadTheme');
|
||||||
|
}, nextHalfHour);
|
||||||
|
// console.log('Update theme in', nextHalfHour, 'ms');
|
||||||
|
|
||||||
|
if ( watchDark.matches ) {
|
||||||
|
changed('dark');
|
||||||
|
} else if ( watchLight.matches ) {
|
||||||
|
changed('light');
|
||||||
|
} else {
|
||||||
|
changed(fromClock());
|
||||||
|
}
|
||||||
|
|
||||||
|
watchDark.addListener((e) => {
|
||||||
|
if ( e.matches ) {
|
||||||
|
changed('dark');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watchLight.addListener((e) => {
|
||||||
|
if ( e.matches ) {
|
||||||
|
changed('light');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watchNone.addListener((e) => {
|
||||||
|
if ( e.matches ) {
|
||||||
|
changed(fromClock());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function changed(value) {
|
||||||
|
// console.log('Prefers Theme:', value);
|
||||||
|
dispatch('set', { key: PREFERS_SCHEME, value });
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromClock() {
|
||||||
|
const hour = new Date().getHours();
|
||||||
|
|
||||||
|
if ( hour < 7 || hour >= 18 ) {
|
||||||
|
return 'dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'light';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadServer({ state, dispatch, commit }, ignoreKey) {
|
||||||
|
let server = { data: {} };
|
||||||
|
|
||||||
|
try {
|
||||||
|
const all = await dispatch('management/findAll', {
|
||||||
|
type: STEVE.PREFERENCE,
|
||||||
|
opt: {
|
||||||
|
url: 'userpreferences',
|
||||||
|
force: true,
|
||||||
|
watch: false,
|
||||||
|
redirectUnauthorized: false,
|
||||||
|
stream: false,
|
||||||
|
}
|
||||||
|
}, { root: true });
|
||||||
|
|
||||||
|
server = all?.[0];
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error loading preferences', e); // eslint-disable-line no-console
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !server?.data ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in definitions) {
|
||||||
|
const definition = definitions[key];
|
||||||
|
let value = clone(server.data[key]);
|
||||||
|
|
||||||
|
if ( value === undefined || key === ignoreKey) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( definition.parseJSON ) {
|
||||||
|
try {
|
||||||
|
value = JSON.parse(value);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error parsing server pref', key, value, err); // eslint-disable-line no-console
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( definition.mangleRead ) {
|
||||||
|
value = definition.mangleRead(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
commit('load', { key, value });
|
||||||
|
}
|
||||||
|
|
||||||
|
return server;
|
||||||
|
},
|
||||||
|
|
||||||
|
setLastVisited({ state, dispatch }, route) {
|
||||||
|
if (!route) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toSave = getLoginRoute(route);
|
||||||
|
|
||||||
|
return dispatch('set', { key: LAST_VISITED, value: toSave });
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleTheme({ getters, dispatch }) {
|
||||||
|
const value = getters[THEME] === 'light' ? 'dark' : 'light';
|
||||||
|
|
||||||
|
return dispatch('set', { key: THEME, value });
|
||||||
|
},
|
||||||
|
|
||||||
|
setBrandStyle({ rootState, rootGetters }, dark = false) {
|
||||||
|
if (rootState.managementReady) {
|
||||||
|
try {
|
||||||
|
const brandSetting = rootGetters['management/byId'](MANAGEMENT.SETTING, SETTING.BRAND);
|
||||||
|
|
||||||
|
if (brandSetting && brandSetting.value && brandSetting.value !== '') {
|
||||||
|
const brand = brandSetting.value;
|
||||||
|
|
||||||
|
const brandMeta = require(`~/assets/brand/${ brand }/metadata.json`);
|
||||||
|
const hasStylesheet = brandMeta.hasStylesheet === 'true';
|
||||||
|
|
||||||
|
if (hasStylesheet) {
|
||||||
|
document.body.classList.add(brand);
|
||||||
|
} else {
|
||||||
|
// TODO option apply color at runtime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getLoginRoute(route) {
|
||||||
|
let parts = route.name?.split('-') || [];
|
||||||
|
const params = {};
|
||||||
|
const routeParams = route.params || {};
|
||||||
|
|
||||||
|
// Find the 'resource' part of the route, if it is there
|
||||||
|
const index = parts.findIndex(p => p === 'resource');
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
parts = parts.slice(0, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just keep the params that are needed
|
||||||
|
parts.forEach((param) => {
|
||||||
|
if (routeParams[param]) {
|
||||||
|
params[param] = routeParams[param];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: parts.join('-'),
|
||||||
|
params
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue