mirror of
https://github.com/ajnart/homarr.git
synced 2026-02-26 16:30:57 +01:00
refactor: improve behaviour of edit mode when navigating away and back (#525)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { Dispatch, PropsWithChildren, SetStateAction } from "react";
|
||||
import { createContext, useCallback, useContext, useEffect, useState } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
@@ -13,6 +13,8 @@ const BoardContext = createContext<{
|
||||
board: RouterOutputs["board"]["getHomeBoard"];
|
||||
isReady: boolean;
|
||||
markAsReady: (id: string) => void;
|
||||
isEditMode: boolean;
|
||||
setEditMode: Dispatch<SetStateAction<boolean>>;
|
||||
} | null>(null);
|
||||
|
||||
export const BoardProvider = ({
|
||||
@@ -24,11 +26,11 @@ export const BoardProvider = ({
|
||||
const pathname = usePathname();
|
||||
const utils = clientApi.useUtils();
|
||||
const [readySections, setReadySections] = useState<string[]>([]);
|
||||
const [isEditMode, setEditMode] = useState(false);
|
||||
const { data } = clientApi.board.getBoardByName.useQuery(
|
||||
{ name: initialBoard.name },
|
||||
{
|
||||
initialData: initialBoard,
|
||||
refetchOnMount: false,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
},
|
||||
@@ -60,6 +62,8 @@ export const BoardProvider = ({
|
||||
board: data,
|
||||
isReady: data.sections.length === readySections.length,
|
||||
markAsReady,
|
||||
isEditMode,
|
||||
setEditMode,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
@@ -102,3 +106,13 @@ export const useOptionalBoard = () => {
|
||||
|
||||
return context?.board;
|
||||
};
|
||||
|
||||
export const useEditMode = () => {
|
||||
const context = useContext(BoardContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error("Board is required");
|
||||
}
|
||||
|
||||
return [context.isEditMode, context.setEditMode] as const;
|
||||
};
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
IconPlus,
|
||||
IconSettings,
|
||||
} from "@tabler/icons-react";
|
||||
import { useAtom, useAtomValue } from "jotai";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
@@ -20,16 +19,15 @@ import { showErrorNotification, showSuccessNotification } from "@homarr/notifica
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
|
||||
import { editModeAtom } from "~/components/board/editMode";
|
||||
import { ItemSelectModal } from "~/components/board/items/item-select-modal";
|
||||
import { useBoardPermissions } from "~/components/board/permissions/client";
|
||||
import { useCategoryActions } from "~/components/board/sections/category/category-actions";
|
||||
import { CategoryEditModal } from "~/components/board/sections/category/category-edit-modal";
|
||||
import { HeaderButton } from "~/components/layout/header/button";
|
||||
import { useRequiredBoard } from "./_context";
|
||||
import { useEditMode, useRequiredBoard } from "./_context";
|
||||
|
||||
export const BoardContentHeaderActions = () => {
|
||||
const isEditMode = useAtomValue(editModeAtom);
|
||||
const [isEditMode] = useEditMode();
|
||||
const board = useRequiredBoard();
|
||||
const { hasChangeAccess } = useBoardPermissions(board);
|
||||
|
||||
@@ -107,7 +105,7 @@ const AddMenu = () => {
|
||||
};
|
||||
|
||||
const EditModeMenu = () => {
|
||||
const [isEditMode, setEditMode] = useAtom(editModeAtom);
|
||||
const [isEditMode, setEditMode] = useEditMode();
|
||||
const board = useRequiredBoard();
|
||||
const utils = clientApi.useUtils();
|
||||
const t = useScopedI18n("board.action.edit");
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { atom } from "jotai";
|
||||
|
||||
export const editModeAtom = atom(false);
|
||||
@@ -11,13 +11,12 @@ import {
|
||||
IconTransitionTop,
|
||||
IconTrash,
|
||||
} from "@tabler/icons-react";
|
||||
import { useAtomValue } from "jotai";
|
||||
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import type { TablerIcon } from "@homarr/ui";
|
||||
|
||||
import type { CategorySection } from "~/app/[locale]/boards/_types";
|
||||
import { editModeAtom } from "../../editMode";
|
||||
import { useEditMode } from "~/app/[locale]/boards/(content)/_context";
|
||||
import { useCategoryMenuActions } from "./category-menu-actions";
|
||||
|
||||
interface Props {
|
||||
@@ -56,7 +55,7 @@ export const CategoryMenu = ({ category }: Props) => {
|
||||
};
|
||||
|
||||
const useActions = (category: CategorySection) => {
|
||||
const isEditMode = useAtomValue(editModeAtom);
|
||||
const [isEditMode] = useEditMode();
|
||||
const editModeActions = useEditModeActions(category);
|
||||
const nonEditModeActions = useNonEditModeActions(category);
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* eslint-disable react/no-unknown-property */
|
||||
// Ignored because of gridstack attributes
|
||||
|
||||
import { useMemo } from "react";
|
||||
import type { RefObject } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { ActionIcon, Card, Menu } from "@mantine/core";
|
||||
import { useElementSize } from "@mantine/hooks";
|
||||
import { IconDotsVertical, IconLayoutKanban, IconPencil, IconTrash } from "@tabler/icons-react";
|
||||
import combineClasses from "clsx";
|
||||
import { useAtomValue } from "jotai";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useConfirmModal, useModalAction } from "@homarr/modals";
|
||||
@@ -21,8 +20,7 @@ import {
|
||||
} from "@homarr/widgets";
|
||||
|
||||
import type { Item } from "~/app/[locale]/boards/_types";
|
||||
import { useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
|
||||
import { editModeAtom } from "../editMode";
|
||||
import { useEditMode, useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
|
||||
import { useItemActions } from "../items/item-actions";
|
||||
import type { UseGridstackRefs } from "./gridstack/use-gridstack";
|
||||
import classes from "./item.module.css";
|
||||
@@ -97,7 +95,7 @@ interface ItemContentProps {
|
||||
|
||||
const BoardItemContent = ({ item, ...dimensions }: ItemContentProps) => {
|
||||
const board = useRequiredBoard();
|
||||
const editMode = useAtomValue(editModeAtom);
|
||||
const [isEditMode] = useEditMode();
|
||||
const serverData = useServerDataFor(item.id);
|
||||
const Comp = loadWidgetDynamic(item.kind);
|
||||
const options = reduceWidgetOptionsWithDefaultValues(item.kind, item.options);
|
||||
@@ -112,7 +110,7 @@ const BoardItemContent = ({ item, ...dimensions }: ItemContentProps) => {
|
||||
options={options as never}
|
||||
integrations={item.integrations}
|
||||
serverData={serverData?.data as never}
|
||||
isEditMode={editMode}
|
||||
isEditMode={isEditMode}
|
||||
boardId={board.id}
|
||||
itemId={item.id}
|
||||
{...dimensions}
|
||||
@@ -126,7 +124,7 @@ const ItemMenu = ({ offset, item }: { offset: number; item: Item }) => {
|
||||
const t = useI18n();
|
||||
const { openModal } = useModalAction(WidgetEditModal);
|
||||
const { openConfirmModal } = useConfirmModal();
|
||||
const isEditMode = useAtomValue(editModeAtom);
|
||||
const [isEditMode] = useEditMode();
|
||||
const { updateItemOptions, updateItemAdvancedOptions, updateItemIntegrations, removeItem } = useItemActions();
|
||||
const { data: integrationData, isPending } = clientApi.integration.all.useQuery();
|
||||
const currentDefinition = useMemo(() => widgetImports[item.kind].definition, [item.kind]);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { RefObject } from "react";
|
||||
import { useAtomValue } from "jotai";
|
||||
|
||||
import type { EmptySection } from "~/app/[locale]/boards/_types";
|
||||
import { editModeAtom } from "../editMode";
|
||||
import { useEditMode } from "~/app/[locale]/boards/(content)/_context";
|
||||
import { SectionContent } from "./content";
|
||||
import { useGridstack } from "./gridstack/use-gridstack";
|
||||
|
||||
@@ -15,7 +14,7 @@ const defaultClasses = "grid-stack grid-stack-empty min-row";
|
||||
|
||||
export const BoardEmptySection = ({ section, mainRef }: Props) => {
|
||||
const { refs } = useGridstack({ section, mainRef });
|
||||
const isEditMode = useAtomValue(editModeAtom);
|
||||
const [isEditMode] = useEditMode();
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import type { MutableRefObject, RefObject } from "react";
|
||||
import { createRef, useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import { useAtomValue } from "jotai";
|
||||
|
||||
import type { GridItemHTMLElement, GridStack, GridStackNode } from "@homarr/gridstack";
|
||||
|
||||
import type { Section } from "~/app/[locale]/boards/_types";
|
||||
import { useMarkSectionAsReady, useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
|
||||
import { editModeAtom } from "../../editMode";
|
||||
import { useEditMode, useMarkSectionAsReady, useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
|
||||
import { useItemActions } from "../../items/item-actions";
|
||||
import { initializeGridstack } from "./init-gridstack";
|
||||
|
||||
@@ -26,7 +24,7 @@ interface UseGridstackProps {
|
||||
}
|
||||
|
||||
export const useGridstack = ({ section, mainRef }: UseGridstackProps): UseGristackReturnType => {
|
||||
const isEditMode = useAtomValue(editModeAtom);
|
||||
const [isEditMode] = useEditMode();
|
||||
const markAsReady = useMarkSectionAsReady();
|
||||
const { moveAndResizeItem, moveItemToSection } = useItemActions();
|
||||
// define reference for wrapper - is used to calculate the width of the wrapper
|
||||
|
||||
Reference in New Issue
Block a user