mirror of
https://github.com/ajnart/homarr.git
synced 2026-07-04 12:29:07 +02:00
♻️ Migrate category toggle and actions
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
{
|
||||
"header": {
|
||||
"customize": "Customize board"
|
||||
},
|
||||
"category": {
|
||||
"actions": {
|
||||
"add": "Add category",
|
||||
"addAbove": "Add category above",
|
||||
"addBelow": "Add category below"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,6 @@
|
||||
},
|
||||
"menu": {
|
||||
"moveUp": "Move up",
|
||||
"moveDown": "Move down",
|
||||
"addCategory": "Add category {{location}}",
|
||||
"addAbove": "above",
|
||||
"addBelow": "below"
|
||||
"moveDown": "Move down"
|
||||
}
|
||||
}
|
||||
177
src/components/Board/Sections/Category/CategoryMenu.tsx
Normal file
177
src/components/Board/Sections/Category/CategoryMenu.tsx
Normal file
@@ -0,0 +1,177 @@
|
||||
import { ActionIcon, List, Menu, Stack, Text, createStyles } from '@mantine/core';
|
||||
import { modals } from '@mantine/modals';
|
||||
import {
|
||||
IconDotsVertical,
|
||||
IconEdit,
|
||||
IconRowInsertBottom,
|
||||
IconRowInsertTop,
|
||||
IconSettings,
|
||||
IconShare3,
|
||||
IconTransitionBottom,
|
||||
IconTransitionTop,
|
||||
IconTrash,
|
||||
TablerIconsProps,
|
||||
} from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useEditModeStore } from '~/components/Dashboard/Views/useEditModeStore';
|
||||
import { useCategoryActionHelper } from '~/components/Dashboard/Wrappers/Category/useCategoryActions';
|
||||
|
||||
import { AppItem, CategorySection } from '../../context';
|
||||
|
||||
type CategoryMenuProps = {
|
||||
category: CategorySection;
|
||||
};
|
||||
|
||||
export const CategoryMenu = ({ category }: CategoryMenuProps) => {
|
||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||
const editModeActions = useEditModeActions(category);
|
||||
const nonEditModeActions = useNonEditModeActions(category);
|
||||
const { t } = useTranslation(['layout/common', 'boards/common', 'common']);
|
||||
|
||||
const Icon = isEditMode ? IconSettings : IconDotsVertical;
|
||||
const actions = isEditMode ? editModeActions : nonEditModeActions;
|
||||
|
||||
return (
|
||||
<Menu withArrow withinPortal>
|
||||
<Menu.Target>
|
||||
<ActionIcon mr="md">
|
||||
<Icon />
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
{actions.map((action) => (
|
||||
<>
|
||||
{action.group && <Menu.Label>{t(action.group)}</Menu.Label>}
|
||||
<Menu.Item
|
||||
key={action.label}
|
||||
icon={<action.icon size="1rem" />}
|
||||
onClick={action.onClick}
|
||||
color={action.color}
|
||||
>
|
||||
{t(action.label)}
|
||||
</Menu.Item>
|
||||
</>
|
||||
))}
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
const useEditModeActions = (category: CategorySection): ActionDefinition[] => {
|
||||
const { addCategoryAbove, addCategoryBelow, moveCategoryUp, moveCategoryDown, edit, remove } =
|
||||
useCategoryActionHelper(category);
|
||||
|
||||
return [
|
||||
{
|
||||
icon: IconEdit,
|
||||
label: 'common:edit',
|
||||
onClick: edit,
|
||||
},
|
||||
{
|
||||
icon: IconTrash,
|
||||
color: 'red',
|
||||
label: 'common:remove',
|
||||
onClick: remove,
|
||||
},
|
||||
{
|
||||
group: 'common:changePosition',
|
||||
icon: IconTransitionTop,
|
||||
label: 'menu.moveUp',
|
||||
onClick: moveCategoryUp,
|
||||
},
|
||||
{
|
||||
icon: IconTransitionBottom,
|
||||
label: 'menu.moveDown',
|
||||
onClick: moveCategoryDown,
|
||||
},
|
||||
{
|
||||
group: 'boards/common:category.actions.add',
|
||||
icon: IconRowInsertTop,
|
||||
label: 'boards/common:category.actions.addAbove',
|
||||
onClick: addCategoryAbove,
|
||||
},
|
||||
{
|
||||
icon: IconRowInsertBottom,
|
||||
label: 'boards/common:category.actions.addBelow',
|
||||
onClick: addCategoryBelow,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const useNonEditModeActions = (category: CategorySection): ActionDefinition[] => {
|
||||
const openAllApps = useOpenAllApps();
|
||||
const apps = useMemo(
|
||||
() => category.items.filter((x): x is AppItem => x.type === 'app'),
|
||||
[category.items.length]
|
||||
);
|
||||
|
||||
return [
|
||||
{
|
||||
icon: IconShare3,
|
||||
label: 'actions.category.openAllInNewTab',
|
||||
onClick: openAllApps(apps),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
type ActionDefinition = {
|
||||
icon: (props: TablerIconsProps) => JSX.Element;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
color?: string;
|
||||
group?: string;
|
||||
};
|
||||
|
||||
const useStyles = createStyles(() => ({
|
||||
listItem: {
|
||||
'& div': {
|
||||
maxWidth: 'calc(100% - 23px)',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const useOpenAllApps = () => {
|
||||
const { classes } = useStyles();
|
||||
const { t } = useTranslation(['layout/common', 'common']);
|
||||
|
||||
return useCallback((apps: AppItem[]) => {
|
||||
return () => {
|
||||
for (let i = 0; i < apps.length; i += 1) {
|
||||
const app = apps[i];
|
||||
const popUp = window.open(app.externalUrl ?? app.internalUrl, app.id);
|
||||
|
||||
if (popUp !== null) continue;
|
||||
|
||||
modals.openConfirmModal({
|
||||
title: <Text weight="bold">{t('modals.blockedPopups.title')}</Text>,
|
||||
children: (
|
||||
<Stack maw="100%">
|
||||
<Text>{t('modals.blockedPopups.text')}</Text>
|
||||
<List>
|
||||
<List.Item className={classes.listItem}>
|
||||
{t('modals.blockedPopups.list.browserPermission')}
|
||||
</List.Item>
|
||||
<List.Item className={classes.listItem}>
|
||||
{t('modals.blockedPopups.list.adBlockers')}
|
||||
</List.Item>
|
||||
<List.Item className={classes.listItem}>
|
||||
{t('modals.blockedPopups.list.otherBrowser')}
|
||||
</List.Item>
|
||||
</List>
|
||||
</Stack>
|
||||
),
|
||||
labels: {
|
||||
confirm: t('common:close'),
|
||||
cancel: '',
|
||||
},
|
||||
cancelProps: {
|
||||
display: 'none',
|
||||
},
|
||||
closeOnClickOutside: false,
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
@@ -3,7 +3,7 @@ import { useCallback } from 'react';
|
||||
import { v4 } from 'uuid';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
import { type CategorySection, type EmptySection } from './context';
|
||||
import { type CategorySection, type EmptySection } from '../../context';
|
||||
|
||||
type AddCategory = {
|
||||
name: string;
|
||||
115
src/components/Board/Sections/CategorySection.tsx
Normal file
115
src/components/Board/Sections/CategorySection.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import { Accordion, Group, List, Stack, Text, Title, createStyles } from '@mantine/core';
|
||||
import { modals } from '@mantine/modals';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { AppItem, CategorySection } from '~/components/Board/context';
|
||||
|
||||
import { useEditModeStore } from '../../Dashboard/Views/useEditModeStore';
|
||||
import { WrapperContent } from '../../Dashboard/Wrappers/WrapperContent';
|
||||
import { useGridstack } from '../../Dashboard/Wrappers/gridstack/use-gridstack';
|
||||
import { useCardStyles } from '../../layout/Common/useCardStyles';
|
||||
import { CategoryMenu } from './Category/CategoryMenu';
|
||||
|
||||
interface DashboardCategoryProps {
|
||||
section: CategorySection;
|
||||
isOpened: boolean;
|
||||
toggle: (categoryId: string) => void;
|
||||
}
|
||||
|
||||
export const BoardCategorySection = ({ section, isOpened, toggle }: DashboardCategoryProps) => {
|
||||
const { refs } = useGridstack({ section });
|
||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||
const { classes: cardClasses, cx } = useCardStyles(true);
|
||||
const { t } = useTranslation(['layout/common', 'common']);
|
||||
const openAllApps = useOpenAllApps();
|
||||
const apps = useMemo(
|
||||
() => section.items.filter((x): x is AppItem => x.type === 'app'),
|
||||
[section.items.length]
|
||||
);
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
classNames={{
|
||||
item: cx(cardClasses.card, 'dashboard-gs-category'),
|
||||
}}
|
||||
mx={10}
|
||||
chevronPosition="left"
|
||||
multiple
|
||||
variant="separated"
|
||||
radius="lg"
|
||||
value={isOpened || isEditMode ? [section.id] : []}
|
||||
onChange={() => !isEditMode && toggle(section.id)}
|
||||
>
|
||||
<Accordion.Item value={section.id}>
|
||||
<Group noWrap align="center">
|
||||
<Accordion.Control>
|
||||
<Title order={3}>{section.name}</Title>
|
||||
</Accordion.Control>
|
||||
<CategoryMenu category={section} />
|
||||
</Group>
|
||||
<Accordion.Panel>
|
||||
<div
|
||||
className="grid-stack grid-stack-category"
|
||||
data-category={section.id}
|
||||
ref={refs.wrapper}
|
||||
>
|
||||
<WrapperContent items={section.items} refs={refs} />
|
||||
</div>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = createStyles(() => ({
|
||||
listItem: {
|
||||
'& div': {
|
||||
maxWidth: 'calc(100% - 23px)',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const useOpenAllApps = () => {
|
||||
const { classes } = useStyles();
|
||||
const { t } = useTranslation(['layout/common', 'common']);
|
||||
|
||||
return useCallback((apps: AppItem[]) => {
|
||||
return () => {
|
||||
for (let i = 0; i < apps.length; i += 1) {
|
||||
const app = apps[i];
|
||||
const popUp = window.open(app.externalUrl ?? app.internalUrl, app.id);
|
||||
|
||||
if (popUp !== null) continue;
|
||||
|
||||
modals.openConfirmModal({
|
||||
title: <Text weight="bold">{t('modals.blockedPopups.title')}</Text>,
|
||||
children: (
|
||||
<Stack maw="100%">
|
||||
<Text>{t('modals.blockedPopups.text')}</Text>
|
||||
<List>
|
||||
<List.Item className={classes.listItem}>
|
||||
{t('modals.blockedPopups.list.browserPermission')}
|
||||
</List.Item>
|
||||
<List.Item className={classes.listItem}>
|
||||
{t('modals.blockedPopups.list.adBlockers')}
|
||||
</List.Item>
|
||||
<List.Item className={classes.listItem}>
|
||||
{t('modals.blockedPopups.list.otherBrowser')}
|
||||
</List.Item>
|
||||
</List>
|
||||
</Stack>
|
||||
),
|
||||
labels: {
|
||||
confirm: t('common:close'),
|
||||
cancel: '',
|
||||
},
|
||||
cancelProps: {
|
||||
display: 'none',
|
||||
},
|
||||
closeOnClickOutside: false,
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
@@ -11,7 +11,7 @@ interface EmptySectionWrapperProps {
|
||||
|
||||
const defaultClasses = 'grid-stack grid-stack-empty min-row';
|
||||
|
||||
export const EmptySectionWrapper = ({ section }: EmptySectionWrapperProps) => {
|
||||
export const BoardEmptySection = ({ section }: EmptySectionWrapperProps) => {
|
||||
const { refs } = useGridstack({ section });
|
||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { MobileRibbons } from './Mobile/Ribbon/MobileRibbon';
|
||||
import { BoardView } from './Views/DashboardView';
|
||||
import { useEditModeStore } from './Views/useEditModeStore';
|
||||
|
||||
export const Board = () => {
|
||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* The following elemens are splitted because gridstack doesn't reinitialize them when using same item. */}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Box, Group, LoadingOverlay, Stack } from '@mantine/core';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useLocalStorage } from '@mantine/hooks';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import {
|
||||
CategorySection,
|
||||
EmptySection,
|
||||
@@ -9,17 +10,32 @@ import {
|
||||
import { useResize } from '~/hooks/use-resize';
|
||||
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
||||
|
||||
import { EmptySectionWrapper } from '../../Board/Sections/EmptySection';
|
||||
import { DashboardCategory } from '../Wrappers/Category/Category';
|
||||
import { BoardCategorySection } from '../../Board/Sections/CategorySection';
|
||||
import { BoardEmptySection } from '../../Board/Sections/EmptySection';
|
||||
import { DashboardSidebar } from '../Wrappers/Sidebar/Sidebar';
|
||||
import { useGridstackStore } from '../Wrappers/gridstack/store';
|
||||
|
||||
export const BoardView = () => {
|
||||
const boardName = useRequiredBoard().name;
|
||||
const stackedSections = useStackedSections();
|
||||
const sidebarsVisible = useSidebarVisibility();
|
||||
const { isReady, mainAreaRef } = usePrepareGridstack();
|
||||
const leftSidebarSection = useSidebarSection('left');
|
||||
const rightSidebarSection = useSidebarSection('right');
|
||||
const [toggledCategories, setToggledCategories] = useLocalStorage({
|
||||
key: `${boardName}-category-section-toggled`,
|
||||
// This is a bit of a hack to toggle the categories on the first load, return a string[] of the categories
|
||||
defaultValue: stackedSections.filter((s) => s.type === 'category').map((s) => s.id),
|
||||
});
|
||||
const toggleCategory = useCallback((categoryId: string) => {
|
||||
setToggledCategories((current) => {
|
||||
console.log('toggle', current, categoryId);
|
||||
if (current.includes(categoryId)) {
|
||||
return current.filter((x) => x !== categoryId);
|
||||
}
|
||||
return [...current, categoryId];
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box h="100%" pos="relative">
|
||||
@@ -41,9 +57,14 @@ export const BoardView = () => {
|
||||
<Stack ref={mainAreaRef} mx={-10} style={{ flexGrow: 1 }}>
|
||||
{stackedSections.map((item) =>
|
||||
item.type === 'category' ? (
|
||||
<DashboardCategory key={item.id} section={item} />
|
||||
<BoardCategorySection
|
||||
key={item.id}
|
||||
section={item}
|
||||
isOpened={toggledCategories.includes(item.id)}
|
||||
toggle={toggleCategory}
|
||||
/>
|
||||
) : (
|
||||
<EmptySectionWrapper key={item.id} section={item} />
|
||||
<BoardEmptySection key={item.id} section={item} />
|
||||
)
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { BoardView } from './DashboardView';
|
||||
|
||||
export const DashboardDetailView = () => <BoardView />;
|
||||
@@ -1,3 +0,0 @@
|
||||
import { BoardView } from './DashboardView';
|
||||
|
||||
export const DashboardEditView = () => <BoardView />;
|
||||
@@ -1,142 +0,0 @@
|
||||
import {
|
||||
Accordion,
|
||||
ActionIcon,
|
||||
Box,
|
||||
List,
|
||||
Menu,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
createStyles,
|
||||
} from '@mantine/core';
|
||||
import { modals } from '@mantine/modals';
|
||||
import { IconDotsVertical, IconShare3 } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { CategorySection } from '~/components/Board/context';
|
||||
|
||||
import { useCardStyles } from '../../../layout/Common/useCardStyles';
|
||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||
import { WrapperContent } from '../WrapperContent';
|
||||
import { useGridstack } from '../gridstack/use-gridstack';
|
||||
import { CategoryEditMenu } from './CategoryEditMenu';
|
||||
|
||||
interface DashboardCategoryProps {
|
||||
section: CategorySection;
|
||||
}
|
||||
|
||||
export const DashboardCategory = ({ section }: DashboardCategoryProps) => {
|
||||
const { refs } = useGridstack({ section });
|
||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||
const { classes: cardClasses, cx } = useCardStyles(true);
|
||||
const { classes } = useStyles();
|
||||
const { t } = useTranslation(['layout/common', 'common']);
|
||||
|
||||
//const categoryList = config?.categories.map((x) => x.name) ?? [];
|
||||
/*const [toggledCategories, setToggledCategories] = useLocalStorage({
|
||||
key: `${config?.configProperties.name}-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 handleMenuClick = () => {
|
||||
for (let i = 0; i < apps.length; i += 1) {
|
||||
const app = apps[i];
|
||||
const popUp = window.open(app.url, app.id);
|
||||
|
||||
if (popUp === null) {
|
||||
modals.openConfirmModal({
|
||||
title: <Text weight="bold">{t('modals.blockedPopups.title')}</Text>,
|
||||
children: (
|
||||
<Stack maw="100%">
|
||||
<Text>{t('modals.blockedPopups.text')}</Text>
|
||||
<List>
|
||||
<List.Item className={classes.listItem}>
|
||||
{t('modals.blockedPopups.list.browserPermission')}
|
||||
</List.Item>
|
||||
<List.Item className={classes.listItem}>
|
||||
{t('modals.blockedPopups.list.adBlockers')}
|
||||
</List.Item>
|
||||
<List.Item className={classes.listItem}>
|
||||
{t('modals.blockedPopups.list.otherBrowser')}
|
||||
</List.Item>
|
||||
</List>
|
||||
</Stack>
|
||||
),
|
||||
labels: {
|
||||
confirm: t('common:close'),
|
||||
cancel: '',
|
||||
},
|
||||
cancelProps: {
|
||||
display: 'none',
|
||||
},
|
||||
closeOnClickOutside: false,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// value={isEditMode ? categoryList : toggledCategories}
|
||||
/*
|
||||
onChange={(state) => {
|
||||
// Cancel if edit mode is on
|
||||
if (isEditMode) return;
|
||||
setToggledCategories([...state]);
|
||||
}}
|
||||
*/
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
classNames={{
|
||||
item: cx(cardClasses.card, 'dashboard-gs-category'),
|
||||
}}
|
||||
mx={10}
|
||||
chevronPosition="left"
|
||||
multiple
|
||||
variant="separated"
|
||||
radius="lg"
|
||||
>
|
||||
<Accordion.Item value={section.name}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Accordion.Control icon={isEditMode && <CategoryEditMenu category={section} />}>
|
||||
<Title order={3}>{section.name}</Title>
|
||||
</Accordion.Control>
|
||||
{!isEditMode && (
|
||||
<Menu withArrow withinPortal>
|
||||
<Menu.Target>
|
||||
<ActionIcon variant="light" mr="md">
|
||||
<IconDotsVertical />
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
onClick={/*handleMenuClick*/ undefined}
|
||||
icon={<IconShare3 size="1rem" />}
|
||||
>
|
||||
{t('actions.category.openAllInNewTab')}
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
)}
|
||||
</Box>
|
||||
<Accordion.Panel>
|
||||
<div
|
||||
className="grid-stack grid-stack-category"
|
||||
data-category={section.id}
|
||||
ref={refs.wrapper}
|
||||
>
|
||||
<WrapperContent items={section.items} refs={refs} />
|
||||
</div>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = createStyles(() => ({
|
||||
listItem: {
|
||||
'& div': {
|
||||
maxWidth: 'calc(100% - 23px)',
|
||||
},
|
||||
},
|
||||
}));
|
||||
@@ -1,58 +0,0 @@
|
||||
import { ActionIcon, Menu } from '@mantine/core';
|
||||
import {
|
||||
IconEdit,
|
||||
IconRowInsertBottom,
|
||||
IconRowInsertTop,
|
||||
IconSettings,
|
||||
IconTransitionBottom,
|
||||
IconTransitionTop,
|
||||
IconTrash,
|
||||
} from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useConfigContext } from '~/config/provider';
|
||||
import { CategoryType } from '~/types/category';
|
||||
|
||||
import { useCategoryActionHelper } from './useCategoryActions';
|
||||
|
||||
interface CategoryEditMenuProps {
|
||||
category: CategoryType;
|
||||
}
|
||||
|
||||
export const CategoryEditMenu = ({ category }: CategoryEditMenuProps) => {
|
||||
const { name: configName } = useConfigContext();
|
||||
const { addCategoryAbove, addCategoryBelow, moveCategoryUp, moveCategoryDown, edit, remove } =
|
||||
useCategoryActionHelper(configName, category);
|
||||
const { t } = useTranslation(['layout/common', 'common']);
|
||||
|
||||
return (
|
||||
<Menu withinPortal withArrow>
|
||||
<Menu.Target>
|
||||
<ActionIcon>
|
||||
<IconSettings />
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item icon={<IconEdit size={20} />} onClick={edit}>
|
||||
{t('common:edit')}
|
||||
</Menu.Item>
|
||||
<Menu.Item icon={<IconTrash size={20} />} onClick={remove}>
|
||||
{t('common:remove')}
|
||||
</Menu.Item>
|
||||
<Menu.Label>{t('common:changePosition')}</Menu.Label>
|
||||
<Menu.Item icon={<IconTransitionTop size={20} />} onClick={moveCategoryUp}>
|
||||
{t('menu.moveUp')}
|
||||
</Menu.Item>
|
||||
<Menu.Item icon={<IconTransitionBottom size={20} />} onClick={moveCategoryDown}>
|
||||
{t('menu.moveDown')}
|
||||
</Menu.Item>
|
||||
<Menu.Label>{t('menu.addCategory', { location: '' })}</Menu.Label>
|
||||
<Menu.Item icon={<IconRowInsertTop size={20} />} onClick={addCategoryAbove}>
|
||||
{t('menu.addCategory', { location: t('menu.addAbove') })}
|
||||
</Menu.Item>
|
||||
<Menu.Item icon={<IconRowInsertBottom size={20} />} onClick={addCategoryBelow}>
|
||||
{t('menu.addCategory', { location: t('menu.addBelow') })}
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
@@ -1,16 +1,12 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { useCategoryActions } from '~/components/Board/category-actions';
|
||||
import { useCategoryActions } from '~/components/Board/Sections/Category/category-actions';
|
||||
import { useRequiredBoard } from '~/components/Board/context';
|
||||
import { useConfigStore } from '~/config/store';
|
||||
import { openContextModalGeneric } from '~/tools/mantineModalManagerExtensions';
|
||||
import { AppType } from '~/types/app';
|
||||
import { CategoryType } from '~/types/category';
|
||||
import { WrapperType } from '~/types/wrapper';
|
||||
import { IWidget } from '~/widgets/widgets';
|
||||
|
||||
import { CategoryEditModalInnerProps } from './CategoryEditModal';
|
||||
|
||||
export const useCategoryActionHelper = (configName: string | undefined, category: CategoryType) => {
|
||||
export const useCategoryActionHelper = (category: CategoryType) => {
|
||||
const boardName = useRequiredBoard().name;
|
||||
const { addCategory, moveCategory, removeCategory, renameCategory } = useCategoryActions({
|
||||
boardName,
|
||||
|
||||
Reference in New Issue
Block a user