@@ -1 +1 @@ | |||
{"version":3,"sourceRoot":"","sources":["settings.scss","../../../core/css/functions.scss"],"names":[],"mappings":"AAQC,0BACC,WAKF,OACC,WAID,4BC8CC,2CD1CD,mBC0CC,kDDtCD,qBCsCC,yCDlCD,0BCkCC,wCD9BD,oEC8BC,2CD1BD,oCACC,oBACA,0BACA,+BACA,mBAGD,4BACC,oBACA,kCAGD,yBACC,WAIA,wCACC,kBACA,yDACC,gBAIA,mOACC,WAKH,uCACC,aAGD,sCACC,WAED,uDACC,WAKD,gBACC,WAIF,mBACC,aACA,aACA,iBACA,uEACA,qBAEA,4BACC,kBACA,SAEA,+BACC,mBAIA,qCACC,iBAKH,kCACC,iBACA,mBACA,gBAGD,mGACC,4BACA,kBACA,WAMF,oBACC,kBACA,wCACC,0BAIF,aACC,qBACA,YACA,kBACA,8CACA,wCACA,wCACA,8CACA,6CAEA,qFAIC,6DACA,oDAGD,uBACC,kBACA,yBAIF,6BACC,oBACA,kCAEA,mCACC,WAIA,oCACC,kBACA,oBACA,iBACA,2BACA,WACA,mBACA,QAEA,0CACC,mBACA,uBACA,gBAKD,gIACC,kBACA,UACA,UACA,oBACA,YAKH,qCACC,kBACA,UACA,MACA,SAEA,yCACC,qBAIF,4CACC,eAGD,4CACC,sBACA,WACA,YACA,YAMF,qBACC,aACA,WACA,SACA,YAEA,uBACC,aAGD,uCACC,sBACA,cACA,yBAIF,iBACC,kBACA,eACA,WACA,YACA,aACA,SACA,gBACA,YAEA,8CAEC,+CACA,wCAEA,0FACC,WAIF,uCACC,kBACA,qBACA,gCACA,WACA,eAEA,wDACC,qBACA,sBACA,eAIF,sCACC,SAGC,4DAEC,iBACA,kBAEA,kFACC,YAGD,mEACC,oDAEA,kFACC,iBAIF,qEACC,WAEA,eAEA,uEACC,eAQN,gBACC,YAIA,2BACC,kCAGD,mBACC,YAIF,sCAEC,aAGD,eACC,WAGD,YACC,qBAIA,aACC,WACA,yBACA,YAGD,WACC,WACA,yBACA,YAMD,oBACC,iBAGD,iBACC,eAKD,iCACC,aACA,eACA,sBACA,SACA,gDACC,aACA,eACA,sBACA,sDACC,oBAIF,kGACC,cACA,YACA,gBAKA,iEACC,kBACA,UAED,+EACC,oBACA,eACA,wBACA,UAIF,wCACC,WAGD,iDACC,qBAGD,sDACC,kBACA,OACA,WACA,0BACA,eACA,gBACA,WAQF,oBACC,gBAGD,wBACC,iBAGD,oDACC,WACA,YACA,mBACA,wCAOD,oBACC,UACA,cACA,gBACA,uBAGD,2BACC,UAKD,oCAEC,cAKD,wEAEC,aAIF,gBACC,kBACA,QACA,QAEA,sBACC,YAGD,sBACC,iBAKF,WACC,WAEA,cACC,WACA,kBACA,4CACA,gBACA,mBAGD,cACC,4CACA,kBACA,gBACA,mBAKD,gBACC,kBACA,cACA,eACA,uBACA,gBAGD,wBACC,kBAEA,gCACC,kBAIF,sCACC,kBAGD,sDAEC,cACA,eACA,eAEA,0EACC,UACA,qBACA,uBACA,gBAIF,8BACC,eAGD,kCACC,mBACA,cAIF,2BACC,mBAID,4BACC,WACA,SACA,QAGD,0BACC,mBAGD,SACC,gBAKA,oBACC,mBACA,iBACA,WAGD,gCACC,kBAIA,gGACC,cAIF,4BACC,gBAGD,iCACC,gBAGD,8BACC,oCAIF,aACC,gBACA,iBACA,oCAGD,aACC,oCAIA,qCACC,aAMD,0BACC,SAGD,2BACC,cACA,aAGD,qDACC,WACA,aACA,qBACA,WAGD,kCACC,YAGD,0BACC,gBAEA,iCACC,cAOD,4FAEC,qBACA,WACA,YACA,kBACA,WAIF,uFACC,oCAGD,iDACC,cAGD,kDACC,cAGD,sCACC,kBACA,MACA,QACA,aACA,WACA,UACA,WACA,YAGD,wCACC,aACA,mBAEA,oDACC,YAIF,yCACC,0BACA,iBAGD,iOAKC,cAIF,2CACC,gBACC,UAED,kCACC,WAIF,2CACC,gBACC,UAED,kCACC,WAIF,2CACC,gBACC,UAED,kCACC,WAIF,0CACC,gBACC,UAED,kCACC,YAIF,2CACC,gBACC,WAIF,0CACC,gBACC,YAKF,0CAEE,kEACC,yBAKH,0CACC,iCACC,yBAIF,SACC,gBAEA,0BACC,4CAID,YACC,mBAEA,uBACC,iBACA,2BACA,qBAKH,iBACC,cACA,yBAGD,WACC,kBACA,aACA,UACA,gBAGD,0DACC,WACA,cACA,eACA,WAGD,2BACC,WACA,kBACA,QAGD,iBACC,WAGD,gBACC,mBAKD,kBACC,0BAGD,kBACC,cAGD,sBACC,mBACA,wBACA,2BAGD,WA4GC,aACA,eACA,yBA1GA,0BACC,wBAGD,gCACC,iBAGD,oBACC,OAXgB,KAYhB,QAbiB,IAejB,aAdgB,KAehB,WACA,8CACA,gBACA,MACA,UACA,aACA,mBAGD,qBAQC,oBAPA,0CACC,cACA,WACA,YACA,mBAKD,8BACC,kBACA,UACA,SAEA,gCACC,mBACA,eACA,sBACA,WACA,4CACA,YACA,sBAGD,uCACC,aACA,QACA,eACA,oBAGD,uCACC,8CAKF,oCACC,aAEA,0CACC,iBAIF,gCACC,WACA,YACA,iBAGD,kGAEC,eACA,WACA,YACA,WACA,sBACA,qBAGD,8BACC,iBAEA,kDACC,qBACA,QACA,kBAKH,+CACC,kBACA,YAEA,WACA,YACA,WAOD,kBACC,aAGD,oBACC,kBACA,cAEA,gCACC,cACA,aAGD,0BACC,8CAKD,8BACC,cAGD,+BACC,gBAGD,+BACC,mBAEA,oEACC,kBAKD,8DACC,iBAKD,oEACC,kBAMH,wBACC,kBAEA,4BACC,mBACA,YAGD,2BACC,iBACA,iBACA,mBACA,mCACC,kBACA,SACA,iBAGD,oCACC,gBAOJ,yBACC,uCACC,oBAMA,8BACC,gBAMH,KACC,mBACA,mBAGD,SACC,aAGD,mBACC,mBAGD,eACC,gBAOA,+IACC,sBAEA,+KACC,aAGD,mKACC,WACA,YACA,kCACA,qBACA,kBAGD,mOACC,sCAGD,mNACC,sCAGD,mNACC,oCAMF,sBACC,aAGD,YACC,oBAGD,kBACC,kBAGD,yBACC,kBAGD,sBACC,kBAGD,oCACC,uBAIF,yCACC,kBAGD,wBACC,qBAGD,2BACC,wBAEA,gBACA,aACA,iBACA,sBAKD,WACC,kBACA,2BACA,WAGD,2DAGC,qBAIA,mCACC,qBACA,YACA,iBAGD,+EAEC,YAIF,yBACC,mCACC,YACA,gBACA,cACA,iDAIF,eACC,WAGD,SACC,iBAGD,QACC,qBACA,YACA,WACA,2BAEA,gBACC,kBAIF,qBACC,sBACA,qBACA,YACA,iBAGD,kBACC,qBACA,gBAIA,aACC,sCACA,mCAGD,WACC,oCAGD,mBACC,sCACA,oBAMF,8CACC,WACA,YAGD,wBACC,WACA,YACA,mBACA,kBACA,+DAIA,oBACC,iBACA,gBAEA,uBACC,cAGD,uBACC,kBAIF,0BACC,YACA,gCAGD,oDACC,yBAGD,wDACC,2BAGD,uBACC,cAKD,oBACC,0BAGD,oCACC,gBAIF,2BACC,aACA,eACA,mDAEA,8BACC,kBAGD,6BACC,WAIF,eACC,mBAEA,iBACC,qBACA,cAIF,SACC,UAGD,eACC,iBACA,mBACA,WAGD,UACI,+CAGJ,2BACE,GACE,YAGJ,mCACE,GACE","file":"settings.css"} | |||
{"version":3,"sourceRoot":"","sources":["settings.scss","../../../core/css/functions.scss"],"names":[],"mappings":"AAQC,0BACC,WAKF,OACC,WAID,4BC8CC,2CD1CD,mBC0CC,kDDtCD,qBCsCC,yCDlCD,0BCkCC,wCD9BD,oEC8BC,2CD1BD,oCACC,oBACA,0BACA,+BACA,mBAGD,4BACC,oBACA,kCAGD,yBACC,WAIA,wCACC,kBACA,yDACC,gBAIA,mOACC,WAKH,uCACC,aAGD,sCACC,WAED,uDACC,WAKD,gBACC,WAIF,mBACC,aACA,aACA,iBACA,uEACA,qBAEA,4BACC,kBACA,SAEA,+BACC,mBAIA,qCACC,iBAKH,kCACC,iBACA,mBACA,gBAGD,mGACC,4BACA,kBACA,WAMF,oBACC,kBACA,wCACC,0BAIF,aACC,qBACA,YACA,kBACA,8CACA,wCACA,wCACA,8CACA,6CAEA,qFAIC,6DACA,oDAGD,uBACC,kBACA,yBAIF,6BACC,oBACA,kCAEA,mCACC,WAIA,oCACC,kBACA,oBACA,iBACA,2BACA,WACA,mBACA,QAEA,0CACC,mBACA,uBACA,gBAKD,gIACC,kBACA,UACA,UACA,oBACA,YAKH,qCACC,kBACA,UACA,MACA,SAEA,yCACC,qBAIF,4CACC,eAGD,4CACC,sBACA,WACA,YACA,YAMF,qBACC,aACA,WACA,SACA,YAEA,uBACC,aAGD,uCACC,sBACA,cACA,yBAIF,iBACC,kBACA,eACA,WACA,YACA,aACA,SACA,gBACA,YAEA,8CAEC,+CACA,wCAEA,0FACC,WAIF,uCACC,kBACA,qBACA,gCACA,WACA,eAEA,wDACC,qBACA,sBACA,eAIF,sCACC,SAGC,4DAEC,iBACA,kBAEA,kFACC,YAGD,mEACC,oDAEA,kFACC,iBAIF,qEACC,WAEA,eAEA,uEACC,eAQN,gBACC,YAIA,2BACC,kCAGD,mBACC,YAIF,sCAEC,aAGD,eACC,WAGD,YACC,qBAIA,aACC,WACA,yBACA,YAGD,WACC,WACA,yBACA,YAMD,oBACC,iBAGD,iBACC,eAKD,iCACC,aACA,eACA,sBACA,SACA,gDACC,aACA,eACA,sBACA,sDACC,oBAIF,kGACC,cACA,YACA,gBAKA,iEACC,kBACA,UAED,+EACC,oBACA,eACA,wBACA,UAIF,wCACC,WAGD,iDACC,qBAGD,sDACC,kBACA,OACA,WACA,0BACA,eACA,gBACA,WAQF,oBACC,gBAGD,wBACC,iBAGD,oDACC,WACA,YACA,mBACA,wCAOD,oBACC,UACA,cACA,gBACA,uBAGD,2BACC,UAKD,oCAEC,cAKD,wEAEC,aAIF,gBACC,kBACA,QACA,QAEA,sBACC,YAGD,sBACC,iBAKF,WACC,WAEA,cACC,WACA,kBACA,4CACA,gBACA,mBAGD,cACC,4CACA,kBACA,gBACA,mBAKD,gBACC,kBACA,cACA,eACA,uBACA,gBAGD,wBACC,kBAEA,gCACC,kBAIF,sCACC,kBAGD,sDAEC,cACA,eACA,eAEA,0EACC,UACA,qBACA,uBACA,gBAIF,8BACC,eAGD,kCACC,mBACA,cAIF,2BACC,mBAMA,oBACC,mBACA,iBACA,WAGD,gCACC,kBAIA,gGACC,cAMH,SACC,gBAEA,0BACC,4CAID,YACC,mBAEA,uBACC,iBACA,2BACA,qBAMH,KACC,mBACA,mBAGD,SACC,aAGD,mBACC,mBAGD,eACC,gBAOA,+IACC,sBAEA,+KACC,aAGD,mKACC,WACA,YACA,kCACA,qBACA,kBAGD,mOACC,sCAGD,mNACC,sCAGD,mNACC,oCAMF,sBACC,aAGD,YACC,oBAGD,kBACC,kBAGD,yBACC,kBAGD,sBACC,kBAGD,oCACC,uBAIF,yCACC,kBAGD,wBACC,qBAGD,2BACC,wBAEA,gBACA,aACA,iBACA,sBAKD,WACC,kBACA,2BACA,WAGD,2DAGC,qBAIA,mCACC,qBACA,YACA,iBAGD,+EAEC,YAIF,yBACC,mCACC,YACA,gBACA,cACA,iDAIF,eACC,WAGD,SACC,iBAGD,QACC,qBACA,YACA,WACA,2BAEA,gBACC,kBAIF,qBACC,sBACA,qBACA,YACA,iBAGD,kBACC,qBACA,gBAIA,aACC,sCACA,mCAGD,WACC,oCAGD,mBACC,sCACA,oBAMF,8CACC,WACA,YAGD,wBACC,WACA,YACA,mBACA,kBACA,+DAIA,oBACC,iBACA,gBAEA,uBACC,cAGD,uBACC,kBAIF,0BACC,YACA,gCAGD,oDACC,yBAGD,wDACC,2BAGD,uBACC,cAKD,oBACC,0BAGD,oCACC,gBAIF,2BACC,aACA,eACA,mDAEA,8BACC,kBAGD,6BACC,WAIF,eACC,mBAEA,iBACC,qBACA,cAIF,SACC,UAGD,eACC,iBACA,mBACA,WAGD,UACI,+CAGJ,2BACE,GACE,YAGJ,mCACE,GACE","file":"settings.css"} |
@@ -538,21 +538,7 @@ span.usersLastLoginTooltip { | |||
white-space: nowrap; | |||
} | |||
/* APPS */ | |||
#app-content > svg.app-filter { | |||
float: left; | |||
height: 0; | |||
width: 0; | |||
} | |||
#app-category-app-bundles { | |||
margin-bottom: 20px; | |||
} | |||
.appinfo { | |||
margin: 1em 40px; | |||
} | |||
/* SETTINGS NAVIGATION */ | |||
#app-navigation { | |||
/* Navigation icons */ | |||
img { | |||
@@ -570,188 +556,9 @@ span.usersLastLoginTooltip { | |||
display: block; | |||
} | |||
} | |||
.appwarning { | |||
background: #fcc; | |||
} | |||
&.appwarning:hover { | |||
background: #fbb; | |||
} | |||
.app-external { | |||
color: var(--color-text-maxcontrast); | |||
} | |||
} | |||
span.version { | |||
margin-left: 1em; | |||
margin-right: 1em; | |||
color: var(--color-text-maxcontrast); | |||
} | |||
.app-version { | |||
color: var(--color-text-maxcontrast); | |||
} | |||
.app-settings-content { | |||
#searchresults { | |||
display: none; | |||
} | |||
} | |||
#apps-list.store { | |||
.section { | |||
border: 0; | |||
} | |||
.app-name { | |||
display: block; | |||
margin: 5px 0; | |||
} | |||
.app-image-icon .icon-settings-dark { | |||
width: 100%; | |||
height: 150px; | |||
background-size: 45px; | |||
opacity: 0.5; | |||
} | |||
.app-score-image { | |||
height: 14px; | |||
} | |||
.actions { | |||
margin-top: 10px; | |||
button { | |||
margin: 10px 0; | |||
} | |||
} | |||
} | |||
#app-sidebar #app-details-view { | |||
h2 { | |||
.icon-settings-dark, | |||
svg { | |||
display: inline-block; | |||
width: 16px; | |||
height: 16px; | |||
margin-right: 10px; | |||
opacity: .7; | |||
} | |||
} | |||
.app-author, .app-licence { | |||
color: var(--color-text-maxcontrast); | |||
} | |||
.app-dependencies { | |||
margin: 10px 0; | |||
} | |||
.app-description p { | |||
margin: 10px 0; | |||
} | |||
.close { | |||
position: absolute; | |||
top: 0; | |||
right: 0; | |||
padding: 14px; | |||
opacity: 0.5; | |||
z-index: 1; | |||
width: 44px; | |||
height: 44px; | |||
} | |||
.actions { | |||
display: flex; | |||
align-items: center; | |||
.app-groups { | |||
padding: 5px; | |||
} | |||
} | |||
.appslink { | |||
text-decoration: underline; | |||
margin-right: 5px; | |||
} | |||
.app-level, | |||
.actions, | |||
.documentation, | |||
.app-dependencies, | |||
.app-description { | |||
margin: 20px 0; | |||
} | |||
} | |||
@media only screen and (min-width: 1601px) { | |||
.store .section { | |||
width: 25%; | |||
} | |||
.with-app-sidebar .store .section { | |||
width: 33%; | |||
} | |||
} | |||
@media only screen and (max-width: 1600px) { | |||
.store .section { | |||
width: 25%; | |||
} | |||
.with-app-sidebar .store .section { | |||
width: 33%; | |||
} | |||
} | |||
@media only screen and (max-width: 1400px) { | |||
.store .section { | |||
width: 33%; | |||
} | |||
.with-app-sidebar .store .section { | |||
width: 50%; | |||
} | |||
} | |||
@media only screen and (max-width: 900px) { | |||
.store .section { | |||
width: 50%; | |||
} | |||
.with-app-sidebar .store .section { | |||
width: 100%; | |||
} | |||
} | |||
@media only screen and (max-width: variables.$breakpoint-mobile) { | |||
.store .section { | |||
width: 50%; | |||
} | |||
} | |||
@media only screen and (max-width: 480px) { | |||
.store .section { | |||
width: 100%; | |||
} | |||
} | |||
/* hide app version and level on narrower screens */ | |||
@media only screen and (max-width: 900px) { | |||
.apps-list.installed { | |||
.app-version, .app-level { | |||
display: none !important; | |||
} | |||
} | |||
} | |||
@media only screen and (max-width: 500px) { | |||
.apps-list.installed .app-groups { | |||
display: none !important; | |||
} | |||
} | |||
/* SETTINGS SECTIONS */ | |||
.section { | |||
margin-bottom: 0; | |||
/* section divider lines, none needed for last one */ | |||
@@ -771,256 +578,6 @@ span.version { | |||
} | |||
} | |||
.followupsection { | |||
display: block; | |||
padding: 0 30px 30px 30px; | |||
} | |||
.app-image { | |||
position: relative; | |||
height: 150px; | |||
opacity: 1; | |||
overflow: hidden; | |||
} | |||
.app-description-toggle-show, .app-description-toggle-hide { | |||
clear: both; | |||
padding: 7px 0; | |||
cursor: pointer; | |||
opacity: .5; | |||
} | |||
.app-description-container { | |||
clear: both; | |||
position: relative; | |||
top: 7px; | |||
} | |||
.app-description { | |||
clear: both; | |||
} | |||
#app-category-1 { | |||
margin-bottom: 18px; | |||
} | |||
/* capitalize 'Other' category */ | |||
#app-category-925 { | |||
text-transform: capitalize; | |||
} | |||
.app-dependencies { | |||
color: #ce3702; | |||
} | |||
.missing-dependencies { | |||
list-style: initial; | |||
list-style-type: initial; | |||
list-style-position: inside; | |||
} | |||
.apps-list { | |||
$toolbar-padding: 8px; | |||
$toolbar-height: 44px + $toolbar-padding * 2; | |||
.app-list-move { | |||
transition: transform 1s; | |||
} | |||
#app-list-update-all { | |||
margin-left: 10px; | |||
} | |||
.toolbar { | |||
height: $toolbar-height; | |||
padding: $toolbar-padding; | |||
// Leave room for app-navigation-toggle | |||
padding-left: $toolbar-height; | |||
width: 100%; | |||
background-color: var(--color-main-background); | |||
position: sticky; | |||
top: 0; | |||
z-index: 1; | |||
display: flex; | |||
align-items: center; | |||
} | |||
&.installed { | |||
.apps-list-container { | |||
display: table; | |||
width: 100%; | |||
height: auto; | |||
white-space: normal; | |||
} | |||
margin-bottom: 100px; | |||
.section { | |||
display: table-row; | |||
padding: 0; | |||
margin: 0; | |||
> * { | |||
display: table-cell; | |||
height: initial; | |||
vertical-align: middle; | |||
float: none; | |||
border-bottom: 1px solid var(--color-border); | |||
padding: 6px; | |||
box-sizing: border-box; | |||
} | |||
> .actions { | |||
display: flex; | |||
gap: 8px; | |||
flex-wrap: wrap; | |||
justify-content: end; | |||
} | |||
&.selected { | |||
background-color: var(--color-background-dark); | |||
} | |||
} | |||
.groups-enable { | |||
margin-top: 0; | |||
label { | |||
margin-right: 3px; | |||
} | |||
} | |||
.app-image { | |||
width: 44px; | |||
height: auto; | |||
text-align: right; | |||
} | |||
.app-image-icon svg, | |||
.app-image-icon .icon-settings-dark { | |||
margin-top: 5px; | |||
width: 20px; | |||
height: 20px; | |||
opacity: .5; | |||
background-size: cover; | |||
display: inline-block; | |||
} | |||
.actions { | |||
text-align: right; | |||
.icon-loading-small { | |||
display: inline-block; | |||
top: 4px; | |||
margin-right: 10px; | |||
} | |||
} | |||
} | |||
&:not(.installed) .app-image-icon svg { | |||
position: absolute; | |||
bottom: 43px; | |||
/* position halfway vertically */ | |||
width: 64px; | |||
height: 64px; | |||
opacity: .1; | |||
} | |||
display: flex; | |||
flex-wrap: wrap; | |||
align-content: flex-start; | |||
&.hidden { | |||
display: none; | |||
} | |||
.section { | |||
position: relative; | |||
flex: 0 0 auto; | |||
h2.app-name { | |||
display: block; | |||
margin: 8px 0; | |||
} | |||
&:hover { | |||
background-color: var(--color-background-dark); | |||
} | |||
} | |||
.app-description { | |||
p { | |||
margin: 10px 0; | |||
} | |||
ul { | |||
list-style: disc; | |||
} | |||
ol { | |||
list-style: decimal; | |||
ol, ul { | |||
padding-left: 15px; | |||
} | |||
} | |||
> { | |||
ul, ol { | |||
margin-left: 19px; | |||
} | |||
} | |||
ul { | |||
ol, ul { | |||
padding-left: 15px; | |||
} | |||
} | |||
} | |||
/* Bundle header */ | |||
.apps-header { | |||
position: relative; | |||
div { | |||
display: table-cell; | |||
height: 70px; | |||
} | |||
h2 { | |||
padding-left: 6px; | |||
padding-top: 15px; | |||
margin-bottom: 12px; | |||
.enable { | |||
position: relative; | |||
top: -1px; | |||
margin-left: 12px; | |||
} | |||
+ .section { | |||
margin-top: 50px; | |||
} | |||
} | |||
} | |||
} | |||
// Display buttons above each other on mobile | |||
@media (max-width: math.div(variables.$breakpoint-mobile, 2)) { | |||
.apps-list.installed .section > .actions { | |||
display: table-cell; | |||
} | |||
} | |||
#apps-list-search { | |||
.section { | |||
h2 { | |||
margin-bottom: 0; | |||
} | |||
} | |||
} | |||
/* LOG */ | |||
#log { | |||
white-space: normal; |
@@ -22,9 +22,14 @@ | |||
<template> | |||
<div id="app-content-inner"> | |||
<div id="apps-list" class="apps-list" :class="{installed: (useBundleView || useListView), store: useAppStoreView}"> | |||
<div id="apps-list" | |||
class="apps-list" | |||
:class="{ | |||
'apps-list--list-view': (useBundleView || useListView), | |||
'apps-list--store-view': useAppStoreView, | |||
}"> | |||
<template v-if="useListView"> | |||
<div v-if="showUpdateAll" class="toolbar"> | |||
<div v-if="showUpdateAll" class="apps-list__toolbar"> | |||
{{ n('settings', '%n app has an update available', '%n apps have an update available', counter) }} | |||
<NcButton v-if="showUpdateAll" | |||
id="app-list-update-all" | |||
@@ -34,25 +39,25 @@ | |||
</NcButton> | |||
</div> | |||
<div v-if="!showUpdateAll" class="toolbar"> | |||
<div v-if="!showUpdateAll" class="apps-list__toolbar"> | |||
{{ t('settings', 'All apps are up-to-date.') }} | |||
</div> | |||
<transition-group name="app-list" tag="table" class="apps-list-container"> | |||
<tr key="app-list-view-header" class="apps-header"> | |||
<th class="app-image"> | |||
<TransitionGroup name="apps-list" tag="table" class="apps-list__list-container"> | |||
<tr key="app-list-view-header"> | |||
<th> | |||
<span class="hidden-visually">{{ t('settings', 'Icon') }}</span> | |||
</th> | |||
<th class="app-name"> | |||
<th> | |||
<span class="hidden-visually">{{ t('settings', 'Name') }}</span> | |||
</th> | |||
<th class="app-version"> | |||
<th> | |||
<span class="hidden-visually">{{ t('settings', 'Version') }}</span> | |||
</th> | |||
<th class="app-level"> | |||
<th> | |||
<span class="hidden-visually">{{ t('settings', 'Level') }}</span> | |||
</th> | |||
<th class="actions"> | |||
<th> | |||
<span class="hidden-visually">{{ t('settings', 'Actions') }}</span> | |||
</th> | |||
</tr> | |||
@@ -60,33 +65,33 @@ | |||
:key="app.id" | |||
:app="app" | |||
:category="category" /> | |||
</transition-group> | |||
</TransitionGroup> | |||
</template> | |||
<table v-if="useBundleView" | |||
class="apps-list-container"> | |||
<tr key="app-list-view-header" class="apps-header"> | |||
<th id="app-table-col-icon" class="app-image"> | |||
class="apps-list__list-container"> | |||
<tr key="app-list-view-header"> | |||
<th id="app-table-col-icon"> | |||
<span class="hidden-visually">{{ t('settings', 'Icon') }}</span> | |||
</th> | |||
<th id="app-table-col-name" class="app-name"> | |||
<th id="app-table-col-name"> | |||
<span class="hidden-visually">{{ t('settings', 'Name') }}</span> | |||
</th> | |||
<th id="app-table-col-version" class="app-version"> | |||
<th id="app-table-col-version"> | |||
<span class="hidden-visually">{{ t('settings', 'Version') }}</span> | |||
</th> | |||
<th id="app-table-col-level" class="app-level"> | |||
<th id="app-table-col-level"> | |||
<span class="hidden-visually">{{ t('settings', 'Level') }}</span> | |||
</th> | |||
<th id="app-table-col-actions" class="actions"> | |||
<th id="app-table-col-actions"> | |||
<span class="hidden-visually">{{ t('settings', 'Actions') }}</span> | |||
</th> | |||
</tr> | |||
<template v-for="bundle in bundles"> | |||
<tr :key="bundle.id"> | |||
<th :id="`app-table-rowgroup-${bundle.id}`" colspan="5" scope="rowgroup"> | |||
<div class="app-bundle-heading"> | |||
<span class="app-bundle-header"> | |||
<div class="apps-list__bundle-heading"> | |||
<span class="apps-list__bundle-header"> | |||
{{ bundle.name }} | |||
</span> | |||
<NcButton type="secondary" @click="toggleBundle(bundle.id)"> | |||
@@ -103,7 +108,7 @@ | |||
:category="category" /> | |||
</template> | |||
</table> | |||
<ul v-if="useAppStoreView" class="apps-store-view"> | |||
<ul v-if="useAppStoreView" class="apps-list__store-container"> | |||
<AppItem v-for="app in apps" | |||
:key="app.id" | |||
:app="app" | |||
@@ -112,20 +117,34 @@ | |||
</ul> | |||
</div> | |||
<div id="apps-list-search" class="apps-list installed"> | |||
<div class="apps-list-container"> | |||
<template v-if="search !== '' && searchApps.length > 0"> | |||
<div class="section"> | |||
<div /> | |||
<td colspan="5"> | |||
<h2>{{ t('settings', 'Results from other categories') }}</h2> | |||
</td> | |||
</div> | |||
<div id="apps-list-search" class="apps-list apps-list--list-view"> | |||
<div class="apps-list__list-container"> | |||
<table v-if="search !== '' && searchApps.length > 0" class="apps-list__list-container"> | |||
<caption class="apps-list__bundle-header"> | |||
{{ t('settings', 'Results from other categories') }} | |||
</caption> | |||
<tr key="app-list-view-header"> | |||
<th> | |||
<span class="hidden-visually">{{ t('settings', 'Icon') }}</span> | |||
</th> | |||
<th> | |||
<span class="hidden-visually">{{ t('settings', 'Name') }}</span> | |||
</th> | |||
<th> | |||
<span class="hidden-visually">{{ t('settings', 'Version') }}</span> | |||
</th> | |||
<th> | |||
<span class="hidden-visually">{{ t('settings', 'Level') }}</span> | |||
</th> | |||
<th> | |||
<span class="hidden-visually">{{ t('settings', 'Actions') }}</span> | |||
</th> | |||
</tr> | |||
<AppItem v-for="app in searchApps" | |||
:key="app.id" | |||
:app="app" | |||
:category="category" /> | |||
</template> | |||
</table> | |||
</div> | |||
</div> | |||
@@ -133,8 +152,6 @@ | |||
<div id="app-list-empty-icon" class="icon-settings-dark" /> | |||
<h2>{{ t('settings', 'No apps found for your version') }}</h2> | |||
</div> | |||
<div id="searchresults" /> | |||
</div> | |||
</template> | |||
@@ -208,6 +225,7 @@ export default { | |||
// An app level of `200` will be set for apps featured on the app store | |||
return apps.filter(app => app.level === 200) | |||
} | |||
// filter app store categories | |||
return apps.filter(app => { | |||
return app.appstore && app.category !== undefined | |||
@@ -311,20 +329,72 @@ export default { | |||
</script> | |||
<style lang="scss" scoped> | |||
.app-bundle-heading { | |||
$toolbar-padding: 8px; | |||
$toolbar-height: 44px + $toolbar-padding * 2; | |||
.apps-list { | |||
display: flex; | |||
flex-wrap: wrap; | |||
align-content: flex-start; | |||
// For transition group | |||
&--move { | |||
transition: transform 1s; | |||
} | |||
#app-list-update-all { | |||
margin-left: 10px; | |||
} | |||
&__toolbar { | |||
height: $toolbar-height; | |||
padding: $toolbar-padding; | |||
// Leave room for app-navigation-toggle | |||
padding-left: $toolbar-height; | |||
width: 100%; | |||
background-color: var(--color-main-background); | |||
position: sticky; | |||
top: 0; | |||
z-index: 1; | |||
display: flex; | |||
align-items: center; | |||
} | |||
&--list-view { | |||
margin-bottom: 100px; | |||
// For positioning link overlay on rows | |||
position: relative; | |||
} | |||
&__list-container { | |||
width: 100%; | |||
} | |||
&__store-container { | |||
display: flex; | |||
flex-wrap: wrap; | |||
} | |||
&__bundle-heading { | |||
display: flex; | |||
align-items: center; | |||
margin: 20px 10px 20px 0; | |||
} | |||
.app-bundle-header { | |||
&__bundle-header { | |||
margin: 0 10px 0 50px; | |||
font-weight: bold; | |||
font-size: 20px; | |||
line-height: 30px; | |||
color: var(--color-text-light); | |||
} | |||
.apps-store-view { | |||
display: flex; | |||
flex-wrap: wrap; | |||
} | |||
#apps-list-search { | |||
.app-item { | |||
h2 { | |||
margin-bottom: 0; | |||
} | |||
} | |||
} | |||
</style> |
@@ -22,8 +22,13 @@ | |||
<template> | |||
<component :is="listView ? `tr` : `li`" | |||
class="section" | |||
:class="{ selected: isSelected }"> | |||
class="app-item" | |||
:class="{ | |||
'app-item--list-view': listView, | |||
'app-item--store-view': !listView, | |||
'app-item--selected': isSelected, | |||
'app-item--with-sidebar': withSidebar, | |||
}"> | |||
<component :is="dataItemTag" | |||
class="app-image app-image-icon" | |||
:headers="getDataItemHeaders(`app-table-col-icon`)"> | |||
@@ -73,11 +78,11 @@ | |||
<span v-else-if="app.appstoreData.releases[0].version">{{ app.appstoreData.releases[0].version }}</span> | |||
</component> | |||
<component :is="dataItemTag" :headers="getDataItemHeaders(`app-table-col-level`)"> | |||
<component :is="dataItemTag" :headers="getDataItemHeaders(`app-table-col-level`)" class="app-level"> | |||
<AppLevelBadge :level="app.level" /> | |||
<AppScore v-if="hasRating && !listView" :score="app.score" /> | |||
</component> | |||
<component :is="dataItemTag" :headers="getDataItemHeaders(`app-table-col-actions`)" class="actions"> | |||
<component :is="dataItemTag" :headers="getDataItemHeaders(`app-table-col-actions`)" class="app-actions"> | |||
<div v-if="app.error" class="warning"> | |||
{{ app.error }} | |||
</div> | |||
@@ -168,6 +173,9 @@ export default { | |||
dataItemTag() { | |||
return this.listView ? 'td' : 'div' | |||
}, | |||
withSidebar() { | |||
return !!this.$route.params.id | |||
}, | |||
}, | |||
watch: { | |||
'$route.params.id'(id) { | |||
@@ -200,21 +208,191 @@ export default { | |||
</script> | |||
<style scoped lang="scss"> | |||
@use '../../../../../core/css/variables.scss' as variables; | |||
@use 'sass:math'; | |||
.app-item { | |||
position: relative; | |||
&:hover { | |||
background-color: var(--color-background-dark); | |||
} | |||
&--list-view { | |||
--app-item-padding: calc(var(--default-grid-baseline) * 2); | |||
--app-item-height: calc(var(--default-clickable-area) + var(--app-item-padding) * 2); | |||
&.app-item--selected { | |||
background-color: var(--color-background-dark); | |||
} | |||
> * { | |||
vertical-align: middle; | |||
border-bottom: 1px solid var(--color-border); | |||
padding: var(--app-item-padding); | |||
height: var(--app-item-height); | |||
} | |||
.app-image { | |||
width: var(--default-clickable-area); | |||
height: auto; | |||
text-align: right; | |||
} | |||
.app-image-icon svg, | |||
.app-image-icon .icon-settings-dark { | |||
margin-top: 5px; | |||
width: 20px; | |||
height: 20px; | |||
opacity: .5; | |||
background-size: cover; | |||
display: inline-block; | |||
} | |||
.app-name { | |||
padding: 0 var(--app-item-padding); | |||
} | |||
.app-name--link { | |||
height: var(--app-item-height); | |||
display: flex; | |||
align-items: center; | |||
} | |||
// Note: because of Safari bug, we cannot position link overlay relative to the table row | |||
// So we need to manually position it relative to the table container and cell | |||
// See: https://bugs.webkit.org/show_bug.cgi?id=240961 | |||
.app-name--link::after { | |||
content: ''; | |||
position: absolute; | |||
left: 0; | |||
right: 0; | |||
height: var(--app-item-height); | |||
} | |||
.app-actions { | |||
display: flex; | |||
gap: var(--app-item-padding); | |||
flex-wrap: wrap; | |||
justify-content: end; | |||
.icon-loading-small { | |||
display: inline-block; | |||
top: 4px; | |||
margin-right: 10px; | |||
} | |||
} | |||
/* hide app version and level on narrower screens */ | |||
@media only screen and (max-width: 900px) { | |||
.app-version, | |||
.app-level { | |||
display: none; | |||
} | |||
} | |||
/* Hide actions on a small screen. Click on app opens fill-screen sidebar with the buttons */ | |||
@media only screen and (max-width: math.div(variables.$breakpoint-mobile, 2)) { | |||
.app-actions { | |||
display: none; | |||
} | |||
} | |||
} | |||
&--store-view { | |||
padding: 30px; | |||
.app-image-icon .icon-settings-dark { | |||
width: 100%; | |||
height: 150px; | |||
background-size: 45px; | |||
opacity: 0.5; | |||
} | |||
.app-image-icon svg { | |||
position: absolute; | |||
bottom: 43px; | |||
/* position halfway vertically */ | |||
width: 64px; | |||
height: 64px; | |||
opacity: .1; | |||
} | |||
.app-name { | |||
margin: 5px 0; | |||
} | |||
.app-name--link::after { | |||
content: ''; | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
right: 0; | |||
bottom: 0; | |||
} | |||
.app-actions { | |||
margin: 10px 0; | |||
} | |||
@media only screen and (min-width: 1601px) { | |||
width: 25%; | |||
&.app-item--with-sidebar { | |||
width: 33%; | |||
} | |||
} | |||
@media only screen and (max-width: 1600px) { | |||
width: 25%; | |||
&.app-item--with-sidebar { | |||
width: 33%; | |||
} | |||
} | |||
@media only screen and (max-width: 1400px) { | |||
width: 33%; | |||
&.app-item--with-sidebar { | |||
width: 50%; | |||
} | |||
} | |||
@media only screen and (max-width: 900px) { | |||
width: 50%; | |||
&.app-item--with-sidebar { | |||
width: 100%; | |||
} | |||
} | |||
@media only screen and (max-width: variables.$breakpoint-mobile) { | |||
width: 50%; | |||
} | |||
@media only screen and (max-width: 480px) { | |||
width: 100%; | |||
} | |||
} | |||
} | |||
.app-icon { | |||
filter: var(--background-invert-if-bright); | |||
} | |||
.app-image img { | |||
width: 100%; | |||
} | |||
.app-image { | |||
position: relative; | |||
height: 150px; | |||
opacity: 1; | |||
overflow: hidden; | |||
.app-name--link::after { | |||
content: ''; | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
right: 0; | |||
bottom: 0; | |||
img { | |||
width: 100%; | |||
} | |||
} | |||
.app-version { | |||
color: var(--color-text-maxcontrast); | |||
} | |||
</style> |
@@ -426,4 +426,9 @@ export default { | |||
background: var(--color-error); | |||
} | |||
.missing-dependencies { | |||
list-style: initial; | |||
list-style-type: initial; | |||
list-style-position: inside; | |||
} | |||
</style> |
@@ -1,14 +1,12 @@ | |||
import type { RouteConfig } from 'vue-router' | |||
import { defineAsyncComponent } from 'vue' | |||
// Dynamic loading | |||
const AppStore = defineAsyncComponent(() => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStore.vue')) | |||
const AppStoreNavigation = defineAsyncComponent(() => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStoreNavigation.vue')) | |||
const AppstoreSidebar = defineAsyncComponent(() => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStoreSidebar.vue')) | |||
const AppStore = () => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStore.vue') | |||
const AppStoreNavigation = () => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStoreNavigation.vue') | |||
const AppStoreSidebar = () => import(/* webpackChunkName: 'settings-apps-view' */'../views/AppStoreSidebar.vue') | |||
const UserManagement = defineAsyncComponent(() => import(/* webpackChunkName: 'settings-users' */'../views/UserManagement.vue')) | |||
const UserManagementNavigation = defineAsyncComponent(() => import(/* webpackChunkName: 'settings-users' */'../views/UserManagementNavigation.vue')) | |||
const UserManagement = () => import(/* webpackChunkName: 'settings-users' */'../views/UserManagement.vue') | |||
const UserManagementNavigation = () => import(/* webpackChunkName: 'settings-users' */'../views/UserManagementNavigation.vue') | |||
const routes: RouteConfig[] = [ | |||
{ | |||
@@ -39,7 +37,7 @@ const routes: RouteConfig[] = [ | |||
components: { | |||
default: AppStore, | |||
navigation: AppStoreNavigation, | |||
sidebar: AppstoreSidebar, | |||
sidebar: AppStoreSidebar, | |||
}, | |||
children: [ | |||
{ |
@@ -39,11 +39,10 @@ describe('Settings: App management', { testIsolation: true }, () => { | |||
}) | |||
it('Can enable an installed app', () => { | |||
cy.get('#apps-list').should('be.visible') | |||
cy.get('#apps-list').should('exist') | |||
// Wait for the app list to load | |||
.contains('tr', 'QA testing', { timeout: 10000 }) | |||
.should('exist') | |||
.find('.actions') | |||
// I enable the "QA testing" app | |||
.contains('button', 'Enable') | |||
.click({ force: true }) | |||
@@ -51,10 +50,9 @@ describe('Settings: App management', { testIsolation: true }, () => { | |||
handlePasswordConfirmation(admin.password) | |||
// Wait until we see the disable button for the app | |||
cy.get('#apps-list').should('be.visible') | |||
cy.get('#apps-list').should('exist') | |||
.contains('tr', 'QA testing') | |||
.should('exist') | |||
.find('.actions') | |||
// I see the disable button for the app | |||
.contains('button', 'Disable', { timeout: 10000 }) | |||
@@ -62,15 +60,15 @@ describe('Settings: App management', { testIsolation: true }, () => { | |||
cy.get('#app-category-enabled a').click({ force: true }) | |||
cy.url().should('match', /settings\/apps\/enabled$/) | |||
// I see that the "QA testing" app has been enabled | |||
cy.get('.apps-list-container').contains('tr', 'QA testing') | |||
cy.get('#apps-list').contains('tr', 'QA testing') | |||
}) | |||
it('Can disable an installed app', () => { | |||
cy.get('#apps-list').should('be.visible') | |||
cy.get('#apps-list') | |||
.should('exist') | |||
// Wait for the app list to load | |||
.contains('tr', 'Update notification', { timeout: 10000 }) | |||
.should('exist') | |||
.find('.actions') | |||
// I disable the "Update notification" app | |||
.contains('button', 'Disable') | |||
.click({ force: true }) | |||
@@ -78,10 +76,9 @@ describe('Settings: App management', { testIsolation: true }, () => { | |||
handlePasswordConfirmation(admin.password) | |||
// Wait until we see the disable button for the app | |||
cy.get('#apps-list').should('be.visible') | |||
cy.get('#apps-list').should('exist') | |||
.contains('tr', 'Update notification') | |||
.should('exist') | |||
.find('.actions') | |||
// I see the enable button for the app | |||
.contains('button', 'Enable', { timeout: 10000 }) | |||
@@ -89,7 +86,7 @@ describe('Settings: App management', { testIsolation: true }, () => { | |||
cy.get('#app-category-disabled a').click({ force: true }) | |||
cy.url().should('match', /settings\/apps\/disabled$/) | |||
// I see that the "Update notification" app has been disabled | |||
cy.get('.apps-list-container').contains('tr', 'Update notification') | |||
cy.get('#apps-list').contains('tr', 'Update notification') | |||
}) | |||
it('Browse enabled apps', () => { | |||
@@ -102,8 +99,8 @@ describe('Settings: App management', { testIsolation: true }, () => { | |||
cy.get('#app-category-enabled').find('.active').should('exist') | |||
// I see that there are only enabled apps | |||
cy.get('#apps-list') | |||
.should('be.visible') | |||
.find('tr .actions') | |||
.should('exist') | |||
.find('tr button') | |||
.each(($action) => { | |||
cy.wrap($action).should('not.contain', 'Enable') | |||
}) | |||
@@ -119,8 +116,8 @@ describe('Settings: App management', { testIsolation: true }, () => { | |||
cy.get('#app-category-disabled').find('.active').should('exist') | |||
// I see that there are only disabled apps | |||
cy.get('#apps-list') | |||
.should('be.visible') | |||
.find('tr .actions') | |||
.should('exist') | |||
.find('tr button') | |||
.each(($action) => { | |||
cy.wrap($action).should('not.contain', 'Disable') | |||
}) |