prepare settings modal navigation
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ export default class SettingsViewModel {
|
||||
usernameInput: React.State<string> = new React.State("");
|
||||
firstDayOfWeekInput: React.State<string> = new React.State("0");
|
||||
isShowingSettingsModal: React.State<boolean> = new React.State(false);
|
||||
selectedModalPage: React.State<SettingsModalPages> = new React.State(
|
||||
SettingsModalPages.Appearance,
|
||||
);
|
||||
|
||||
// guards
|
||||
cannotSetName: React.State<boolean> = React.createProxyState(
|
||||
@@ -33,11 +36,15 @@ export default class SettingsViewModel {
|
||||
|
||||
showSettingsModal = (): void => {
|
||||
this.isShowingSettingsModal.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
hideSettingsModal = (): void => {
|
||||
this.isShowingSettingsModal.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
showModalPage = (page: SettingsModalPages): void => {
|
||||
this.selectedModalPage.value = page;
|
||||
};
|
||||
|
||||
// init
|
||||
constructor(
|
||||
@@ -54,3 +61,8 @@ export default class SettingsViewModel {
|
||||
this.firstDayOfWeekInput.subscribe(this.setFirstDayofWeek);
|
||||
}
|
||||
}
|
||||
|
||||
export enum SettingsModalPages {
|
||||
Appearance,
|
||||
Regional,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user