diff --git a/src-ui/cypress/e2e/settings/settings.cy.ts b/src-ui/cypress/e2e/settings/settings.cy.ts index 157796a11..0480e39ce 100644 --- a/src-ui/cypress/e2e/settings/settings.cy.ts +++ b/src-ui/cypress/e2e/settings/settings.cy.ts @@ -46,7 +46,7 @@ describe('settings', () => { }) }) - cy.viewport(1024, 1280) + cy.viewport(1024, 1600) cy.visit('/settings') cy.wait('@savedViews') }) diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html index 8f94f80aa..7d003225d 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.html +++ b/src-ui/src/app/components/app-frame/app-frame.component.html @@ -4,11 +4,11 @@ (click)="isMenuCollapsed = !isMenuCollapsed"> - - + + - Paperless-ngx + Paperless-ngx
@@ -51,48 +51,54 @@
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.scss b/src-ui/src/app/components/app-frame/app-frame.component.scss index 296df26c3..95d7ed7ef 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.scss +++ b/src-ui/src/app/components/app-frame/app-frame.component.scss @@ -1,3 +1,6 @@ +@import "node_modules/bootstrap/scss/functions"; +@import "node_modules/bootstrap/scss/variables"; + /* * Sidebar */ @@ -14,6 +17,17 @@ width: 0.8em; height: 0.8em; } + + // These come from the col-md-3 col-lg-2 classes for regular sidebar, needed for animation + @media (min-width: 768px) { + max-width: 25%; + } + + @media (min-width: 992px) { + max-width: 16.66666667%; + } + + transition: all .2s ease; } @media (max-width: 767.98px) { .sidebar { @@ -21,6 +35,90 @@ } } +main { + transition: all .2s ease; +} + +.sidebar-slim-toggler { + display: none; // hide on mobile +} + +.sidebar li.nav-item span, +.sidebar .sidebar-heading span { + transition: all .1s ease; +} + +@media(min-width: 768px) { + .sidebar.slim { + max-width: 50px; + + li.nav-item span.badge { + display: inline-block; + margin-right: 2px; + } + } + + .sidebar.slim:not(.animating) { + li.nav-item span, + .sidebar-heading span { + display: none; + } + } + + .sidebar.animating { + li.nav-item span, + .sidebar-heading span { + display: unset; + position: absolute; + opacity: 0; + overflow: hidden; + } + } + + .sidebar:not(.slim):not(.animating) { + li.nav-item span, + .sidebar-heading span { + position: unset; + opacity: 1; + overflow: auto; + } + } + + .sidebar.slim, + .sidebar.animating { + .text-truncate { + text-overflow: unset !important; + word-wrap: break-word !important; + } + } + + .sidebar.slim { + li.nav-item span.badge { + display: inline-block; + margin-right: 2px; + } + } + + .col-slim { + padding-left: calc(50px + $grid-gutter-width) !important; + } + + .sidebar-slim-toggler { + display: block; + position: absolute; + right: -12px; + top: 60px; + z-index: 996; + --bs-btn-padding-x: 0.35rem; + --bs-btn-padding-y: 0.125rem; + } +} + +::ng-deep .popover-slim .popover-body { + --bs-popover-body-padding-x: .5rem; + --bs-popover-body-padding-y: .5rem; +} + .sidebar-sticky { position: relative; top: 0; diff --git a/src-ui/src/app/components/app-frame/app-frame.component.ts b/src-ui/src/app/components/app-frame/app-frame.component.ts index ee8deb5f8..b189409a8 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.ts @@ -58,6 +58,35 @@ export class AppFrameComponent implements OnInit, ComponentCanDeactivate { isMenuCollapsed: boolean = true + slimSidebarAnimating: boolean = false + + toggleSlimSidebar(): void { + this.slimSidebarAnimating = true + this.slimSidebarEnabled = !this.slimSidebarEnabled + setTimeout(() => { + this.slimSidebarAnimating = false + }, 200) // slightly longer than css animation for slim sidebar + } + + get slimSidebarEnabled(): boolean { + return this.settingsService.get(SETTINGS_KEYS.SLIM_SIDEBAR) + } + + set slimSidebarEnabled(enabled: boolean) { + this.settingsService.set(SETTINGS_KEYS.SLIM_SIDEBAR, enabled) + this.settingsService + .storeSettings() + .pipe(first()) + .subscribe({ + error: (error) => { + this.toastService.showError( + $localize`An error occurred while saving settings.` + ) + console.log(error) + }, + }) + } + closeMenu() { this.isMenuCollapsed = true } diff --git a/src-ui/src/app/components/document-list/document-list.component.scss b/src-ui/src/app/components/document-list/document-list.component.scss index b2d138d7f..3361d7c3a 100644 --- a/src-ui/src/app/components/document-list/document-list.component.scss +++ b/src-ui/src/app/components/document-list/document-list.component.scss @@ -11,7 +11,7 @@ tr { } $paperless-card-breakpoints: ( - 0: 2, // xs + // 0: 2, // xs is manual for slim-sidebar 768px: 3, //md 992px: 4, //lg 1200px: 5, //xl @@ -22,6 +22,12 @@ $paperless-card-breakpoints: ( ); .row-cols-paperless-cards { + // xs, we dont want in .col-slim block + > * { + flex: 0 0 auto; + width: calc(100% / 2); + } + @each $width, $n_cols in $paperless-card-breakpoints { @media(min-width: $width) { > * { @@ -32,6 +38,17 @@ $paperless-card-breakpoints: ( } } +::ng-deep .col-slim .row-cols-paperless-cards { + @each $width, $n_cols in $paperless-card-breakpoints { + @media(min-width: $width) { + > * { + flex: 0 0 auto; + width: calc(100% / ($n-cols + 1)) !important; + } + } + } +} + .dropdown-menu-right { right: 0 !important; left: auto !important; diff --git a/src-ui/src/app/components/manage/settings/settings.component.html b/src-ui/src/app/components/manage/settings/settings.component.html index e2afd76ea..ab4cbc4c4 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.html +++ b/src-ui/src/app/components/manage/settings/settings.component.html @@ -89,6 +89,17 @@
+
+
+ Sidebar +
+
+ + + +
+
+
Dark mode diff --git a/src-ui/src/app/components/manage/settings/settings.component.ts b/src-ui/src/app/components/manage/settings/settings.component.ts index 0cf336a01..7a1f31820 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -28,6 +28,7 @@ import { import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' import { ActivatedRoute } from '@angular/router' import { ViewportScroller } from '@angular/common' +import { ForwardRefHandling } from '@angular/compiler' @Component({ selector: 'app-settings', @@ -43,6 +44,7 @@ export class SettingsComponent bulkEditConfirmationDialogs: new FormControl(null), bulkEditApplyOnClose: new FormControl(null), documentListItemPerPage: new FormControl(null), + slimSidebarEnabled: new FormControl(null), darkModeUseSystem: new FormControl(null), darkModeEnabled: new FormControl(null), darkModeInvertThumbs: new FormControl(null), @@ -99,17 +101,8 @@ export class SettingsComponent } } - ngOnInit() { - this.savedViewService.listAll().subscribe((r) => { - this.savedViews = r.results - this.initialize() - }) - } - - initialize() { - this.unsubscribeNotifier.next(true) - - let storeData = { + private getCurrentSettings() { + return { bulkEditConfirmationDialogs: this.settings.get( SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS ), @@ -119,6 +112,7 @@ export class SettingsComponent documentListItemPerPage: this.settings.get( SETTINGS_KEYS.DOCUMENT_LIST_SIZE ), + slimSidebarEnabled: this.settings.get(SETTINGS_KEYS.SLIM_SIDEBAR), darkModeUseSystem: this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM), darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED), darkModeInvertThumbs: this.settings.get( @@ -149,6 +143,19 @@ export class SettingsComponent SETTINGS_KEYS.UPDATE_CHECKING_ENABLED ), } + } + + ngOnInit() { + this.savedViewService.listAll().subscribe((r) => { + this.savedViews = r.results + this.initialize() + }) + } + + initialize() { + this.unsubscribeNotifier.next(true) + + let storeData = this.getCurrentSettings() for (let view of this.savedViews) { storeData.savedViews[view.id.toString()] = { @@ -232,6 +239,10 @@ export class SettingsComponent SETTINGS_KEYS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage ) + this.settings.set( + SETTINGS_KEYS.SLIM_SIDEBAR, + this.settingsForm.value.slimSidebarEnabled + ) this.settings.set( SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem diff --git a/src-ui/src/app/data/paperless-uisettings.ts b/src-ui/src/app/data/paperless-uisettings.ts index b32315e62..403d11f08 100644 --- a/src-ui/src/app/data/paperless-uisettings.ts +++ b/src-ui/src/app/data/paperless-uisettings.ts @@ -37,6 +37,7 @@ export const SETTINGS_KEYS = { NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD: 'general-settings:notifications:consumer-suppress-on-dashboard', COMMENTS_ENABLED: 'general-settings:comments-enabled', + SLIM_SIDEBAR: 'general-settings:slim-sidebar', UPDATE_CHECKING_ENABLED: 'general-settings:update-checking:enabled', UPDATE_CHECKING_BACKEND_SETTING: 'general-settings:update-checking:backend-setting', @@ -58,6 +59,11 @@ export const SETTINGS: PaperlessUiSetting[] = [ type: 'boolean', default: false, }, + { + key: SETTINGS_KEYS.SLIM_SIDEBAR, + type: 'boolean', + default: false, + }, { key: SETTINGS_KEYS.DOCUMENT_LIST_SIZE, type: 'number', diff --git a/src-ui/src/styles.scss b/src-ui/src/styles.scss index d85fb7dc7..b66710dcd 100644 --- a/src-ui/src/styles.scss +++ b/src-ui/src/styles.scss @@ -1,9 +1,9 @@ // bs options $enable-negative-margins: true; +@import "theme"; @import "node_modules/bootstrap/scss/bootstrap"; @import "~@ng-select/ng-select/themes/default.theme.css"; -@import "theme"; @import "print"; // Paperless-ngx styles @@ -84,6 +84,15 @@ svg.logo { } } +.btn-dark { + --bs-btn-color: var(--bs-gray-600); + --bs-btn-bg: var(--bs-gray-200); + --bs-btn-border-color: var(--bs-gray-200); + --bs-btn-hover-bg: var(--bs-gray-400); + --bs-btn-hover-border-color: var(--bs-gray-500); + --bs-btn-active-bg: var(--bs-gray-200); +} + .text-primary { color: var(--bs-primary) !important; } @@ -405,6 +414,11 @@ textarea, vertical-align: text-bottom; } +.sidebaricon-sm { + width: 12px; + height: 12px; +} + table.table { color: var(--bs-body-color); diff --git a/src-ui/src/theme.scss b/src-ui/src/theme.scss index bf9be6662..99dcb8bab 100644 --- a/src-ui/src/theme.scss +++ b/src-ui/src/theme.scss @@ -79,6 +79,15 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,