Skip to content

Commit

Permalink
feat: Theme option tabs (#94)
Browse files Browse the repository at this point in the history
* feat: Theme option tabs

* oops

* also forgot to remove

* fix: add theme colors to tabs

* format

* make tabs optional

* types

* types

* fix fallback tab appearing with other tabs

* remove the huge if condition

* format...again

* ts-ignore
  • Loading branch information
ricewind012 authored Aug 31, 2024
1 parent bef2bdd commit cc6e339
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 53 deletions.
149 changes: 100 additions & 49 deletions assets/src/custom_components/ThemeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import React, { useEffect, useState } from "react"
import {
Classes,
DialogBody,
DialogBodyText,
DialogButton,
DialogControlsSection,
DialogHeader,
Dropdown,
Millennium,
ModalPosition,
SidebarNavigation,
type SidebarNavigationPage,
SingleDropdownOption,
Toggle,
pluginSelf,
Expand All @@ -16,8 +17,7 @@ import { BBCodeParser } from "../components/BBCodeParser";
import { Field } from "../custom_components/Field";
import { Conditions, ConditionsStore, ICondition, ThemeItem } from "../types"
import { settingsClasses } from "../classes"
import { locale } from "../locales"
import { SettingsDialogSubHeader } from "../components/SettingsDialogSubHeader";
import { locale } from "../locales";

interface ConditionalComponent {
condition: string,
Expand Down Expand Up @@ -55,6 +55,17 @@ interface ColorProps {
defaultColor: string
}

const ThemeEditorContainer: React.FC = ({ children }) => (
<ModalPosition>
<style>
{`.DialogBody { margin-bottom: 48px; }
input.colorPicker { margin-left: 10px !important; border: unset !important; min-width: 38px; width: 38px !important; height: 38px; !important; background: transparent; padding: unset !important; }`}
</style>

{children}
</ModalPosition>
);

export class RenderThemeEditor extends React.Component {

GetConditionType = (value: any): ConditionType => {
Expand Down Expand Up @@ -171,64 +182,104 @@ export class RenderThemeEditor extends React.Component {
}

RenderColorsOpts: React.FC = () => {
const activeTheme: ThemeItem = pluginSelf.activeTheme as ThemeItem
const [themeColors, setThemeColors] = useState<ColorProps[]>()

useEffect(() => {
if (activeTheme?.data?.RootColors) {
Millennium.callServerMethod("cfg.get_color_opts")
.then((result: any) => {
console.log(JSON.parse(result) as ColorProps[])
setThemeColors(JSON.parse(result) as ColorProps[])
})
}
Millennium.callServerMethod("cfg.get_color_opts")
.then((result: any) => {
console.log(JSON.parse(result) as ColorProps[])
setThemeColors(JSON.parse(result) as ColorProps[])
})
}, [])

return themeColors && <DialogControlsSection>
<SettingsDialogSubHeader>{locale.customThemeSettingsColorsHeader}</SettingsDialogSubHeader>
<DialogBodyText className='_3fPiC9QRyT5oJ6xePCVYz8'>{locale.customThemeSettingsColorsDescription}</DialogBodyText>

{themeColors?.map((color: any, index: number) => <this.RenderColorComponent color={color} index={index}/>)}
</DialogControlsSection>
return (
<>
{themeColors?.map((color, index) => (
<this.RenderColorComponent color={color} index={index} />
))}
</>
);
}

render() {
const activeTheme: ThemeItem = pluginSelf.activeTheme as ThemeItem

const themeConditions: Conditions = activeTheme.data.Conditions
const savedConditions = pluginSelf?.conditionals?.[activeTheme.native] as ConditionsStore
const entries = Object.entries(themeConditions);

const themeHasColors = !!activeTheme.data.RootColors;
const themeHasTabs = entries.map((e) => e[1]).some((e) => !!e.tab);

const colorPage: SidebarNavigationPage = {
visible: themeHasColors,
title: locale.customThemeSettingsColors,
content: (
<DialogBody className={Classes.SettingsDialogBodyFade}>
<this.RenderColorsOpts />
</DialogBody>
),
};
const otherPages: SidebarNavigationPage[] = entries
.reduce<{ title: string; conditions: Conditions[] }[]>((vec, entry) => {
const [name, patch] = entry;
const { tab } = patch;
const condition = { [name]: patch };
const page = {
title: tab,
conditions: [condition],
};

const foundTab = vec.find((e) => e.title === tab);
if (foundTab) {
foundTab.conditions.push(condition);
return vec;
}

vec.push(page);
return vec;
}, [])
.map(({ title, conditions }) => ({
title,
content: (
<DialogBody className={Classes.SettingsDialogBodyFade}>
{Object.entries(
conditions.reduce((a, b) => Object.assign(a, b)),
).map(([key, value]) => (
<this.RenderComponent
condition={key}
store={savedConditions}
value={value}
/>
))}
</DialogBody>
),
}));
const pageWithoutTitle = {
...otherPages.find((e) => !e.title),
title: locale.customThemeSettingsConfig,
};
const tabs = themeHasTabs
? otherPages.filter((e) => e !== pageWithoutTitle)
: [pageWithoutTitle];

const className = `${settingsClasses.SettingsModal} ${settingsClasses.DesktopPopup}`;
const pages = [...tabs, colorPage];
const title = `Editing ${activeTheme?.data?.name ?? activeTheme.native}`;

return (
<div className="ModalPosition" tabIndex={0}>

<style>
{
`.DialogBody.${Classes.SettingsDialogBodyFade}:last-child { padding-bottom: 65px; }
input.colorPicker { margin-left: 10px !important; border: unset !important; min-width: 38px; width: 38px !important; height: 38px; !important; background: transparent; padding: unset !important; }`
}
</style>

<div className="ModalPosition_Content" style={{width: "100vw", height: "100vh"}}>
<div className={`${Classes.PagedSettingsDialog} ${Classes.SettingsModal} ${Classes.DesktopPopup} Panel`}>
<div className="DialogContentTransition Panel" style={{minWidth: "100vw"}}>
<div className={`DialogContent _DialogLayout ${Classes.PagedSettingsDialog_PageContent} `}>
<div className="DialogContent_InnerWidth">
<DialogHeader>Editing {activeTheme?.data?.name ?? activeTheme.native}</DialogHeader>
<DialogBody className={Classes.SettingsDialogBodyFade}>
{themeConditions && <DialogControlsSection>
<SettingsDialogSubHeader>{locale.customThemeSettingsConfigHeader}</SettingsDialogSubHeader>
<DialogBodyText className='_3fPiC9QRyT5oJ6xePCVYz8'>{locale.customThemeSettingsConfigDescription}</DialogBodyText>

{Object.entries(themeConditions).map(([key, value]) => <this.RenderComponent condition={key} store={savedConditions} value={value}/>)}
</DialogControlsSection>}
<this.RenderColorsOpts/>
</DialogBody>
</div>
</div>
</div>
</div>
</div>
</div>
)
<ThemeEditorContainer>
{!themeHasTabs && !themeHasColors && (
<style>{`
.PageListColumn {
display: none !important;
}
`}</style>
)}

{/* @ts-ignore: className hasn't been added to DFL yet */}
<SidebarNavigation className={className} pages={pages} title={title} />
</ThemeEditorContainer>
);
}
}
6 changes: 2 additions & 4 deletions assets/src/locales/locales/english.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
"optionReloadLater": "Reload Later",
"updatePanelUpdateNotifications": "Push Notifications",
"updatePanelUpdateNotificationsTooltip": "Get Millennium to give you a reminder when a item in your library has an update!",
"customThemeSettingsColorsHeader": "Color Options",
"customThemeSettingsColorsDescription": "Customize the colors of your theme.",
"customThemeSettingsConfigHeader": "Custom Settings",
"customThemeSettingsConfigDescription": "Customize the settings of your theme."
"customThemeSettingsColors": "Colors",
"customThemeSettingsConfig": "Custom Settings"
}
7 changes: 7 additions & 0 deletions assets/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ export interface Conditions {
export interface ICondition {
default?: string;
description?: string;
/**
* Tab to put the condition in.
*
* If no condition uses them, there will be no tabs.
* If some conditions include it, those who don't will be put in a separate tab.
*/
tab?: string;
values: {
[condition: string]: ConditionalControlFlow
};
Expand Down

0 comments on commit cc6e339

Please sign in to comment.