feat(ui): portainer base component css change [EE-3381] (#7115)

pull/7084/head
Chaim Lev-Ari 2022-06-23 09:32:18 +03:00 committed by GitHub
parent 825269c119
commit f78a6568a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 999 additions and 1596 deletions

View File

@ -58,6 +58,7 @@ overrides:
parser: '@typescript-eslint/parser' parser: '@typescript-eslint/parser'
plugins: plugins:
- '@typescript-eslint' - '@typescript-eslint'
- 'regex'
extends: extends:
- airbnb - airbnb
- airbnb-typescript - airbnb-typescript
@ -103,6 +104,7 @@ overrides:
'react/jsx-no-bind': off 'react/jsx-no-bind': off
'no-await-in-loop': 'off' 'no-await-in-loop': 'off'
'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }] 'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }]
'regex/invalid': ['error', [{ 'regex': 'data-feather="(.*)"', 'message': 'Please use `react-feather` package instead' }]]
- files: - files:
- app/**/*.test.* - app/**/*.test.*
extends: extends:

View File

@ -1,4 +1,5 @@
import $ from 'jquery'; import $ from 'jquery';
import feather from 'feather-icons';
import { PortainerEndpointTypes } from 'Portainer/models/endpoint/models'; import { PortainerEndpointTypes } from 'Portainer/models/endpoint/models';
/* @ngInject */ /* @ngInject */
@ -21,6 +22,10 @@ export function onStartupAngular($rootScope, $state, $interval, LocalStorage, En
HttpRequestHelper.resetAgentHeaders(); HttpRequestHelper.resetAgentHeaders();
}); });
$transitions.onSuccess({}, () => {
feather.replace();
});
// Keep-alive Edge endpoints by sending a ping request every minute // Keep-alive Edge endpoints by sending a ping request every minute
$interval(() => { $interval(() => {
ping(EndpointProvider, SystemService); ping(EndpointProvider, SystemService);

View File

@ -67,7 +67,6 @@ body,
} }
.form-section-title { .form-section-title {
border-bottom: 1px solid var(--border-form-section-title-color);
margin-top: 5px; margin-top: 5px;
margin-bottom: 15px; margin-bottom: 15px;
color: var(--text-form-section-title-color); color: var(--text-form-section-title-color);
@ -149,7 +148,7 @@ a[ng-click] {
} }
.fa.red-icon { .fa.red-icon {
color: #ae2323; color: #f04438;
} }
.fa.orange-icon { .fa.orange-icon {
@ -221,12 +220,13 @@ a[ng-click] {
} }
.blocklist-item { .blocklist-item {
padding: 7px; padding: 10px;
margin-bottom: 7px; margin-bottom: 10px;
cursor: pointer; cursor: pointer;
border: 1px solid var(--border-blocklist-color); border: 1px solid var(--border-blocklist);
border-radius: 2px; border-radius: 8px;
box-shadow: var(--shadow-box-color); box-shadow: var(--shadow-box-color);
margin-right: 10px;
} }
.blocklist-item--disabled { .blocklist-item--disabled {
@ -243,6 +243,7 @@ a[ng-click] {
.blocklist-item:hover { .blocklist-item:hover {
background-color: var(--bg-blocklist-hover-color); background-color: var(--bg-blocklist-hover-color);
color: var(--text-blocklist-hover-color); color: var(--text-blocklist-hover-color);
border: 1px solid var(--ui-blue-4);
} }
.blocklist-item-box { .blocklist-item-box {
@ -379,12 +380,13 @@ a[ng-click] {
} }
.panel-body { .panel-body {
padding-top: 30px; padding: 30px 25px;
background-color: var(--white-color) fff; background-color: var(--white-color);
border-radius: 8px;
} }
.user-box { .user-box {
margin-right: 25px; margin-right: 15px;
} }
.select-endpoint { .select-endpoint {

View File

@ -0,0 +1,468 @@
/* Main Color UI Override*/
.blue {
background: var(--bg-dashboard-item) !important;
}
/* Button */
.btn {
border-radius: 5px;
}
.btn-primary {
background-color: var(--ui-blue-1);
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active .active {
background-color: var(--ui-blue-2);
}
.btn-danger {
background-color: var(--ui-red-1);
}
.btn-success {
background-color: var(--ui-green-1);
}
/* Toggle switch */
.switch {
position: relative;
display: inline-block;
width: 42px;
height: 25px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* Toggle */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--bg-switch-box-color);
-webkit-transition: 0.4s;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: '';
height: 19px;
width: 19px;
left: 3px;
bottom: 3px;
background-color: var(--white-color);
-webkit-transition: 0.4s;
transition: 0.4s;
}
input:checked + .slider {
background-color: var(--ui-blue-1);
}
input:focus + .slider {
box-shadow: 0 0 1px var(--ui-blue-1);
}
input:checked + .slider:before {
-webkit-transform: translateX(17px);
-ms-transform: translateX(17px);
transform: translateX(17px);
}
.slider.round {
border-radius: 25px;
}
.slider.round:before {
border-radius: 50%;
}
/* Checkbox */
.md-checkbox input[type='checkbox']:enabled + label:before {
background-color: var(--bg-checkbox) !important;
border: 1px solid var(--border-checkbox) !important;
border-radius: 5px;
}
.md-checkbox input[type='checkbox']:disabled + label:before {
border-radius: 5px;
}
.md-checkbox input[type='checkbox']:checked + label:before {
background-color: var(--ui-blue-1) !important;
color: var(--ui-blue-1) !important;
border: 1px solid var(--ui-blue-1) !important;
}
.md-checkbox input[type='checkbox']:checked + .checkmark {
border-color: var(--grey-6);
background-color: var(--bg-checkbox);
}
/* Slider */
.rzslider .rz-pointer {
background-color: var(--white-color);
border: 3px solid var(--ui-blue-1);
width: 25px;
height: 25px;
top: -10px;
}
.rzslider .rz-bar {
background-color: var(--ui-grey-1);
height: 8px;
border-radius: 5px;
}
.rzslider .rz-selection {
background-color: var(--ui-blue-1);
}
.rzslider .rz-pointer:after {
display: none;
}
/* Datatable */
.datatable .searchBar {
border: 1px solid var(--border-searchbar);
padding: 5px;
background: var(--bg-searchbar) !important;
border-radius: 5px;
}
.datatable .searchBar input[type='text'] {
border: 0px !important;
}
.datatable .toolBar {
border-radius: 8px;
}
.datatable .footer {
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
padding-bottom: 10px;
}
.datatable .toolBar {
padding-top: 20px !important;
padding-bottom: 20px !important;
}
.toolBar {
display: flex;
flex-direction: row;
}
.toolBar > .toolBarTitle {
flex: auto;
}
.toolBar > .searchBar {
flex: right;
margin-right: 10px;
width: 500px;
height: 30px;
}
.datatable .searchBar {
padding: 4px 10px !important;
font-size: 14px;
}
.toolBar > .actionBar {
flex: right;
margin-right: 10px;
}
.datatable .actionBar {
padding: 0px !important;
}
.toolBar > .settings {
width: 60px;
}
.datatable .toolBar .settings {
margin-right: 5px !important;
}
/* Widget */
.widget .widget-icon i {
color: var(--ui-blue-1);
}
.widget .widget-body table thead {
border-top: 1px solid var(--border-table-color);
}
/* Button Group */
.input-group-addon:first-child {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.input-group .form-control:not(:first-child):not(:last-child) {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.input-group-btn:last-child .btn {
margin-left: 5px;
border-radius: 5px;
}
.form-control {
border-radius: 5px;
}
.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
background-color: var(--bg-button-group);
border: 1px solid var(--border-button-group);
color: var(--text-button-group);
}
.btn-group .btn-light {
background-color: var(--bg-button-group);
border: 1px solid var(--border-button-group);
color: var(--text-button-group);
}
.btn-group .btn-dangerlight {
background-color: var(--ui-red-2);
border: 1px solid var(--border-button-group);
color: var(--ui-red-1);
}
.btn.active {
box-shadow: none;
}
/* Toaster */
#toast-container > .toast-success {
background-image: url(../images/icon-success.svg) !important;
background-position: top 20px left 20px;
}
#toast-container > .toast-error {
background-image: url(../images/icon-error.svg) !important;
background-position: top 20px left 20px;
}
#toast-container > .toast-warning {
background-image: url(../images/icon-warning.svg) !important;
}
.toast-success .toast-progress {
background-color: var(--ui-green-1);
}
.toast-warning .toast-progress {
background-color: var(--ui-yellow);
}
.toast-error .toast-progress {
background-color: var(--ui-red-1);
}
#toast-container > div {
color: var(--ui-grey-4);
background-color: var(--white-color);
border-radius: 8px;
padding: 20px 20px 20px 80px;
width: 300px;
opacity: 1;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
filter: alpha(opacity=100);
}
#toast-container > div:hover {
-moz-box-shadow: 0 0 12px var(--ui-grey-4);
-webkit-box-shadow: 0 0 12px var(--ui-grey-4);
box-shadow: 0 0 12px var(--ui-grey-4);
}
.toast-close-button {
color: var(--black-color);
text-decoration: none;
cursor: pointer;
opacity: 0.4;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
filter: alpha(opacity=40);
}
.toast-close-button:hover,
.toast-close-button:focus {
color: var(--black-color);
text-decoration: none;
cursor: pointer;
opacity: 0.6;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60);
filter: alpha(opacity=60);
}
.toast-title {
color: var(--black-color);
padding: 10px 0px;
}
/* Modal */
.modal-dialog {
width: 450px;
}
.modal-content {
padding: 55px 20px 20px 20px;
background-image: url(../images/icon-warning.svg);
background-repeat: no-repeat;
background-position: top 10px left 10px;
}
.modal-header {
padding: 10px 0px 10px 0px;
border-bottom: none;
}
.modal-header .close {
margin-top: -40px;
}
.modal-header .modal-title {
font-weight: bold;
}
.modal-body {
padding: 10px 0px;
border-bottom: none;
}
.modal-body .bootbox-body {
font-size: 12px;
color: var(--text-bootbox);
}
.modal-footer {
padding: 10px 0px;
border-top: none;
display: flex;
}
.modal-footer .bootbox-cancel {
width: 100%;
}
.modal-footer .bootbox-accept {
width: 100%;
}
.bootbox-checkbox-list {
border: 0px;
}
/* Boxselector */
.boxselector_wrapper input[type='radio']:checked + label,
.box-selector-item input[type='radio']:checked + label {
background: var(--ui-blue-3) !important;
color: black !important;
border-radius: 8px;
border-color: var(--ui-blue-4);
padding: 15px;
box-shadow: none;
}
.boxselector_wrapper input[type='radio']:not(:disabled) + label,
.box-selector-item input[type='radio']:not(:disabled) + label {
background: var(--ui-grey-2);
color: var(--black-color) !important;
border-radius: 8px;
border-color: var(--ui-grey-1);
padding: 15px;
box-shadow: none;
}
.row.header {
background-color: var(--bg-body-color) !important;
margin-bottom: 5px !important;
}
/* Databatle Setting Menu */
.tableMenu {
border: 1px solid var(--border-bootbox);
border-radius: 8px;
}
[data-reach-menu-list],
[data-reach-menu-items] {
background: none;
}
.dropdown-menu {
border-radius: 8px;
}
.dropdown-menu .tableMenu {
border: 0px;
}
/* Myaccount Dropdown Menu */
.myaccount-dropdown {
color: var(--text-body-color);
font-size: 17px;
}
.myaccount-container {
margin-top: -25px;
}
.myaccount-icon {
padding-left: 8px;
}
.myaccount-link {
display: inline;
border: 0px;
background: none;
padding: 10px 0px;
}
/* Status Indicator Label Style */
.label {
border-radius: 8px !important;
}
.label-danger {
background-color: var(--ui-red-1);
}
.label-success {
background-color: var(--ui-green-1);
}
/* Feather Icon */
.feather {
height: 18px;
width: 18px;
position: relative;
top: 3px;
margin-right: 5px;
color: var(--text-body-color);
}

View File

@ -21,3 +21,4 @@ import './app.css';
import './theme.css'; import './theme.css';
import './vendor-override.css'; import './vendor-override.css';
import '../fonts/nomad-icon.css'; import '../fonts/nomad-icon.css';
import './bootstrap-override.css';

View File

@ -94,20 +94,18 @@
/* Fonts */ /* Fonts */
@font-face { @font-face {
font-family: 'Montserrat'; font-family: 'Inter';
src: url('../fonts/montserrat-regular-webfont.eot'); src: url('../fonts/Inter-VariableFont.ttf') format('truetype');
src: url('../fonts/montserrat-regular-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/montserrat-regular-webfont.woff') format('woff'),
url('../fonts/montserrat-regular-webfont.ttf') format('truetype'), url('../fonts/montserrat-regular-webfont.svg#montserratregular') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@media screen and (-webkit-min-device-pixel-ratio: 0) { @media screen and (-webkit-min-device-pixel-ratio: 0) {
@font-face { @font-face {
font-family: 'Montserrat'; font-family: 'Inter';
src: url('../fonts/montserrat-regular-webfont.svg') format('svg'); src: url('../fonts/Inter-VariableFont.ttf') format('truetype');
} }
select { select {
font-family: Arial, Helvetica, sans-serif; font-family: Inter, Arial, Helvetica, sans-serif;
} }
} }
/* Base */ /* Base */
@ -116,7 +114,7 @@ html {
} }
body { body {
background: var(--bg-body-color); background: var(--bg-body-color);
font-family: 'Montserrat'; font-family: 'Inter';
color: var(--text-body-color) !important; color: var(--text-body-color) !important;
} }
.row { .row {
@ -172,9 +170,8 @@ div.input-mask {
-moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
background: var(--bg-widget-color); background: var(--bg-widget-color);
border: 1px solid transparent; border: 1px solid var(--border-widget);
border-radius: 2px; border-radius: 8px;
border-color: var(--border-widget-color);
} }
.widget .widget-header .pagination, .widget .widget-header .pagination,
.widget .widget-footer .pagination { .widget .widget-footer .pagination {
@ -182,9 +179,7 @@ div.input-mask {
} }
.widget .widget-header { .widget .widget-header {
color: var(--text-widget-header-color); color: var(--text-widget-header-color);
background-color: var(--bg-widget-header-color);
padding: 10px 15px; padding: 10px 15px;
border-bottom: 1px solid var(--border-widget-color);
line-height: 30px; line-height: 30px;
} }
.widget .widget-header i { .widget .widget-header i {
@ -192,6 +187,7 @@ div.input-mask {
} }
.widget .widget-body { .widget .widget-body {
padding: 20px; padding: 20px;
border-radius: 8px;
} }
.widget .widget-body table thead { .widget .widget-body table thead {
background: var(--bg-widget-table-color); background: var(--bg-widget-table-color);

View File

@ -1,5 +1,20 @@
/* Color Variable */ /* Color Variable */
html { :root {
--ui-blue-1: #0086c9;
--ui-blue-2: #026aa2;
--ui-blue-3: #e0f2fe;
--ui-blue-4: #0ba5ec;
--ui-red-1: #d92d20;
--ui-red-2: #fef3f2;
--ui-green-1: #12b76a;
--ui-yellow: #fdb022;
--ui-grey-1: #d0d5dd;
--ui-grey-2: #f9fafb;
--ui-grey-3: #344054;
--ui-grey-4: #667085;
--ui-grey-5: #101828;
--ui-grey-6: #f2f4f7;
--black-color: #000; --black-color: #000;
--white-color: #fff; --white-color: #fff;
@ -94,11 +109,9 @@ html {
--orange-1: #e86925; --orange-1: #e86925;
--BE-only: var(--orange-1); --BE-only: var(--orange-1);
}
/* Default Theme */ /* Default Theme */
:root { --bg-card-color: var(--white-color);
--bg-card-color: var(--grey-10);
--bg-main-color: var(--white-color); --bg-main-color: var(--white-color);
--bg-body-color: var(--grey-9); --bg-body-color: var(--grey-9);
--bg-checkbox-border-color: var(--grey-49); --bg-checkbox-border-color: var(--grey-49);
@ -109,10 +122,10 @@ html {
--bg-widget-table-color: var(--grey-13); --bg-widget-table-color: var(--grey-13);
--bg-header-color: var(--white-color); --bg-header-color: var(--white-color);
--bg-hover-table-color: var(--grey-14); --bg-hover-table-color: var(--grey-14);
--bg-switch-box-color: var(--white-color); --bg-switch-box-color: var(--ui-grey-1);
--bg-input-group-addon-color: var(--grey-11); --bg-input-group-addon-color: var(--ui-grey-6);
--bg-btn-default-color: var(--white-color); --bg-btn-default-color: var(--white-color);
--bg-blocklist-hover-color: var(--grey-12); --bg-blocklist-hover-color: var(--ui-blue-3);
--bg-boxselector-color: var(--white-color); --bg-boxselector-color: var(--white-color);
--bg-table-color: var(--white-color); --bg-table-color: var(--white-color);
--bg-md-checkbox-color: var(--grey-12); --bg-md-checkbox-color: var(--grey-12);
@ -143,6 +156,13 @@ html {
--bg-panel-body-color: var(--white-color); --bg-panel-body-color: var(--white-color);
--bg-codemirror-color: var(--white-color); --bg-codemirror-color: var(--white-color);
--bg-codemirror-selected-color: var(--grey-22); --bg-codemirror-selected-color: var(--grey-22);
--bg-tooltip-color: var(--ui-grey-5);
--bg-input-sm-color: var(--white-color);
--bg-service-datatable-thead: var(--grey-23);
--bg-app-datatable-thead: var(--grey-23);
--bg-inner-datatable-thead: var(--grey-23);
--bg-service-datatable-tbody: var(--grey-24);
--bg-app-datatable-tbody: var(--grey-24);
--bg-multiselect-color: var(--white-color); --bg-multiselect-color: var(--white-color);
--bg-daterangepicker-color: var(--white-color); --bg-daterangepicker-color: var(--white-color);
--bg-calendar-color: var(--white-color); --bg-calendar-color: var(--white-color);
@ -162,6 +182,10 @@ html {
--bg-stepper-item-active: var(--white-color); --bg-stepper-item-active: var(--white-color);
--bg-stepper-item-counter: var(--grey-61); --bg-stepper-item-counter: var(--grey-61);
--bg-sortbutton-color: var(--white-color); --bg-sortbutton-color: var(--white-color);
--bg-dashboard-item: var(--ui-blue-3);
--bg-searchbar: var(--ui-grey-2);
--bg-inputbox: var(--ui-grey-2);
--bg-dropdown-hover: var(--ui-grey-6);
--text-main-color: var(--grey-7); --text-main-color: var(--grey-7);
--text-body-color: var(--grey-6); --text-body-color: var(--grey-6);
@ -193,14 +217,14 @@ html {
--text-blocklist-item-selected-color: var(--grey-37); --text-blocklist-item-selected-color: var(--grey-37);
--text-progress-bar-color: var(--grey-27); --text-progress-bar-color: var(--grey-27);
--text-pagination-color: var(--grey-26); --text-pagination-color: var(--grey-26);
--text-pagination-span-color: var(--blue-2); --text-pagination-span-color: var(--ui-grey-6);
--text-pagination-span-hover-color: var(--blue-4); --text-pagination-span-hover-color: var(--blue-4);
--text-ui-select-color: var(--grey-6); --text-ui-select-color: var(--grey-6);
--text-ui-select-hover-color: var(--grey-28); --text-ui-select-hover-color: var(--grey-28);
--text-summary-color: var(--black-color); --text-summary-color: var(--black-color);
--text-multiselect-button-color: var(--grey-29); --text-tooltip-color: var(--white-color);
--text-multiselect-item-color: var(--grey-30);
--text-sidebar-list-color: var(--grey-56); --text-sidebar-list-color: var(--grey-56);
--text-rzslider-color: var(--grey-36); --text-rzslider-color: var(--grey-36);
--text-rzslider-limit-color: var(--grey-36); --text-rzslider-limit-color: var(--grey-36);
--text-daterangepicker-end-date: var(--grey-57); --text-daterangepicker-end-date: var(--grey-57);
@ -210,6 +234,7 @@ html {
--text-input-autofill-color: var(--black-color); --text-input-autofill-color: var(--black-color);
--text-button-hover-color: var(--grey-6); --text-button-hover-color: var(--grey-6);
--text-small-select-color: var(--grey-25); --text-small-select-color: var(--grey-25);
--text-bootbox: var(--ui-grey-4);
--border-color: var(--grey-42); --border-color: var(--grey-42);
--border-widget-color: var(--grey-43); --border-widget-color: var(--grey-43);
@ -244,6 +269,9 @@ html {
--border-tooltip-color: var(--grey-47); --border-tooltip-color: var(--grey-47);
--border-modal: 0px; --border-modal: 0px;
--border-sortbutton: var(--grey-8); --border-sortbutton: var(--grey-8);
--border-bootbox: var(--ui-grey-1);
--border-blocklist: var(--ui-grey-1);
--border-widget: var(--ui-grey-1);
--hover-sidebar-color: var(--grey-37); --hover-sidebar-color: var(--grey-37);
--shadow-box-color: 0 3px 10px -2px var(--grey-50); --shadow-box-color: 0 3px 10px -2px var(--grey-50);
@ -265,9 +293,13 @@ html {
--text-multiselect-item: var(--grey-30); --text-multiselect-item: var(--grey-30);
--bg-multiselect-helpercontainer: var(--white-color); --bg-multiselect-helpercontainer: var(--white-color);
--text-input-textarea: var(--white-color); --text-input-textarea: var(--white-color);
--bg-service-datatable-thead: var(--grey-23);
--bg-inner-datatable-thead: var(--grey-23); --border-checkbox: var(--ui-grey-1);
--bg-service-datatable-tbody: var(--grey-24); --bg-checkbox: var(--white-color);
--border-searchbar: var(--ui-grey-1);
--bg-button-group: var(--white-color);
--border-button-group: var(--ui-grey-1);
--text-button-group: var(--ui-grey-3);
} }
:root[theme='dark'] { :root[theme='dark'] {
@ -337,6 +369,10 @@ html {
--bg-stepper-item-active: var(--grey-1); --bg-stepper-item-active: var(--grey-1);
--bg-stepper-item-counter: var(--grey-7); --bg-stepper-item-counter: var(--grey-7);
--bg-sortbutton-color: var(--grey-1); --bg-sortbutton-color: var(--grey-1);
--bg-dashboard-item: var(--grey-3);
--bg-searchbar: var(--grey-1);
--bg-inputbox: var(--grey-2);
--bg-dropdown-hover: var(--grey-3);
--text-main-color: var(--white-color); --text-main-color: var(--white-color);
--text-body-color: var(--white-color); --text-body-color: var(--white-color);
@ -368,7 +404,7 @@ html {
--text-blocklist-item-selected-color: var(--white-color); --text-blocklist-item-selected-color: var(--white-color);
--text-progress-bar-color: var(--white-color); --text-progress-bar-color: var(--white-color);
--text-pagination-color: var(--white-color); --text-pagination-color: var(--white-color);
--text-pagination-span-color: var(--blue-2); --text-pagination-span-color: var(--ui-grey-6);
--text-pagination-span-hover-color: var(--white-color); --text-pagination-span-hover-color: var(--white-color);
--text-ui-select-color: var(--white-color); --text-ui-select-color: var(--white-color);
--text-ui-select-hover-color: var(--white-color); --text-ui-select-hover-color: var(--white-color);
@ -385,6 +421,7 @@ html {
--text-input-autofill-color: var(--grey-8); --text-input-autofill-color: var(--grey-8);
--text-button-hover-color: var(--white-color); --text-button-hover-color: var(--white-color);
--text-small-select-color: var(--grey-7); --text-small-select-color: var(--grey-7);
--text-bootbox: var(--white-color);
--border-color: var(--grey-3); --border-color: var(--grey-3);
--border-widget-color: var(--grey-1); --border-widget-color: var(--grey-1);
@ -408,9 +445,7 @@ html {
--border-pagination-color: var(--grey-3); --border-pagination-color: var(--grey-3);
--border-pagination-span-color: var(--grey-3); --border-pagination-span-color: var(--grey-3);
--border-pagination-hover-color: var(--grey-3); --border-pagination-hover-color: var(--grey-3);
--border-pagination-hover-color: var(--grey-3); --border-boxselector-wrapper-hover: 3px solid var(--blue-8);
--border-multiselect-button-color: var(--grey-3);
--border-searchbar-color: var(--grey-1);
--border-panel-color: var(--grey-2); --border-panel-color: var(--grey-2);
--border-daterangepicker-color: var(--grey-3); --border-daterangepicker-color: var(--grey-3);
--border-calendar-table: var(--grey-3); --border-calendar-table: var(--grey-3);
@ -420,6 +455,9 @@ html {
--border-tooltip-color: var(--grey-3); --border-tooltip-color: var(--grey-3);
--border-modal: 0px; --border-modal: 0px;
--border-sortbutton: var(--grey-3); --border-sortbutton: var(--grey-3);
--border-bootbox: var(--ui-grey-3);
--border-blocklist: var(--ui-grey-3);
--border-widget: var(--ui-grey-3);
--hover-sidebar-color: var(--grey-3); --hover-sidebar-color: var(--grey-3);
--blue-color: var(--blue-2); --blue-color: var(--blue-2);
@ -439,9 +477,13 @@ html {
--text-multiselect-item: var(--white-color); --text-multiselect-item: var(--white-color);
--bg-multiselect-helpercontainer: var(--grey-1); --bg-multiselect-helpercontainer: var(--grey-1);
--text-input-textarea: var(--grey-1); --text-input-textarea: var(--grey-1);
--bg-service-datatable-thead: var(--grey-1);
--bg-inner-datatable-thead: var(--grey-1); --border-checkbox: var(--ui-grey-1);
--bg-service-datatable-tbody: var(--grey-1); --bg-checkbox: var(--white-color);
--border-searchbar: var(--ui-grey-1);
--bg-button-group: var(--white-color);
--border-button-group: var(--ui-grey-1);
--text-button-group: var(--ui-grey-3);
} }
:root[theme='highcontrast'] { :root[theme='highcontrast'] {
@ -465,7 +507,7 @@ html {
--bg-motd-body-color: var(--black-color); --bg-motd-body-color: var(--black-color);
--bg-blocklist-hover-color: var(--black-color); --bg-blocklist-hover-color: var(--black-color);
--bg-blocklist-item-selected-color: var(--black-color); --bg-blocklist-item-selected-color: var(--black-color);
--bg-input-group-addon-color: var(--grey-1); --bg-input-group-addon-color: var(--grey-3);
--bg-table-color: var(--black-color); --bg-table-color: var(--black-color);
--bg-codemirror-gutters-color: var(--black-color); --bg-codemirror-gutters-color: var(--black-color);
--bg-codemirror-color: var(--black-color); --bg-codemirror-color: var(--black-color);
@ -512,6 +554,9 @@ html {
--bg-stepper-item-active: var(--black-color); --bg-stepper-item-active: var(--black-color);
--bg-stepper-item-counter: var(--grey-3); --bg-stepper-item-counter: var(--grey-3);
--bg-sortbutton-color: var(--grey-1); --bg-sortbutton-color: var(--grey-1);
--bg-inputbox: var(--black-color);
--bg-searchbar: var(--black-color);
--bg-dropdown-hover: var(--black-color);
--text-main-color: var(--white-color); --text-main-color: var(--white-color);
--text-body-color: var(--white-color); --text-body-color: var(--white-color);
@ -554,7 +599,8 @@ html {
--text-btn-default-color: var(--white-color); --text-btn-default-color: var(--white-color);
--text-small-select-color: var(--white-color); --text-small-select-color: var(--white-color);
--text-multiselect-item-color: var(--white-color); --text-multiselect-item-color: var(--white-color);
--text-pagination-span-color: var(--blue-2); --text-pagination-span-color: var(--ui-grey-6);
--text-bootbox: var(--white-color);
--border-color: var(--grey-55); --border-color: var(--grey-55);
--border-widget-color: var(--white-color); --border-widget-color: var(--white-color);
@ -584,6 +630,9 @@ html {
--border-modal: 1px solid var(--white-color); --border-modal: 1px solid var(--white-color);
--border-blocklist-color: var(--white-color); --border-blocklist-color: var(--white-color);
--border-sortbutton: var(--black-color); --border-sortbutton: var(--black-color);
--border-bootbox: var(--black-color);
--border-blocklist: var(--white-color);
--border-widget: var(--white-color);
--hover-sidebar-color: var(--blue-9); --hover-sidebar-color: var(--blue-9);
--hover-sidebar-color: var(--black-color); --hover-sidebar-color: var(--black-color);
@ -605,4 +654,11 @@ html {
--text-cm-meta-color: var(--white-color); --text-cm-meta-color: var(--white-color);
--text-cm-string-color: var(--red-7); --text-cm-string-color: var(--red-7);
--text-progress-bar-color: var(--black-color); --text-progress-bar-color: var(--black-color);
--border-checkbox: var(--ui-grey-1);
--bg-checkbox: var(--white-color);
--border-searchbar: var(--ui-grey-1);
--bg-button-group: var(--white-color);
--border-button-group: var(--ui-grey-1);
--text-button-group: var(--ui-grey-3);
} }

View File

@ -1,7 +1,7 @@
/* Overide Vendor CSS */ /* Overide Vendor CSS */
.form-control { .form-control {
background-color: var(--bg-main-color) !important;
border: 1px solid var(--border-form-control-color); border: 1px solid var(--border-form-control-color);
background-color: var(--bg-inputbox);
color: var(--text-form-control-color); color: var(--text-form-control-color);
} }
@ -10,7 +10,7 @@
} }
.table > thead > tr > th { .table > thead > tr > th {
border-bottom: 2px solid var(--border-table-color); border-bottom: 1px solid var(--border-table-color);
} }
.table-hover > tbody > tr:hover { .table-hover > tbody > tr:hover {
@ -243,6 +243,10 @@ json-tree .branch-preview {
.panel { .panel {
border: 1px solid var(--border-panel-color); border: 1px solid var(--border-panel-color);
background-color: var(--bg-panel-body-color); background-color: var(--bg-panel-body-color);
border-radius: 8px;
-webkit-box-shadow: 0 4px 4px rgba(0, 0, 0, 0.05);
-moz-box-shadow: 0 4px 4px rgba(0, 0, 0, 0.05);
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.05);
} }
.theme-information .col-sm-12 { .theme-information .col-sm-12 {
@ -381,3 +385,10 @@ fieldset[disabled] .btn {
.multiSelect.inlineBlock button { .multiSelect.inlineBlock button {
margin: 0; margin: 0;
} }
.nav-tabs > li.active > a {
border-top: 0px;
border-left: 0px;
border-right: 0px;
border-bottom: 3px solid red;
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 86 KiB

View File

@ -0,0 +1,5 @@
<svg width="46" height="46" viewBox="0 0 46 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="3" width="40" height="40" rx="20" fill="#FEE4E2"/>
<path d="M23 19.6667V23M23 26.3333H23.0083M31.3333 23C31.3333 27.6024 27.6024 31.3333 23 31.3333C18.3976 31.3333 14.6667 27.6024 14.6667 23C14.6667 18.3976 18.3976 14.6667 23 14.6667C27.6024 14.6667 31.3333 18.3976 31.3333 23Z" stroke="#D92D20" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="3" y="3" width="40" height="40" rx="20" stroke="#FEF3F2" stroke-width="6"/>
</svg>

After

Width:  |  Height:  |  Size: 571 B

View File

@ -0,0 +1,5 @@
<svg width="46" height="46" viewBox="0 0 46 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="3" width="40" height="40" rx="20" fill="#D1FADF"/>
<path d="M31.3333 22.2333V23C31.3323 24.797 30.7504 26.5456 29.6744 27.9849C28.5985 29.4241 27.0861 30.4771 25.3628 30.9866C23.6395 31.4961 21.7977 31.4349 20.112 30.8122C18.4264 30.1894 16.9872 29.0384 16.0091 27.5309C15.031 26.0234 14.5665 24.2401 14.6847 22.4469C14.803 20.6538 15.4977 18.9469 16.6652 17.5809C17.8328 16.2148 19.4106 15.2628 21.1635 14.8668C22.9163 14.4708 24.7502 14.6519 26.3917 15.3833M31.3333 16.3333L23 24.675L20.5 22.175" stroke="#039855" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="3" y="3" width="40" height="40" rx="20" stroke="#ECFDF3" stroke-width="6"/>
</svg>

After

Width:  |  Height:  |  Size: 791 B

View File

@ -0,0 +1,5 @@
<svg width="46" height="46" viewBox="0 0 46 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="3" width="40" height="40" rx="20" fill="#FEF0C7"/>
<path d="M22.9998 19.6667V23M22.9998 26.3333H23.0081M21.5748 15.3833L14.5165 27.1667C14.3709 27.4187 14.2939 27.7044 14.2931 27.9954C14.2923 28.2864 14.3677 28.5726 14.5118 28.8254C14.6559 29.0783 14.8637 29.2889 15.1146 29.4365C15.3654 29.5841 15.6505 29.6635 15.9415 29.6667H30.0581C30.3491 29.6635 30.6342 29.5841 30.885 29.4365C31.1359 29.2889 31.3437 29.0783 31.4878 28.8254C31.6319 28.5726 31.7073 28.2864 31.7065 27.9954C31.7057 27.7044 31.6287 27.4187 31.4831 27.1667L24.4248 15.3833C24.2762 15.1384 24.0671 14.9359 23.8175 14.7954C23.5679 14.6549 23.2863 14.581 22.9998 14.581C22.7134 14.581 22.4317 14.6549 22.1821 14.7954C21.9325 14.9359 21.7234 15.1384 21.5748 15.3833Z" stroke="#DC6803" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="3" y="3" width="40" height="40" rx="20" stroke="#FFFAEB" stroke-width="6"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -24,7 +24,9 @@ test('dashboard items should render correctly', async () => {
const subscriptionElements = within(subscriptionsItem); const subscriptionElements = within(subscriptionsItem);
expect(subscriptionElements.getByLabelText('value')).toBeVisible(); expect(subscriptionElements.getByLabelText('value')).toBeVisible();
expect(subscriptionElements.getByLabelText('icon')).toHaveClass('fa-th-list'); expect(subscriptionElements.getByRole('img', { hidden: true })).toHaveClass(
'fa-th-list'
);
expect(subscriptionElements.getByLabelText('resourceType')).toHaveTextContent( expect(subscriptionElements.getByLabelText('resourceType')).toHaveTextContent(
'Subscriptions' 'Subscriptions'
); );
@ -34,7 +36,7 @@ test('dashboard items should render correctly', async () => {
const resourceGroupElements = within(resourceGroupsItem); const resourceGroupElements = within(resourceGroupsItem);
expect(resourceGroupElements.getByLabelText('value')).toBeVisible(); expect(resourceGroupElements.getByLabelText('value')).toBeVisible();
expect(resourceGroupElements.getByLabelText('icon')).toHaveClass( expect(resourceGroupElements.getByRole('img', { hidden: true })).toHaveClass(
'fa-th-list' 'fa-th-list'
); );
expect( expect(

View File

@ -141,6 +141,14 @@ export function ContainersDatatable({
return ( return (
<TableContainer> <TableContainer>
<TableTitle icon="fa-cubes" label="Containers"> <TableTitle icon="fa-cubes" label="Containers">
<SearchBar value={searchBarValue} onChange={handleSearchBarChange} />
<TableActions>
<ContainersDatatableActions
selectedItems={selectedFlatRows.map((row) => row.original)}
isAddActionVisible={isAddActionVisible}
endpointId={endpoint.Id}
/>
</TableActions>
<TableTitleActions> <TableTitleActions>
<ColumnVisibilityMenu<DockerContainer> <ColumnVisibilityMenu<DockerContainer>
columns={columnsToHide} columns={columnsToHide}
@ -156,16 +164,6 @@ export function ContainersDatatable({
</TableTitleActions> </TableTitleActions>
</TableTitle> </TableTitle>
<TableActions>
<ContainersDatatableActions
selectedItems={selectedFlatRows.map((row) => row.original)}
isAddActionVisible={isAddActionVisible}
endpointId={endpoint.Id}
/>
</TableActions>
<SearchBar value={searchBarValue} onChange={handleSearchBarChange} />
<Table <Table
className={tableProps.className} className={tableProps.className}
role={tableProps.role} role={tableProps.role}

View File

@ -68,7 +68,7 @@ export function ContainersDatatableActions({
<ButtonGroup> <ButtonGroup>
<Authorized authorizations="DockerContainerStart"> <Authorized authorizations="DockerContainerStart">
<Button <Button
color="success" color="light"
onClick={() => onStartClick(selectedItems)} onClick={() => onStartClick(selectedItems)}
disabled={selectedItemCount === 0 || !hasStoppedItemsSelected} disabled={selectedItemCount === 0 || !hasStoppedItemsSelected}
> >
@ -79,7 +79,7 @@ export function ContainersDatatableActions({
<Authorized authorizations="DockerContainerStop"> <Authorized authorizations="DockerContainerStop">
<Button <Button
color="danger" color="light"
onClick={() => onStopClick(selectedItems)} onClick={() => onStopClick(selectedItems)}
disabled={selectedItemCount === 0 || !hasRunningItemsSelected} disabled={selectedItemCount === 0 || !hasRunningItemsSelected}
> >
@ -90,7 +90,7 @@ export function ContainersDatatableActions({
<Authorized authorizations="DockerContainerKill"> <Authorized authorizations="DockerContainerKill">
<Button <Button
color="danger" color="light"
onClick={() => onKillClick(selectedItems)} onClick={() => onKillClick(selectedItems)}
disabled={selectedItemCount === 0} disabled={selectedItemCount === 0}
> >
@ -101,6 +101,7 @@ export function ContainersDatatableActions({
<Authorized authorizations="DockerContainerRestart"> <Authorized authorizations="DockerContainerRestart">
<Button <Button
color="light"
onClick={() => onRestartClick(selectedItems)} onClick={() => onRestartClick(selectedItems)}
disabled={selectedItemCount === 0} disabled={selectedItemCount === 0}
> >
@ -111,6 +112,7 @@ export function ContainersDatatableActions({
<Authorized authorizations="DockerContainerPause"> <Authorized authorizations="DockerContainerPause">
<Button <Button
color="light"
onClick={() => onPauseClick(selectedItems)} onClick={() => onPauseClick(selectedItems)}
disabled={selectedItemCount === 0 || !hasRunningItemsSelected} disabled={selectedItemCount === 0 || !hasRunningItemsSelected}
> >
@ -121,6 +123,7 @@ export function ContainersDatatableActions({
<Authorized authorizations="DockerContainerUnpause"> <Authorized authorizations="DockerContainerUnpause">
<Button <Button
color="light"
onClick={() => onResumeClick(selectedItems)} onClick={() => onResumeClick(selectedItems)}
disabled={selectedItemCount === 0 || !hasPausedItemsSelected} disabled={selectedItemCount === 0 || !hasPausedItemsSelected}
> >
@ -131,7 +134,7 @@ export function ContainersDatatableActions({
<Authorized authorizations="DockerContainerDelete"> <Authorized authorizations="DockerContainerDelete">
<Button <Button
color="danger" color="dangerlight"
onClick={() => onRemoveClick(selectedItems)} onClick={() => onRemoveClick(selectedItems)}
disabled={selectedItemCount === 0} disabled={selectedItemCount === 0}
> >

View File

@ -14,29 +14,29 @@
<rd-widget-header icon="fa-cogs" title-text="Actions"></rd-widget-header> <rd-widget-header icon="fa-cogs" title-text="Actions"></rd-widget-header>
<rd-widget-body classes="padding"> <rd-widget-body classes="padding">
<div class="btn-group" role="group" aria-label="..."> <div class="btn-group" role="group" aria-label="...">
<button authorization="DockerContainerStart" class="btn btn-success btn-sm" ng-click="start()" ng-disabled="container.State.Running || container.IsPortainer" <button authorization="DockerContainerStart" class="btn btn-light btn-sm" ng-click="start()" ng-disabled="container.State.Running || container.IsPortainer"
><i class="fa fa-play space-right" aria-hidden="true"></i>Start</button ><i class="fa fa-play space-right" aria-hidden="true"></i>Start</button
> >
<button authorization="DockerContainerStop" class="btn btn-danger btn-sm" ng-click="stop()" ng-disabled="!container.State.Running || container.IsPortainer" <button authorization="DockerContainerStop" class="btn btn-light btn-sm" ng-click="stop()" ng-disabled="!container.State.Running || container.IsPortainer"
><i class="fa fa-stop space-right" aria-hidden="true"></i>Stop</button ><i class="fa fa-stop space-right" aria-hidden="true"></i>Stop</button
> >
<button authorization="DockerContainerKill" class="btn btn-danger btn-sm" ng-click="kill()" ng-disabled="!container.State.Running || container.IsPortainer" <button authorization="DockerContainerKill" class="btn btn-light btn-sm" ng-click="kill()" ng-disabled="!container.State.Running || container.IsPortainer"
><i class="fa fa-bomb space-right" aria-hidden="true"></i>Kill</button ><i class="fa fa-bomb space-right" aria-hidden="true"></i>Kill</button
> >
<button authorization="DockerContainerRestart" class="btn btn-primary btn-sm" ng-click="restart()" ng-disabled="!container.State.Running || container.IsPortainer" <button authorization="DockerContainerRestart" class="btn btn-light btn-sm" ng-click="restart()" ng-disabled="!container.State.Running || container.IsPortainer"
><i class="fa fa-sync space-right" aria-hidden="true"></i>Restart</button ><i class="fa fa-sync space-right" aria-hidden="true"></i>Restart</button
> >
<button <button
authorization="DockerContainerPause" authorization="DockerContainerPause"
class="btn btn-primary btn-sm" class="btn btn-light btn-sm"
ng-click="pause()" ng-click="pause()"
ng-disabled="!container.State.Running || container.State.Paused || container.IsPortainer" ng-disabled="!container.State.Running || container.State.Paused || container.IsPortainer"
><i class="fa fa-pause space-right" aria-hidden="true"></i>Pause</button ><i class="fa fa-pause space-right" aria-hidden="true"></i>Pause</button
> >
<button authorization="DockerContainerUnpause" class="btn btn-primary btn-sm" ng-click="unpause()" ng-disabled="!container.State.Paused || container.IsPortainer" <button authorization="DockerContainerUnpause" class="btn btn-light btn-sm" ng-click="unpause()" ng-disabled="!container.State.Paused || container.IsPortainer"
><i class="fa fa-play space-right" aria-hidden="true"></i>Resume</button ><i class="fa fa-play space-right" aria-hidden="true"></i>Resume</button
> >
<button authorization="DockerContainerDelete" class="btn btn-danger btn-sm" ng-click="confirmRemove()" ng-disabled="container.IsPortainer" <button authorization="DockerContainerDelete" class="btn btn-dangerlight btn-sm" ng-click="confirmRemove()" ng-disabled="container.IsPortainer"
><i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove</button ><i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove</button
> >
</div> </div>
@ -51,7 +51,7 @@
<span ng-hide="state.recreateContainerInProgress"><i class="fa fa-sync space-right" aria-hidden="true"></i>Recreate</span> <span ng-hide="state.recreateContainerInProgress"><i class="fa fa-sync space-right" aria-hidden="true"></i>Recreate</span>
<span ng-show="state.recreateContainerInProgress">Recreation in progress...</span> <span ng-show="state.recreateContainerInProgress">Recreation in progress...</span>
</button> </button>
<a class="btn btn-primary btn-sm" type="button" ui-sref="docker.containers.new({ from: container.Id, nodeName: nodeName })" ng-disabled="container.IsPortainer" <a class="btn btn-light btn-sm" type="button" ui-sref="docker.containers.new({ from: container.Id, nodeName: nodeName })" ng-disabled="container.IsPortainer"
><i class="fa fa-copy space-right" aria-hidden="true"></i>Duplicate/Edit</a ><i class="fa fa-copy space-right" aria-hidden="true"></i>Duplicate/Edit</a
> >
</div> </div>

View File

@ -35,6 +35,7 @@
</head> </head>
<body ng-controller="MainController"> <body ng-controller="MainController">
<react-query-dev-tools></react-query-dev-tools>
<div <div
id="page-wrapper" id="page-wrapper"
ng-class="{ ng-class="{

View File

@ -2,7 +2,7 @@
<rd-widget> <rd-widget>
<rd-widget-body classes="no-padding"> <rd-widget-body classes="no-padding">
<div class="toolBar"> <div class="toolBar">
<div class="toolBarTitle"> <i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px"></i> {{ $ctrl.titleText }} </div> <div class="toolBarTitle"> <pr-icon icon="$ctrl.titleIcon" class-name="space-right"></pr-icon> {{ $ctrl.titleText }} </div>
</div> </div>
<div class="actionBar"> <div class="actionBar">

View File

@ -1,11 +1,3 @@
<div class="breadcrumb-links"> <div class="breadcrumb-links">
<div class="pull-left" ng-transclude></div> <div class="pull-left" ng-transclude></div>
<div class="pull-right" ng-if="$ctrl.username && $ctrl.display">
<a ui-sref="portainer.account" style="margin-right: 5px">
<u> <i class="fa fa-wrench" aria-hidden="true"></i> my account </u>
</a>
<a ui-sref="portainer.logout({performApiLogout: true})" class="text-danger" style="margin-right: 25px" data-cy="template-logoutButton">
<u> <i class="fa fa-sign-out-alt" aria-hidden="true"></i> log out</u>
</a>
</div>
</div> </div>

View File

@ -1,8 +1,20 @@
<div class="page white-space-normal"> <div class="page white-space-normal">
{{ $ctrl.titleText }} {{ $ctrl.titleText }}
<span class="header_title_content" ng-transclude></span> <span class="header_title_content" ng-transclude></span>
<span class="pull-right user-box" ng-if="$ctrl.username && $ctrl.display">
<i class="fa fa-user-circle" aria-hidden="true"></i>
{{ $ctrl.username }}
</span>
</div> </div>
<span class="pull-right myaccount-container" uib-dropdown on-toggle="toggled(open)">
<a href class="myaccount-dropdown" uib-dropdown-toggle>
<span class="pull-right user-box" ng-if="$ctrl.username">
<i class="fa fa-user-circle" aria-hidden="true"></i> {{ $ctrl.username }} <i class="fa fa-angle-down myaccount-icon" aria-hidden="true"></i>
</span>
</a>
<ul class="dropdown-menu" uib-dropdown-menu>
<li>
<a ui-sref="portainer.account">My account</a>
</li>
<li>
<a ui-sref="portainer.logout({performApiLogout: true})" data-cy="template-logoutButton">Log out</a>
</li>
</ul>
</span>

View File

@ -1,10 +1,9 @@
.tooltip.portainer-tooltip .tooltip-inner { .tooltip.portainer-tooltip .tooltip-inner {
font-family: Montserrat;
background-color: var(--bg-tooltip-color); background-color: var(--bg-tooltip-color);
padding: 0.833em 1em; padding: 0.833em 1em;
color: var(--text-tooltip-color); color: var(--text-tooltip-color);
border: 1px solid var(--border-tooltip-color); border: 1px solid var(--border-tooltip-color) !important;
border-radius: 0.14285714rem; border-radius: 10px;
box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15); box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15);
} }

View File

@ -13,9 +13,9 @@ export const TooltipAngular: IComponentOptions = {
tooltip-class="portainer-tooltip" tooltip-class="portainer-tooltip"
uib-tooltip="{{$ctrl.message}}" uib-tooltip="{{$ctrl.message}}"
> >
<i <i
class="fa fa-question-circle blue-icon tooltip-icon" class="fa fa-question-circle blue-icon tooltip-icon"
aria-hidden="true" aria-hidden="true"
></i> ></i>
</span>`, </span>`,
}; };

View File

@ -2,7 +2,7 @@
<rd-widget> <rd-widget>
<rd-widget-body classes="no-padding"> <rd-widget-body classes="no-padding">
<div class="toolBar"> <div class="toolBar">
<div class="toolBarTitle"> <i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px"></i> {{ $ctrl.titleText }} </div> <div class="toolBarTitle"> <pr-icon icon="$ctrl.titleIcon" class-name="space-right"></pr-icon> {{ $ctrl.titleText }} </div>
</div> </div>
<div class="actionBar"> <div class="actionBar">
<button type="button" class="btn btn-sm btn-primary" ui-state="$ctrl.createPath"> <i class="fa fa-plus space-right" aria-hidden="true"></i>Add Custom Template </button> <button type="button" class="btn btn-sm btn-primary" ui-state="$ctrl.createPath"> <i class="fa fa-plus space-right" aria-hidden="true"></i>Add Custom Template </button>

View File

@ -1,5 +1,5 @@
<span class="setting" ng-class="{ 'setting-active': $ctrl.state.isOpen }" uib-dropdown dropdown-append-to-body auto-close="disabled" is-open="$ctrl.state.isOpen"> <span class="setting" ng-class="{ 'setting-active': $ctrl.state.isOpen }" uib-dropdown dropdown-append-to-body auto-close="disabled" is-open="$ctrl.state.isOpen">
<span uib-dropdown-toggle><i class="fa fa-columns space-right" aria-hidden="true"></i>Columns</span> <span uib-dropdown-toggle aria-label="Columns"><i class="fa fa-columns space-right" aria-hidden="true"></i></span>
<div class="dropdown-menu dropdown-menu-right" uib-dropdown-menu> <div class="dropdown-menu dropdown-menu-right" uib-dropdown-menu>
<div class="tableMenu"> <div class="tableMenu">
<div class="menuHeader"> Show / Hide Columns </div> <div class="menuHeader"> Show / Hide Columns </div>

View File

@ -1,12 +1,11 @@
.datatable .toolBar { .datatable .toolBar {
background-color: var(--bg-card-color); background-color: var(--bg-card-color);
color: var(--text-main-color);
overflow: auto; overflow: auto;
padding: 10px; padding: 10px;
font-size: 16px;
} }
.datatable .actionBar { .datatable .actionBar {
color: #767676;
padding: 10px; padding: 10px;
} }
@ -26,12 +25,11 @@
} }
.datatable .toolBar .setting-active { .datatable .toolBar .setting-active {
color: #337ab7; color: var(--ui-blue-4);
} }
.datatable .searchBar { .datatable .searchBar {
border-top: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
padding: 8px; padding: 8px;
background: var(--bg-main-color); background: var(--bg-main-color);
} }
@ -116,7 +114,7 @@
.tableMenu { .tableMenu {
color: #767676; color: #767676;
padding: 10px; padding: 15px;
background-color: var(--bg-dropdown-menu-color) !important; background-color: var(--bg-dropdown-menu-color) !important;
} }
@ -125,11 +123,8 @@
} }
.tableMenu .menuContent { .tableMenu .menuContent {
border-bottom: 1px solid #777;
font-size: 12px; font-size: 12px;
margin: 10px 0; margin: 10px 0;
max-height: 140px;
overflow-y: auto;
} }
.tableMenu .menuContent .md-radio:first-child { .tableMenu .menuContent .md-radio:first-child {

View File

@ -3,10 +3,45 @@
<rd-widget-body classes="no-padding"> <rd-widget-body classes="no-padding">
<div class="toolBar"> <div class="toolBar">
<div class="toolBarTitle"><i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px"></i> {{ $ctrl.titleText }} </div> <div class="toolBarTitle"><i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px"></i> {{ $ctrl.titleText }} </div>
<div class="searchBar">
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
<input
type="text"
class="searchInput"
ng-model="$ctrl.state.textFilter"
ng-change="$ctrl.onTextFilterChange()"
placeholder="Search..."
auto-focus
ng-model-options="{ debounce: 300 }"
data-cy="stack-searchInput"
/>
</div>
<div class="actionBar" ng-if="!$ctrl.offlineMode" authorization="PortainerStackCreate, PortainerStackDelete">
<button
type="button"
class="btn btn-sm btn-danger"
authorization="PortainerStackDelete"
ng-disabled="$ctrl.state.selectedItemCount === 0"
ng-click="$ctrl.removeAction($ctrl.state.selectedItems)"
data-cy="stack-removeStackButton"
>
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove
</button>
<button
ng-disabled="!$ctrl.createEnabled"
type="button"
class="btn btn-sm btn-primary"
ui-sref="docker.stacks.newstack"
authorization="PortainerStackCreate"
data-cy="stack-addStackButton"
>
<i class="fa fa-plus space-right" aria-hidden="true"></i>Add stack
</button>
</div>
<div class="settings"> <div class="settings">
<datatable-columns-visibility columns="$ctrl.columnVisibility.columns" on-change="($ctrl.onColumnVisibilityChange)"></datatable-columns-visibility> <datatable-columns-visibility columns="$ctrl.columnVisibility.columns" on-change="($ctrl.onColumnVisibilityChange)"></datatable-columns-visibility>
<span class="setting" ng-class="{ 'setting-active': $ctrl.settings.open }" uib-dropdown dropdown-append-to-body auto-close="disabled" is-open="$ctrl.settings.open"> <span class="setting" ng-class="{ 'setting-active': $ctrl.settings.open }" uib-dropdown dropdown-append-to-body auto-close="disabled" is-open="$ctrl.settings.open">
<span uib-dropdown-toggle><i class="fa fa-cog" aria-hidden="true"></i> Settings</span> <span uib-dropdown-toggle aria-label="Settings"><i class="fa fa-cog" aria-hidden="true"></i></span>
<div class="dropdown-menu dropdown-menu-right" uib-dropdown-menu> <div class="dropdown-menu dropdown-menu-right" uib-dropdown-menu>
<div class="tableMenu"> <div class="tableMenu">
<div class="menuHeader"> Table settings </div> <div class="menuHeader"> Table settings </div>
@ -43,41 +78,7 @@
</span> </span>
</div> </div>
</div> </div>
<div class="actionBar" ng-if="!$ctrl.offlineMode" authorization="PortainerStackCreate, PortainerStackDelete">
<button
type="button"
class="btn btn-sm btn-danger"
authorization="PortainerStackDelete"
ng-disabled="$ctrl.state.selectedItemCount === 0"
ng-click="$ctrl.removeAction($ctrl.state.selectedItems)"
data-cy="stack-removeStackButton"
>
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove
</button>
<button
ng-disabled="!$ctrl.createEnabled"
type="button"
class="btn btn-sm btn-primary"
ui-sref="docker.stacks.newstack"
authorization="PortainerStackCreate"
data-cy="stack-addStackButton"
>
<i class="fa fa-plus space-right" aria-hidden="true"></i>Add stack
</button>
</div>
<div class="searchBar">
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
<input
type="text"
class="searchInput"
ng-model="$ctrl.state.textFilter"
ng-change="$ctrl.onTextFilterChange()"
placeholder="Search..."
auto-focus
ng-model-options="{ debounce: 300 }"
data-cy="stack-searchInput"
/>
</div>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover nowrap-cells" data-cy="stack-stackTable"> <table class="table table-hover nowrap-cells" data-cy="stack-stackTable">
<thead> <thead>

View File

@ -1,7 +1,7 @@
<div class="toolBar"> <div class="toolBar">
<div class="toolBarTitle"> <div class="toolBarTitle">
<i class="fa" ng-class="$ctrl.icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="$ctrl.icon" feather="$ctrl.featherIcon" class-name="space-right"></pr-icon>
<span style="margin-right: 10px">{{ $ctrl.title }}</span> {{ $ctrl.title }}
<be-feature-indicator feature="$ctrl.feature"></be-feature-indicator> <be-feature-indicator feature="$ctrl.feature"></be-feature-indicator>
</div> </div>
</div> </div>

View File

@ -3,6 +3,7 @@ export const datatableTitlebar = {
icon: '@', icon: '@',
title: '@', title: '@',
feature: '@', feature: '@',
featherIcon: '<',
}, },
templateUrl: './datatable-titlebar.html', templateUrl: './datatable-titlebar.html',
}; };

View File

@ -3,6 +3,7 @@ export const rdWidgetTitle = {
bindings: { bindings: {
titleText: '@', titleText: '@',
icon: '@', icon: '@',
featherIcon: '<',
classes: '@?', classes: '@?',
}, },
transclude: { transclude: {
@ -12,7 +13,7 @@ export const rdWidgetTitle = {
<div class="widget-header"> <div class="widget-header">
<div class="row"> <div class="row">
<span ng-class="$ctrl.classes" class="pull-left"> <span ng-class="$ctrl.classes" class="pull-left">
<i class="fa" ng-class="$ctrl.icon"></i> <pr-icon icon="$ctrl.icon" feather="$ctrl.featherIcon"></pr-icon>
<span ng-transclude="title">{{ $ctrl.titleText }}</span> <span ng-transclude="title">{{ $ctrl.titleText }}</span>
</span> </span>
<span ng-class="$ctrl.classes" class="pull-right" ng-transclude></span> <span ng-class="$ctrl.classes" class="pull-right" ng-transclude></span>

View File

@ -1,15 +1,21 @@
import clsx from 'clsx';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
interface Props { import { Icon, IconProps } from '@/react/components/Icon';
interface Props extends IconProps {
value: string | number; value: string | number;
icon: string; icon: string;
} }
export function Stat({ value, icon, children }: PropsWithChildren<Props>) { export function Stat({
value,
icon,
children,
featherIcon,
}: PropsWithChildren<Props>) {
return ( return (
<span> <span>
<i className={clsx('fa space-right', icon)} aria-hidden="true" /> <Icon className="space-right" icon={icon} feather={featherIcon} />
<span>{value}</span> <span>{value}</span>
{children && <span className="space-left">{children}</span>} {children && <span className="space-left">{children}</span>}
</span> </span>

View File

@ -1,3 +1,8 @@
.actionBar {
margin-left: 15px !important;
margin-right: 15px !important;
}
.actionBar .description { .actionBar .description {
margin-bottom: 10px; margin-bottom: 10px;
} }
@ -9,6 +14,12 @@
.filter-container { .filter-container {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
padding: 0px 5px;
}
.filter-container input[type='text'] {
background: none !important;
border: 0px !important;
} }
.filter-left { .filter-left {
@ -61,3 +72,8 @@
width: 100%; width: 100%;
padding-left: 10px; padding-left: 10px;
} }
.filterSearchbar input[type='text'] {
background: none !important;
border: 0px !important;
}

View File

@ -279,7 +279,6 @@ export function EnvironmentList({ onClickItem, onRefresh }: Props) {
<TableActions className={styles.actionBar}> <TableActions className={styles.actionBar}>
<div className={styles.description}> <div className={styles.description}>
<i className="fa fa-exclamation-circle blue-icon space-right" />
Click on an environment to manage Click on an environment to manage
</div> </div>
<div className={styles.actionButton}> <div className={styles.actionButton}>

View File

@ -1,7 +1,10 @@
import angular from 'angular'; import angular from 'angular';
import { r2a } from '@/react-tools/react2angular'; import { r2a } from '@/react-tools/react2angular';
import { Icon } from '@/react/components/Icon';
import { ReactQueryDevtoolsWrapper } from '@/react/components/ReactQueryDevtoolsWrapper';
import { PageHeader } from '@@/PageHeader';
import { TagSelector } from '@@/TagSelector'; import { TagSelector } from '@@/TagSelector';
import { Loading } from '@@/Widget/Loading'; import { Loading } from '@@/Widget/Loading';
import { PasswordCheckHint } from '@@/PasswordCheckHint'; import { PasswordCheckHint } from '@@/PasswordCheckHint';
@ -24,4 +27,10 @@ export const componentsModule = angular
r2a(PasswordCheckHint, ['forceChangePassword', 'passwordValid']) r2a(PasswordCheckHint, ['forceChangePassword', 'passwordValid'])
) )
.component('rdLoading', r2a(Loading, [])) .component('rdLoading', r2a(Loading, []))
.component('viewLoading', r2a(ViewLoading, ['message'])).name; .component('viewLoading', r2a(ViewLoading, ['message']))
.component(
'pageHeader',
r2a(PageHeader, ['title', 'breadcrumbs', 'loading', 'onReload', 'reload'])
)
.component('prIcon', r2a(Icon, ['className', 'feather', 'icon']))
.component('reactQueryDevTools', r2a(ReactQueryDevtoolsWrapper, [])).name;

View File

@ -108,7 +108,7 @@ export function AutoEnvCreationSettingsForm({ settings }: Props) {
isLoading={mutation.isLoading} isLoading={mutation.isLoading}
disabled={!isValid || !dirty} disabled={!isValid || !dirty}
> >
Save Settings Save settings
</LoadingButton> </LoadingButton>
</div> </div>
</div> </div>

View File

@ -115,7 +115,7 @@ export function EdgeComputeSettings({ settings, onSubmit }: Props) {
isLoading={isSubmitting} isLoading={isSubmitting}
loadingText="Saving settings..." loadingText="Saving settings..."
> >
Save Settings Save settings
</LoadingButton> </LoadingButton>
</div> </div>
</div> </div>

View File

@ -149,7 +149,7 @@ export function SettingsFDO({ settings, onSubmit }: Props) {
isLoading={isSubmitting} isLoading={isSubmitting}
loadingText="Saving settings..." loadingText="Saving settings..."
> >
Save Settings Save settings
</LoadingButton> </LoadingButton>
</div> </div>
</div> </div>

View File

@ -252,7 +252,7 @@ export function SettingsOpenAMT({ settings, onSubmit }: Props) {
isLoading={isSubmitting} isLoading={isSubmitting}
loadingText="Saving settings..." loadingText="Saving settings..."
> >
Save Settings Save settings
</LoadingButton> </LoadingButton>
</div> </div>
</div> </div>

View File

@ -76,7 +76,7 @@
analytics-event="portainer-settings-edit" analytics-event="portainer-settings-edit"
analytics-properties="{ metadata: { forceHTTPS: $ctrl.formValues.forceHTTPS } }" analytics-properties="{ metadata: { forceHTTPS: $ctrl.formValues.forceHTTPS } }"
> >
<span ng-hide="$ctrl.state.actionInProgress || $ctrl.state.reloadingPage">Apply Changes</span> <span ng-hide="$ctrl.state.actionInProgress || $ctrl.state.reloadingPage">Apply changes</span>
<span ng-show="$ctrl.state.actionInProgress">Saving in progress...</span> <span ng-show="$ctrl.state.actionInProgress">Saving in progress...</span>
<span ng-show="$ctrl.state.reloadingPage">Reloading Page...</span> <span ng-show="$ctrl.state.reloadingPage">Reloading Page...</span>
</button> </button>

View File

@ -1,7 +1,4 @@
<rd-header> <page-header title="'Authentication settings'" breadcrumbs="[{label:'Settings', link:'portainer.settings'}, 'Authentication']"> </page-header>
<rd-header-title title-text="Authentication settings"></rd-header-title>
<rd-header-content> <a ui-sref="portainer.settings">Settings</a> &gt; Authentication </rd-header-content>
</rd-header>
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
@ -13,7 +10,7 @@
<div class="form-group"> <div class="form-group">
<label for="user_timeout" class="col-sm-2 control-label text-left"> <label for="user_timeout" class="col-sm-2 control-label text-left">
Session lifetime Session lifetime
<portainer-tooltip message="Time before users are forced to relogin."></portainer-tooltip> <portainer-tooltip message="Time before users are forced to relogin." position="bottom"></portainer-tooltip>
</label> </label>
<div class="col-sm-10"> <div class="col-sm-10">
<select <select

View File

@ -3,7 +3,7 @@
<div class="sidebar-header"> <div class="sidebar-header">
<a ui-sref="portainer.home" data-cy="portainerSidebar-homeImage"> <a ui-sref="portainer.home" data-cy="portainerSidebar-homeImage">
<img ng-if="logo" ng-src="{{ logo }}" class="img-responsive logo" /> <img ng-if="logo" ng-src="{{ logo }}" class="img-responsive logo" />
<img ng-if="!logo" src="~@/assets/images/logo.png" class="img-responsive logo" alt="Portainer" /> <img ng-if="!logo" src="~@/assets/images/logo_alt.svg" class="img-responsive logo" alt="Portainer" />
</a> </a>
<a ng-click="toggleSidebar()"><span class="menu-icon glyphicon glyphicon-transfer"></span></a> <a ng-click="toggleSidebar()"><span class="menu-icon glyphicon glyphicon-transfer"></span></a>
</div> </div>

View File

@ -1,4 +1,3 @@
import { ReactQueryDevtools } from 'react-query/devtools';
import { QueryClientProvider } from 'react-query'; import { QueryClientProvider } from 'react-query';
import { UIRouterContextComponent } from '@uirouter/react-hybrid'; import { UIRouterContextComponent } from '@uirouter/react-hybrid';
import { PropsWithChildren, StrictMode } from 'react'; import { PropsWithChildren, StrictMode } from 'react';
@ -11,9 +10,6 @@ import { createQueryClient } from './react-query';
const queryClient = createQueryClient(); const queryClient = createQueryClient();
export function RootProvider({ children }: PropsWithChildren<unknown>) { export function RootProvider({ children }: PropsWithChildren<unknown>) {
const showReactQueryDevtools =
process.env.SHOW_REACT_QUERY_DEV_TOOLS === 'true';
return ( return (
<StrictMode> <StrictMode>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
@ -22,7 +18,6 @@ export function RootProvider({ children }: PropsWithChildren<unknown>) {
<UserProvider>{children}</UserProvider> <UserProvider>{children}</UserProvider>
</UIRouterContextComponent> </UIRouterContextComponent>
</UIStateProvider> </UIStateProvider>
{showReactQueryDevtools && <ReactQueryDevtools />}
</QueryClientProvider> </QueryClientProvider>
</StrictMode> </StrictMode>
); );

View File

@ -2,6 +2,7 @@ import clsx from 'clsx';
import ReactTooltip from 'react-tooltip'; import ReactTooltip from 'react-tooltip';
import { isLimitedToBE } from '@/portainer/feature-flags/feature-flags.service'; import { isLimitedToBE } from '@/portainer/feature-flags/feature-flags.service';
import { Icon } from '@/react/components/Icon';
import './BoxSelectorItem.css'; import './BoxSelectorItem.css';
@ -53,9 +54,10 @@ export function BoxSelectorItem<T extends number | string>({
<div className="boxselector_header"> <div className="boxselector_header">
{!!option.icon && ( {!!option.icon && (
<i <Icon
className={clsx(option.icon, 'space-right')} icon={option.icon}
aria-hidden="true" feather={option.featherIcon}
className="space-right"
/> />
)} )}
{option.label} {option.label}

View File

@ -3,6 +3,7 @@ import type { FeatureId } from '@/portainer/feature-flags/enums';
export interface BoxSelectorOption<T> { export interface BoxSelectorOption<T> {
id: string; id: string;
icon: string; icon: string;
featherIcon?: boolean;
label: string; label: string;
description: string; description: string;
value: T; value: T;

View File

@ -10,12 +10,6 @@ test('should show provided resource value', async () => {
expect(value).toHaveTextContent('1'); expect(value).toHaveTextContent('1');
}); });
test('should show provided icon', async () => {
const { getByLabelText } = renderComponent(0, 'fa fa-th-list');
const icon = getByLabelText('icon');
expect(icon).toHaveClass('fa-th-list');
});
test('should show provided resource type', async () => { test('should show provided resource type', async () => {
const { getByLabelText } = renderComponent(0, '', 'Test'); const { getByLabelText } = renderComponent(0, '', 'Test');
const title = getByLabelText('resourceType'); const title = getByLabelText('resourceType');

View File

@ -1,19 +1,30 @@
import { ReactNode } from 'react';
import { Icon, IconProps } from '@/react/components/Icon';
import { Widget, WidgetBody } from '@@/Widget'; import { Widget, WidgetBody } from '@@/Widget';
interface Props { interface Props extends IconProps {
value: number; value?: number;
icon: string;
type: string; type: string;
children?: ReactNode;
} }
export function DashboardItem({ value, icon, type }: Props) { export function DashboardItem({
value,
icon,
type,
children,
featherIcon,
}: Props) {
return ( return (
<div className="col-sm-12 col-md-6" aria-label={type}> <div className="col-sm-12 col-md-6" aria-label={type}>
<Widget> <Widget>
<WidgetBody> <WidgetBody>
<div className="widget-icon blue pull-left"> <div className="widget-icon blue pull-left">
<i className={icon} aria-hidden="true" aria-label="icon" /> <Icon icon={icon} feather={featherIcon} />
</div> </div>
<div className="pull-right">{children}</div>
<div className="title" aria-label="value"> <div className="title" aria-label="value">
{value} {value}
</div> </div>

View File

@ -0,0 +1,48 @@
import clsx from 'clsx';
import { ComponentType, ReactNode, useEffect } from 'react';
import featherIcons from 'feather-icons';
import { isValidElementType } from 'react-is';
export interface IconProps {
icon: ReactNode | ComponentType<unknown>;
featherIcon?: boolean;
}
interface Props {
icon: ReactNode | ComponentType<unknown>;
feather?: boolean;
className?: string;
}
export function Icon({ icon, feather, className }: Props) {
useEffect(() => {
if (feather) {
featherIcons.replace();
}
}, [feather]);
if (typeof icon !== 'string') {
const Icon = isValidElementType(icon) ? icon : null;
return (
<span className={className} aria-hidden="true" role="img">
{Icon == null ? <>{icon}</> : <Icon />}
</span>
);
}
if (feather) {
return (
<i
data-feather={icon}
className={className}
aria-hidden="true"
role="img"
/>
);
}
return (
<i className={clsx('fa', icon, className)} aria-hidden="true" role="img" />
);
}

View File

@ -10,7 +10,7 @@ export interface Crumb {
linkParams?: Record<string, unknown>; linkParams?: Record<string, unknown>;
} }
interface Props { interface Props {
breadcrumbs: Crumb[]; breadcrumbs: (Crumb | string)[];
} }
export function Breadcrumbs({ breadcrumbs }: Props) { export function Breadcrumbs({ breadcrumbs }: Props) {
@ -26,7 +26,11 @@ export function Breadcrumbs({ breadcrumbs }: Props) {
); );
} }
function renderCrumb(crumb: Crumb) { function renderCrumb(crumb: Crumb | string) {
if (typeof crumb === 'string') {
return crumb;
}
if (crumb.link) { if (crumb.link) {
return ( return (
<Link to={crumb.link} params={crumb.linkParams}> <Link to={crumb.link} params={crumb.linkParams}>

View File

@ -1,19 +0,0 @@
.user-links {
margin-right: 25px;
}
.user-links > * + * {
margin-left: 5px;
}
.link {
cursor: pointer;
}
.link .link-text {
text-decoration: underline;
}
.link .link-icon {
margin-right: 2px;
}

View File

@ -1,6 +1,4 @@
import { UserContext } from '@/portainer/hooks/useUser'; import { renderWithQueryClient } from '@/react-tools/test-utils';
import { UserViewModel } from '@/portainer/models/user';
import { render } from '@/react-tools/test-utils';
import { HeaderContainer } from './HeaderContainer'; import { HeaderContainer } from './HeaderContainer';
import { HeaderContent } from './HeaderContent'; import { HeaderContent } from './HeaderContent';
@ -11,7 +9,7 @@ test('should not render without a wrapping HeaderContainer', async () => {
.mockImplementation(() => jest.fn()); .mockImplementation(() => jest.fn());
function renderComponent() { function renderComponent() {
return render(<HeaderContent />); return renderWithQueryClient(<HeaderContent />);
} }
expect(renderComponent).toThrowErrorMatchingSnapshot(); expect(renderComponent).toThrowErrorMatchingSnapshot();
@ -20,22 +18,14 @@ test('should not render without a wrapping HeaderContainer', async () => {
}); });
test('should display a HeaderContent', async () => { test('should display a HeaderContent', async () => {
const username = 'username';
const user = new UserViewModel({ Username: username });
const userProviderState = { user };
const content = 'content'; const content = 'content';
const { queryByText } = render( const { queryByText } = renderWithQueryClient(
<UserContext.Provider value={userProviderState}> <HeaderContainer>
<HeaderContainer> <HeaderContent>{content}</HeaderContent>
<HeaderContent>{content}</HeaderContent> </HeaderContainer>
</HeaderContainer>
</UserContext.Provider>
); );
const contentElement = queryByText(content); const contentElement = queryByText(content);
expect(contentElement).toBeVisible(); expect(contentElement).toBeVisible();
expect(queryByText('my account')).toBeVisible();
expect(queryByText('log out')).toBeVisible();
}); });

View File

@ -1,43 +1,13 @@
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import clsx from 'clsx';
import { useUser } from '@/portainer/hooks/useUser';
import { Link } from '@@/Link';
import styles from './HeaderContent.module.css';
import { useHeaderContext } from './HeaderContainer'; import { useHeaderContext } from './HeaderContainer';
export function HeaderContent({ children }: PropsWithChildren<unknown>) { export function HeaderContent({ children }: PropsWithChildren<unknown>) {
useHeaderContext(); useHeaderContext();
const { user } = useUser();
return ( return (
<div className="breadcrumb-links"> <div className="breadcrumb-links">
<div className="pull-left">{children}</div> <div className="pull-left">{children}</div>
{user && !window.ddExtension && (
<div className={clsx('pull-right', styles.userLinks)}>
<Link to="portainer.account" className={styles.link}>
<i
className={clsx('fa fa-wrench', styles.linkIcon)}
aria-hidden="true"
/>
<span className={styles.linkText}>my account</span>
</Link>
<Link
to="portainer.logout"
params={{ performApiLogout: true }}
className={clsx('text-danger', styles.link)}
data-cy="template-logoutButton"
>
<i
className={clsx('fa fa-sign-out-alt', styles.linkIcon)}
aria-hidden="true"
/>
<span className={styles.linkText}>log out</span>
</Link>
</div>
)}
</div> </div>
); );
} }

View File

@ -0,0 +1,31 @@
.menu-button {
border: 0px;
font-size: 17px;
background: none;
margin-right: 15px;
}
.menu-list {
background: var(--bg-dropdown-menu-color);
border-radius: 8px;
border: 1px solid var(--ui-grey-1) !important;
width: 180px;
padding: 5px !important;
box-shadow: 0 6px 12px rgb(0 0 0 / 18%);
}
.menu-link {
display: block;
padding: 5px 20px;
font-weight: 500;
line-height: 1.42857143;
white-space: nowrap;
font-size: 14px;
color: var(--text-dropdown-menu-color);
text-decoration: none !important;
}
.menu-link:hover {
background: var(--bg-dropdown-hover);
color: var(--text-dropdown-menu-color);
}

View File

@ -1,8 +1,14 @@
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { Menu, MenuButton, MenuList, MenuLink } from '@reach/menu-button';
import clsx from 'clsx';
import { User, ChevronDown } from 'react-feather';
import { useUser } from '@/portainer/hooks/useUser'; import { useUser } from '@/portainer/hooks/useUser';
import { Link } from '@@/Link';
import { useHeaderContext } from './HeaderContainer'; import { useHeaderContext } from './HeaderContainer';
import styles from './HeaderTitle.module.css';
interface Props { interface Props {
title: string; title: string;
@ -16,12 +22,25 @@ export function HeaderTitle({ title, children }: PropsWithChildren<Props>) {
<div className="page white-space-normal"> <div className="page white-space-normal">
{title} {title}
<span className="header_title_content">{children}</span> <span className="header_title_content">{children}</span>
{user && !window.ddExtension && ( <Menu>
<span className="pull-right user-box"> <MenuButton className={clsx('pull-right', styles.menuButton)}>
<i className="fa fa-user-circle" aria-hidden="true" /> <User className="feather" />
{user.Username} {user && <span>{user.Username}</span>}
</span> <ChevronDown className="feather" />
)} </MenuButton>
<MenuList className={styles.menuList}>
<MenuLink
className={styles.menuLink}
as={Link}
to="portainer.account"
>
My account
</MenuLink>
<MenuLink className={styles.menuLink} as={Link} to="portainer.logout">
Log out
</MenuLink>
</MenuList>
</Menu>
</div> </div>
); );
} }

View File

@ -1,4 +1,5 @@
import { useRouter } from '@uirouter/react'; import { useRouter } from '@uirouter/react';
import { RefreshCw } from 'react-feather';
import { Button } from '../buttons'; import { Button } from '../buttons';
@ -11,12 +12,25 @@ import styles from './PageHeader.module.css';
interface Props { interface Props {
reload?: boolean; reload?: boolean;
loading?: boolean;
onReload?(): Promise<void> | void;
breadcrumbs?: Crumb[]; breadcrumbs?: Crumb[];
title: string; title: string;
} }
export function PageHeader({ title, breadcrumbs = [], reload }: Props) { export function PageHeader({
title,
breadcrumbs = [],
reload,
loading,
onReload,
}: Props) {
const router = useRouter(); const router = useRouter();
function onClickedRefresh() {
return onReload ? onReload() : router.stateService.reload();
}
return ( return (
<HeaderContainer> <HeaderContainer>
<HeaderTitle title={title}> <HeaderTitle title={title}>
@ -24,10 +38,11 @@ export function PageHeader({ title, breadcrumbs = [], reload }: Props) {
<Button <Button
color="link" color="link"
size="medium" size="medium"
onClick={() => router.stateService.reload()} onClick={onClickedRefresh}
className={styles.reloadButton} className={styles.reloadButton}
disabled={loading}
> >
<i className="fa fa-sync" aria-hidden="true" /> <RefreshCw className="feather" />
</Button> </Button>
)} )}
</HeaderTitle> </HeaderTitle>

View File

@ -65,7 +65,7 @@
.pagination > .active > span:focus, .pagination > .active > span:focus,
.pagination > .active > button:focus { .pagination > .active > button:focus {
z-index: 3; z-index: 3;
color: #fff; color: #1d2939;
cursor: default; cursor: default;
background-color: var(--text-pagination-span-color); background-color: var(--text-pagination-span-color);
border-color: var(--text-pagination-span-color); border-color: var(--text-pagination-span-color);

View File

@ -0,0 +1,8 @@
import { ReactQueryDevtools } from 'react-query/devtools';
export function ReactQueryDevtoolsWrapper() {
const showReactQueryDevtools =
process.env.SHOW_REACT_QUERY_DEV_TOOLS === 'true';
return <>{showReactQueryDevtools && <ReactQueryDevtools />}</>;
}

View File

@ -4,12 +4,10 @@
} }
.tooltip { .tooltip {
font-family: Montserrat !important;
background-color: var(--bg-tooltip-color) !important; background-color: var(--bg-tooltip-color) !important;
padding: 0.833em 1em !important; padding: 0.833em 1em !important;
color: var(--text-tooltip-color) !important; color: var(--text-tooltip-color) !important;
border: 1px solid var(--border-tooltip-color) !important; border-radius: 10px !important;
border-radius: 0.14285714rem !important;
box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15) !important; box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15) !important;
max-width: 200px; max-width: 200px;
text-align: center; text-align: center;
@ -19,5 +17,5 @@
.icon { .icon {
margin-left: 5px; margin-left: 5px;
font-size: 1.3em; cursor: pointer;
} }

View File

@ -1,5 +1,5 @@
import ReactTooltip from 'react-tooltip'; import ReactTooltip from 'react-tooltip';
import clsx from 'clsx'; import { HelpCircle } from 'react-feather';
import styles from './Tooltip.module.css'; import styles from './Tooltip.module.css';
@ -12,12 +12,8 @@ export interface Props {
export function Tooltip({ message, position = 'bottom' }: Props) { export function Tooltip({ message, position = 'bottom' }: Props) {
return ( return (
<span className="interactive"> <span data-tip={message} className={styles.icon}>
<i <HelpCircle className="feather" aria-hidden="true" />
className={clsx('fa fa-question-circle blue-icon', styles.icon)}
aria-hidden="true"
data-tip={message}
/>
<ReactTooltip <ReactTooltip
multiline multiline
type="info" type="info"

View File

@ -1,11 +1,14 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { PropsWithChildren, ReactNode } from 'react'; import { PropsWithChildren, ReactNode } from 'react';
import { Icon } from '@/react/components/Icon';
import { useWidgetContext } from './Widget'; import { useWidgetContext } from './Widget';
interface Props { interface Props {
title: ReactNode; title: ReactNode;
icon: ReactNode; icon: ReactNode;
featherIcon?: boolean;
className?: string; className?: string;
} }
@ -14,6 +17,7 @@ export function WidgetTitle({
icon, icon,
className, className,
children, children,
featherIcon,
}: PropsWithChildren<Props>) { }: PropsWithChildren<Props>) {
useWidgetContext(); useWidgetContext();
@ -21,7 +25,7 @@ export function WidgetTitle({
<div className="widget-header"> <div className="widget-header">
<div className="row"> <div className="row">
<span className={clsx('pull-left', className)}> <span className={clsx('pull-left', className)}>
{typeof icon === 'string' ? <i className={clsx('fa', icon)} /> : icon} <Icon icon={icon} feather={featherIcon} className="space-right" />
<span>{title}</span> <span>{title}</span>
</span> </span>
<span className={clsx('pull-right', className)}>{children}</span> <span className={clsx('pull-right', className)}>{children}</span>

View File

@ -2,7 +2,15 @@ import { MouseEventHandler, PropsWithChildren } from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
type Type = 'submit' | 'button' | 'reset'; type Type = 'submit' | 'button' | 'reset';
type Color = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'link'; type Color =
| 'default'
| 'primary'
| 'success'
| 'warning'
| 'danger'
| 'link'
| 'light'
| 'dangerlight';
type Size = 'xsmall' | 'small' | 'medium' | 'large'; type Size = 'xsmall' | 'small' | 'medium' | 'large';
export interface Props { export interface Props {

View File

@ -1,6 +1,7 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { Menu, MenuButton, MenuList } from '@reach/menu-button'; import { Menu, MenuButton, MenuList } from '@reach/menu-button';
import { ColumnInstance } from 'react-table'; import { ColumnInstance } from 'react-table';
import { Columns } from 'react-feather';
import { Checkbox } from '@@/form-components/Checkbox'; import { Checkbox } from '@@/form-components/Checkbox';
@ -28,7 +29,13 @@ export function ColumnVisibilityMenu<D extends object>({
'setting-active': isExpanded, 'setting-active': isExpanded,
})} })}
> >
<i className="fa fa-columns" aria-hidden="true" /> Columns <Columns
size="13"
className="space-right"
strokeWidth="3px"
aria-hidden="true"
aria-label="Columns"
/>
</MenuButton> </MenuButton>
<MenuList> <MenuList>
<div className="tableMenu"> <div className="tableMenu">

View File

@ -1,6 +1,7 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { Menu, MenuButton, MenuList } from '@reach/menu-button'; import { Menu, MenuButton, MenuList } from '@reach/menu-button';
import { PropsWithChildren, ReactNode } from 'react'; import { PropsWithChildren, ReactNode } from 'react';
import { MoreVertical } from 'react-feather';
import { useTableContext } from './TableContainer'; import { useTableContext } from './TableContainer';
@ -23,7 +24,13 @@ export function TableSettingsMenu({
'setting-active': isExpanded, 'setting-active': isExpanded,
})} })}
> >
<i className="fa fa-cog" aria-hidden="true" /> Settings <MoreVertical
size="13"
className="space-right"
strokeWidth="3px"
aria-hidden="true"
aria-label="Settings"
/>
</MenuButton> </MenuButton>
<MenuList> <MenuList>
<div className="tableMenu"> <div className="tableMenu">

View File

@ -1,15 +1,18 @@
import clsx from 'clsx';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { Icon } from '@/react/components/Icon';
import { useTableContext } from './TableContainer'; import { useTableContext } from './TableContainer';
interface Props { interface Props {
icon: string; icon: string;
label: string; label: string;
featherIcon?: boolean;
} }
export function TableTitle({ export function TableTitle({
icon, icon,
featherIcon,
label, label,
children, children,
}: PropsWithChildren<Props>) { }: PropsWithChildren<Props>) {
@ -18,7 +21,8 @@ export function TableTitle({
return ( return (
<div className="toolBar"> <div className="toolBar">
<div className="toolBarTitle"> <div className="toolBarTitle">
<i className={clsx('space-right', 'fa', icon)} aria-hidden="true" /> <Icon icon={icon} feather={featherIcon} className="space-right" />
{label} {label}
</div> </div>
{children} {children}

View File

@ -28,5 +28,5 @@
.slider :global .rc-slider-mark-text, .slider :global .rc-slider-mark-text,
.slider :global .rc-slider-tooltip-inner { .slider :global .rc-slider-tooltip-inner {
font-family: Montserrat, serif; font-family: Inter, serif;
} }

View File

@ -49,7 +49,7 @@ export function Switch({
disabled={disabled || limitedToBE} disabled={disabled || limitedToBE}
onChange={({ target: { checked } }) => onChange(checked)} onChange={({ target: { checked } }) => onChange(checked)}
/> />
<i data-cy={dataCy} /> <span className="slider round" data-cy={dataCy} />
</label> </label>
{limitedToBE && <BEFeatureIndicator featureId={featureId} />} {limitedToBE && <BEFeatureIndicator featureId={featureId} />}
</> </>

View File

@ -105,6 +105,7 @@
"codemirror": "~5.64.0", "codemirror": "~5.64.0",
"core-js": "^3.19.3", "core-js": "^3.19.3",
"fast-json-patch": "^3.1.0", "fast-json-patch": "^3.1.0",
"feather-icons": "^4.29.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"filesize": "~3.3.0", "filesize": "~3.3.0",
"filesize-parser": "^1.5.0", "filesize-parser": "^1.5.0",
@ -127,8 +128,8 @@
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-feather": "^2.0.9", "react-feather": "^2.0.9",
"react-i18next": "^11.12.0", "react-i18next": "^11.12.0",
"react-is": "^17.0.2", "react-is": "^18.2.0",
"react-query": "^3.34.3", "react-query": "^3.33.4",
"react-select": "^5.2.1", "react-select": "^5.2.1",
"react-table": "^7.7.0", "react-table": "^7.7.0",
"react-tooltip": "^4.2.21", "react-tooltip": "^4.2.21",
@ -164,6 +165,7 @@
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"@types/angular": "^1.8.3", "@types/angular": "^1.8.3",
"@types/bootbox": "^5.2.2", "@types/bootbox": "^5.2.2",
"@types/feather-icons": "^4.7.0",
"@types/file-saver": "^2.0.4", "@types/file-saver": "^2.0.4",
"@types/jest": "^27.0.3", "@types/jest": "^27.0.3",
"@types/jquery": "^3.5.10", "@types/jquery": "^3.5.10",
@ -171,6 +173,7 @@
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"@types/react": "^17.0.37", "@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.11",
"@types/react-is": "^17.0.3",
"@types/react-table": "^7.7.6", "@types/react-table": "^7.7.6",
"@types/sanitize-html": "^2.5.0", "@types/sanitize-html": "^2.5.0",
"@types/toastr": "^2.1.39", "@types/toastr": "^2.1.39",
@ -201,6 +204,7 @@
"eslint-plugin-promise": "^5.2.0", "eslint-plugin-promise": "^5.2.0",
"eslint-plugin-react": "^7.27.1", "eslint-plugin-react": "^7.27.1",
"eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-regex": "^1.9.0",
"eslint-plugin-storybook": "^0.5.5", "eslint-plugin-storybook": "^0.5.5",
"eslint-webpack-plugin": "^3.1.1", "eslint-webpack-plugin": "^3.1.1",
"grunt": "^1.4.1", "grunt": "^1.4.1",

View File

@ -3193,6 +3193,11 @@
"@types/qs" "*" "@types/qs" "*"
"@types/serve-static" "*" "@types/serve-static" "*"
"@types/feather-icons@^4.7.0":
version "4.7.0"
resolved "https://registry.yarnpkg.com/@types/feather-icons/-/feather-icons-4.7.0.tgz#ec66bc046bcd1513835f87541ecef54b50c57ec9"
integrity sha512-vflOrmlHMGIGVN4AEl6ErPCNKBLcP1ehEpLqnJkTgf69r5QmJzy7BF1WzbBc8Hqs9KffROPT/JqlSKH4o5vB/w==
"@types/file-saver@^2.0.4": "@types/file-saver@^2.0.4":
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.4.tgz#aaf9b96296150d737b2fefa535ced05ed8013d84" resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.4.tgz#aaf9b96296150d737b2fefa535ced05ed8013d84"
@ -3435,6 +3440,13 @@
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react-is@^17.0.3":
version "17.0.3"
resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a"
integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==
dependencies:
"@types/react" "*"
"@types/react-syntax-highlighter@11.0.5": "@types/react-syntax-highlighter@11.0.5":
version "11.0.5" version "11.0.5"
resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087"
@ -6270,6 +6282,11 @@ core-js@^3.0.4, core-js@^3.19.3, core-js@^3.6.5, core-js@^3.8.2:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.0.tgz#f479dbfc3dffb035a0827602dd056839a774aa71" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.0.tgz#f479dbfc3dffb035a0827602dd056839a774aa71"
integrity sha512-YUdI3fFu4TF/2WykQ2xzSiTQdldLB4KVuL9WeAy5XONZYt5Cun/fpQvctoKbCgvPhmzADeesTk/j2Rdx77AcKQ== integrity sha512-YUdI3fFu4TF/2WykQ2xzSiTQdldLB4KVuL9WeAy5XONZYt5Cun/fpQvctoKbCgvPhmzADeesTk/j2Rdx77AcKQ==
core-js@^3.1.3:
version "3.23.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.1.tgz#9f9a9255115f62c512db56d567f636da32ca0b78"
integrity sha512-wfMYHWi1WQjpgZNC9kAlN4ut04TM9fUTdi7CqIoTVM7yaiOUQTklOzfb+oWH3r9edQcT3F887swuVmxrV+CC8w==
core-util-is@~1.0.0: core-util-is@~1.0.0:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
@ -7616,6 +7633,11 @@ eslint-plugin-react@^7.27.1:
semver "^6.3.0" semver "^6.3.0"
string.prototype.matchall "^4.0.6" string.prototype.matchall "^4.0.6"
eslint-plugin-regex@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-regex/-/eslint-plugin-regex-1.9.0.tgz#4eb4f903edeeec3d641a7fcc10ad4d5209cb783d"
integrity sha512-T7/Rn6qp/Wp9VlLraimUvGW81wkJ661wFicHyHrm4iSJfl33yyUFJEwknYjFjrUTXAsYA6wCCvPpBss/gOlVNA==
eslint-plugin-storybook@^0.5.5: eslint-plugin-storybook@^0.5.5:
version "0.5.6" version "0.5.6"
resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.5.6.tgz#10bce5933ad9ceec46cdf5ed5a81b50b33f73bf7" resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.5.6.tgz#10bce5933ad9ceec46cdf5ed5a81b50b33f73bf7"
@ -8129,6 +8151,14 @@ fd-slicer@~1.1.0:
dependencies: dependencies:
pend "~1.2.0" pend "~1.2.0"
feather-icons@^4.29.0:
version "4.29.0"
resolved "https://registry.yarnpkg.com/feather-icons/-/feather-icons-4.29.0.tgz#4e40e3cbb7bf359ffbbf700edbebdde4e4a74ab6"
integrity sha512-Y7VqN9FYb8KdaSF0qD1081HCkm0v4Eq/fpfQYQnubpqi0hXx14k+gF9iqtRys1SIcTEi97xDi/fmsPFZ8xo0GQ==
dependencies:
classnames "^2.2.5"
core-js "^3.1.3"
fecha@^4.2.0: fecha@^4.2.0:
version "4.2.1" version "4.2.1"
resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.1.tgz#0a83ad8f86ef62a091e22bb5a039cd03d23eecce" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.1.tgz#0a83ad8f86ef62a091e22bb5a039cd03d23eecce"
@ -13987,6 +14017,11 @@ react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-popper-tooltip@^3.1.1: react-popper-tooltip@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz#329569eb7b287008f04fcbddb6370452ad3f9eac" resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz#329569eb7b287008f04fcbddb6370452ad3f9eac"
@ -14004,10 +14039,10 @@ react-popper@^2.2.4:
react-fast-compare "^3.0.1" react-fast-compare "^3.0.1"
warning "^4.0.2" warning "^4.0.2"
react-query@^3.34.3: react-query@^3.33.4:
version "3.34.14" version "3.39.1"
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.34.14.tgz#ba944c1c36bbeab3e7037c42e8e862acd73111ff" resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.1.tgz#3876c0fdac7a3b5a84e195534e5fa8fbdd628847"
integrity sha512-KVMnM8omt+81oO9fPZfM65pGhQilpWzGsNwAqeeLMB2sG3xwY3bpIEYbhDf7FFgsqhAQfSzmCL4gRSiJaWIDwA== integrity sha512-qYKT1bavdDiQZbngWZyPotlBVzcBjDYEJg5RQLBa++5Ix5jjfbEYJmHSZRZD+USVHUSvl/ey9Hu+QfF1QAK80A==
dependencies: dependencies:
"@babel/runtime" "^7.5.5" "@babel/runtime" "^7.5.5"
broadcast-channel "^3.4.1" broadcast-channel "^3.4.1"