Make layout fully responsive using matchMedia('(max-width: 799px)')

This commit is contained in:
the-djmaze 2023-02-23 13:54:32 +01:00
parent 1dbd9bda0c
commit e1833ae032
64 changed files with 239 additions and 434 deletions

View file

@ -96,7 +96,7 @@ export const EditorDefaultType = {
* @enum {number}
*/
export const Layout = {
NoPreview: 0,
// NoPreview: 0,
SidePreview: 1,
BottomPreview: 2
};

View file

@ -86,7 +86,7 @@ export class UserSettingsGeneral extends AbstractViewSettings {
layoutTypes: () => {
translateTrigger();
return [
{ id: Layout.NoPreview, name: i18n('SETTINGS_GENERAL/LAYOUT_NO_SPLIT') },
{ id: 0, name: i18n('SETTINGS_GENERAL/LAYOUT_NO_SPLIT') },
{ id: Layout.SidePreview, name: i18n('SETTINGS_GENERAL/LAYOUT_VERTICAL_SPLIT') },
{ id: Layout.BottomPreview, name: i18n('SETTINGS_GENERAL/LAYOUT_HORIZONTAL_SPLIT') }
];

View file

@ -8,6 +8,9 @@ import { addSubscribablesTo } from 'External/ko';
let __themeTimer = 0;
export const
// Also see Styles/_Values.less @maxMobileWidth
isMobile = matchMedia('(max-width: 799px)'),
ThemeStore = {
theme: ko.observable(''),
themes: ko.observableArray(),
@ -16,7 +19,7 @@ export const
fontSansSerif: ko.observable(''),
fontSerif: ko.observable(''),
fontMono: ko.observable(''),
isMobile: ko.observable($htmlCL.contains('rl-mobile')),
isMobile: ko.observable(false),
populate: () => {
const themes = Settings.app('themes');
@ -62,8 +65,6 @@ export const
convertThemeName = theme => theme.replace(/@[a-z]+$/, '').replace(/([A-Z])/g, ' $1').trim();
addSubscribablesTo(ThemeStore, {
isMobile: value => $htmlCL.toggle('rl-mobile', value),
fontSansSerif: value => {
if (null != value) {
let cl = appEl.classList;
@ -97,3 +98,9 @@ addSubscribablesTo(ThemeStore, {
appEl.style.backgroundImage = value ? "url("+serverRequestRaw('UserBackground', value)+")" : null;
}
});
isMobile.onchange = e => {
ThemeStore.isMobile(e.matches);
$htmlCL.toggle('rl-mobile', e.matches);
};
isMobile.onchange(isMobile);

View file

@ -49,11 +49,10 @@ export const SettingsUserStore = new class {
self.init();
self.usePreviewPane = koComputable(() => Layout.NoPreview !== self.layout() && !ThemeStore.isMobile());
self.usePreviewPane = koComputable(() => self.layout() && !ThemeStore.isMobile());
const toggleLayout = () => {
const value = ThemeStore.isMobile() ? Layout.NoPreview : self.layout();
$htmlCL.toggle('rl-no-preview-pane', Layout.NoPreview === value);
const value = ThemeStore.isMobile() ? 0 : self.layout();
$htmlCL.toggle('rl-side-preview-pane', Layout.SidePreview === value);
$htmlCL.toggle('rl-bottom-preview-pane', Layout.BottomPreview === value);
fireEvent('rl-layout', value);

View file

@ -73,7 +73,7 @@
}
/* desktop */
@media screen and (min-width: 1000px) {
@media screen and (min-width: @maxMobileWidth + 1px) {
.toggleLeft {
display: none;
}
@ -86,8 +86,7 @@
}
}
/* mobile and tablet */
@media screen and (max-width: 999px) {
@media screen and (max-width: @maxMobileWidth) {
#rl-right {
min-width: calc(100% - @rlLowMargin);
}

View file

@ -4,7 +4,7 @@ html.list-loading body {
cursor: progress;
}
@media screen and (min-width: 1000px) {
@media screen and (min-width: @maxMobileWidth + 1px) {
#rl-app {
background-image: var(--main-bg-image);
background-size: var(--main-bg-size);
@ -73,13 +73,14 @@ dialog:not(.animate) {
/**
* https://github.com/the-djmaze/snappymail/issues/686
@media screen and (max-width: 999px) {
*/
.rl-mobile dialog {
margin: 0 auto;
/* max-height: calc(100vh - 86px);*/
/* max-height: 100dvh;*/
width: 100%;
@media screen and (max-width: @maxMobileWidth) {
dialog {
margin: 0 auto;
/* max-height: calc(100vh - 86px);*/
/* max-height: 100dvh;*/
width: 100%;
}
}
dialog > header {

View file

@ -171,8 +171,7 @@ summary.legend {
color: var(--error-clr, #b94a48);
}
/* mobile and tablet */
@media screen and (max-width: 999px) {
@media screen and (max-width: @maxMobileWidth) {
.form-horizontal {
.control-group {
> label {

View file

@ -10,7 +10,7 @@
100% {background-position: 60px 0;}
}
@media screen and (min-width: 1000px) {
@media screen and (min-width: @maxMobileWidth + 1px) {
.b-folders li .anim-action-class {
animation: highlight-folder-row 0.5s linear;

View file

@ -195,7 +195,7 @@
}
}
html:not(.rl-mobile) {
@media screen and (min-width: @maxMobileWidth + 1px) {
.hideContactListCheckbox {
.checkboxItem {
visibility: hidden;

View file

@ -44,7 +44,6 @@
}
.b-toolbar {
padding: 10px 0 10px @rlLowMargin;
color: #fff;
}

View file

@ -12,6 +12,10 @@
/* transition: width 0.3s ease-out;*/
width: @rlLeftWidth;
z-index: 0;
.b-toolbar {
padding: 10px 0 10px @rlLowMargin;
}
}
#rl-right:not([hidden]) {
@ -19,9 +23,6 @@
flex-grow: 1;
width: 20%;
}
.rl-bottom-preview-pane #rl-right {
flex-direction: column;
}
/*
.resizable::after {
@ -41,47 +42,12 @@
}
*/
.resizer {
background: #aaa;
background: rgba(255,255,255,0.5);
display: none;
opacity: 0;
position: absolute;
z-index: 102;
}
.resizer:hover {
opacity: 1;
}
html:not(.rl-left-panel-disabled) #rl-left {
resize: horizontal; overflow: auto;
min-width: 155px;
max-width: 350px;
}
#rl-left > .resizer,
.rl-side-preview-pane #V-MailMessageList .resizer {
cursor: ew-resize;
cursor: col-resize;
right: 0;
top: 0;
bottom: 0;
width: 5px;
}
.rl-bottom-preview-pane #V-MailMessageList .resizer {
cursor: ns-resize;
cursor: row-resize;
bottom: 0;
left: 0;
right: 0;
height: 5px;
}
html:not(.rl-left-panel-disabled) #rl-left > .resizer,
#V-MailMessageList .resizer {
display: block;
}
#top-system-dropdown-id::after,
#button-add-prop-dropdown-id::after {
content: '▼';
@ -140,38 +106,6 @@ html:not(.rl-left-panel-disabled) #rl-left > .resizer,
margin-right: 8px;
}
/* desktop */
@media screen and (min-width: 1000px) {
}
/* desktop-large */
@media screen and (min-width: 1401px) {
#rl-left {
width: @rlLeftWidth + 20;
}
}
/* mobile and tablet */
@media screen and (max-width: 999px) {
#rl-left {
width: 155px;
}
#rl-app {
#V-SettingsPane {
margin-right: 0;
}
#rl-settings-subscreen {
padding: 10px;
}
}
.dropdown-menu a {
line-height: 2.5;
}
}
html:not(.rl-ctrl-key-pressed) .visible-on-ctrl-btn,
html.rl-ctrl-key-pressed .hidden-on-ctrl-btn,
.show-on-panel-disabled {
@ -197,32 +131,6 @@ html.rl-left-panel-disabled {
}
}
html.rl-mobile #rl-left > .resizer,
html.rl-no-preview-pane #rl-right .resizer {
display: none !important;
}
html.rl-mobile {
#rl-left {
background: var(--main-bg-color, #aaa);
border-right: 1px solid var(--border-color, #ddd);
position: fixed;
left: 0;
top: 0;
bottom: 0;
width: 50vw;
z-index: 3;
}
&.rl-left-panel-disabled #rl-left {
display: none;
}
}
html.rl-mobile .hide-mobile,
html:not(.rl-mobile) .show-mobile {
display: none !important;
}
.e-paginator {
a {
@ -243,3 +151,94 @@ html:not(.rl-mobile) .show-mobile {
}
}
}
@media screen and (max-width: @maxMobileWidth) {
#rl-app {
#V-SettingsPane {
margin-right: 0;
}
#rl-settings-subscreen {
padding: 10px;
}
}
#rl-left {
background: var(--main-bg-color, #aaa);
border-right: 1px solid var(--border-color, #ddd);
position: fixed;
left: 0;
top: 0;
bottom: 0;
width: 155px;
z-index: 3;
}
.rl-left-panel-disabled #rl-left {
display: none;
}
.dropdown-menu a {
line-height: 2.5;
}
.hide-mobile,
#rl-left .resizer,
#rl-right .resizer {
display: none !important;
}
}
/* desktop */
@media screen and (min-width: @maxMobileWidth + 1px) {
.toggleLeft,
#V-MailMessageList .buttonCompose {
display: none !important;
}
.rl-bottom-preview-pane #rl-right {
flex-direction: column;
}
.resizer {
background: #aaa;
background: rgba(255,255,255,0.5);
display: none;
opacity: 0;
position: absolute;
z-index: 102;
}
.resizer:hover {
opacity: 1;
}
#rl-left > .resizer,
.rl-side-preview-pane #V-MailMessageList .resizer {
cursor: ew-resize;
cursor: col-resize;
right: 0;
top: 0;
bottom: 0;
width: 5px;
}
.rl-bottom-preview-pane #V-MailMessageList .resizer {
cursor: ns-resize;
cursor: row-resize;
bottom: 0;
left: 0;
right: 0;
height: 5px;
}
html:not(.rl-left-panel-disabled) #rl-left > .resizer,
#V-MailMessageList .resizer {
display: block;
}
html:not(.rl-bottom-preview-pane):not(.rl-side-preview-pane) #rl-right .resizer {
display: none !important;
}
}
/* desktop-large */
@media screen and (min-width: 1401px) {
#rl-left {
width: @rlLeftWidth + 20;
}
}

