mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 07:25:48 +01:00
Trying to fix linting errors
This commit is contained in:
@@ -1,163 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { Accordion, Grid, Stack, Title, useMantineColorScheme } from '@mantine/core';
|
|
||||||
import {
|
|
||||||
closestCenter,
|
|
||||||
DndContext,
|
|
||||||
DragOverlay,
|
|
||||||
MouseSensor,
|
|
||||||
TouchSensor,
|
|
||||||
useSensor,
|
|
||||||
useSensors,
|
|
||||||
} from '@dnd-kit/core';
|
|
||||||
|
|
||||||
import { arrayMove, SortableContext } from '@dnd-kit/sortable';
|
|
||||||
import { useLocalStorage } from '@mantine/hooks';
|
|
||||||
import * as Modules from '../../modules';
|
|
||||||
import { useConfig } from '../../tools/state';
|
|
||||||
|
|
||||||
import { AppShelfItem, SortableItem } from './AppShelfItem';
|
|
||||||
import { ModuleWrapper } from '../../modules/moduleWrapper';
|
|
||||||
import { UsenetModule, TorrentsModule } from '../../modules';
|
|
||||||
|
|
||||||
const AppShelf = (props: any) => {
|
|
||||||
const { config, setConfig } = useConfig();
|
|
||||||
// Extract all the categories from the services in config
|
|
||||||
const categoryList = config.apps.reduce((acc, cur) => {
|
|
||||||
if (cur.category && !acc.includes(cur.category)) {
|
|
||||||
acc.push(cur.category);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, [] as string[]);
|
|
||||||
|
|
||||||
const [toggledCategories, setToggledCategories] = useLocalStorage({
|
|
||||||
key: 'app-shelf-toggled',
|
|
||||||
// This is a bit of a hack to toggle the categories on the first load, return a string[] of the categories
|
|
||||||
defaultValue: categoryList,
|
|
||||||
});
|
|
||||||
const [activeId, setActiveId] = useState(null);
|
|
||||||
const { colorScheme } = useMantineColorScheme();
|
|
||||||
|
|
||||||
const sensors = useSensors(
|
|
||||||
useSensor(TouchSensor, {
|
|
||||||
activationConstraint: {
|
|
||||||
delay: 500,
|
|
||||||
tolerance: 5,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
useSensor(MouseSensor, {
|
|
||||||
// Require the mouse to move by 10 pixels before activating
|
|
||||||
activationConstraint: {
|
|
||||||
delay: 500,
|
|
||||||
tolerance: 5,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
function handleDragStart(event: any) {
|
|
||||||
const { active } = event;
|
|
||||||
|
|
||||||
setActiveId(active.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragEnd(event: any) {
|
|
||||||
const { active, over } = event;
|
|
||||||
|
|
||||||
if (active.id !== over.id) {
|
|
||||||
const newConfig = { ...config };
|
|
||||||
const activeIndex = newConfig.apps.findIndex((e) => e.id === active.id);
|
|
||||||
const overIndex = newConfig.apps.findIndex((e) => e.id === over.id);
|
|
||||||
newConfig.apps = arrayMove(newConfig.apps, activeIndex, overIndex);
|
|
||||||
setConfig(newConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
setActiveId(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const getItems = (filter?: string) => {
|
|
||||||
// If filter is not set, return all the services without a category or a null category
|
|
||||||
let filtered = config.apps;
|
|
||||||
const modules = Object.values(Modules).map((module) => module);
|
|
||||||
|
|
||||||
if (!filter) {
|
|
||||||
filtered = config.apps.filter((e) => !e.category || e.category === null);
|
|
||||||
}
|
|
||||||
if (filter) {
|
|
||||||
filtered = config.apps.filter((e) => e.category === filter);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<DndContext
|
|
||||||
sensors={sensors}
|
|
||||||
collisionDetection={closestCenter}
|
|
||||||
onDragStart={handleDragStart}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
>
|
|
||||||
<SortableContext items={config.apps}>
|
|
||||||
<Grid gutter="lg" grow={config.settings.grow}>
|
|
||||||
{filtered.map((service) => (
|
|
||||||
<Grid.Col key={service.id} span="content">
|
|
||||||
<SortableItem service={service} key={service.id} id={service.id}>
|
|
||||||
<AppShelfItem service={service} />
|
|
||||||
</SortableItem>
|
|
||||||
</Grid.Col>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
</SortableContext>
|
|
||||||
<DragOverlay
|
|
||||||
style={{
|
|
||||||
// Add a shadow to the drag overlay
|
|
||||||
boxShadow: '0 0 10px rgba(0, 0, 0, 0.5)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{activeId ? (
|
|
||||||
<AppShelfItem service={config.apps.find((e) => e.id === activeId)} id={activeId} />
|
|
||||||
) : null}
|
|
||||||
</DragOverlay>
|
|
||||||
</DndContext>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack>
|
|
||||||
<Accordion
|
|
||||||
variant="separated"
|
|
||||||
radius="lg"
|
|
||||||
order={2}
|
|
||||||
multiple
|
|
||||||
value={toggledCategories}
|
|
||||||
onChange={(state) => {
|
|
||||||
setToggledCategories([...state]);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{categoryList.map((category, idx) => (
|
|
||||||
<Accordion.Item
|
|
||||||
style={{
|
|
||||||
background: `rgba(${colorScheme === 'dark' ? '32, 33, 35,' : '255, 255, 255,'} \
|
|
||||||
${(config.settings.appOpacity || 100) / 100}`,
|
|
||||||
borderColor: `rgba(${colorScheme === 'dark' ? '32, 33, 35,' : '233, 236, 239,'} \
|
|
||||||
${(config.settings.appOpacity || 100) / 100}`,
|
|
||||||
}}
|
|
||||||
key={category}
|
|
||||||
value={idx.toString()}
|
|
||||||
>
|
|
||||||
<Accordion.Control>
|
|
||||||
<Title
|
|
||||||
order={5}
|
|
||||||
style={{
|
|
||||||
minWidth: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{category}
|
|
||||||
</Title>
|
|
||||||
</Accordion.Control>
|
|
||||||
<Accordion.Panel>{getItems(category)}</Accordion.Panel>
|
|
||||||
</Accordion.Item>
|
|
||||||
))}
|
|
||||||
</Accordion>
|
|
||||||
{getItems()}
|
|
||||||
<ModuleWrapper mt="xl" module={TorrentsModule} />
|
|
||||||
<ModuleWrapper mt="xl" module={UsenetModule} />
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AppShelf;
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
import { useSortable } from '@dnd-kit/sortable';
|
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
|
||||||
import {
|
|
||||||
Anchor,
|
|
||||||
AspectRatio,
|
|
||||||
Card,
|
|
||||||
Center,
|
|
||||||
createStyles,
|
|
||||||
Image,
|
|
||||||
Text,
|
|
||||||
useMantineColorScheme,
|
|
||||||
} from '@mantine/core';
|
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import PingComponent from '../../modules/ping/PingModule';
|
|
||||||
import { useConfig } from '../../tools/state';
|
|
||||||
import { serviceItem } from '../../tools/types';
|
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
|
||||||
item: {
|
|
||||||
transition: 'box-shadow 150ms ease, transform 100ms ease',
|
|
||||||
|
|
||||||
'&:hover': {
|
|
||||||
boxShadow: `${theme.shadows.md} !important`,
|
|
||||||
transform: 'scale(1.05)',
|
|
||||||
},
|
|
||||||
[theme.fn.smallerThan('sm')]: {
|
|
||||||
WebkitUserSelect: 'none',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
export function SortableItem(props: any) {
|
|
||||||
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
|
|
||||||
id: props.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
transform: CSS.Transform.toString(transform),
|
|
||||||
transition,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AppShelfItem(props: any) {
|
|
||||||
const { service }: { service: serviceItem } = props;
|
|
||||||
const [hovering, setHovering] = useState(false);
|
|
||||||
const { config } = useConfig();
|
|
||||||
const { colorScheme } = useMantineColorScheme();
|
|
||||||
const { classes } = useStyles();
|
|
||||||
return (
|
|
||||||
<motion.div
|
|
||||||
animate={{
|
|
||||||
scale: [0.9, 1.06, 1],
|
|
||||||
rotate: [0, 5, 0],
|
|
||||||
}}
|
|
||||||
transition={{ duration: 0.6, type: 'spring', damping: 10, mass: 0.75, stiffness: 100 }}
|
|
||||||
key={service.name}
|
|
||||||
onHoverStart={() => {
|
|
||||||
setHovering(true);
|
|
||||||
}}
|
|
||||||
onHoverEnd={() => {
|
|
||||||
setHovering(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
withBorder
|
|
||||||
radius="lg"
|
|
||||||
shadow="md"
|
|
||||||
className={classes.item}
|
|
||||||
style={{
|
|
||||||
// Use the grab cursor when hovering over the card
|
|
||||||
cursor: hovering ? 'grab' : 'auto',
|
|
||||||
background: `rgba(${colorScheme === 'dark' ? '37, 38, 43,' : '255, 255, 255,'} \
|
|
||||||
${(config.settings.appOpacity || 100) / 100}`,
|
|
||||||
borderColor: `rgba(${colorScheme === 'dark' ? '37, 38, 43,' : '233, 236, 239,'} \
|
|
||||||
${(config.settings.appOpacity || 100) / 100}`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Card.Section>
|
|
||||||
<Anchor
|
|
||||||
target={service.newTab === false ? '_top' : '_blank'}
|
|
||||||
href={service.openedUrl ? service.openedUrl : service.url}
|
|
||||||
style={{ color: 'inherit', fontStyle: 'inherit', fontSize: 'inherit' }}
|
|
||||||
>
|
|
||||||
<Text mt="sm" align="center" lineClamp={1} weight={550}>
|
|
||||||
{service.name}
|
|
||||||
</Text>
|
|
||||||
</Anchor>
|
|
||||||
<motion.div
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 15,
|
|
||||||
right: 15,
|
|
||||||
alignSelf: 'flex-end',
|
|
||||||
}}
|
|
||||||
animate={{
|
|
||||||
opacity: hovering ? 1 : 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* <TileMenu service={service} /> TODO: Remove this component */}
|
|
||||||
</motion.div>
|
|
||||||
</Card.Section>
|
|
||||||
<Card.Section>
|
|
||||||
<Center>
|
|
||||||
<AspectRatio
|
|
||||||
ratio={3 / 5}
|
|
||||||
m="lg"
|
|
||||||
style={{
|
|
||||||
height: 75 * ((config.settings.appCardWidth ?? 1) * 1.2),
|
|
||||||
width: 75 * ((config.settings.appCardWidth ?? 1) * 2),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<motion.i
|
|
||||||
whileHover={{
|
|
||||||
scale: 1.1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Anchor
|
|
||||||
href={service.openedUrl ?? service.url}
|
|
||||||
target={service.newTab === false ? '_top' : '_blank'}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
styles={{ root: { cursor: 'pointer' } }}
|
|
||||||
width={75 * ((config.settings.appCardWidth ?? 1) * 1.2)}
|
|
||||||
height={75 * ((config.settings.appCardWidth ?? 1) * 1.2)}
|
|
||||||
src={service.icon}
|
|
||||||
fit="contain"
|
|
||||||
/>
|
|
||||||
</Anchor>
|
|
||||||
</motion.i>
|
|
||||||
</AspectRatio>
|
|
||||||
{service.ping !== false && <PingComponent url={service.url} status={service.status} />}
|
|
||||||
</Center>
|
|
||||||
</Card.Section>
|
|
||||||
</Card>
|
|
||||||
</motion.div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Jellyseerr": "settings",
|
|
||||||
"Overseerr": "settings",
|
|
||||||
"Sonarr": "settings/general",
|
|
||||||
"Radarr": "settings/general",
|
|
||||||
"Readarr": "settings/general",
|
|
||||||
"Lidarr": "settings/general",
|
|
||||||
"Sabnzbd": "sabnzbd/config/general"
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export { default as AppShelf } from './AppShelf';
|
|
||||||
export * from './AppShelfItem';
|
|
||||||
@@ -33,8 +33,9 @@ export const WidgetElementType = ({ id, image, disabled, widget }: WidgetElement
|
|||||||
{
|
{
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
properties: Object.entries(widget.options).reduce((prev, [k, v]) => {
|
properties: Object.entries(widget.options).reduce((prev, [k, v]) => {
|
||||||
prev[k] = v.defaultValue;
|
const newPrev = prev;
|
||||||
return prev;
|
newPrev[k] = v.defaultValue;
|
||||||
|
return newPrev;
|
||||||
}, {} as IWidget<string, any>['properties']),
|
}, {} as IWidget<string, any>['properties']),
|
||||||
area: {
|
area: {
|
||||||
type: 'wrapper',
|
type: 'wrapper',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
interface ServiceIconProps {
|
interface ServiceIconProps {
|
||||||
size: '100%' | number;
|
size: '100%' | number;
|
||||||
service: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppIcon = ({ size }: ServiceIconProps) => null;
|
export const AppIcon = ({ size }: ServiceIconProps) => null;
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ export const initializeGridstack = (
|
|||||||
const columnCount = areaType === 'sidebar' ? 4 : Math.floor(wrapperRef.current.offsetWidth / 64);
|
const columnCount = areaType === 'sidebar' ? 4 : Math.floor(wrapperRef.current.offsetWidth / 64);
|
||||||
const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 64);
|
const minRow = areaType !== 'sidebar' ? 1 : Math.floor(wrapperRef.current.offsetHeight / 64);
|
||||||
// initialize gridstack
|
// initialize gridstack
|
||||||
gridRef.current = GridStack.init(
|
const newGrid = gridRef;
|
||||||
|
newGrid.current = GridStack.init(
|
||||||
{
|
{
|
||||||
column: columnCount,
|
column: columnCount,
|
||||||
margin: 10,
|
margin: 10,
|
||||||
@@ -37,7 +38,7 @@ export const initializeGridstack = (
|
|||||||
// selector of the gridstack item (it's eather category or wrapper)
|
// selector of the gridstack item (it's eather category or wrapper)
|
||||||
`.grid-stack-${areaType}[data-${areaType}='${areaId}']`
|
`.grid-stack-${areaType}[data-${areaType}='${areaId}']`
|
||||||
);
|
);
|
||||||
const grid = gridRef.current;
|
const grid = newGrid.current;
|
||||||
|
|
||||||
// Add listener for moving items around in a wrapper
|
// Add listener for moving items around in a wrapper
|
||||||
grid.on('change', (_, el) => {
|
grid.on('change', (_, el) => {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { getCookie, setCookie } from 'cookies-next';
|
import { getCookie, setCookie } from 'cookies-next';
|
||||||
import { GetServerSidePropsContext } from 'next';
|
import { GetServerSidePropsContext } from 'next';
|
||||||
import { SSRConfig } from 'next-i18next';
|
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||||
|
|
||||||
import LoadConfigComponent from '../components/Config/LoadConfig';
|
import LoadConfigComponent from '../components/Config/LoadConfig';
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import usenet from './useNet/UseNetTile';
|
|||||||
import weather from './weather/WeatherTile';
|
import weather from './weather/WeatherTile';
|
||||||
import bitTorrent from './bitTorrent/BitTorrentTile';
|
import bitTorrent from './bitTorrent/BitTorrentTile';
|
||||||
import torrentNetworkTraffic from './torrentNetworkTraffic/TorrentNetworkTrafficTile';
|
import torrentNetworkTraffic from './torrentNetworkTraffic/TorrentNetworkTrafficTile';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
calendar,
|
calendar,
|
||||||
dashdot,
|
dashdot,
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import { Center, Group, Skeleton, Stack, Text, Title } from '@mantine/core';
|
import { Center, Group, Skeleton, Stack, Text, Title } from '@mantine/core';
|
||||||
import { IconArrowDownRight, IconArrowUpRight, IconCloudRain } from '@tabler/icons';
|
import { IconArrowDownRight, IconArrowUpRight, IconCloudRain } from '@tabler/icons';
|
||||||
import { HomarrCardWrapper } from '../../components/Dashboard/Tiles/HomarrCardWrapper';
|
|
||||||
import { BaseTileProps } from '../../components/Dashboard/Tiles/type';
|
|
||||||
import { WidgetsMenu } from '../../components/Dashboard/Tiles/Widgets/WidgetsMenu';
|
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { IWidget } from '../widgets';
|
import { IWidget } from '../widgets';
|
||||||
import { useWeatherForCity } from './useWeatherForCity';
|
import { useWeatherForCity } from './useWeatherForCity';
|
||||||
|
|||||||
Reference in New Issue
Block a user