prepare settings modal navigation

This commit is contained in:
marlon-erler
2026-04-08 21:03:36 +00:00
parent b94216e4c0
commit 9883160113
9 changed files with 264 additions and 138 deletions
-93
View File
@@ -1,93 +0,0 @@
import "./fileBrowser.css";
import * as React from "bloatless-react";
import { DangerousActionButton } from "./dangerousActionButton";
import { DirectoryItemList } from "./directoryItemList";
import { PATH_COMPONENT_SEPARATOR } from "../../Model/Global/storageModel";
import StorageViewModel from "../../ViewModel/Global/storageViewModel";
import { translations } from "../translations";
export function FileBrowser(storageViewModel: StorageViewModel) {
const detailView = React.createProxyState(
[storageViewModel.selectedPath],
() => {
if (storageViewModel.selectedPath.value == PATH_COMPONENT_SEPARATOR)
return (
<div>
<span class="secondary">
{translations.storage.noItemSelected}
</span>
<hr></hr>
{DangerousActionButton(
translations.storage.removeJunkButton,
"delete_forever",
storageViewModel.removeJunk,
)}
</div>
);
return (
<div class="flex-column gap">
<div class="tile flex-no">
<div>
<b>{translations.storage.path}</b>
<span
class="break-all"
subscribe:innerText={
storageViewModel.selectedPath
}
></span>
</div>
</div>
<div class="tile flex-no">
<div>
<b>{translations.storage.content}</b>
<code
subscribe:innerText={
storageViewModel.selectedFileContent
}
></code>
</div>
</div>
{DangerousActionButton(
translations.storage.deleteItem,
"delete_forever",
storageViewModel.deleteSelectedItem,
)}
</div>
);
},
);
const view = (
<div class="file-browser">
<div>
<div class="scroll-area">
{DirectoryItemList(storageViewModel)}
</div>
<div class="detail-button-wrapper">
<button class="ghost" on:click={scrollToDetails}>
<span
class="ellipsis"
subscribe:innerText={
storageViewModel.selectedFileName
}
></span>
<span class="icon">arrow_forward</span>
</button>
</div>
</div>
<div class="scroll-area" children:set={detailView}></div>
</div>
);
function scrollToDetails() {
view.scrollLeft = view.scrollWidth;
}
return view;
}
@@ -1,4 +1,4 @@
.file-browser {
.split-modal {
width: 100%;
height: 100%;
@@ -10,7 +10,7 @@
scroll-snap-type: x mandatory;
}
.file-browser > div {
.split-modal > div {
display: flex;
flex-direction: column;
@@ -21,7 +21,7 @@
scroll-snap-align: center;
}
.file-browser .scroll-area {
.split-modal .scroll-area {
flex: 1;
width: 100%;
overflow-y: auto;
@@ -29,7 +29,7 @@
padding: var(--content-padding);
}
.file-browser .detail-button-wrapper {
.split-modal .detail-button-wrapper {
display: flex;
justify-content: end;
@@ -37,11 +37,11 @@
padding: var(--content-padding);
}
.file-browser .detail-button-wrapper button {
.split-modal .detail-button-wrapper button {
max-width: 100%;
}
.file-browser code {
.split-modal code {
width: 100%;
height: 100%;
@@ -49,14 +49,14 @@
}
@media (min-width: 640px) {
.file-browser > div {
.split-modal > div {
width: 50%;
min-width: 50%;
flex: 1;
}
.file-browser .detail-button-wrapper {
.split-modal .detail-button-wrapper {
display: none;
}
}
+33
View File
@@ -0,0 +1,33 @@
import "./splitModal.css";
import * as React from "bloatless-react";
export function SplitModal(
leftView: React.State<Element>,
rightView: React.State<Element>,
scrollButtonLabel: React.State<string>,
) {
const view = (
<div class="split-modal">
<div>
<div class="scroll-area" children:set={leftView}></div>
<div class="detail-button-wrapper">
<button class="ghost" on:click={scrollToDetails}>
<span
class="ellipsis"
subscribe:innerText={scrollButtonLabel}
></span>
<span class="icon">arrow_forward</span>
</button>
</div>
</div>
<div class="scroll-area" children:set={rightView}></div>
</div>
);
function scrollToDetails() {
view.scrollLeft = view.scrollWidth;
}
return view;
}
+52 -5
View File
@@ -1,15 +1,25 @@
import * as React from "bloatless-react";
import { FileBrowser } from "../Components/fileBrowser";
import StorageViewModel from "../../ViewModel/Global/storageViewModel";
import { translations } from "../translations";
import SettingsViewModel from "../../ViewModel/Global/settingsViewModel";
import SettingsViewModel, {
SettingsModalPages,
} from "../../ViewModel/Global/settingsViewModel";
import { SplitModal } from "../Components/splitModal";
export function SettingsModal(settingsViewModel: SettingsViewModel) {
return (
<div class="modal" toggle:open={settingsViewModel.isShowingSettingsModal}>
<div
class="modal"
toggle:open={settingsViewModel.isShowingSettingsModal}
>
<div style="max-width: 64rem">
<main class="padding-0"></main>
<main class="padding-0">
{SplitModal(
new React.State(SettingsLeftPane(settingsViewModel)),
new React.State(<span>settings</span>),
new React.State(""),
)}
</main>
<button on:click={settingsViewModel.hideSettingsModal}>
{translations.general.closeButton}
<span class="icon">close</span>
@@ -18,3 +28,40 @@ export function SettingsModal(settingsViewModel: SettingsViewModel) {
</div>
);
}
function SettingsLeftPane(settingsViewModel: SettingsViewModel) {
return (
<div class="flex-column">
{SettingsPaneButton(
settingsViewModel,
SettingsModalPages.Appearance,
translations.settings.pages.appearance,
)}
{SettingsPaneButton(
settingsViewModel,
SettingsModalPages.Regional,
translations.settings.pages.regional,
)}
</div>
);
}
function SettingsPaneButton(
settingsViewModel: SettingsViewModel,
page: SettingsModalPages,
label: string,
) {
const isSelected = React.createProxyState(
[settingsViewModel.selectedModalPage],
() => settingsViewModel.selectedModalPage.value == page,
);
return (
<button
toggle:selected={isSelected}
on:click={() => settingsViewModel.showModalPage(page)}
>
{label}
</button>
);
}
+65 -2
View File
@@ -1,14 +1,77 @@
import * as React from "bloatless-react";
import { FileBrowser } from "../Components/fileBrowser";
import { PATH_COMPONENT_SEPARATOR } from "../../Model/Global/storageModel";
import StorageViewModel from "../../ViewModel/Global/storageViewModel";
import { translations } from "../translations";
import { SplitModal } from "../Components/splitModal";
import { DangerousActionButton } from "../Components/dangerousActionButton";
import { DirectoryItemList } from "../Components/directoryItemList";
export function StorageModal(storageViewModel: StorageViewModel) {
const detailView = React.createProxyState(
[storageViewModel.selectedPath],
() => {
if (storageViewModel.selectedPath.value == PATH_COMPONENT_SEPARATOR)
return (
<div>
<span class="secondary">
{translations.storage.noItemSelected}
</span>
<hr></hr>
{DangerousActionButton(
translations.storage.removeJunkButton,
"delete_forever",
storageViewModel.removeJunk,
)}
</div>
);
return (
<div class="flex-column gap">
<div class="tile flex-no">
<div>
<b>{translations.storage.path}</b>
<span
class="break-all"
subscribe:innerText={
storageViewModel.selectedPath
}
></span>
</div>
</div>
<div class="tile flex-no">
<div>
<b>{translations.storage.content}</b>
<code
subscribe:innerText={
storageViewModel.selectedFileContent
}
></code>
</div>
</div>
{DangerousActionButton(
translations.storage.deleteItem,
"delete_forever",
storageViewModel.deleteSelectedItem,
)}
</div>
);
},
);
return (
<div class="modal" toggle:open={storageViewModel.isShowingStorageModal}>
<div style="max-width: 64rem">
<main class="padding-0">{FileBrowser(storageViewModel)}</main>
<main class="padding-0">
{SplitModal(
new React.State(DirectoryItemList(storageViewModel)),
detailView,
storageViewModel.selectedFileName,
)}
</main>
<button on:click={storageViewModel.hideStorageModal}>
{translations.general.closeButton}
<span class="icon">close</span>
+20
View File
@@ -76,6 +76,13 @@ const englishTranslations = {
addChatButton: "Add chat",
},
settings: {
pages: {
appearance: "Appearance",
regional: "Language+Region",
},
},
connectionModal: {
connectionModalHeadline: "Manage Connections",
@@ -358,6 +365,13 @@ const allTranslations: { [language: string]: typeof englishTranslations } = {
addChatButton: "Chat hinzufügen",
},
settings: {
pages: {
appearance: "Erscheinungsbild",
regional: "Sprache+Region",
},
},
connectionModal: {
connectionModalHeadline: "Verbindungen verwalten",
@@ -611,6 +625,12 @@ const allTranslations: { [language: string]: typeof englishTranslations } = {
addChatButton: "Añadir chat",
},
settings: {
pages: {
appearance: "Aspecto",
regional: "Idioma+Región",
},
},
connectionModal: {
connectionModalHeadline: "Gestionar Conexiones",