Style updates and bugfixes

This commit is contained in:
Sean Edmunds
2025-07-17 17:01:31 -05:00
parent ea216e9da2
commit 5e5a077ec3
48 changed files with 565 additions and 524 deletions

32
package-lock.json generated
View File

@@ -21,18 +21,18 @@
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-router-dom": "^7.6.3", "react-router-dom": "^7.7.0",
"recharts": "^3.1.0" "recharts": "^3.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^24.0.13", "@types/node": "^24.0.14",
"@types/react": "^19.1.8", "@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6", "@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.6.0", "@vitejs/plugin-react": "^4.6.0",
"@vitest/coverage-v8": "^3.2.4", "@vitest/coverage-v8": "^3.2.4",
"@vitest/ui": "^3.2.4", "@vitest/ui": "^3.2.4",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vite": "^7.0.4", "vite": "^7.0.5",
"vite-plugin-dts": "~4.5.4", "vite-plugin-dts": "~4.5.4",
"vite-tsconfig-paths": "^5.1.4", "vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4" "vitest": "^3.2.4"
@@ -1847,9 +1847,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "24.0.13", "version": "24.0.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz",
"integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==", "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -3694,9 +3694,9 @@
} }
}, },
"node_modules/react-router": { "node_modules/react-router": {
"version": "7.6.3", "version": "7.7.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.3.tgz", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.0.tgz",
"integrity": "sha512-zf45LZp5skDC6I3jDLXQUu0u26jtuP4lEGbc7BbdyxenBN1vJSTA18czM2D+h5qyMBuMrD+9uB+mU37HIoKGRA==", "integrity": "sha512-3FUYSwlvB/5wRJVTL/aavqHmfUKe0+Xm9MllkYgGo9eDwNdkvwlJGjpPxono1kCycLt6AnDTgjmXvK3/B4QGuw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cookie": "^1.0.1", "cookie": "^1.0.1",
@@ -3716,12 +3716,12 @@
} }
}, },
"node_modules/react-router-dom": { "node_modules/react-router-dom": {
"version": "7.6.3", "version": "7.7.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.3.tgz", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.7.0.tgz",
"integrity": "sha512-DiWJm9qdUAmiJrVWaeJdu4TKu13+iB/8IEi0EW/XgaHCjW/vWGrwzup0GVvaMteuZjKnh5bEvJP/K0MDnzawHw==", "integrity": "sha512-wwGS19VkNBkneVh9/YD0pK3IsjWxQUVMDD6drlG7eJpo1rXBtctBqDyBm/k+oKHRAm1x9XWT3JFC82QI9YOXXA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"react-router": "7.6.3" "react-router": "7.7.0"
}, },
"engines": { "engines": {
"node": ">=20.0.0" "node": ">=20.0.0"
@@ -4517,9 +4517,9 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "7.0.4", "version": "7.0.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz",
"integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@@ -23,18 +23,18 @@
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-router-dom": "^7.6.3", "react-router-dom": "^7.7.0",
"recharts": "^3.1.0" "recharts": "^3.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^24.0.13", "@types/node": "^24.0.14",
"@types/react": "^19.1.8", "@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6", "@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.6.0", "@vitejs/plugin-react": "^4.6.0",
"@vitest/coverage-v8": "^3.2.4", "@vitest/coverage-v8": "^3.2.4",
"@vitest/ui": "^3.2.4", "@vitest/ui": "^3.2.4",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vite": "^7.0.4", "vite": "^7.0.5",
"vite-plugin-dts": "~4.5.4", "vite-plugin-dts": "~4.5.4",
"vite-tsconfig-paths": "^5.1.4", "vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4" "vitest": "^3.2.4"

View File

@@ -3,7 +3,7 @@ import { useCommonHooks } from 'hooks';
import type React from 'react'; import type React from 'react';
import { cardClasses, modalClasses } from 'styles'; import { cardClasses, modalClasses } from 'styles';
function AddItemModal({ export function AddItemModal({
modalOpened, modalOpened,
handleClose, handleClose,
body, body,
@@ -53,5 +53,3 @@ function AddItemModal({
</Modal.Root> </Modal.Root>
); );
} }
export default AddItemModal;

View File

@@ -2,7 +2,7 @@ import { Button, Card, Group, Modal } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { cardClasses, modalClasses } from 'styles'; import { cardClasses, modalClasses } from 'styles';
function DeleteItemModal({ export function DeleteItemModal({
modalOpened, modalOpened,
handleClose, handleClose,
handleDelete, handleDelete,
@@ -56,5 +56,3 @@ function DeleteItemModal({
</Modal.Root> </Modal.Root>
); );
} }
export default DeleteItemModal;

View File

@@ -2,7 +2,7 @@ import { ActionIcon, Group, Switch, Tooltip } from '@mantine/core';
import { FiPower, FiTrash } from 'react-icons/fi'; import { FiPower, FiTrash } from 'react-icons/fi';
import { switchClasses } from 'styles'; import { switchClasses } from 'styles';
function ManagementPill({ export function ManagementPill({
enabled, enabled,
onChange, onChange,
openModal, openModal,
@@ -72,5 +72,3 @@ function ManagementPill({
</Group> </Group>
); );
} }
export default ManagementPill;

View File

@@ -1,7 +1,7 @@
import { Card, Text } from '@mantine/core'; import { Card, Text } from '@mantine/core';
import { cardClasses } from 'styles'; import { cardClasses } from 'styles';
function NoItemsFound() { export function NoItemsFound() {
return ( return (
<Card <Card
className={cardClasses.settingsItem} className={cardClasses.settingsItem}
@@ -18,5 +18,3 @@ function NoItemsFound() {
</Card> </Card>
); );
} }
export default NoItemsFound;

View File

@@ -21,7 +21,7 @@ import {
textInputClasses, textInputClasses,
} from 'styles'; } from 'styles';
function SettingsHeader({ export function SettingsHeader({
setDragging, setDragging,
openModal, openModal,
title, title,
@@ -124,7 +124,7 @@ function SettingsHeader({
disabled={disabled} disabled={disabled}
w={isMobile ? undefined : '35%'} w={isMobile ? undefined : '35%'}
pos={isMobile ? undefined : 'absolute'} pos={isMobile ? undefined : 'absolute'}
className={textInputClasses.settingsItem} className={textInputClasses.default}
classNames={textInputClasses} classNames={textInputClasses}
autoFocus={!isMobile} autoFocus={!isMobile}
placeholder={searchPlaceholder ?? 'Search...'} placeholder={searchPlaceholder ?? 'Search...'}
@@ -181,5 +181,3 @@ function SettingsHeader({
</Group> </Group>
); );
} }
export default SettingsHeader;

View File

@@ -1,5 +1,10 @@
export { AddItemModal } from './AddItemModal';
export { AppItem } from './Applications/AppItem'; export { AppItem } from './Applications/AppItem';
export { DeleteItemModal } from './DeleteItemModal';
export { HelpModal } from './HelpModal/HelpModal'; export { HelpModal } from './HelpModal/HelpModal';
export { Loading } from './Loading'; export { Loading } from './Loading';
export { MainLayout } from './MainLayout'; export { MainLayout } from './MainLayout';
export { ManagementPill } from './ManagementPill';
export { NoItemsFound } from './NoItemsFound';
export { SettingsHeader } from './SettingsHeader';
export { WeatherModal } from './WeatherModal/WeatherModal'; export { WeatherModal } from './WeatherModal/WeatherModal';

View File

@@ -29,6 +29,7 @@ function BookmarkGroups({
</Text> </Text>
{bookmarks {bookmarks
?.toSorted((a: Bookmark, b: Bookmark) => (a.index > b.index ? 1 : -1)) ?.toSorted((a: Bookmark, b: Bookmark) => (a.index > b.index ? 1 : -1))
.filter((bookmark: Bookmark) => bookmark.enabled === 1)
.map((bookmark: Bookmark) => ( .map((bookmark: Bookmark) => (
<BookmarkItem <BookmarkItem
key={bookmark.name} key={bookmark.name}

View File

@@ -28,7 +28,7 @@ function WakeOnLan({
<Button <Button
classNames={buttonClasses} classNames={buttonClasses}
className={buttonClasses.wakeButton} className={buttonClasses.wakeButton}
disabled={!wolClients?.length || selectedClient?.macAddress === undefined} disabled={!wolClients?.length && selectedClient?.macAddress === undefined}
onClick={() => { onClick={() => {
handleWakeRequest(); handleWakeRequest();
}} }}

View File

@@ -17,8 +17,8 @@ import Bookmarks from './components/Bookmarks/Bookmarks';
import Messages from './components/Messages/Messages'; import Messages from './components/Messages/Messages';
import Personalization from './components/Personalization/Personalization'; import Personalization from './components/Personalization/Personalization';
import SearchProviders from './components/Search/SearchProviders'; import SearchProviders from './components/Search/SearchProviders';
import ThemePlaylists from './components/Theming/ThemePlaylists'; import { ThemePlaylists } from './components/Theming/ThemePlaylists';
import Themes from './components/Theming/Themes'; import { Themes } from './components/Theming/Themes';
import WolClients from './components/WoL/WolClients'; import WolClients from './components/WoL/WolClients';
export function Settings() { export function Settings() {

View File

@@ -15,7 +15,7 @@ import {
} from '@mantine/core'; } from '@mantine/core';
import { isNotEmpty, useForm } from '@mantine/form'; import { isNotEmpty, useForm } from '@mantine/form';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { AppItem } from 'components'; import { AddItemModal, AppItem, NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import * as fa6 from 'react-icons/fa6'; import * as fa6 from 'react-icons/fa6';
@@ -23,9 +23,6 @@ import * as si from 'react-icons/si';
import * as vsc from 'react-icons/vsc'; import * as vsc from 'react-icons/vsc';
import { popoverClasses, selectClasses, textInputClasses } from 'styles'; import { popoverClasses, selectClasses, textInputClasses } from 'styles';
import type { Application } from 'types'; import type { Application } from 'types';
import AddItemModal from '../Shared/AddItemModal';
import NoItemsFound from '../Shared/NoItemsFound';
import SettingsHeader from '../Shared/SettingsHeader';
import DebouncedApplication from './DebouncedApplication'; import DebouncedApplication from './DebouncedApplication';
const ApplicationSettings = () => { const ApplicationSettings = () => {

View File

@@ -9,12 +9,11 @@ import {
TextInput, TextInput,
} from '@mantine/core'; } from '@mantine/core';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { DeleteItemModal, ManagementPill } from 'components';
import { useCommonHooks, useMutationAPI } from 'hooks'; import { useCommonHooks, useMutationAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { cardClasses, selectClasses, textInputClasses } from 'styles'; import { cardClasses, selectClasses, textInputClasses } from 'styles';
import type { Application } from 'types'; import type { Application } from 'types';
import DeleteItemModal from '../Shared/DeleteItemModal';
import ManagementPill from '../Shared/ManagementPill';
function DebouncedApplication({ function DebouncedApplication({
application, application,

View File

@@ -13,12 +13,14 @@ function BookmarkSettingsItem({ bookmark }: { bookmark: Bookmark }) {
color: 'var(--mantine-color-primary-0)', color: 'var(--mantine-color-primary-0)',
backgroundColor: 'var(--mantine-color-search-0)', backgroundColor: 'var(--mantine-color-search-0)',
padding: '.5rem', padding: '.5rem',
filter: bookmark.enabled ? '' : 'grayscale(100%)',
}} }}
onMouseEnter={() => setHover(true)} onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)} onMouseLeave={() => setHover(false)}
> >
<Group> <Group justify='space-between'>
<Text>{bookmark.name}</Text> <Text>{bookmark.name}</Text>
{!bookmark.enabled && !hover && <Text opacity={0.25}>Disabled</Text>}
{hover && ( {hover && (
<ActionIcon <ActionIcon
variant='subtle' variant='subtle'

View File

@@ -2,13 +2,11 @@ import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
import { Box, Button, Card, Group, Stack, Text, TextInput } from '@mantine/core'; import { Box, Button, Card, Group, Stack, Text, TextInput } from '@mantine/core';
import { isNotEmpty, useForm } from '@mantine/form'; import { isNotEmpty, useForm } from '@mantine/form';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { textInputClasses } from 'styles'; import { textInputClasses } from 'styles';
import type { Bookmark, BookmarkGroup } from 'types'; import type { Bookmark, BookmarkGroup } from 'types';
import AddItemModal from '../Shared/AddItemModal';
import NoItemsFound from '../Shared/NoItemsFound';
import SettingsHeader from '../Shared/SettingsHeader';
import DebouncedGroupSettingsItem from './DebouncedGroupSettingsItem'; import DebouncedGroupSettingsItem from './DebouncedGroupSettingsItem';
const BookmarkSettings = () => { const BookmarkSettings = () => {

View File

@@ -1,11 +1,10 @@
import { ActionIcon, Card, Group, Stack, Text, TextInput, Tooltip } from '@mantine/core'; import { Card, Group, Stack, Text, TextInput } from '@mantine/core';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { DeleteItemModal, ManagementPill } from 'components';
import { useCommonHooks, useMutationAPI } from 'hooks'; import { useCommonHooks, useMutationAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FiTrash } from 'react-icons/fi';
import { cardClasses, textInputClasses } from 'styles'; import { cardClasses, textInputClasses } from 'styles';
import type { Bookmark } from 'types'; import type { Bookmark } from 'types';
import DeleteItemModal from '../Shared/DeleteItemModal';
function DebouncedBookmarkItem({ function DebouncedBookmarkItem({
bookmarkItem, bookmarkItem,
@@ -66,25 +65,16 @@ function DebouncedBookmarkItem({
} }
title='Delete Bookmark' title='Delete Bookmark'
/> />
<Tooltip <ManagementPill
c={'var(--mantine-color-primary-0)'} enabled={bookmark.enabled}
bg={'var(--mantine-color-background-0)'} onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
bd={'solid 1px var(--mantine-color-border-0)'} setUpdate({
label={'Delete'} ...bookmark,
> enabled: event.target.checked ? 1 : 0,
<ActionIcon });
pos={'absolute'}
right={'1rem'}
top={'1rem'}
color='red'
onClick={() => {
setIsOpen(true);
openModal();
}} }}
> openModal={openModal}
<FiTrash color='black' /> />
</ActionIcon>
</Tooltip>
<Group justify='center' style={{ flexDirection: isMobile ? 'column' : 'row' }}> <Group justify='center' style={{ flexDirection: isMobile ? 'column' : 'row' }}>
<TextInput <TextInput
label={'Name'} label={'Name'}

View File

@@ -19,13 +19,13 @@ import {
} from '@mantine/core'; } from '@mantine/core';
import { isNotEmpty, useForm } from '@mantine/form'; import { isNotEmpty, useForm } from '@mantine/form';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { DeleteItemModal } from 'components';
import { useCommonHooks, useMutationAPI } from 'hooks'; import { useCommonHooks, useMutationAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FaChevronLeft } from 'react-icons/fa'; import { FaChevronLeft } from 'react-icons/fa';
import { FiPlus, FiTrash } from 'react-icons/fi'; import { FiPlus, FiTrash } from 'react-icons/fi';
import { buttonClasses, cardClasses, popoverClasses, textInputClasses } from 'styles'; import { buttonClasses, cardClasses, popoverClasses, textInputClasses } from 'styles';
import type { Bookmark, BookmarkGroup } from 'types'; import type { Bookmark, BookmarkGroup } from 'types';
import DeleteItemModal from '../Shared/DeleteItemModal';
import BookmarkSettingsItem from './BookmarkSettingsItem'; import BookmarkSettingsItem from './BookmarkSettingsItem';
import DebouncedBookmarkItem from './DebouncedBookmarkItem'; import DebouncedBookmarkItem from './DebouncedBookmarkItem';
@@ -51,6 +51,7 @@ function DebouncedGroupSettingsItem({
url: '', url: '',
category: group.categoryName, category: group.categoryName,
index: 0, index: 0,
enabled: 1,
}); });
const form = useForm({ const form = useForm({
initialValues: newBookmark, initialValues: newBookmark,
@@ -111,7 +112,7 @@ function DebouncedGroupSettingsItem({
modalOpened={modalOpened} modalOpened={modalOpened}
handleClose={closeModal} handleClose={closeModal}
handleDelete={() => { handleDelete={() => {
handleDelete(bookmarks, 'api/bookmarks'); bookmarks.length > 0 && handleDelete(bookmarks, 'api/bookmarks');
handleDelete(group, 'api/bookmarkCategories'); handleDelete(group, 'api/bookmarkCategories');
}} }}
title='Delete message' title='Delete message'
@@ -141,12 +142,13 @@ function DebouncedGroupSettingsItem({
pt={0} pt={0}
bg={'var(--mantine-color-search-0)'} bg={'var(--mantine-color-search-0)'}
c={'var(--mantine-color-background-0)'} c={'var(--mantine-color-background-0)'}
mt={'-1.5rem'}
> >
<Stack gap={0}> <Stack gap={0}>
<Group gap={0} align='center' justify='space-between'> <Group gap={0} align='end' justify='space-between'>
<TextInput <TextInput
label={'Name'} label={'Name'}
w={isMobile ? undefined : '58%'} w={isMobile ? undefined : '56%'}
className={textInputClasses.settingsItem} className={textInputClasses.settingsItem}
classNames={textInputClasses} classNames={textInputClasses}
defaultValue={group.categoryName} defaultValue={group.categoryName}
@@ -187,9 +189,8 @@ function DebouncedGroupSettingsItem({
label={'Delete'} label={'Delete'}
> >
<ActionIcon <ActionIcon
mt={'2.5rem'}
size={'lg'}
color='red' color='red'
size={'2.75rem'}
onClick={() => { onClick={() => {
openModal(); openModal();
}} }}
@@ -206,8 +207,8 @@ function DebouncedGroupSettingsItem({
borderRadius: '100%', borderRadius: '100%',
backgroundColor: 'var(--mantine-color-accent-0)', backgroundColor: 'var(--mantine-color-accent-0)',
position: 'absolute', position: 'absolute',
top: isMobile ? 170 : 92, top: isMobile ? 156 : 78,
right: 18, right: 22,
zIndex: 10, zIndex: 10,
fontSize: '1.25rem', fontSize: '1.25rem',
textAlign: 'center', textAlign: 'center',

View File

@@ -1,10 +1,9 @@
import { Card, Stack, Text, TextInput } from '@mantine/core'; import { Card, Stack, Text, TextInput } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks'; import { useDisclosure } from '@mantine/hooks';
import { DeleteItemModal, ManagementPill } from 'components';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { cardClasses, textInputClasses } from 'styles'; import { cardClasses, textInputClasses } from 'styles';
import type { Message } from 'types'; import type { Message } from 'types';
import DeleteItemModal from '../Shared/DeleteItemModal';
import ManagementPill from '../Shared/ManagementPill';
function DebouncedMessage({ function DebouncedMessage({
label, label,

View File

@@ -1,12 +1,10 @@
import { Button, Grid, GridCol, Group, Stack, TextInput } from '@mantine/core'; import { Button, Grid, GridCol, Group, Stack, TextInput } from '@mantine/core';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { textInputClasses } from 'styles'; import { textInputClasses } from 'styles';
import type { Message } from 'types'; import type { Message } from 'types';
import AddItemModal from '../Shared/AddItemModal';
import NoItemsFound from '../Shared/NoItemsFound';
import SettingsHeader from '../Shared/SettingsHeader';
import DebouncedMessage from './DebouncedMessage'; import DebouncedMessage from './DebouncedMessage';
const MessageSettings = () => { const MessageSettings = () => {

View File

@@ -1,10 +1,10 @@
import { Card, Grid, GridCol, Overlay, Stack, TextInput } from '@mantine/core'; import { Card, Grid, GridCol, Overlay, Stack, TextInput } from '@mantine/core';
import { useDebouncedValue } from '@mantine/hooks'; import { useDebouncedValue } from '@mantine/hooks';
import { SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { cardClasses, textInputClasses } from 'styles'; import { cardClasses, textInputClasses } from 'styles';
import type { PersonalData } from 'types'; import type { PersonalData } from 'types';
import SettingsHeader from '../Shared/SettingsHeader';
const PersonalizationSettings = () => { const PersonalizationSettings = () => {
const endpoint = 'api/personalData'; const endpoint = 'api/personalData';

View File

@@ -1,11 +1,11 @@
import { ActionIcon, Card, Group, Stack, Text, TextInput, Tooltip } from '@mantine/core'; import { ActionIcon, Card, Group, Stack, Text, TextInput, Tooltip } from '@mantine/core';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { DeleteItemModal } from 'components';
import { useCommonHooks, useMutationAPI } from 'hooks'; import { useCommonHooks, useMutationAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FiTrash } from 'react-icons/fi'; import { FiTrash } from 'react-icons/fi';
import { cardClasses, textInputClasses } from 'styles'; import { cardClasses, textInputClasses } from 'styles';
import type { Provider } from 'types'; import type { Provider } from 'types';
import DeleteItemModal from '../Shared/DeleteItemModal';
function DebouncedSearchProvider({ function DebouncedSearchProvider({
searchProvider, searchProvider,

View File

@@ -10,13 +10,11 @@ import {
} from '@mantine/core'; } from '@mantine/core';
import { isNotEmpty, useForm } from '@mantine/form'; import { isNotEmpty, useForm } from '@mantine/form';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { popoverClasses, textInputClasses } from 'styles'; import { popoverClasses, textInputClasses } from 'styles';
import type { Provider } from 'types'; import type { Provider } from 'types';
import AddItemModal from '../Shared/AddItemModal';
import NoItemsFound from '../Shared/NoItemsFound';
import SettingsHeader from '../Shared/SettingsHeader';
import DebouncedSearchProvider from './DebouncedSearchProvider'; import DebouncedSearchProvider from './DebouncedSearchProvider';
import SearchProviderSettingsItem from './SearchProviderSettingsItem'; import SearchProviderSettingsItem from './SearchProviderSettingsItem';

View File

@@ -24,10 +24,10 @@ import {
scrollAreaClasses, scrollAreaClasses,
textInputClasses, textInputClasses,
} from 'styles'; } from 'styles';
import DeleteBackground from './DeleteBackground'; import { DeleteBackground } from './DeleteBackground';
import EditBackground from './EditBackground'; import { EditBackground } from './EditBackground';
function BackgroundModal({ export function BackgroundModal({
backgroundModalOpened, backgroundModalOpened,
handleBackgroundModalClose, handleBackgroundModalClose,
backgroundList, backgroundList,
@@ -97,7 +97,7 @@ function BackgroundModal({
<Group w={'100%'} justify='center'> <Group w={'100%'} justify='center'>
<TextInput <TextInput
w={'50%'} w={'50%'}
className={textInputClasses.settingsItem} className={textInputClasses.default}
classNames={textInputClasses} classNames={textInputClasses}
autoFocus={!isMobile} autoFocus={!isMobile}
placeholder={'Search...'} placeholder={'Search...'}
@@ -385,4 +385,3 @@ function BackgroundModal({
</Modal.Root> </Modal.Root>
); );
} }
export default BackgroundModal;

View File

@@ -1,7 +1,7 @@
import { Button, Card, Group, Stack, Text } from '@mantine/core'; import { Button, Card, Group, Stack, Text } from '@mantine/core';
import { useMutationAPI } from 'hooks'; import { useMutationAPI } from 'hooks';
function DeleteBackground({ export function DeleteBackground({
fileName, fileName,
setFileName, setFileName,
inUseBackgrounds, inUseBackgrounds,
@@ -91,5 +91,3 @@ function DeleteBackground({
</Stack> </Stack>
); );
} }
export default DeleteBackground;

View File

@@ -3,7 +3,7 @@ import { useCommonHooks, useMutationAPI } from 'hooks';
import { useState } from 'react'; import { useState } from 'react';
import { textInputClasses } from 'styles'; import { textInputClasses } from 'styles';
function EditBackground({ export function EditBackground({
fileName, fileName,
setFileName, setFileName,
inUseBackgrounds, inUseBackgrounds,
@@ -125,5 +125,3 @@ function EditBackground({
</Stack> </Stack>
); );
} }
export default EditBackground;

View File

@@ -0,0 +1,3 @@
export { BackgroundModal } from './BackgroundModal';
export { DeleteBackground } from './DeleteBackground';
export { EditBackground } from './EditBackground';

View File

@@ -1,360 +0,0 @@
import { ActionIcon, Box, Button, Group, Overlay, Stack, Text } from '@mantine/core';
import { useDisclosure, useElementSize } from '@mantine/hooks';
import { useCommonHooks, useMutationAPI } from 'hooks';
import { useState } from 'react';
import { FiEdit, FiShuffle, FiTrash, FiX } from 'react-icons/fi';
import { actionIconClasses, boxClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types';
import DeleteItemModal from '../Shared/DeleteItemModal';
function ThemeCard({
cardTheme,
handleOpen,
allThemes,
themePlaylists,
}: {
cardTheme: Theme;
handleOpen: (cardTheme: Theme) => void;
allThemes: Theme[];
themePlaylists: ThemePlaylist[];
}) {
const { ref, width } = useElementSize();
const [hover, setHover] = useState(false);
const [shiftEdit, setShiftEdit] = useState<number>(0);
const [shiftShuffle, setShiftShuffle] = useState<number>(0);
const { mutateAsync } = useMutationAPI();
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const {
handleThemeChange,
currentTheme,
setCurrentTheme,
theme,
currentThemePlaylist,
setCurrentThemePlaylist,
handleDelete,
} = useCommonHooks();
return (
<Group
gap={0}
align='center'
justify='center'
display={'grid'}
pos={'relative'}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<DeleteItemModal
modalOpened={modalOpened}
handleClose={closeModal}
handleDelete={() => handleDelete(cardTheme, 'api/themes')}
title={'Delete Theme'}
body={
<Stack gap={0} w={'100%'} h={'150px'} align='center'>
<Text>Are you sure you want to delete this theme?</Text>
<Text
style={{
fontWeight: 'bold',
fontSize: '1.25rem',
padding: '.5rem',
margin: '.5rem',
borderRadius: '10px',
backgroundColor: 'var(--mantine-color-accent-0)',
color: 'var(--mantine-color-background-0)',
}}
>{`"${cardTheme.name}"`}</Text>
<Text c='red'>This action cannot be undone!</Text>
</Stack>
}
/>
<Text
style={{
position: 'absolute',
top: -10,
left: 6,
paddingRight: '.25rem',
paddingLeft: '.25rem',
backgroundColor: cardTheme.backgroundColor,
borderRadius: '4px',
color:
cardTheme.borderColor === cardTheme.primaryColor
? cardTheme.accentColor
: cardTheme.primaryColor,
zIndex: 3,
boxShadow: `2px -2px ${cardTheme.borderColor}`,
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
maxWidth: width,
}}
>
{cardTheme.name}
</Text>
<Button
ref={ref}
w={'275px'}
h={'115px'}
disabled={currentTheme === 'random'}
style={{
backgroundImage:
cardTheme?.backgroundImage.toLocaleLowerCase() !== 'none'
? `linear-gradient(${cardTheme?.backgroundColor}33, ${cardTheme?.backgroundColor}33), url(../../api/backgroundImages/${cardTheme?.backgroundImage})`
: undefined,
backgroundSize:
cardTheme?.backgroundImage.toLocaleLowerCase() !== 'none'
? 'cover'
: undefined,
backgroundColor:
cardTheme?.backgroundImage.toLocaleLowerCase() === 'none'
? cardTheme?.backgroundColor
: undefined,
backgroundPosition: 'center',
border: `solid ${cardTheme.borderColor}`,
filter: cardTheme.randomEnabled === 0 ? 'saturate(0)' : undefined,
}}
onClick={() => {
setCurrentTheme(cardTheme.name);
setCurrentThemePlaylist('All');
}}
>
{hover && currentTheme !== 'random' && <Overlay opacity={0.5} zIndex={1} />}
<Group w={'100%'} gap={0} display={'flex'} align='center' justify='center'>
<Stack align='center'>
<Box
style={{
height: '10px',
width: '100px',
backgroundColor: cardTheme.searchColor,
borderRadius: '4px',
borderBottom: `solid 1px ${cardTheme.borderColor}`,
}}
/>
<Group justify='space-between'>
<Box
style={{
height: '.5rem',
width: '100px',
backgroundColor: cardTheme.accentColor,
borderRadius: '4px',
}}
/>
<Box
style={{
height: '.5rem',
width: '50px',
backgroundColor: cardTheme.primaryColor,
borderRadius: '4px',
}}
/>
</Group>
<Group w={'100%'}>
<Box
style={{
height: '.25rem',
width: '40px',
backgroundColor: cardTheme.primaryColor,
borderRadius: '4px',
}}
/>
</Group>
<Group w={'100%'}>
<Box
className={boxClasses.appItemExampleText}
style={{
color: cardTheme.primaryColor,
}}
>
lorem ipsum
</Box>
<Box
className={boxClasses.appItemExampleText}
style={{
color: cardTheme.primaryColor,
}}
>
lorem ipsum
</Box>
<Box
className={boxClasses.appItemExampleText}
style={{
color: cardTheme.primaryColor,
}}
>
lorem ipsum
</Box>
</Group>
</Stack>
</Group>
</Button>
{currentTheme === 'random' && cardTheme.name === theme.name ? (
<Group>
<Group
onMouseEnter={() => {
setShiftEdit(2);
}}
onMouseLeave={() => {
setShiftEdit(0);
}}
onClick={() => {
handleOpen(cardTheme);
}}
gap={0}
style={{
zIndex: 300,
position: 'absolute',
left: 0,
top: 27 - shiftEdit,
backgroundColor:
shiftEdit === 0 ? 'white' : 'var(--mantine-color-background-0)',
borderRadius: '50%',
padding: '.5rem',
margin: '.25rem',
border: 'solid 2px',
borderColor: shiftEdit === 0 ? 'black' : 'var(--mantine-color-primary-0)',
transition: 'all 0.2s',
}}
>
<FiEdit
style={{
fontSize: '2rem',
fontWeight: 'bold',
color: shiftEdit === 0 ? 'black' : 'var(--mantine-color-primary-0)',
cursor: 'pointer',
}}
/>
</Group>
<Group
onMouseEnter={() => {
setShiftShuffle(2);
}}
onMouseLeave={() => {
setShiftShuffle(0);
}}
onClick={() => {
if (currentThemePlaylist === 'All') {
handleThemeChange(allThemes, false);
} else if (currentThemePlaylist === 'Backgrounds') {
handleThemeChange(
allThemes.filter(
(a: any) => !a.backgroundImage.toLowerCase().includes('none'),
),
false,
);
} else if (currentThemePlaylist === 'No Backgrounds') {
handleThemeChange(
allThemes.filter((a: any) =>
a.backgroundImage.toLowerCase().includes('none'),
),
false,
);
} else {
const themeList = JSON.parse(
themePlaylists?.find(
(t: ThemePlaylist) => t?.name === currentThemePlaylist,
)?.themeList ?? '[]',
);
const filteredThemes = allThemes.filter((t: Theme) =>
themeList.includes(t.id),
);
handleThemeChange(filteredThemes, false);
}
}}
gap={0}
style={{
zIndex: 300,
position: 'absolute',
right: 0,
top: 27 - shiftShuffle,
backgroundColor:
shiftShuffle === 0 ? 'white' : 'var(--mantine-color-background-0)',
borderRadius: '50%',
padding: '.5rem',
margin: '.25rem',
border: 'solid 2px',
borderColor:
shiftShuffle === 0 ? 'black' : 'var(--mantine-color-primary-0)',
transition: 'all 0.2s',
}}
>
<FiShuffle
style={{
fontSize: '2rem',
fontWeight: 'bold',
color: shiftShuffle === 0 ? 'black' : 'var(--mantine-color-primary-0)',
cursor: 'pointer',
}}
/>
</Group>
</Group>
) : (
<Stack
gap={0}
style={{
zIndex: 2,
padding: '.25rem',
paddingRight: 0,
position: 'absolute',
right: 3,
backgroundColor: '#ffffff88',
borderTopLeftRadius: '10px',
borderBottomLeftRadius: '10px',
}}
align='center'
justify='center'
display={
currentTheme === 'random' && cardTheme.name === theme.name ? 'none' : 'flex'
}
>
<ActionIcon
style={{
background: cardTheme.randomEnabled
? 'rgba(255,255,255,1)'
: 'rgba(255,255,255,0.5)',
margin: '.125rem',
}}
styles={{
root: {
height: '10px',
width: '10px',
},
icon: {
height: '20px',
width: '20px',
fontSize: '1.5rem',
margin: 0,
padding: 0,
},
}}
onClick={() => {
mutateAsync({
method: 'PATCH',
endpoint: 'api/themes',
body: { ...cardTheme, randomEnabled: !cardTheme.randomEnabled },
});
}}
>
{cardTheme.randomEnabled ? <FiShuffle color='green' /> : <FiX color='red' />}
</ActionIcon>
<ActionIcon
className={actionIconClasses.themeEditButton}
classNames={actionIconClasses}
onClick={() => handleOpen(cardTheme)}
mb={'.125rem'}
>
<FiEdit color='green' />
</ActionIcon>
<ActionIcon
className={actionIconClasses.themeEditButton}
classNames={actionIconClasses}
onClick={openModal}
disabled={cardTheme.name === theme.name}
>
<FiTrash color='red' />
</ActionIcon>
</Stack>
)}
</Group>
);
}
export default ThemeCard;

View File

@@ -8,15 +8,15 @@ import {
Text, Text,
} from '@mantine/core'; } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks'; import { useDisclosure } from '@mantine/hooks';
import { DeleteItemModal } from 'components';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { useState } from 'react'; import { useState } from 'react';
import { FiEdit, FiTrash } from 'react-icons/fi'; import { FiEdit, FiTrash } from 'react-icons/fi';
import { scrollAreaClasses } from 'styles'; import { scrollAreaClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
import DeleteItemModal from '../Shared/DeleteItemModal'; import { ThemeMiniCard } from '../Themes';
import ThemeMiniCard from './ThemeMiniCard';
function ThemePlaylistCard({ export function ThemePlaylistCard({
cardPlaylist, cardPlaylist,
handleOpen, handleOpen,
allThemes, allThemes,
@@ -220,5 +220,3 @@ function ThemePlaylistCard({
</Group> </Group>
); );
} }
export default ThemePlaylistCard;

View File

@@ -3,10 +3,9 @@ import { useCommonHooks } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { modalClasses, textInputClasses } from 'styles'; import { modalClasses, textInputClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
import ThemePreviewMiddleware from './ThemePreivewMiddleware'; import { ThemePreviewMiddleware, TimeOfDay } from '../Themes';
import TimeOfDay from './TimeOfDay';
function ThemePlaylistModal({ export function ThemePlaylistModal({
opened, opened,
handleClose, handleClose,
createPlaylist, createPlaylist,
@@ -127,5 +126,3 @@ function ThemePlaylistModal({
</Modal.Root> </Modal.Root>
); );
} }
export default ThemePlaylistModal;

View File

@@ -1,16 +1,15 @@
import { Box, Group, SimpleGrid, Stack, Text } from '@mantine/core'; import { Box, Group, SimpleGrid, Stack, Text } from '@mantine/core';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FiChevronRight } from 'react-icons/fi'; import { FiChevronRight } from 'react-icons/fi';
import { textClasses } from 'styles'; import { textClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
import NoItemsFound from '../Shared/NoItemsFound'; import { ThemePlaylistCard } from './ThemePlaylistCard';
import SettingsHeader from '../Shared/SettingsHeader'; import { ThemePlaylistModal } from './ThemePlaylistModal';
import ThemePlaylistCard from './ThemePlaylistCard';
import ThemePlaylistModal from './ThemePlaylistModal';
function ThemePlaylists() { export function ThemePlaylists() {
const themesEndpoint = 'api/themes'; const themesEndpoint = 'api/themes';
const { data: allThemeData } = useQueryAPI({ const { data: allThemeData } = useQueryAPI({
endpoint: themesEndpoint, endpoint: themesEndpoint,
@@ -303,5 +302,3 @@ function ThemePlaylists() {
</Stack> </Stack>
); );
} }
export default ThemePlaylists;

View File

@@ -0,0 +1,3 @@
export { ThemePlaylistCard } from './ThemePlaylistCard';
export { ThemePlaylistModal } from './ThemePlaylistModal';
export { ThemePlaylists } from './ThemePlaylists';

View File

@@ -0,0 +1,205 @@
import { Box, Button, Group, Overlay, Stack, Text } from '@mantine/core';
import {
useDebouncedState,
useDisclosure,
useElementSize,
useHover,
} from '@mantine/hooks';
import { DeleteItemModal } from 'components';
import { useCommonHooks } from 'hooks';
import { boxClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types';
import { ThemeManagementPill } from '.';
export function ThemeCard({
cardTheme,
handleOpen,
allThemes,
themePlaylists,
}: {
cardTheme: Theme;
handleOpen: (cardTheme: Theme) => void;
allThemes: Theme[];
themePlaylists: ThemePlaylist[];
}) {
const { hovered, ref: hoverRef } = useHover();
const { ref, width } = useElementSize();
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const { currentTheme, setCurrentTheme, setCurrentThemePlaylist, handleDelete } =
useCommonHooks();
const [hoverPreview, setHoverPreview] = useDebouncedState(false, 1000);
return (
<Group
gap={0}
align='center'
justify='center'
display={'grid'}
pos={'relative'}
ref={hoverRef}
>
<DeleteItemModal
modalOpened={modalOpened}
handleClose={closeModal}
handleDelete={() => handleDelete(cardTheme, 'api/themes')}
title={'Delete Theme'}
body={
<Stack gap={0} w={'100%'} h={'150px'} align='center'>
<Text>Are you sure you want to delete this theme?</Text>
<Text
style={{
fontWeight: 'bold',
fontSize: '1.25rem',
padding: '.5rem',
margin: '.5rem',
borderRadius: '10px',
backgroundColor: 'var(--mantine-color-accent-0)',
color: 'var(--mantine-color-background-0)',
}}
>{`"${cardTheme.name}"`}</Text>
<Text c='red'>This action cannot be undone!</Text>
</Stack>
}
/>
<Text
style={{
position: 'absolute',
top: -10,
left: 6,
paddingRight: '.25rem',
paddingLeft: '.25rem',
backgroundColor: cardTheme.backgroundColor,
borderRadius: '4px',
color:
cardTheme.borderColor === cardTheme.primaryColor
? cardTheme.accentColor
: cardTheme.primaryColor,
zIndex: 3,
boxShadow: `2px -2px ${cardTheme.borderColor}`,
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
maxWidth: width,
}}
>
{cardTheme.name}
</Text>
<Group
style={{
width: '100%',
height: '100%',
overflow: 'hidden',
transition: 'all .2s ease',
position: 'absolute',
}}
>
<ThemeManagementPill
allThemes={allThemes}
cardTheme={cardTheme}
handleOpen={handleOpen}
themePlaylists={themePlaylists}
openModal={openModal}
hovered={hovered}
setHoverPreview={setHoverPreview}
/>
</Group>
<Button
ref={ref}
w={'275px'}
h={'115px'}
disabled={currentTheme === 'random'}
style={{
backgroundImage:
cardTheme?.backgroundImage.toLocaleLowerCase() !== 'none'
? `linear-gradient(${cardTheme?.backgroundColor}33, ${cardTheme?.backgroundColor}33), url(../../api/backgroundImages/${cardTheme?.backgroundImage})`
: undefined,
backgroundSize:
cardTheme?.backgroundImage.toLocaleLowerCase() !== 'none'
? 'cover'
: undefined,
backgroundColor:
cardTheme?.backgroundImage.toLocaleLowerCase() === 'none'
? cardTheme?.backgroundColor
: undefined,
backgroundPosition: 'center',
border: `solid ${cardTheme.borderColor}`,
filter:
cardTheme.randomEnabled === 0 && !hoverPreview ? 'saturate(0)' : undefined,
transition: 'all .2s ease',
}}
onClick={() => {
setCurrentTheme(cardTheme.name);
setCurrentThemePlaylist('All');
}}
>
{hovered && currentTheme !== 'random' && <Overlay opacity={0.5} zIndex={1} />}
<Group w={'100%'} gap={0} display={'flex'} align='center' justify='center'>
<Stack align='center'>
<Box
style={{
height: '10px',
width: '100px',
backgroundColor: cardTheme.searchColor,
borderRadius: '4px',
borderBottom: `solid 1px ${cardTheme.borderColor}`,
}}
/>
<Group justify='space-between'>
<Box
style={{
height: '.5rem',
width: '100px',
backgroundColor: cardTheme.accentColor,
borderRadius: '4px',
}}
/>
<Box
style={{
height: '.5rem',
width: '50px',
backgroundColor: cardTheme.primaryColor,
borderRadius: '4px',
}}
/>
</Group>
<Group w={'100%'}>
<Box
style={{
height: '.25rem',
width: '40px',
backgroundColor: cardTheme.primaryColor,
borderRadius: '4px',
}}
/>
</Group>
<Group w={'100%'}>
<Box
className={boxClasses.appItemExampleText}
style={{
color: cardTheme.primaryColor,
}}
>
lorem ipsum
</Box>
<Box
className={boxClasses.appItemExampleText}
style={{
color: cardTheme.primaryColor,
}}
>
lorem ipsum
</Box>
<Box
className={boxClasses.appItemExampleText}
style={{
color: cardTheme.primaryColor,
}}
>
lorem ipsum
</Box>
</Group>
</Stack>
</Group>
</Button>
</Group>
);
}

View File

@@ -4,7 +4,7 @@ import { FiCheck } from 'react-icons/fi';
import { boxClasses, textClasses } from 'styles'; import { boxClasses, textClasses } from 'styles';
import type { Theme } from 'types'; import type { Theme } from 'types';
function ExampleApp({ selectedTheme }: { selectedTheme: Theme }) { export function ThemeExampleApp({ selectedTheme }: { selectedTheme: Theme }) {
const [textDecoration, setTextDecoration] = useState<string>('none'); const [textDecoration, setTextDecoration] = useState<string>('none');
return ( return (
@@ -53,5 +53,3 @@ function ExampleApp({ selectedTheme }: { selectedTheme: Theme }) {
</Box> </Box>
); );
} }
export default ExampleApp;

View File

@@ -0,0 +1,227 @@
import { ActionIcon, Group, isLightColor, Stack, Text } from '@mantine/core';
import { useCommonHooks, useMutationAPI } from 'hooks';
import { useState } from 'react';
import { FaTrashAlt } from 'react-icons/fa';
import { FaPencil } from 'react-icons/fa6';
import { FiEdit, FiPower, FiShuffle } from 'react-icons/fi';
import { actionIconClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types';
export function ThemeManagementPill({
allThemes,
cardTheme,
handleOpen,
themePlaylists,
openModal,
hovered,
setHoverPreview,
}: {
allThemes: Theme[];
cardTheme: Theme;
handleOpen: (cardTheme: Theme) => void;
themePlaylists: ThemePlaylist[];
openModal: () => void;
hovered: boolean;
setHoverPreview: (hoverPreview: boolean) => void;
}) {
const { currentTheme, currentThemePlaylist, handleThemeChange, theme } =
useCommonHooks();
const [shiftEdit, setShiftEdit] = useState<number>(0);
const [shiftShuffle, setShiftShuffle] = useState<number>(0);
const { mutateAsync } = useMutationAPI();
return (
<>
{currentTheme === 'random' && cardTheme.name === theme.name ? (
<Group>
<Group
onMouseEnter={() => {
setShiftEdit(2);
}}
onMouseLeave={() => {
setShiftEdit(0);
}}
onClick={() => {
handleOpen(cardTheme);
}}
gap={0}
style={{
zIndex: 300,
position: 'absolute',
left: 0,
top: 27 - shiftEdit,
backgroundColor:
shiftEdit === 0 ? 'white' : 'var(--mantine-color-background-0)',
borderRadius: '50%',
padding: '.5rem',
margin: '.25rem',
border: 'solid 2px',
borderColor: shiftEdit === 0 ? 'black' : 'var(--mantine-color-primary-0)',
transition: 'all 0.2s',
}}
>
<FiEdit
style={{
fontSize: '2rem',
fontWeight: 'bold',
color: shiftEdit === 0 ? 'black' : 'var(--mantine-color-primary-0)',
cursor: 'pointer',
}}
/>
</Group>
<Group
onMouseEnter={() => {
setShiftShuffle(2);
}}
onMouseLeave={() => {
setShiftShuffle(0);
}}
onClick={() => {
if (currentThemePlaylist === 'All') {
handleThemeChange(allThemes, false);
} else if (currentThemePlaylist === 'Backgrounds') {
handleThemeChange(
allThemes.filter(
(a: any) => !a.backgroundImage.toLowerCase().includes('none'),
),
false,
);
} else if (currentThemePlaylist === 'No Backgrounds') {
handleThemeChange(
allThemes.filter((a: any) =>
a.backgroundImage.toLowerCase().includes('none'),
),
false,
);
} else {
const themeList = JSON.parse(
themePlaylists?.find(
(t: ThemePlaylist) => t?.name === currentThemePlaylist,
)?.themeList ?? '[]',
);
const filteredThemes = allThemes.filter((t: Theme) =>
themeList.includes(t.id),
);
handleThemeChange(filteredThemes, false);
}
}}
gap={0}
style={{
zIndex: 300,
position: 'absolute',
right: 0,
top: 27 - shiftShuffle,
backgroundColor:
shiftShuffle === 0 ? 'white' : 'var(--mantine-color-background-0)',
borderRadius: '50%',
padding: '.5rem',
margin: '.25rem',
border: 'solid 2px',
borderColor:
shiftShuffle === 0 ? 'black' : 'var(--mantine-color-primary-0)',
transition: 'all 0.2s',
}}
>
<FiShuffle
style={{
fontSize: '2rem',
fontWeight: 'bold',
color: shiftShuffle === 0 ? 'black' : 'var(--mantine-color-primary-0)',
cursor: 'pointer',
}}
/>
</Group>
</Group>
) : (
<>
<Stack
gap={0}
style={{
zIndex: 2,
paddingRight: 0,
position: 'absolute',
right: 3,
top: 3,
marginRight: !hovered ? 0 : '-2.5rem',
transition: 'all 0.2s',
}}
align='center'
justify='center'
h={'100%'}
>
{!cardTheme.randomEnabled && (
<Text
style={{
writingMode: 'sideways-lr',
color: isLightColor(cardTheme.backgroundColor) ? 'black' : 'white',
opacity: 0.5,
}}
>
DISABLED
</Text>
)}
</Stack>
<Stack
gap={0}
style={{
zIndex: 2,
padding: '.25rem',
paddingRight: 0,
position: 'absolute',
right: 3,
backgroundColor: '#ffffff33',
borderTopLeftRadius: '10px',
borderBottomLeftRadius: '10px',
marginRight: hovered ? 0 : '-2.5rem',
transition: 'all 0.2s',
backdropFilter: 'blur(10px)',
}}
align='center'
justify='center'
>
<ActionIcon
className={actionIconClasses.themeEditButton}
classNames={actionIconClasses}
onMouseEnter={() => setHoverPreview(true)}
onMouseLeave={() => setHoverPreview(false)}
onClick={() => {
mutateAsync({
method: 'PATCH',
endpoint: 'api/themes',
body: { ...cardTheme, randomEnabled: !cardTheme.randomEnabled },
});
}}
mb={'.125rem'}
>
<FiPower
color={cardTheme.randomEnabled ? 'DarkOliveGreen' : '#33333388'}
style={{ strokeWidth: 4, filter: 'brightness(1.5)' }}
/>
</ActionIcon>
<ActionIcon
className={actionIconClasses.themeEditButton}
classNames={actionIconClasses}
onClick={() => handleOpen(cardTheme)}
mb={'.125rem'}
>
<FaPencil
style={{ padding: '.25rem', filter: 'brightness(1.5)' }}
color='DarkOliveGreen'
/>
</ActionIcon>
<ActionIcon
className={actionIconClasses.themeEditButton}
classNames={actionIconClasses}
onClick={openModal}
disabled={cardTheme.name === theme.name}
>
<FaTrashAlt
style={{ padding: '.25rem', filter: 'brightness(1.25)' }}
color='Brown'
/>
</ActionIcon>
</Stack>
</>
)}
</>
);
}

View File

@@ -1,7 +1,7 @@
import { Group, Text } from '@mantine/core'; import { Group, Text } from '@mantine/core';
import type { Theme } from 'types'; import type { Theme } from 'types';
function ThemeMiniCard({ miniTheme }: { miniTheme: Theme | undefined }) { export function ThemeMiniCard({ miniTheme }: { miniTheme: Theme | undefined }) {
return ( return (
<Group <Group
gap={0} gap={0}
@@ -41,5 +41,3 @@ function ThemeMiniCard({ miniTheme }: { miniTheme: Theme | undefined }) {
</Group> </Group>
); );
} }
export default ThemeMiniCard;

View File

@@ -19,9 +19,9 @@ import { useEffect, useRef, useState } from 'react';
import { FiImage, FiSave, FiX } from 'react-icons/fi'; import { FiImage, FiSave, FiX } from 'react-icons/fi';
import { modalClasses, scrollAreaClasses, textClasses, textInputClasses } from 'styles'; import { modalClasses, scrollAreaClasses, textClasses, textInputClasses } from 'styles';
import type { Theme } from 'types'; import type { Theme } from 'types';
import ThemeModalContent from './ThemeModalContent'; import { ThemeModalContent } from './ThemeModalContent';
function ThemeModal({ export function ThemeModal({
opened, opened,
handleClose, handleClose,
selectedTheme, selectedTheme,
@@ -547,5 +547,3 @@ function ThemeModal({
</Modal.Root> </Modal.Root>
); );
} }
export default ThemeModal;

View File

@@ -2,7 +2,7 @@ import { ColorSwatch, Group, Stack, Text } from '@mantine/core';
import { colorSwatchClasses, textClasses } from 'styles'; import { colorSwatchClasses, textClasses } from 'styles';
import type { Theme } from 'types'; import type { Theme } from 'types';
function ThemeModalColorSwatches({ export function ThemeModalColorSwatches({
selectedTheme, selectedTheme,
openPicker, openPicker,
setColorId, setColorId,
@@ -93,5 +93,3 @@ function ThemeModalColorSwatches({
</Stack> </Stack>
); );
} }
export default ThemeModalColorSwatches;

View File

@@ -2,9 +2,9 @@ import { Select, Stack, Text } from '@mantine/core';
import SearchSelectItem from 'pages/homepage/components/Search/SearchSelectItem'; import SearchSelectItem from 'pages/homepage/components/Search/SearchSelectItem';
import { selectClasses } from 'styles'; import { selectClasses } from 'styles';
import type { Theme } from 'types'; import type { Theme } from 'types';
import ThemeExampleApp from './ThemeExampleApp'; import { ThemeExampleApp } from './ThemeExampleApp';
export default function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) { export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
return ( return (
<Stack <Stack
style={{ style={{

View File

@@ -1,10 +1,10 @@
import { Group, Overlay, Popover } from '@mantine/core'; import { Group, Overlay, Popover } from '@mantine/core';
import { useLongPress } from '@mantine/hooks'; import { useLongPress } from '@mantine/hooks';
import { useState } from 'react'; import { useState } from 'react';
import ThemeMiniCard from './ThemeMiniCard'; import { ThemeMiniCard } from './ThemeMiniCard';
import ThemeModalContent from './ThemeModalContent'; import { ThemeModalContent } from './ThemeModalContent';
function ThemePreviewMiddleware({ export function ThemePreviewMiddleware({
newThemeList, newThemeList,
miniTheme, miniTheme,
themeList, themeList,
@@ -79,5 +79,3 @@ function ThemePreviewMiddleware({
</Popover> </Popover>
); );
} }
export default ThemePreviewMiddleware;

View File

@@ -7,7 +7,7 @@ import {
} from '@mantine/core'; } from '@mantine/core';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
function ThemeSelect({ export function ThemeSelect({
allThemes, allThemes,
createPlaylist, createPlaylist,
themeList, themeList,
@@ -124,5 +124,3 @@ function ThemeSelect({
</Group> </Group>
); );
} }
export default ThemeSelect;

View File

@@ -1,5 +1,6 @@
import { Box, Button, Group, Overlay, Stack, Switch, Text, Tooltip } from '@mantine/core'; import { Box, Button, Group, Overlay, Stack, Switch, Text, Tooltip } from '@mantine/core';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FaDice } from 'react-icons/fa'; import { FaDice } from 'react-icons/fa';
@@ -7,13 +8,11 @@ import { FiChevronRight, FiEdit, FiMousePointer, FiStopCircle } from 'react-icon
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { switchClasses, textClasses } from 'styles'; import { switchClasses, textClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
import NoItemsFound from '../Shared/NoItemsFound'; import { BackgroundModal } from '../Backgrounds';
import SettingsHeader from '../Shared/SettingsHeader'; import { ThemeCard } from './ThemeCard';
import BackgroundModal from './BackgroundModal'; import { ThemeModal } from './ThemeModal';
import ThemeCard from './ThemeCard';
import ThemeModal from './ThemeModal';
function ThemesSettings() { export function Themes() {
const themesEndpoint = 'api/themes'; const themesEndpoint = 'api/themes';
const backgroundsEndpoint = 'api/backgrounds'; const backgroundsEndpoint = 'api/backgrounds';
const { data: allThemeData, isFetched: isAllThemeDataFetched } = useQueryAPI({ const { data: allThemeData, isFetched: isAllThemeDataFetched } = useQueryAPI({
@@ -62,6 +61,7 @@ function ThemesSettings() {
currentThemePlaylist, currentThemePlaylist,
setCurrentThemePlaylist, setCurrentThemePlaylist,
} = useCommonHooks(); } = useCommonHooks();
const handleOpen = (theme: Theme) => { const handleOpen = (theme: Theme) => {
setSelectedTheme(theme); setSelectedTheme(theme);
openThemeModal(); openThemeModal();
@@ -379,4 +379,3 @@ function ThemesSettings() {
</Stack> </Stack>
); );
} }
export default ThemesSettings;

View File

@@ -1,8 +1,8 @@
import { Stack } from '@mantine/core'; import { Stack } from '@mantine/core';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
import ThemeSelect from './ThemeSelect'; import { ThemeSelect } from './ThemeSelect';
function TimeOfDay({ export function TimeOfDay({
createPlaylist, createPlaylist,
themeList, themeList,
allThemes, allThemes,
@@ -50,5 +50,3 @@ function TimeOfDay({
</Stack> </Stack>
); );
} }
export default TimeOfDay;

View File

@@ -0,0 +1,11 @@
export { ThemeCard } from './ThemeCard';
export { ThemeExampleApp } from './ThemeExampleApp';
export { ThemeManagementPill } from './ThemeManagementPill';
export { ThemeMiniCard } from './ThemeMiniCard';
export { ThemeModal } from './ThemeModal';
export { ThemeModalColorSwatches } from './ThemeModalColorSwatches';
export { ThemeModalContent } from './ThemeModalContent';
export { ThemePreviewMiddleware } from './ThemePreviewMiddleware';
export { ThemeSelect } from './ThemeSelect';
export { Themes } from './Themes';
export { TimeOfDay } from './TimeOfDay';

View File

@@ -1,11 +1,11 @@
import { ActionIcon, Card, Group, Stack, Text, TextInput, Tooltip } from '@mantine/core'; import { ActionIcon, Card, Group, Stack, Text, TextInput, Tooltip } from '@mantine/core';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { DeleteItemModal } from 'components';
import { useCommonHooks, useMutationAPI } from 'hooks'; import { useCommonHooks, useMutationAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FiTrash } from 'react-icons/fi'; import { FiTrash } from 'react-icons/fi';
import { cardClasses, textInputClasses } from 'styles'; import { cardClasses, textInputClasses } from 'styles';
import type { WolClient } from 'types'; import type { WolClient } from 'types';
import DeleteItemModal from '../Shared/DeleteItemModal';
function DebouncedWolClient({ function DebouncedWolClient({
wolClient, wolClient,

View File

@@ -1,13 +1,11 @@
import { Button, Grid, GridCol, Group, Popover, Stack, TextInput } from '@mantine/core'; import { Button, Grid, GridCol, Group, Popover, Stack, TextInput } from '@mantine/core';
import { isNotEmpty, matches, useForm } from '@mantine/form'; import { isNotEmpty, matches, useForm } from '@mantine/form';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { popoverClasses, textInputClasses } from 'styles'; import { popoverClasses, textInputClasses } from 'styles';
import type { WolClient } from 'types'; import type { WolClient } from 'types';
import AddItemModal from '../Shared/AddItemModal';
import NoItemsFound from '../Shared/NoItemsFound';
import SettingsHeader from '../Shared/SettingsHeader';
import DebouncedWolClient from './DebouncedWolClient'; import DebouncedWolClient from './DebouncedWolClient';
import WolClientSettingsItem from './WolClientSettingsItem'; import WolClientSettingsItem from './WolClientSettingsItem';

View File

@@ -11,25 +11,24 @@
.loader { .loader {
display: none; display: none;
} }
:hover {
background-color: transparent !important;
color: var(--mantine-color-primary-0) !important;
}
} }
} }
.themeEditButton { .themeEditButton {
&.root { &.root {
background-color: white !important; background-color: transparent;
border-radius: 100%;
.icon { .icon {
font-size: 1.5rem; font-size: 1.5rem;
} }
.loader { .loader {
display: none; display: none;
} }
:hover {
background-color: var(--mantine-color-primary-0) !important; }
color: var(--mantine-color-accent-0) !important; &:hover {
&.root {
background-color: white;
} }
} }
} }

View File

@@ -16,6 +16,10 @@
padding-left: .5rem; padding-left: .5rem;
opacity: 1; opacity: 1;
} }
::placeholder {
color: var(--mantine-color-accent-0);
opacity: 50%;
}
} }
} }
} }
@@ -57,7 +61,7 @@
opacity: 1; opacity: 1;
} }
::placeholder { ::placeholder {
color: var(--mantine-color-accent-0) !important; color: var(--mantine-color-accent-0);
opacity: 50%; opacity: 50%;
} }
} }

View File

@@ -4,4 +4,5 @@ export interface Bookmark {
url: string; url: string;
index: number; index: number;
category: string; category: string;
enabled: number;
} }