mirror of
https://github.com/mue/mue.git
synced 2026-07-13 12:07:45 +02:00
refactor: Convert pure components to functional components
Co-authored-by: David Ralph <me@davidcralph.co.uk> Co-authored-by: Isaac <contact@eartharoid.me>
This commit is contained in:
@@ -1,31 +1,16 @@
|
||||
import variables from 'config/variables';
|
||||
import { Suspense, lazy, useState, memo } from 'react';
|
||||
import {
|
||||
MdSettings,
|
||||
MdOutlineShoppingBasket,
|
||||
MdOutlineExtension,
|
||||
MdClose,
|
||||
MdSearch,
|
||||
MdOutlineKeyboardArrowRight,
|
||||
} from 'react-icons/md';
|
||||
import { memo, Suspense, lazy } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Button } from 'components/Elements';
|
||||
|
||||
import './scss/index.scss';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
/*import Settings from '../../../features/misc/views/Settings';
|
||||
import Addons from '../../../features/misc/views/Addons';
|
||||
import Marketplace from '../../../features/misc/views/Marketplace';*/
|
||||
import Navbar from './backend/TabNavbar';
|
||||
import { TabProvider, useTab } from './backend/TabContext';
|
||||
|
||||
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 renderLoader = () => (
|
||||
<div style={{ display: 'flex', width: '100%', minHeight: '100%' }}>
|
||||
<div className="modalSidebar">
|
||||
<span className="mainTitle">Mue</span>
|
||||
</div>
|
||||
<div className="flex w-full min-h-full">
|
||||
<div className="modalTabContent">
|
||||
<div className="emptyItems">
|
||||
<div className="emptyMessage">
|
||||
@@ -39,134 +24,13 @@ const renderLoader = () => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const navbarLogo = (
|
||||
<svg
|
||||
width="123"
|
||||
height="123"
|
||||
viewBox="0 0 123 123"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="w-[40px] h-[40px]"
|
||||
>
|
||||
<g filter="url(#filter0_d_2473_27)">
|
||||
<circle cx="61.5" cy="61.5" r="50.5" fill="url(#paint0_linear_2473_27)" />
|
||||
<path
|
||||
d="M68.2969 43.1796V48.5603H79.9348V60.1055H85.3638V43.1796H68.2969Z"
|
||||
fill="url(#paint1_linear_2473_27)"
|
||||
/>
|
||||
<path
|
||||
d="M72.1542 61.0483H67.344V65.8527H62.9056V61.0483H58.0919V56.6185H62.9056V51.8175H67.344V56.6185H72.1542V61.0483ZM78.6447 49.6043H67.034V45.5766H47.5625V72.0938H82.6836V61.1961H78.6447V49.6043Z"
|
||||
fill="url(#paint2_linear_2473_27)"
|
||||
/>
|
||||
<path
|
||||
d="M46.358 50.518H42.6289V77.0352H77.75V73.3029H46.358V50.518Z"
|
||||
fill="url(#paint3_linear_2473_27)"
|
||||
/>
|
||||
<path
|
||||
d="M41.4205 55.4516H37.6914V81.9688H72.8125V78.2365H41.4205V55.4516Z"
|
||||
fill="url(#paint4_linear_2473_27)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_d_2473_27"
|
||||
x="0.3"
|
||||
y="0.3"
|
||||
width="122.4"
|
||||
height="122.4"
|
||||
filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB"
|
||||
>
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<feColorMatrix
|
||||
in="SourceAlpha"
|
||||
type="matrix"
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||
result="hardAlpha"
|
||||
/>
|
||||
<feOffset />
|
||||
<feGaussianBlur stdDeviation="5.35" />
|
||||
<feComposite in2="hardAlpha" operator="out" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_2473_27" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_2473_27" result="shape" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
id="paint0_linear_2473_27"
|
||||
x1="104.324"
|
||||
y1="35.24"
|
||||
x2="16.959"
|
||||
y2="88.366"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#FF5C25" />
|
||||
<stop offset="0.484375" stop-color="#D21A11" />
|
||||
<stop offset="1" stop-color="#FF456E" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_2473_27"
|
||||
x1="76.8303"
|
||||
y1="60.1055"
|
||||
x2="76.8303"
|
||||
y2="43.1796"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#F18D91" />
|
||||
<stop offset="1" stop-color="#FBD3C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_2473_27"
|
||||
x1="65.123"
|
||||
y1="72.0938"
|
||||
x2="65.123"
|
||||
y2="45.5766"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#F18D91" />
|
||||
<stop offset="1" stop-color="#FBD3C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_2473_27"
|
||||
x1="60.1895"
|
||||
y1="77.0352"
|
||||
x2="60.1895"
|
||||
y2="50.518"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#F18D91" />
|
||||
<stop offset="1" stop-color="#FBD3C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint4_linear_2473_27"
|
||||
x1="55.252"
|
||||
y1="81.9688"
|
||||
x2="55.252"
|
||||
y2="55.4516"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#F18D91" />
|
||||
<stop offset="1" stop-color="#FBD3C6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
|
||||
let tabs = [
|
||||
{ id: 'settings', label: 'Settings', icon: <MdSettings /> },
|
||||
{ id: 'addons', label: 'Addons', icon: <MdOutlineExtension /> },
|
||||
{ id: 'marketplace', label: 'Marketplace', icon: <MdOutlineShoppingBasket /> },
|
||||
];
|
||||
|
||||
function MainModal({ modalClose }) {
|
||||
let [activeTab, setActiveTab] = useState(tabs[0].id);
|
||||
let [subTab, setSubTab] = useState('');
|
||||
let [direction, setDirection] = useState(1);
|
||||
const MainModalContent = ({ modalClose }) => {
|
||||
const { activeTab, direction } = useTab();
|
||||
|
||||
const variants = {
|
||||
enter: (direction) => ({
|
||||
x: direction > 0 ? '100%' : '-100%',
|
||||
opacity: 0,
|
||||
overflow: 'none',
|
||||
top: '80px',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
@@ -174,7 +38,6 @@ function MainModal({ modalClose }) {
|
||||
center: {
|
||||
x: 0,
|
||||
opacity: 1,
|
||||
overflow: 'none',
|
||||
top: '80px',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
@@ -182,127 +45,49 @@ function MainModal({ modalClose }) {
|
||||
exit: (direction) => ({
|
||||
x: direction < 0 ? '100%' : '-100%',
|
||||
opacity: 0,
|
||||
overflow: 'none',
|
||||
top: '80px',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
}),
|
||||
};
|
||||
|
||||
const changeTab = (type) => {
|
||||
const currentIndex = tabs.findIndex((tab) => tab.id === activeTab);
|
||||
const newIndex = tabs.findIndex((tab) => tab.id === type);
|
||||
|
||||
setDirection(newIndex > currentIndex ? 1 : -1);
|
||||
setActiveTab(type);
|
||||
setSubTab('');
|
||||
};
|
||||
|
||||
const renderTab = () => {
|
||||
switch (activeTab) {
|
||||
case 'addons':
|
||||
return <Addons modalClose={modalClose} />;
|
||||
return <Addons />;
|
||||
case 'marketplace':
|
||||
return <Marketplace modalClose={modalClose} />;
|
||||
return <Marketplace />;
|
||||
default:
|
||||
return <Settings setSubTab={setSubTab} modalClose={modalClose} />;
|
||||
return <Settings />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col w-[100%] min-w-[100%]">
|
||||
<div className="flex flex-row gap-5 p-5 items-center justify-between">
|
||||
<div className="flex flex-row gap-5 items-center">
|
||||
{navbarLogo}
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<span className="text-xl capitalize tracking-normal">{activeTab}</span>
|
||||
{subTab !== '' && (
|
||||
<>
|
||||
<MdOutlineKeyboardArrowRight />
|
||||
<span className="text-xl capitalize tracking-normal">{subTab}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-5">
|
||||
<AnimatePresence>
|
||||
{activeTab === 'marketplace' && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: '-100%' }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: '-100%' }}
|
||||
>
|
||||
<form className="max-w-md mx-auto relative mr-10">
|
||||
<input
|
||||
label={variables.getMessage('widgets.search')}
|
||||
placeholder={variables.getMessage('widgets.search')}
|
||||
name="filter"
|
||||
id="filter"
|
||||
//value={this.state.filter}
|
||||
//onChange={(event) => this.setState({ filter: event.target.value })}
|
||||
className="h-[40px] block w-full px-4 ps-10 text-sm text-gray-900 border border-[#484848] rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-white/5 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-neutral-100"
|
||||
/>
|
||||
<div className="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
|
||||
<MdSearch />
|
||||
</div>
|
||||
</form>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<div className="flex space-x-1">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => changeTab(tab.id)}
|
||||
className={`${
|
||||
activeTab === tab.id ? '' : 'hover:text-white/60'
|
||||
} flex flex-row gap-2 items-center relative rounded-sm px-3 py-1.5 text-sm text-white outline-sky-400 transition focus-visible:outline-2`}
|
||||
style={{
|
||||
WebkitTapHighlightColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
{activeTab === tab.id && (
|
||||
<motion.span
|
||||
layoutId="bubble"
|
||||
className="absolute inset-0 z-10 bg-[#333] mix-blend-lighten rounded-xl"
|
||||
transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }}
|
||||
/>
|
||||
)}
|
||||
{tab.icon}
|
||||
{variables.getMessage(`modals.main.navbar.${tab.id}`)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<Tooltip
|
||||
style={{ marginLeft: 'auto', justifySelf: 'flex-end' }}
|
||||
title={variables.getMessage('modals.welcome.buttons.close')}
|
||||
key="closeTooltip"
|
||||
>
|
||||
<span className="closeModal" onClick={modalClose}>
|
||||
<MdClose />
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<AnimatePresence initial={false} custom={direction}>
|
||||
<motion.div
|
||||
key={activeTab}
|
||||
custom={direction}
|
||||
variants={variants}
|
||||
initial="enter"
|
||||
animate="center"
|
||||
exit="exit"
|
||||
transition={{ type: 'tween', duration: 0.8 }}
|
||||
className="flex w-[100%] min-w-[100%]"
|
||||
>
|
||||
<Suspense fallback={renderLoader()}>{renderTab()}</Suspense>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</>
|
||||
<div className="flex flex-col w-full min-w-full">
|
||||
<Navbar modalClose={modalClose} />
|
||||
<AnimatePresence initial={false} custom={direction}>
|
||||
<motion.div
|
||||
key={activeTab}
|
||||
custom={direction}
|
||||
variants={variants}
|
||||
initial="enter"
|
||||
animate="center"
|
||||
exit="exit"
|
||||
transition={{ type: 'tween', duration: 0.8 }}
|
||||
className="flex w-full min-w-full"
|
||||
>
|
||||
<Suspense fallback={renderLoader()}>{renderTab()}</Suspense>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const MainModal = ({ modalClose }) => (
|
||||
<TabProvider>
|
||||
<MainModalContent modalClose={modalClose} />
|
||||
</TabProvider>
|
||||
);
|
||||
|
||||
const MemoizedMainModal = memo(MainModal);
|
||||
export { MemoizedMainModal as default, MemoizedMainModal as MainModal };
|
||||
|
||||
39
src/components/Elements/MainModal/backend/TabContext.jsx
Normal file
39
src/components/Elements/MainModal/backend/TabContext.jsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { createContext, useContext, useState } from 'react';
|
||||
|
||||
const TabContext = createContext();
|
||||
|
||||
export const useTab = () => {
|
||||
return useContext(TabContext);
|
||||
};
|
||||
|
||||
export const TabProvider = ({ children }) => {
|
||||
const [activeTab, setActiveTab] = useState('settings');
|
||||
const [subTab, setSubTab] = useState('Overview');
|
||||
const [subSection, setSubSection] = useState('');
|
||||
const [direction, setDirection] = useState(1);
|
||||
|
||||
const changeTab = (type) => {
|
||||
const tabs = [
|
||||
{ id: 'settings', label: 'Settings' },
|
||||
{ id: 'addons', label: 'Addons' },
|
||||
{ id: 'marketplace', label: 'Marketplace' },
|
||||
];
|
||||
|
||||
const currentIndex = tabs.findIndex((tab) => tab.id === activeTab);
|
||||
const newIndex = tabs.findIndex((tab) => tab.id === type);
|
||||
|
||||
setDirection(newIndex > currentIndex ? 1 : -1);
|
||||
setActiveTab(type);
|
||||
setSubTab('');
|
||||
};
|
||||
|
||||
const setSection = (type) => {
|
||||
setSubTab(type);
|
||||
}
|
||||
|
||||
return (
|
||||
<TabContext.Provider value={{ activeTab, subTab, direction, subSection, changeTab, setSubTab, setSection, setSubSection }}>
|
||||
{children}
|
||||
</TabContext.Provider>
|
||||
);
|
||||
};
|
||||
233
src/components/Elements/MainModal/backend/TabNavbar.jsx
Normal file
233
src/components/Elements/MainModal/backend/TabNavbar.jsx
Normal file
@@ -0,0 +1,233 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
MdSettings,
|
||||
MdOutlineShoppingBasket,
|
||||
MdOutlineExtension,
|
||||
MdOutlineKeyboardArrowRight,
|
||||
MdClose,
|
||||
MdSearch,
|
||||
} from 'react-icons/md';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useTab } from './TabContext';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
import variables from 'config/variables';
|
||||
import clsx from 'clsx';
|
||||
|
||||
const TabNavbar = ({ modalClose }) => {
|
||||
const { activeTab, subTab, changeTab, subSection, setSubTab, setSubSection } = useTab();
|
||||
|
||||
const tabs = [
|
||||
{ id: 'settings', label: 'Settings', icon: <MdSettings /> },
|
||||
{ id: 'addons', label: 'Addons', icon: <MdOutlineExtension /> },
|
||||
{ id: 'marketplace', label: 'Marketplace', icon: <MdOutlineShoppingBasket /> },
|
||||
];
|
||||
|
||||
const navbarLogo = (
|
||||
<svg
|
||||
width="123"
|
||||
height="123"
|
||||
viewBox="0 0 123 123"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="w-[40px] h-[40px]"
|
||||
>
|
||||
<g filter="url(#filter0_d_2473_27)">
|
||||
<circle cx="61.5" cy="61.5" r="50.5" fill="url(#paint0_linear_2473_27)" />
|
||||
<path
|
||||
d="M68.2969 43.1796V48.5603H79.9348V60.1055H85.3638V43.1796H68.2969Z"
|
||||
fill="url(#paint1_linear_2473_27)"
|
||||
/>
|
||||
<path
|
||||
d="M72.1542 61.0483H67.344V65.8527H62.9056V61.0483H58.0919V56.6185H62.9056V51.8175H67.344V56.6185H72.1542V61.0483ZM78.6447 49.6043H67.034V45.5766H47.5625V72.0938H82.6836V61.1961H78.6447V49.6043Z"
|
||||
fill="url(#paint2_linear_2473_27)"
|
||||
/>
|
||||
<path
|
||||
d="M46.358 50.518H42.6289V77.0352H77.75V73.3029H46.358V50.518Z"
|
||||
fill="url(#paint3_linear_2473_27)"
|
||||
/>
|
||||
<path
|
||||
d="M41.4205 55.4516H37.6914V81.9688H72.8125V78.2365H41.4205V55.4516Z"
|
||||
fill="url(#paint4_linear_2473_27)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_d_2473_27"
|
||||
x="0.3"
|
||||
y="0.3"
|
||||
width="122.4"
|
||||
height="122.4"
|
||||
filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB"
|
||||
>
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<feColorMatrix
|
||||
in="SourceAlpha"
|
||||
type="matrix"
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||
result="hardAlpha"
|
||||
/>
|
||||
<feOffset />
|
||||
<feGaussianBlur stdDeviation="5.35" />
|
||||
<feComposite in2="hardAlpha" operator="out" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_2473_27" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="effect1_dropShadow_2473_27"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
<linearGradient
|
||||
id="paint0_linear_2473_27"
|
||||
x1="104.324"
|
||||
y1="35.24"
|
||||
x2="16.959"
|
||||
y2="88.366"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#FF5C25" />
|
||||
<stop offset="0.484375" stop-color="#D21A11" />
|
||||
<stop offset="1" stop-color="#FF456E" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_2473_27"
|
||||
x1="76.8303"
|
||||
y1="60.1055"
|
||||
x2="76.8303"
|
||||
y2="43.1796"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#F18D91" />
|
||||
<stop offset="1" stop-color="#FBD3C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_2473_27"
|
||||
x1="65.123"
|
||||
y1="72.0938"
|
||||
x2="65.123"
|
||||
y2="45.5766"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#F18D91" />
|
||||
<stop offset="1" stop-color="#FBD3C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_2473_27"
|
||||
x1="60.1895"
|
||||
y1="77.0352"
|
||||
x2="60.1895"
|
||||
y2="50.518"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#F18D91" />
|
||||
<stop offset="1" stop-color="#FBD3C6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint4_linear_2473_27"
|
||||
x1="55.252"
|
||||
y1="81.9688"
|
||||
x2="55.252"
|
||||
y2="55.4516"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#F18D91" />
|
||||
<stop offset="1" stop-color="#FBD3C6" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row gap-5 p-5 items-center justify-between">
|
||||
<div className="flex flex-row gap-5 items-center">
|
||||
{navbarLogo}
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<span className="text-xl capitalize tracking-normal">{activeTab}</span>
|
||||
{subTab !== '' && (
|
||||
<>
|
||||
<MdOutlineKeyboardArrowRight />
|
||||
<span onClick={() => setSubSection('')} className={clsx("text-xl capitalize tracking-normal", { 'text-neutral-300 cursor-pointer': subSection !== '' })}>{subTab}</span>
|
||||
</>
|
||||
)}
|
||||
{subSection !== '' && (
|
||||
<>
|
||||
<MdOutlineKeyboardArrowRight />
|
||||
<span className="text-xl capitalize tracking-normal">{subSection}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-5">
|
||||
<AnimatePresence>
|
||||
{activeTab === 'marketplace' && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: '-100%' }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: '-100%' }}
|
||||
>
|
||||
<form className="max-w-md mx-auto relative mr-10">
|
||||
<input
|
||||
label={variables.getMessage('widgets.search')}
|
||||
placeholder={variables.getMessage('widgets.search')}
|
||||
name="filter"
|
||||
id="filter"
|
||||
className="h-[40px] block w-full px-4 ps-10 text-sm text-gray-900 border border-[#484848] rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-white/5 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-neutral-100"
|
||||
/>
|
||||
<div className="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
|
||||
<MdSearch />
|
||||
</div>
|
||||
</form>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<div className="flex space-x-1">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => changeTab(tab.id)}
|
||||
className={`${
|
||||
activeTab === tab.id ? '' : 'hover:text-white/60'
|
||||
} flex flex-row gap-2 items-center relative rounded-sm px-3 py-1.5 text-sm text-white outline-sky-400 transition focus-visible:outline-2`}
|
||||
style={{
|
||||
WebkitTapHighlightColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
{activeTab === tab.id && (
|
||||
<motion.span
|
||||
layoutId="bubble"
|
||||
className="absolute inset-0 z-10 bg-[#333] mix-blend-lighten rounded-xl"
|
||||
transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }}
|
||||
/>
|
||||
)}
|
||||
{tab.icon}
|
||||
{variables.getMessage(`modals.main.navbar.${tab.id}`)}
|
||||
{tab.id === 'addons' && (
|
||||
<span className="px-3 py-1 bg-[#424242] rounded-lg text-xs">
|
||||
{JSON.parse(localStorage.getItem('installed')).length}
|
||||
</span>
|
||||
)}
|
||||
{tab.id === 'marketplace' && (
|
||||
<span className="px-3 py-1 bg-rose-800 rounded-lg text-xs border border-rose-700">
|
||||
NEW
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<Tooltip
|
||||
style={{ marginLeft: 'auto', justifySelf: 'flex-end' }}
|
||||
title={variables.getMessage('modals.welcome.buttons.close')}
|
||||
key="closeTooltip"
|
||||
>
|
||||
<span className="closeModal" onClick={modalClose}>
|
||||
<MdClose />
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { TabNavbar as default, TabNavbar };
|
||||
@@ -0,0 +1,62 @@
|
||||
import { useState, useCallback, memo } from 'react';
|
||||
import variables from 'config/variables';
|
||||
import Tab from './Tab';
|
||||
import { useTab } from './TabContext';
|
||||
|
||||
const Sidebar = memo(({ sections, currentTab, setCurrentTab }) => {
|
||||
const { subTab, setSubTab, setSubSection } = useTab();
|
||||
const handleClick = useCallback(
|
||||
(label) => () => {
|
||||
setCurrentTab(variables.getMessage(label));
|
||||
setSubTab(variables.getMessage(label));
|
||||
setSubSection('');
|
||||
},
|
||||
[setCurrentTab, setSubTab, setSubSection],
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<div className="modalSidebar">
|
||||
{sections.map((section, index) => (
|
||||
<Tab
|
||||
key={index}
|
||||
currentTab={currentTab}
|
||||
label={variables.getMessage(section.label)}
|
||||
onClick={handleClick(section.label)}
|
||||
navbarTab={section.navbar || false}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const Content = memo(({ sections, currentTab }) => (
|
||||
<>
|
||||
{sections.map(
|
||||
({ label, name, component: Component }) =>
|
||||
variables.getMessage(label) === currentTab && (
|
||||
<div
|
||||
className="modalTabContent"
|
||||
key={name}
|
||||
label={variables.getMessage(label)}
|
||||
name={name}
|
||||
>
|
||||
<Component />
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</>
|
||||
));
|
||||
|
||||
const Tabs = ({ sections }) => {
|
||||
const [currentTab, setCurrentTab] = useState(variables.getMessage(sections[0].label));
|
||||
|
||||
return (
|
||||
<div className="flex flex-row w-full">
|
||||
<Sidebar sections={sections} currentTab={currentTab} setCurrentTab={setCurrentTab} />
|
||||
<Content sections={sections} currentTab={currentTab} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { Tabs };
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
.tooltipTitle {
|
||||
@extend %basic;
|
||||
border-radius: 8px !important;
|
||||
|
||||
text-align: center;
|
||||
font-size: 0.6rem;
|
||||
|
||||
@@ -1,65 +1,61 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { compressAccurately, filetoDataURL } from 'image-conversion';
|
||||
import videoCheck from 'features/background/api/videoCheck';
|
||||
|
||||
class FileUpload extends PureComponent {
|
||||
componentDidMount() {
|
||||
document.getElementById(this.props.id).onchange = (e) => {
|
||||
const reader = new FileReader();
|
||||
const file = e.target.files[0];
|
||||
function FileUpload({ id, type, accept, loadFunction }) {
|
||||
const handleChange = (e) => {
|
||||
const reader = new FileReader();
|
||||
const file = e.target.files[0];
|
||||
|
||||
if (this.props.type === 'settings') {
|
||||
reader.readAsText(file, 'UTF-8');
|
||||
reader.onload = (e) => {
|
||||
return this.props.loadFunction(e.target.result);
|
||||
};
|
||||
} else {
|
||||
// background upload
|
||||
const settings = {};
|
||||
if (type === 'settings') {
|
||||
reader.readAsText(file, 'UTF-8');
|
||||
reader.onload = (e) => {
|
||||
return loadFunction(e.target.result);
|
||||
};
|
||||
} else {
|
||||
// background upload
|
||||
const settings = {};
|
||||
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
Object.keys(localStorage).forEach((key) => {
|
||||
settings[key] = localStorage.getItem(key);
|
||||
});
|
||||
|
||||
const settingsSize = new TextEncoder().encode(JSON.stringify(settings)).length;
|
||||
if (videoCheck(file.type) === true) {
|
||||
if (settingsSize + file.size > 4850000) {
|
||||
return toast(variables.getMessage('toasts.no_storage'));
|
||||
}
|
||||
|
||||
return this.props.loadFunction(file);
|
||||
const settingsSize = new TextEncoder().encode(JSON.stringify(settings)).length;
|
||||
if (videoCheck(file.type) === true) {
|
||||
if (settingsSize + file.size > 4850000) {
|
||||
return toast(variables.getMessage('toasts.no_storage'));
|
||||
}
|
||||
|
||||
compressAccurately(file, {
|
||||
size: 450,
|
||||
accuracy: 0.9,
|
||||
}).then(async (res) => {
|
||||
if (settingsSize + res.size > 4850000) {
|
||||
return toast(variables.getMessage('toasts.no_storage'));
|
||||
}
|
||||
|
||||
this.props.loadFunction({
|
||||
target: {
|
||||
result: await filetoDataURL(res),
|
||||
},
|
||||
});
|
||||
});
|
||||
return loadFunction(file);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<input
|
||||
id={this.props.id}
|
||||
type="file"
|
||||
style={{ display: 'none' }}
|
||||
accept={this.props.accept}
|
||||
/>
|
||||
);
|
||||
}
|
||||
compressAccurately(file, {
|
||||
size: 450,
|
||||
accuracy: 0.9,
|
||||
}).then(async (res) => {
|
||||
if (settingsSize + res.size > 4850000) {
|
||||
return toast(variables.getMessage('toasts.no_storage'));
|
||||
}
|
||||
|
||||
loadFunction({
|
||||
target: {
|
||||
result: await filetoDataURL(res),
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<input
|
||||
id={id}
|
||||
type="file"
|
||||
style={{ display: 'none' }}
|
||||
accept={accept}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { FileUpload as default, FileUpload };
|
||||
|
||||
@@ -1,123 +1,78 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
/*import {
|
||||
Radio as RadioUI,
|
||||
RadioGroup,
|
||||
FormControlLabel,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
} from '@mui/material';*/
|
||||
import { Radio as PureRadio, RadioGroup } from '@headlessui/react'
|
||||
import { MdCheckCircle } from "react-icons/md";
|
||||
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Radio as PureRadio, RadioGroup } from '@headlessui/react';
|
||||
import { MdCheckCircle } from 'react-icons/md';
|
||||
|
||||
import EventBus from 'utils/eventbus';
|
||||
import { translations } from 'lib/translations';
|
||||
|
||||
class Radio extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: localStorage.getItem(this.props.name),
|
||||
};
|
||||
}
|
||||
function Radio(props) {
|
||||
const [value, setValue] = useState(localStorage.getItem(props.name));
|
||||
|
||||
handleChange = async (value) => {
|
||||
const handleChange = async (value) => {
|
||||
if (value === 'loading') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.name === 'language') {
|
||||
|
||||
if (props.name === 'language') {
|
||||
// old tab name
|
||||
if (localStorage.getItem('tabName') === variables.getMessage('tabname')) {
|
||||
localStorage.setItem('tabName', translations[value.replace('-', '_')].tabname);
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem(this.props.name, value);
|
||||
|
||||
this.setState({
|
||||
value,
|
||||
});
|
||||
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(value);
|
||||
|
||||
localStorage.setItem(props.name, value);
|
||||
|
||||
setValue(value);
|
||||
|
||||
if (props.onChange) {
|
||||
props.onChange(value);
|
||||
}
|
||||
|
||||
variables.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`);
|
||||
|
||||
if (this.props.element) {
|
||||
if (!document.querySelector(this.props.element)) {
|
||||
|
||||
variables.stats.postEvent('setting', `${props.name} from ${value} to ${value}`);
|
||||
|
||||
if (props.element) {
|
||||
if (!document.querySelector(props.element)) {
|
||||
document.querySelector('.reminder-info').style.display = 'flex';
|
||||
return localStorage.setItem('showReminder', true);
|
||||
}
|
||||
}
|
||||
|
||||
EventBus.emit('refresh', this.props.category);
|
||||
|
||||
EventBus.emit('refresh', props.category);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
{
|
||||
/*<FormControl component="fieldset">
|
||||
<FormLabel
|
||||
className={this.props.smallTitle ? 'radio-title-small' : 'radio-title'}
|
||||
component="legend"
|
||||
>
|
||||
{this.props.title}
|
||||
</FormLabel>
|
||||
<RadioGroup
|
||||
aria-label={this.props.name}
|
||||
name={this.props.name}
|
||||
onChange={this.handleChange}
|
||||
value={this.state.value}
|
||||
>
|
||||
{this.props.options.map((option) => (
|
||||
<FormControlLabel
|
||||
value={option.value}
|
||||
control={<RadioUI />}
|
||||
label={option.name}
|
||||
key={option.name}
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</FormControl>*/
|
||||
},
|
||||
(
|
||||
<div className="w-full">
|
||||
<RadioGroup
|
||||
aria-label={this.props.name}
|
||||
name={this.props.name}
|
||||
onChange={this.handleChange}
|
||||
value={this.state.value}
|
||||
className="space-y-2"
|
||||
>
|
||||
{this.props.options.map((option) => (
|
||||
<PureRadio
|
||||
key={option.name}
|
||||
label={option.name}
|
||||
value={option.value}
|
||||
className="group relative flex cursor-pointer rounded-lg bg-white/5 py-4 px-5 text-white shadow-md transition focus:outline-none data-[focus]:outline-1 data-[focus]:outline-white data-[checked]:bg-white/10"
|
||||
>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="text-sm/6">
|
||||
<p className="font-semibold text-white">{option.name}</p>
|
||||
<div className="flex gap-2 text-white/50">
|
||||
<div>10%</div>
|
||||
<div aria-hidden="true">·</div>
|
||||
<div>sus</div>
|
||||
</div>
|
||||
</div>
|
||||
<MdCheckCircle className="size-6 fill-white opacity-0 transition group-data-[checked]:opacity-100" />
|
||||
</div>
|
||||
</PureRadio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="w-full">
|
||||
<RadioGroup
|
||||
aria-label={props.name}
|
||||
name={props.name}
|
||||
onChange={handleChange}
|
||||
value={value}
|
||||
className="space-y-2"
|
||||
>
|
||||
{props.options.map((option) => (
|
||||
<PureRadio
|
||||
key={option.name}
|
||||
label={option.name}
|
||||
value={option.value}
|
||||
className="group relative flex cursor-pointer rounded-lg bg-white/5 py-4 px-5 text-white shadow-md transition focus:outline-none data-[focus]:outline-1 data-[focus]:outline-white data-[checked]:bg-white/10"
|
||||
>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="text-sm/6">
|
||||
<p className="font-semibold text-white">{option.name}</p>
|
||||
<div className="flex gap-2 text-white/50">
|
||||
<div>10%</div>
|
||||
<div aria-hidden="true">·</div>
|
||||
<div>sus</div>
|
||||
</div>
|
||||
</div>
|
||||
<MdCheckCircle className="size-6 fill-white opacity-0 transition group-data-[checked]:opacity-100" />
|
||||
</div>
|
||||
</PureRadio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { Radio as default, Radio };
|
||||
|
||||
@@ -1,88 +1,77 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Slider } from '@mui/material';
|
||||
import { MdRefresh } from 'react-icons/md';
|
||||
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class SliderComponent extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: localStorage.getItem(this.props.name) || this.props.default,
|
||||
};
|
||||
}
|
||||
function SliderComponent(props) {
|
||||
const [value, setValue] = useState(localStorage.getItem(props.name) || props.default);
|
||||
|
||||
handleChange = (e, text) => {
|
||||
const handleChange = (e, text) => {
|
||||
let { value } = e.target;
|
||||
value = Number(value);
|
||||
|
||||
if (text) {
|
||||
if (value === '') {
|
||||
return this.setState({
|
||||
value: 0,
|
||||
});
|
||||
return setValue(0);
|
||||
}
|
||||
|
||||
if (value > this.props.max) {
|
||||
value = this.props.max;
|
||||
if (value > props.max) {
|
||||
value = props.max;
|
||||
}
|
||||
|
||||
if (value < this.props.min) {
|
||||
value = this.props.min;
|
||||
if (value < props.min) {
|
||||
value = props.min;
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem(this.props.name, value);
|
||||
this.setState({
|
||||
value,
|
||||
});
|
||||
localStorage.setItem(props.name, value);
|
||||
setValue(value);
|
||||
|
||||
if (this.props.element) {
|
||||
if (!document.querySelector(this.props.element)) {
|
||||
if (props.element) {
|
||||
if (!document.querySelector(props.element)) {
|
||||
document.querySelector('.reminder-info').style.display = 'flex';
|
||||
return localStorage.setItem('showReminder', true);
|
||||
}
|
||||
}
|
||||
|
||||
EventBus.emit('refresh', this.props.category);
|
||||
EventBus.emit('refresh', props.category);
|
||||
};
|
||||
|
||||
resetItem = () => {
|
||||
this.handleChange({
|
||||
const resetItem = () => {
|
||||
handleChange({
|
||||
target: {
|
||||
value: this.props.default || '',
|
||||
value: props.default || '',
|
||||
},
|
||||
});
|
||||
toast(variables.getMessage('toasts.reset'));
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<span className={'sliderTitle'}>
|
||||
{this.props.title}
|
||||
<span>{Number(this.state.value)}</span>
|
||||
<span className="link" onClick={this.resetItem}>
|
||||
<MdRefresh />
|
||||
{variables.getMessage('modals.main.settings.buttons.reset')}
|
||||
</span>
|
||||
return (
|
||||
<>
|
||||
<span className={'sliderTitle'}>
|
||||
{props.title}
|
||||
<span>{Number(value)}</span>
|
||||
<span className="link" onClick={resetItem}>
|
||||
<MdRefresh />
|
||||
{variables.getMessage('modals.main.settings.buttons.reset')}
|
||||
</span>
|
||||
<Slider
|
||||
value={Number(this.state.value)}
|
||||
onChange={this.handleChange}
|
||||
valueLabelDisplay="auto"
|
||||
default={Number(this.props.default)}
|
||||
min={Number(this.props.min)}
|
||||
max={Number(this.props.max)}
|
||||
step={Number(this.props.step) || 1}
|
||||
getAriaValueText={(value) => `${value}`}
|
||||
marks={this.props.marks || []}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
</span>
|
||||
<Slider
|
||||
value={Number(value)}
|
||||
onChange={handleChange}
|
||||
valueLabelDisplay="auto"
|
||||
default={Number(props.default)}
|
||||
min={Number(props.min)}
|
||||
max={Number(props.max)}
|
||||
step={Number(props.step) || 1}
|
||||
getAriaValueText={(value) => `${value}`}
|
||||
marks={props.marks || []}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { SliderComponent as default, SliderComponent as Slider };
|
||||
|
||||
@@ -1,79 +1,53 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
//import { Switch as SwitchUI, FormControlLabel } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
import { Field, Label, Switch as SwitchUI } from '@headlessui/react';
|
||||
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class Switch extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
checked: localStorage.getItem(this.props.name) === 'true',
|
||||
};
|
||||
}
|
||||
function Switch(props) {
|
||||
const [checked, setChecked] = useState(localStorage.getItem(props.name) === 'true');
|
||||
|
||||
handleChange = () => {
|
||||
const value = this.state.checked !== true;
|
||||
localStorage.setItem(this.props.name, value);
|
||||
const handleChange = () => {
|
||||
const value = checked !== true;
|
||||
localStorage.setItem(props.name, value);
|
||||
|
||||
this.setState({
|
||||
checked: value,
|
||||
});
|
||||
setChecked(value);
|
||||
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(value);
|
||||
if (props.onChange) {
|
||||
props.onChange(value);
|
||||
}
|
||||
|
||||
variables.stats.postEvent(
|
||||
'setting',
|
||||
`${this.props.name} ${this.state.checked === true ? 'enabled' : 'disabled'}`,
|
||||
`${props.name} ${checked === true ? 'enabled' : 'disabled'}`,
|
||||
);
|
||||
|
||||
if (this.props.element) {
|
||||
if (!document.querySelector(this.props.element)) {
|
||||
if (props.element) {
|
||||
if (!document.querySelector(props.element)) {
|
||||
document.querySelector('.reminder-info').style.display = 'flex';
|
||||
return localStorage.setItem('showReminder', true);
|
||||
}
|
||||
}
|
||||
|
||||
EventBus.emit('refresh', this.props.category);
|
||||
EventBus.emit('refresh', props.category);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
{
|
||||
/*<FormControlLabel
|
||||
control={
|
||||
<SwitchUI
|
||||
name={this.props.name}
|
||||
color="primary"
|
||||
checked={this.state.checked}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
}
|
||||
label={this.props.header ? '' : this.props.text}
|
||||
labelPlacement="start"
|
||||
/>*/
|
||||
},
|
||||
(
|
||||
<Field className="flex flex-row items-center justify-between w-[100%]">
|
||||
<Label>{this.props.header ? '' : this.props.text}</Label>
|
||||
<SwitchUI
|
||||
checked={this.state.checked}
|
||||
onChange={this.handleChange}
|
||||
className="box-border group relative flex h-7 w-14 cursor-pointer rounded-full bg-white/10 p-1 transition-colors duration-200 ease-in-out focus:outline-none data-[focus]:outline-1 data-[focus]:outline-white data-[checked]:bg-white/10"
|
||||
>
|
||||
{' '}
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none inline-block size-5 translate-x-0 rounded-full bg-white ring-0 shadow-lg transition duration-200 ease-in-out group-data-[checked]:translate-x-7"
|
||||
/>
|
||||
</SwitchUI>
|
||||
</Field>
|
||||
)
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Field className="flex flex-row items-center justify-between w-[100%]">
|
||||
<Label>{props.header ? '' : props.text}</Label>
|
||||
<SwitchUI
|
||||
checked={checked}
|
||||
onChange={handleChange}
|
||||
className="box-border group relative flex h-7 w-14 cursor-pointer rounded-full bg-white/10 p-1 transition-colors duration-200 ease-in-out focus:outline-none data-[focus]:outline-1 data-[focus]:outline-white data-[checked]:bg-white/10"
|
||||
>
|
||||
{' '}
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none inline-block size-5 translate-x-0 rounded-full bg-white ring-0 shadow-lg transition duration-200 ease-in-out group-data-[checked]:translate-x-7"
|
||||
/>
|
||||
</SwitchUI>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
export { Switch as default, Switch };
|
||||
|
||||
@@ -1,114 +1,82 @@
|
||||
import variables from 'config/variables';
|
||||
import { PureComponent } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { TextField } from '@mui/material';
|
||||
import { Description, Field, Label, Textarea, Input } from '@headlessui/react';
|
||||
import { Field, Label, Textarea, Input } from '@headlessui/react';
|
||||
import { MdRefresh } from 'react-icons/md';
|
||||
|
||||
import clsx from 'clsx';
|
||||
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
class Text extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: localStorage.getItem(this.props.name) || '',
|
||||
};
|
||||
}
|
||||
function Text(props) {
|
||||
const [value, setValue] = useState(localStorage.getItem(props.name) || '');
|
||||
|
||||
handleChange = (e) => {
|
||||
const handleChange = (e) => {
|
||||
let { value } = e.target;
|
||||
|
||||
// Alex wanted font to work with montserrat and Montserrat, so I made it work
|
||||
if (this.props.upperCaseFirst === true) {
|
||||
if (props.upperCaseFirst === true) {
|
||||
value = value.charAt(0).toUpperCase() + value.slice(1);
|
||||
}
|
||||
|
||||
localStorage.setItem(this.props.name, value);
|
||||
this.setState({
|
||||
value,
|
||||
});
|
||||
localStorage.setItem(props.name, value);
|
||||
setValue(value);
|
||||
|
||||
if (this.props.element) {
|
||||
if (!document.querySelector(this.props.element)) {
|
||||
if (props.element) {
|
||||
if (!document.querySelector(props.element)) {
|
||||
document.querySelector('.reminder-info').style.display = 'flex';
|
||||
return localStorage.setItem('showReminder', true);
|
||||
}
|
||||
}
|
||||
|
||||
EventBus.emit('refresh', this.props.category);
|
||||
EventBus.emit('refresh', props.category);
|
||||
};
|
||||
|
||||
resetItem = () => {
|
||||
this.handleChange({
|
||||
const resetItem = () => {
|
||||
handleChange({
|
||||
target: {
|
||||
value: this.props.default || '',
|
||||
value: props.default || '',
|
||||
},
|
||||
});
|
||||
toast(variables.getMessage('toasts.reset'));
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Field className="w-full">
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
<Label>{this.props.title}</Label>
|
||||
<span className="link" onClick={this.resetItem}>
|
||||
return (
|
||||
<Field className="w-full">
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
<Label>{props.title}</Label>
|
||||
<span className="link" onClick={resetItem}>
|
||||
<MdRefresh />
|
||||
{variables.getMessage('modals.main.settings.buttons.reset')}
|
||||
</span>
|
||||
</div>
|
||||
{this.props.textarea === true ? (
|
||||
<>
|
||||
<Textarea
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
className={clsx(
|
||||
'bg-white/5 box-border mt-3 block w-full resize-none rounded-lg py-1.5 px-3 text-sm/6 text-white border border-[#484848]',
|
||||
'focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-white/25',
|
||||
)}
|
||||
rows={4}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Input
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
placeholder={this.props.placeholder || ''}
|
||||
className={clsx(
|
||||
'box-border mt-3 block w-full rounded-lg bg-white/5 py-1.5 px-3 text-sm/6 text-white border border-[#484848]',
|
||||
'focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-white/25',
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
{props.textarea === true ? (
|
||||
<>
|
||||
<Textarea
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
className={clsx(
|
||||
'bg-white/5 box-border mt-3 block w-full resize-none rounded-lg py-1.5 px-3 text-sm/6 text-white border border-[#484848]',
|
||||
'focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-white/25',
|
||||
)}
|
||||
rows={4}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Input
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
placeholder={props.placeholder || ''}
|
||||
className={clsx(
|
||||
'box-border mt-3 block w-full rounded-lg bg-white/5 py-1.5 px-3 text-sm/6 text-white border border-[#484848]',
|
||||
'focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-white/25',
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
export { Text as default, Text };
|
||||
|
||||
/*<TextField
|
||||
label={this.props.title}
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
varient="outlined"
|
||||
className={this.props.customcss ? 'customcss' : ''}
|
||||
multiline
|
||||
spellCheck={false}
|
||||
minRows={4}
|
||||
maxRows={10}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label={this.props.title}
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
varient="outlined"
|
||||
InputLabelProps={{ shrink: true }}
|
||||
placeholder={this.props.placeholder || ''}
|
||||
/>
|
||||
*/
|
||||
|
||||
@@ -8,12 +8,15 @@ import {
|
||||
} from 'react-icons/md';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import { Button } from 'components/Elements';
|
||||
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
|
||||
|
||||
export const CustomActions = ({ children }) => {
|
||||
return children;
|
||||
};
|
||||
|
||||
function Header(props) {
|
||||
const { setSubSection, subSection } = useTab();
|
||||
|
||||
const [setting, setSetting] = useState(localStorage.getItem(props.setting) === 'true');
|
||||
|
||||
useEffect(() => {
|
||||
@@ -67,15 +70,17 @@ function Header(props) {
|
||||
return (
|
||||
<div className="modalHeader">
|
||||
<span className="mainTitle">
|
||||
{/*}
|
||||
{props.secondaryTitle && (
|
||||
<>
|
||||
<span className="backTitle" onClick={props.goBack}>
|
||||
<span className="backTitle" onClick={() => setSubSection('')}>
|
||||
{props.title}
|
||||
</span>
|
||||
<MdOutlineKeyboardArrowRight />
|
||||
</>
|
||||
)}
|
||||
{props.secondaryTitle || props.title}
|
||||
*/}
|
||||
</span>
|
||||
<div className="headerActions">
|
||||
{props.visibilityToggle && <VisibilityToggle />}
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { MdOutlineKeyboardArrowRight } from 'react-icons/md';
|
||||
import { useTab } from 'components/Elements/MainModal/backend/TabContext';
|
||||
import React from 'react';
|
||||
|
||||
function Section({ title, subtitle, icon, onClick, children }) {
|
||||
function Section({ id, title, subtitle, icon, onClick, children }) {
|
||||
const { setSubSection } = useTab();
|
||||
|
||||
const handleClick = () => {
|
||||
setSubSection(id);
|
||||
if (onClick) {
|
||||
onClick();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="moreSettings" onClick={onClick}>
|
||||
<div className="moreSettings" onClick={handleClick}>
|
||||
<div className="left">
|
||||
{icon}
|
||||
<div className="content">
|
||||
|
||||
Reference in New Issue
Block a user