Refactor MainModal and Tab components to integrate Library and Discover tabs

This commit is contained in:
alexsparkes
2025-10-29 22:56:23 +00:00
parent ac3924aaf7
commit e83afbda2c
37 changed files with 199 additions and 126 deletions

View File

@@ -1,22 +1,20 @@
import variables from 'config/variables';
import { Suspense, lazy, useState, memo, useEffect } from 'react';
import { MdClose } from 'react-icons/md';
import './scss/index.scss';
import { Tooltip } from 'components/Elements';
import ModalLoader from './components/ModalLoader';
import ModalTopBar from './components/ModalTopBar';
import { TAB_TYPES } from './constants/tabConfig';
import { updateHash, onHashChange } from 'utils/deepLinking';
const Settings = lazy(() => import('../../../features/misc/views/Settings'));
const Addons = lazy(() => import('../../../features/misc/views/Addons'));
const Marketplace = lazy(() => import('../../../features/misc/views/Marketplace'));
const Library = lazy(() => import('../../../features/misc/views/Library'));
const Discover = lazy(() => import('../../../features/misc/views/Discover'));
// Map tab types to their corresponding components
const TAB_COMPONENTS = {
[TAB_TYPES.SETTINGS]: Settings,
[TAB_TYPES.ADDONS]: Addons,
[TAB_TYPES.MARKETPLACE]: Marketplace,
[TAB_TYPES.LIBRARY]: Library,
[TAB_TYPES.DISCOVER]: Discover,
};
function MainModal({ modalClose, deepLinkData }) {
@@ -45,17 +43,13 @@ function MainModal({ modalClose, deepLinkData }) {
return (
<div className="frame">
<Tooltip
style={{ position: 'absolute', top: '1rem', right: '1rem' }}
title={variables.getMessage('modals.welcome.buttons.close')}
key="closeTooltip"
>
<span className="closeModal" onClick={modalClose}>
<MdClose />
</span>
</Tooltip>
<ModalTopBar currentTab={currentTab} onTabChange={handleChangeTab} onClose={modalClose} />
<Suspense fallback={<ModalLoader />}>
<TabComponent changeTab={handleChangeTab} deepLinkData={deepLinkData} />
<TabComponent
changeTab={handleChangeTab}
deepLinkData={deepLinkData}
currentTab={currentTab}
/>
</Suspense>
</div>
);

View File

@@ -1,6 +1,6 @@
import variables from 'config/variables';
import { memo, useState, useEffect } from 'react';
import { getIconComponent, DIVIDER_LABELS, MUE_TITLE_LABELS } from '../constants/tabConfig';
import { getIconComponent, DIVIDER_LABELS } from '../constants/tabConfig';
function Tab({ label, currentTab, onClick, navbarTab }) {
const [isExperimental, setIsExperimental] = useState(true);
@@ -15,9 +15,6 @@ function Tab({ label, currentTab, onClick, navbarTab }) {
// Determine if this label should have a divider after it
const hasDivider = DIVIDER_LABELS.some((key) => variables.getMessage(key) === label);
// Determine if this label should have "Mue" title before it
const hasMueTitle = MUE_TITLE_LABELS.some((key) => variables.getMessage(key) === label);
// Build className
const baseClass = navbarTab ? 'navbar-item' : 'tab-list-item';
const activeClass = navbarTab ? 'navbar-item-active' : 'tab-list-active';
@@ -32,7 +29,6 @@ function Tab({ label, currentTab, onClick, navbarTab }) {
return (
<>
{hasMueTitle && <span className="mainTitle">Mue</span>}
<button className={className} onClick={() => onClick(label)}>
{IconComponent && <IconComponent />} <span>{label}</span>
</button>

View File

@@ -1,11 +1,11 @@
import variables from 'config/variables';
import { useState } from 'react';
import Tab from './Tab';
import ModalNavbar from '../components/ModalNavbar';
import ReminderInfo from '../components/ReminderInfo';
import ErrorBoundary from '../../../../features/misc/modals/ErrorBoundary';
import { TAB_TYPES } from '../constants/tabConfig';
const Tabs = ({ children, changeTab, current, navbar = false }) => {
const Tabs = ({ children, navbar = false, currentTab: activeTab }) => {
const [currentTab, setCurrentTab] = useState(children[0]?.props.label);
const [currentName, setCurrentName] = useState(children[0]?.props.name);
const [showReminder, setShowReminder] = useState(localStorage.getItem('showReminder') === 'true');
@@ -24,22 +24,26 @@ const Tabs = ({ children, changeTab, current, navbar = false }) => {
setShowReminder(false);
};
// Only show sidebar for Settings tab
const showSidebar = activeTab === TAB_TYPES.SETTINGS;
return (
<div style={{ display: 'flex', width: '100%', minHeight: '100%' }}>
<div className="modalSidebar">
{children.map((tab, index) => (
<Tab
key={index}
currentTab={currentTab}
label={tab.props.label}
onClick={(nextTab) => handleTabClick(nextTab, tab.props.name)}
navbarTab={navbar}
/>
))}
<ReminderInfo isVisible={showReminder} onHide={handleHideReminder} />
</div>
{showSidebar && (
<div className="modalSidebar">
{children.map((tab, index) => (
<Tab
key={index}
currentTab={currentTab}
label={tab.props.label}
onClick={(nextTab) => handleTabClick(nextTab, tab.props.name)}
navbarTab={navbar}
/>
))}
<ReminderInfo isVisible={showReminder} onHide={handleHideReminder} />
</div>
)}
<div className="modalTabContent">
<ModalNavbar currentTab={current} onChangeTab={changeTab} />
{children.map((tab, index) => {
if (tab.props.label !== currentTab) {
return null;

View File

@@ -0,0 +1,40 @@
import variables from 'config/variables';
import { MdClose } from 'react-icons/md';
import { Tooltip, Button } from 'components/Elements';
import { NAVBAR_BUTTONS } from '../constants/tabConfig';
function ModalTopBar({ currentTab, onTabChange, onClose }) {
return (
<div className="modalTopBar">
<div className="topBarLeft">
<img
src="src/assets/icons/mue_about.png"
alt="Mue"
className="topBarLogo"
draggable={false}
/>
</div>
<div className="topBarRight">
<div className="topBarNavigation">
{NAVBAR_BUTTONS.map(({ tab, icon: Icon, messageKey }) => (
<Button
key={tab}
type="navigation"
onClick={() => onTabChange(tab)}
active={currentTab === tab}
icon={<Icon />}
label={variables.getMessage(messageKey)}
/>
))}
</div>
<Tooltip title={variables.getMessage('modals.welcome.buttons.close')} key="closeTooltip">
<span className="closeModal" onClick={onClose}>
<MdClose />
</span>
</Tooltip>
</div>
</div>
);
}
export default ModalTopBar;

View File

@@ -28,15 +28,15 @@ import {
// Tab type constants
export const TAB_TYPES = {
SETTINGS: 'settings',
ADDONS: 'addons',
MARKETPLACE: 'marketplace',
LIBRARY: 'library',
DISCOVER: 'discover',
};
// Icon component mapping - using component references instead of elements
export const ICON_COMPONENTS = {
SETTINGS: MdSettings,
ADDONS: MdWidgets,
MARKETPLACE: MdShoppingBasket,
LIBRARY: MdWidgets,
DISCOVER: MdShoppingBasket,
NAVBAR: MdMenu,
GREETING: MdEmojiPeople,
TIME: MdAccessAlarm,
@@ -64,8 +64,8 @@ export const ICON_COMPONENTS = {
export const MESSAGE_KEYS = {
OVERVIEW: 'modals.main.marketplace.product.overview',
SETTINGS: 'modals.main.navbar.settings',
ADDONS: 'modals.main.navbar.addons',
MARKETPLACE: 'modals.main.navbar.marketplace',
LIBRARY: 'modals.main.navbar.library',
DISCOVER: 'modals.main.navbar.discover',
NAVBAR: 'modals.main.settings.sections.appearance.navbar.title',
GREETING: 'modals.main.settings.sections.greeting.title',
TIME: 'modals.main.settings.sections.time.title',
@@ -98,8 +98,8 @@ export const getIconComponent = (label, variables) => {
const iconMap = {
[variables.getMessage(MESSAGE_KEYS.OVERVIEW)]: ICON_COMPONENTS.OVERVIEW,
[variables.getMessage(MESSAGE_KEYS.SETTINGS)]: ICON_COMPONENTS.SETTINGS,
[variables.getMessage(MESSAGE_KEYS.ADDONS)]: ICON_COMPONENTS.ADDONS,
[variables.getMessage(MESSAGE_KEYS.MARKETPLACE)]: ICON_COMPONENTS.MARKETPLACE,
[variables.getMessage(MESSAGE_KEYS.LIBRARY)]: ICON_COMPONENTS.LIBRARY,
[variables.getMessage(MESSAGE_KEYS.DISCOVER)]: ICON_COMPONENTS.DISCOVER,
[variables.getMessage(MESSAGE_KEYS.NAVBAR)]: ICON_COMPONENTS.NAVBAR,
[variables.getMessage(MESSAGE_KEYS.GREETING)]: ICON_COMPONENTS.GREETING,
[variables.getMessage(MESSAGE_KEYS.TIME)]: ICON_COMPONENTS.TIME,
@@ -119,7 +119,7 @@ export const getIconComponent = (label, variables) => {
[variables.getMessage(MESSAGE_KEYS.ABOUT)]: ICON_COMPONENTS.ABOUT,
[variables.getMessage(MESSAGE_KEYS.ADDED)]: ICON_COMPONENTS.ADDED,
[variables.getMessage(MESSAGE_KEYS.CREATE)]: ICON_COMPONENTS.CREATE,
[variables.getMessage(MESSAGE_KEYS.ALL_MARKETPLACE)]: ICON_COMPONENTS.ADDONS,
[variables.getMessage(MESSAGE_KEYS.ALL_MARKETPLACE)]: ICON_COMPONENTS.LIBRARY,
[variables.getMessage(MESSAGE_KEYS.PHOTO_PACKS)]: ICON_COMPONENTS.BACKGROUND,
[variables.getMessage(MESSAGE_KEYS.QUOTE_PACKS)]: ICON_COMPONENTS.QUOTE,
[variables.getMessage(MESSAGE_KEYS.PRESET_SETTINGS)]: ICON_COMPONENTS.ADVANCED,
@@ -137,14 +137,14 @@ export const NAVBAR_BUTTONS = [
messageKey: 'modals.main.navbar.settings',
},
{
tab: TAB_TYPES.ADDONS,
icon: ICON_COMPONENTS.ADDONS,
messageKey: 'modals.main.navbar.addons',
tab: TAB_TYPES.LIBRARY,
icon: ICON_COMPONENTS.LIBRARY,
messageKey: 'modals.main.navbar.library',
},
{
tab: TAB_TYPES.MARKETPLACE,
icon: ICON_COMPONENTS.MARKETPLACE,
messageKey: 'modals.main.navbar.marketplace',
tab: TAB_TYPES.DISCOVER,
icon: ICON_COMPONENTS.DISCOVER,
messageKey: 'modals.main.navbar.discover',
},
];
@@ -155,10 +155,3 @@ export const DIVIDER_LABELS = [
'modals.main.marketplace.all',
'modals.main.settings.sections.experimental.title',
];
// Labels that should have "Mue" title before them
export const MUE_TITLE_LABELS = [
'modals.main.marketplace.product.overview',
'modals.main.addons.added',
'modals.main.marketplace.all',
];

View File

@@ -1,5 +1,6 @@
@use 'sass:map';
@use 'scss/variables' as *;
@use 'modules/topBar' as *;
@use 'modules/sidebar' as *;
@use 'modules/navbar' as *;
@use 'modules/modalTabContent' as *;

View File

@@ -13,14 +13,6 @@
height: 80vh;
min-width: 250px;
.mainTitle {
text-align: center;
font-size: 35px;
display: flex;
justify-content: center;
padding: 25px;
}
svg {
margin-left: 20px;
margin-right: 20px;

View File

@@ -0,0 +1,53 @@
@use 'scss/variables' as *;
.modalTopBar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem 1.5rem;
// width: 100%;
.topBarLeft {
display: flex;
align-items: center;
flex-shrink: 0;
.topBarLogo {
height: 32px;
width: auto;
}
}
.topBarRight {
display: flex;
align-items: center;
gap: 0.75rem;
flex-shrink: 0;
.topBarNavigation {
display: flex;
flex-flow: row;
gap: 0.5rem;
}
.closeModal {
display: grid;
place-items: center;
padding: 0.4em;
border-radius: 12px;
cursor: pointer;
transition: 0.5s;
flex-shrink: 0;
svg {
font-size: 1.5em;
}
&:hover {
@include themed {
background: t($modal-sidebarActive);
}
}
}
}
}