Merge pull request #44236 from nextcloud/fix/settings--app-list-scrolling-on-safari

pull/44333/head
John Molakvoæ 2 months ago committed by GitHub
commit 174c10ab3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

File diff suppressed because one or more lines are too long

@ -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')
})

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save