View file

@ -1,34 +1,7 @@
html.rl-mobile, html.rl-no-preview-pane {
.message-selected #V-MailMessageList {
display: none;
}
}
#V-MailMessageList {
position: relative;
}
.rl-side-preview-pane #V-MailMessageList {
height: 100%;
}
.rl-no-preview-pane #V-MailMessageList {
height: 100%;
width: 100%;
}
.rl-side-preview-pane #V-MailMessageList .messageList {
max-width: 50vw;
min-width: 320px;
overflow: auto;
resize: horizontal;
width: 35vw;
}
.rl-bottom-preview-pane #V-MailMessageList .messageList {
height: 35vh;
max-height: 50vh;
min-height: 200px;
overflow: auto;
resize: vertical;
}
#V-MailMessageList.focused .messageList {
border-color: #9d9d9d;
@ -196,18 +169,6 @@ html.rl-mobile, html.rl-no-preview-pane {
}
}
html:not(.rl-mobile) {
.hideMessageListCheckbox {
.checkboxCheckAll {
visibility: hidden;
}
.messageCheckbox {
display: none;
}
}
}
.messageListItem > div + div {
display: flex;
overflow: hidden;
@ -396,12 +357,50 @@ html:not(.rl-mobile) {
}
}
@media screen and (min-width: 1000px) {
html:not(.rl-bottom-preview-pane):not(.rl-side-preview-pane) {
.message-selected #V-MailMessageList {
display: none;
}
#V-MailMessageList {
height: 100%;
width: 100%;
}
}
@media screen and (min-width: @maxMobileWidth + 1px) {
.messageList {
.listDragOver {
transition: all 400ms ease;
}
}
.hideMessageListCheckbox {
.checkboxCheckAll {
visibility: hidden;
}
.checkboxMessage {
display: none;
}
}
.rl-side-preview-pane #V-MailMessageList {
height: 100%;
}
.rl-side-preview-pane #V-MailMessageList .messageList {
max-width: 50vw;
min-width: 320px;
overflow: auto;
resize: horizontal;
width: 35vw;
}
.rl-bottom-preview-pane #V-MailMessageList .messageList {
height: 35vh;
max-height: 50vh;
min-height: 200px;
overflow: auto;
resize: vertical;
}
}
/* desktop-large */

