improve UI of split modals

This commit is contained in:
marlon-erler
2026-04-10 19:00:59 +00:00
parent 3c7b08ad6f
commit 44bacbd59f
9 changed files with 161 additions and 38 deletions
+30 -4
View File
@@ -864,19 +864,34 @@ body[showing-chat] #chat-page-wrapper {
scroll-snap-type: x mandatory;
}
.split-modal > div {
display: flex;
flex-direction: column;
width: 100%;
min-width: calc(100% - 4rem);
flex: 0;
scroll-snap-align: center;
}
.split-modal > div,
.split-modal > div > .scroll-area {
width: 100%;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: safe center;
}
.split-modal[navigation] > div > .scroll-area {
justify-content: start;
}
.split-modal[navigation] > div {
min-width: 100%;
}
.split-modal .scroll-area {
flex: 1;
width: 100%;
overflow-y: auto;
padding: var(--content-padding);
}
.split-modal[navigation] > div:nth-child(2) > .flex-row > button {
transform: translateX(calc(-1 * var(--content-padding)));
}
.split-modal .detail-button-wrapper {
display: flex;
justify-content: end;
@@ -896,11 +911,14 @@ body[showing-chat] #chat-page-wrapper {
flex: 1;
min-width: unset;
}
.split-modal:not([primitive]) > div:first-child {
.split-modal[navigation] > div {
min-width: unset;
}
.split-modal[navigation] > div:first-child {
flex: 0;
min-width: 20rem;
}
.split-modal[primitive] > div {
.split-modal:not([navigation]) > div {
width: 50%;
min-width: 50%;
}
@@ -908,3 +926,11 @@ body[showing-chat] #chat-page-wrapper {
display: none;
}
}
@media (max-width: 640px) {
.split-modal[navigation]:not([page-open]) > div:nth-child(2) {
display: none;
}
.split-modal[navigation][page-open] > div:first-child {
display: none;
}
}
+41 -14
View File
@@ -5025,7 +5025,8 @@
fileVersionLabel: "Version",
searchLabel: "Search",
waitingLabel: "Waiting...",
restoreConnection: "Restore connection"
restoreConnection: "Restore connection",
noPageSelected: "No page selected"
},
regional: {
weekdays: {
@@ -5237,7 +5238,8 @@
fileVersionLabel: "Version",
searchLabel: "Suche",
waitingLabel: "Warten...",
restoreConnection: "Verbindung wiederherstellen"
restoreConnection: "Verbindung wiederherstellen",
noPageSelected: "Keine Seite ausgew\xE4hlt"
},
regional: {
weekdays: {
@@ -5430,7 +5432,8 @@
fileVersionLabel: "Versi\xF3n",
searchLabel: "Buscar",
waitingLabel: "Esperando...",
restoreConnection: "Conectar de nuevo"
restoreConnection: "Conectar de nuevo",
noPageSelected: "No p\xE1gina seleccionada"
},
regional: {
weekdays: {
@@ -6350,9 +6353,7 @@
this.username = new State("");
this.usernameInput = new State("");
this.isShowingSettingsModal = new State(false);
this.selectedModalPage = new State(
0 /* Appearance */
);
this.selectedModalPage = new State(void 0);
this.requiresReload = new State(false);
this.firstDayOfWeek = new State("0");
this.language = new State(
@@ -6428,21 +6429,40 @@
};
// src/View/Components/splitModal.tsx
function SplitModal(leftView, rightView, extendedStyle = false, navigationState) {
function SplitModal(coreViewModel2, leftView, rightView, extendedStyle = false, navigationState) {
function closePage() {
navigationState.value = void 0;
}
const hasPageOpen = new State(false);
navigationState?.subscribe(
(newValue) => hasPageOpen.value = newValue != void 0
);
const view = /* @__PURE__ */ createElement(
"div",
{
class: "split-modal",
"toggle:extended": extendedStyle,
"toggle:primitive": navigationState == void 0
"toggle:navigation": navigationState != void 0,
"toggle:page-open": hasPageOpen
},
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { class: "scroll-area", "children:set": leftView })),
/* @__PURE__ */ createElement("div", { class: "scroll-area", "children:set": rightView })
/* @__PURE__ */ createElement("div", { class: "scroll-area slide-up" }, /* @__PURE__ */ createElement(
"div",
{
class: "flex-row width-100 mobile-only",
"toggle:hidden": navigationState == void 0
},
/* @__PURE__ */ createElement(
"button",
{
class: "ghost square",
"aria-label": coreViewModel2.translations.general.backButton,
"on:click": closePage
},
/* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_back")
)
), /* @__PURE__ */ createElement("div", { "children:set": rightView }))
);
function scrollToDetails() {
view.scrollLeft = view.scrollWidth;
}
navigationState?.subscribe(scrollToDetails);
return view;
}
@@ -6527,6 +6547,7 @@
}
);
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": storageViewModel2.isShowingStorageModal }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", { class: "padding-0" }, SplitModal(
coreViewModel2,
new State(DirectoryItemList(storageViewModel2)),
detailView,
true
@@ -6819,6 +6840,11 @@
[settingsViewModel2.selectedModalPage],
() => {
switch (settingsViewModel2.selectedModalPage.value) {
case void 0: {
return PlaceholderView(
coreViewModel2.translations.general.noPageSelected
);
}
case 0 /* Appearance */:
return SettingsAppearancePane(
coreViewModel2,
@@ -6841,6 +6867,7 @@
"toggle:open": settingsViewModel2.isShowingSettingsModal
},
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", { class: "padding-0" }, SplitModal(
coreViewModel2,
new State(
SettingsLeftPane(coreViewModel2, settingsViewModel2)
),
@@ -6851,7 +6878,7 @@
);
}
function SettingsLeftPane(coreViewModel2, settingsViewModel2) {
return /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, SettingsPaneButton(
return /* @__PURE__ */ createElement("div", { class: "flex-column gap slide-up" }, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.homePage.settingsButton), SettingsPaneButton(
settingsViewModel2,
0 /* Appearance */,
coreViewModel2.translations.settings.pages.appearance
+1 -1
View File
@@ -1128,7 +1128,7 @@ main {
@media (min-width: 640px) {
/* basic */
.mobile-only {
display: none;
display: none !important;
}
/* border, outline, etc */
+39 -5
View File
@@ -15,9 +15,6 @@
}
.split-modal > div {
display: flex;
flex-direction: column;
width: 100%;
min-width: calc(100% - 4rem);
flex: 0;
@@ -25,6 +22,25 @@
scroll-snap-align: center;
}
.split-modal > div,
.split-modal > div > .scroll-area {
width: 100%;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: safe center;
}
.split-modal[navigation] > div > .scroll-area{
justify-content: start;
}
.split-modal[navigation] > div {
min-width: 100%;
}
.split-modal .scroll-area {
flex: 1;
width: 100%;
@@ -33,6 +49,10 @@
padding: var(--content-padding);
}
.split-modal[navigation] > div:nth-child(2) > .flex-row > button {
transform: translateX(calc(-1 * var(--content-padding)));
}
.split-modal .detail-button-wrapper {
display: flex;
justify-content: end;
@@ -58,12 +78,16 @@
min-width: unset;
}
.split-modal:not([primitive]) > div:first-child {
.split-modal[navigation] > div {
min-width: unset;
}
.split-modal[navigation] > div:first-child {
flex: 0;
min-width: 20rem;
}
.split-modal[primitive] > div {
.split-modal:not([navigation]) > div {
width: 50%;
min-width: 50%;
}
@@ -72,3 +96,13 @@
display: none;
}
}
@media (max-width: 640px) {
.split-modal[navigation]:not([page-open]) > div:nth-child(2) {
display: none;
}
.split-modal[navigation][page-open] > div:first-child {
display: none;
}
}
+32 -10
View File
@@ -1,31 +1,53 @@
import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import "./splitModal.css";
import * as React from "bloatless-react";
export function SplitModal(
export function SplitModal<T>(
coreViewModel: CoreViewModel,
leftView: React.State<Element>,
rightView: React.State<Element>,
extendedStyle: boolean = false,
navigationState?: React.State<any>,
navigationState?: React.State<T | undefined>,
) {
function closePage() {
navigationState.value = undefined;
}
const hasPageOpen = new React.State<boolean>(false);
navigationState?.subscribe(
(newValue) => (hasPageOpen.value = newValue != undefined),
);
const view = (
<div
class="split-modal"
toggle:extended={extendedStyle}
toggle:primitive={navigationState == undefined}
toggle:navigation={navigationState != undefined}
toggle:page-open={hasPageOpen}
>
<div>
<div class="scroll-area" children:set={leftView}></div>
</div>
<div class="scroll-area" children:set={rightView}></div>
<div class="scroll-area slide-up">
<div
class="flex-row width-100 mobile-only"
toggle:hidden={navigationState == undefined}
>
<button
class="ghost square"
aria-label={
coreViewModel.translations.general.backButton
}
on:click={closePage}
>
<span class="icon">arrow_back</span>
</button>
</div>
<div children:set={rightView}></div>
</div>
</div>
);
function scrollToDetails() {
view.scrollLeft = view.scrollWidth;
}
navigationState?.subscribe(scrollToDetails);
return view;
}
+9 -1
View File
@@ -11,6 +11,7 @@ import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { Languages, ThemeSettings } from "../../Model/Global/settingsModel";
import { OptionButtonList } from "../Components/optionButtonList";
import { stringify } from "../../Model/Utility/utility";
import { PlaceholderView } from "../Components/placeholderView";
export function SettingsModal(
coreViewModel: CoreViewModel,
@@ -20,6 +21,11 @@ export function SettingsModal(
[settingsViewModel.selectedModalPage],
() => {
switch (settingsViewModel.selectedModalPage.value) {
case undefined: {
return PlaceholderView(
coreViewModel.translations.general.noPageSelected,
);
}
case SettingsModalPages.Appearance:
return SettingsAppearancePane(
coreViewModel,
@@ -44,6 +50,7 @@ export function SettingsModal(
<div>
<main class="padding-0">
{SplitModal(
coreViewModel,
new React.State(
SettingsLeftPane(coreViewModel, settingsViewModel),
),
@@ -66,7 +73,8 @@ function SettingsLeftPane(
settingsViewModel: SettingsViewModel,
) {
return (
<div class="flex-column gap">
<div class="flex-column gap slide-up">
<h2>{coreViewModel.translations.homePage.settingsButton}</h2>
{SettingsPaneButton(
settingsViewModel,
SettingsModalPages.Appearance,
+1
View File
@@ -72,6 +72,7 @@ export function StorageModal(
<div>
<main class="padding-0">
{SplitModal(
coreViewModel,
new React.State(DirectoryItemList(storageViewModel)),
detailView,
true,
+6
View File
@@ -24,6 +24,8 @@ const englishTranslations = {
waitingLabel: "Waiting...",
restoreConnection: "Restore connection",
noPageSelected: "No page selected",
},
regional: {
@@ -328,6 +330,8 @@ export const allTranslations: {
waitingLabel: "Warten...",
restoreConnection: "Verbindung wiederherstellen",
noPageSelected: "Keine Seite ausgewählt",
},
regional: {
@@ -597,6 +601,8 @@ export const allTranslations: {
waitingLabel: "Esperando...",
restoreConnection: "Conectar de nuevo",
noPageSelected: "No página seleccionada",
},
regional: {
+2 -3
View File
@@ -8,9 +8,8 @@ export default class SettingsViewModel {
username: React.State<string> = new React.State("");
usernameInput: React.State<string> = new React.State("");
isShowingSettingsModal: React.State<boolean> = new React.State(false);
selectedModalPage: React.State<SettingsModalPages> = new React.State(
SettingsModalPages.Appearance,
);
selectedModalPage: React.State<SettingsModalPages | undefined> =
new React.State(undefined);
requiresReload = new React.State<boolean>(false);
firstDayOfWeek: React.State<string> = new React.State("0");