View file

@ -30,25 +30,11 @@
}
}
html.rl-no-preview-pane {
#rl-right:not(.message-selected) #V-MailMessageView {
display: none;
}
}
#V-MailMessageView .top-toolbar {
color: #fff;
padding: 10px 0;
}
html.rl-no-preview-pane #V-MailMessageView .top-toolbar {
padding-left: 1px;
}
html:not(.rl-no-preview-pane) #V-MailMessageView .top-toolbar {
visibility: hidden;
}
html.rl-bottom-preview-pane #V-MailMessageView .top-toolbar {
display: none;
}
.messageView {
background-color: #fff;
@ -475,13 +461,27 @@ html.rl-bottom-preview-pane #V-MailMessageView .top-toolbar {
}
}
html.rl-no-preview-pane .messageView {
border: 1px solid var(--border-color, #aaa);
box-shadow: var(--smMainShadow);
html:not(.rl-bottom-preview-pane):not(.rl-side-preview-pane) {
#rl-right:not(.message-selected) #V-MailMessageView {
display: none;
}
#V-MailMessageView .top-toolbar {
padding-left: 1px;
visibility: visible;
}
.messageView {
border: 1px solid var(--border-color, #aaa);
box-shadow: var(--smMainShadow);
}
}
html.rl-bottom-preview-pane .messageView {
height: 100%;
html.rl-bottom-preview-pane {
#V-MailMessageView .top-toolbar {
display: none;
}
.messageView {
height: 100%;
}
}
html.rl-fullscreen {

View file

@ -1,24 +1,5 @@
#V-SettingsMenu {
.b-footer {
position: absolute;
bottom: 10px;
right: 0;
left: 0;
padding: 0 10px 0 5px;
z-index: 101;
}
.b-content {
position: absolute;
top: 50px + @rlLowMargin + 10px;
bottom: @rlLowMargin;
left: 0;
right: 0;
overflow: hidden;
}
nav {
a {
@ -55,11 +36,8 @@
.btn-toolbar {
position: absolute;
top: 0;
right: 0;
left: 0;
padding: 8px 0;
color: #fff;
top: 8px;
left: @rlLowMargin;
}
td {

View file

@ -58,6 +58,7 @@
font-size: 16px;
line-height: 29px;
max-width: 25vw;
margin: 0;
overflow: hidden;
padding: 1px 8px;
text-overflow: ellipsis;

View file

@ -2,11 +2,16 @@
@rlLeftWidth: 200px;
@rlLowMargin: 8px;
/* mobile and tablet */
@maxMobileWidth: 799px;
:root {
--smDialogShrink: 20px;
--smMainShadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.rl-mobile {
/* For Firefox */
--smDialogShrink: 25px;
/* For Firefox */
@media screen and (max-width: @maxMobileWidth) {
:root {
--smDialogShrink: 25px;
}
}

View file

@ -573,7 +573,7 @@ export class MailMessageList extends AbstractViewRight {
fToggle = () => {
let layout = SettingsUserStore.layout();
setLayoutResizer(top, ClientSideKeyNameMessageListSize,
(ThemeStore.isMobile() || Layout.NoPreview === layout)
(ThemeStore.isMobile() || !layout)
? 0
: (Layout.SidePreview === layout ? 'Width' : 'Height')
);

View file

@ -1,5 +1,6 @@
import { settings } from 'Common/Links';
import { mailbox } from 'Common/Links';
import { getFolderInboxName } from 'Common/Cache';
import { AbstractViewLeft } from 'Knoin/AbstractViews';
export class SettingsMenuUserView extends AbstractViewLeft {
@ -15,4 +16,8 @@ export class SettingsMenuUserView extends AbstractViewLeft {
link(route) {
return settings(route);
}
backToInbox() {
hasher.setHash(mailbox(getFolderInboxName()));
}
}

View file

@ -1,5 +1,3 @@
import { mailbox } from 'Common/Links';
import { getFolderInboxName } from 'Common/Cache';
import { leftPanelDisabled, toggleLeftPanel } from 'Common/Globals';
import { MessageUserStore } from 'Stores/User/Message';
@ -25,8 +23,4 @@ export class SettingsPaneUserView extends AbstractViewRight {
ThemeStore.isMobile() && !event.target.closestWithin('.toggleLeft', dom) && leftPanelDisabled(true)
);
}
backToInbox() {
hasher.setHash(mailbox(getFolderInboxName()));
}
}

View file

@ -12,9 +12,7 @@ import { KeyboardShortcutsHelpPopupView } from 'View/Popup/KeyboardShortcutsHelp
import { AccountPopupView } from 'View/Popup/Account';
import { ContactsPopupView } from 'View/Popup/Contacts';
import { doc, leftPanelDisabled, fireEvent, stopEvent, SettingsCapa, registerShortcut } from 'Common/Globals';
import { ThemeStore } from 'Stores/Theme';
import { fireEvent, stopEvent, SettingsCapa, registerShortcut } from 'Common/Globals';
import Remote from 'Remote/User/Fetch';
import { getNotification } from 'Common/Translator';
@ -111,14 +109,6 @@ export class SystemDropDownUserView extends AbstractViewRight {
this.allowContacts && showScreenPopup(ContactsPopupView);
}
toggleLayout()
{
const mobile = !ThemeStore.isMobile();
doc.cookie = 'rllayout=' + (mobile ? 'mobile' : 'desktop') + '; samesite=strict';
ThemeStore.isMobile(mobile);
leftPanelDisabled(mobile);
}
logoutClick() {
rl.app.logout();
}

View file

@ -4,7 +4,6 @@ const
qUri = path => doc.location.pathname.replace(/\/+$/,'') + '/?/' + path,
eId = id => doc.getElementById('rl-'+id),
admin = '1' == eId('app')?.dataset?.admin,
layout = doc.cookie.match(/(^|;) ?rllayout=([^;]+)/) || '',
toggle = div => {
eId('loading').hidden = true;
@ -41,8 +40,6 @@ try {
let RL_APP_DATA = {};
doc.documentElement.classList.toggle('rl-mobile', 'mobile' === layout[2] || (!layout && 1000 > innerWidth));
window.rl = {
adminArea: () => admin,
@ -109,27 +106,27 @@ window.rl = {
init = Object.assign({ headers: {} }, init);
init.headers.Accept = 'application/json';
return rl.fetch(resource, init, postData).then(response => {
if (!response.ok) {
return Promise.reject('Network response error: ' + response.status);
}
/* TODO: use this for non-developers?
response.clone()
let data = response.text();
try {
return JSON.parse(data);
} catch (e) {
console.error(e);
// console.log(data);
return Promise.reject(Notifications.JsonParse);
return {
Result: false,
ErrorCode: 952, // Notifications.JsonParse
ErrorMessage: e.message,
ErrorMessageAdditional: data
if (response.ok) {
/* TODO: use this for non-developers?
response.clone()
let data = response.text();
try {
return JSON.parse(data);
} catch (e) {
console.error(e);
// console.log(data);
return Promise.reject(Notifications.JsonParse);
return {
Result: false,
ErrorCode: 952, // Notifications.JsonParse
ErrorMessage: e.message,
ErrorMessageAdditional: data
}
}
*/
return response.json();
}
*/
return response.json();
return Promise.reject('Network response error: ' + response.status);
});
}
};