diff --git a/docs/pages/docs/api/index.mdx b/docs/pages/docs/api/index.mdx index 53ba14f..d96371d 100644 --- a/docs/pages/docs/api/index.mdx +++ b/docs/pages/docs/api/index.mdx @@ -7,4 +7,5 @@ | `height` | `number` \| `string` | Height of the Isoflow renderer as a CSS value. | `100%` | | `onSceneUpdate` | `function` | A callback that is triggered whenever an item is added, updated or removed from the scene. The callback is called with the updated scene as the first argument. | `undefined` | | `enableDebugTools` | `boolean` | Enables extra tools for debugging purposes. | `false` | -| `editorMode` | `EXPLORABLE_READONLY` \| `NON_INTERACTIVE` \| `EDITABLE` | Enables / disables editor features. | `EDITABLE` | \ No newline at end of file +| `editorMode` | `EXPLORABLE_READONLY` \| `NON_INTERACTIVE` \| `EDITABLE` | Enables / disables editor features. | `EDITABLE` | +| `mainMenuOptions` | `(OPEN \| SAVE_JSON \| CLEAR \| GITHUB \| DISCORD \| VERSION)[]` | Shows / hides options in the main menu. If `[]` is passed, the menu is hidden. | `['OPEN', 'SAVE_JSON', 'CLEAR', 'GITHUB', 'DISCORD', 'VERSION']` | \ No newline at end of file diff --git a/src/Isoflow.tsx b/src/Isoflow.tsx index 0518f08..1586afc 100644 --- a/src/Isoflow.tsx +++ b/src/Isoflow.tsx @@ -20,11 +20,12 @@ import { useWindowUtils } from 'src/hooks/useWindowUtils'; import { sceneInput as sceneValidationSchema } from 'src/validation/scene'; import { UiOverlay } from 'src/components/UiOverlay/UiOverlay'; import { UiStateProvider, useUiStateStore } from 'src/stores/uiStateStore'; -import { INITIAL_SCENE } from 'src/config'; +import { INITIAL_SCENE, MAIN_MENU_OPTIONS } from 'src/config'; import { useIconCategories } from './hooks/useIconCategories'; const App = ({ initialScene, + mainMenuOptions = MAIN_MENU_OPTIONS, width = '100%', height = '100%', onSceneUpdated, @@ -51,7 +52,14 @@ const App = ({ useEffect(() => { uiActions.setZoom(initialScene?.zoom ?? 1); uiActions.setEditorMode(editorMode); - }, [initialScene?.zoom, editorMode, sceneActions, uiActions]); + uiActions.setMainMenuOptions(mainMenuOptions); + }, [ + initialScene?.zoom, + editorMode, + sceneActions, + uiActions, + mainMenuOptions + ]); useEffect(() => { return () => { diff --git a/src/components/MainMenu/MainMenu.tsx b/src/components/MainMenu/MainMenu.tsx index 02221cb..f25db6e 100644 --- a/src/components/MainMenu/MainMenu.tsx +++ b/src/components/MainMenu/MainMenu.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; import { Menu, Typography, Divider, Card } from '@mui/material'; import { Menu as MenuIcon, @@ -22,6 +22,9 @@ export const MainMenu = () => { const isMainMenuOpen = useUiStateStore((state) => { return state.isMainMenuOpen; }); + const mainMenuOptions = useUiStateStore((state) => { + return state.mainMenuOptions; + }); const scene = useSceneStore((state) => { return { title: state.title, @@ -95,6 +98,25 @@ export const MainMenu = () => { uiStateActions.setIsMainMenuOpen(false); }, [uiStateActions, setScene]); + const sectionVisibility = useMemo(() => { + return { + actions: Boolean( + mainMenuOptions.includes('OPEN') || + mainMenuOptions.includes('SAVE_JSON') || + mainMenuOptions.includes('CLEAR') + ), + links: Boolean( + mainMenuOptions.includes('GITHUB') || + mainMenuOptions.includes('DISCORD') + ), + version: Boolean(mainMenuOptions.includes('VERSION')) + }; + }, [mainMenuOptions]); + + if (mainMenuOptions.length === 0) { + return null; + } + return ( } name="Main menu" onClick={onToggleMenu} /> @@ -117,38 +139,65 @@ export const MainMenu = () => { }} > - }> - Open - - }> - Download diagram - - }> - Clear the canvas - - - { - return gotoUrl(`${REPOSITORY_URL}`); - }} - Icon={} - > - GitHub - - { - return gotoUrl('https://discord.gg/QYPkvZth7D'); - }} - Icon={} - > - Discord - - - - - Isoflow v{PACKAGE_VERSION} - - + {mainMenuOptions.includes('OPEN') && ( + }> + Open + + )} + + {mainMenuOptions.includes('SAVE_JSON') && ( + }> + Download diagram + + )} + + {mainMenuOptions.includes('CLEAR') && ( + }> + Clear the canvas + + )} + + {sectionVisibility.links && ( + <> + + + {mainMenuOptions.includes('GITHUB') && ( + { + return gotoUrl(`${REPOSITORY_URL}`); + }} + Icon={} + > + GitHub + + )} + + {mainMenuOptions.includes('DISCORD') && ( + { + return gotoUrl('https://discord.gg/QYPkvZth7D'); + }} + Icon={} + > + Discord + + )} + + )} + + {sectionVisibility.version && ( + <> + + + {mainMenuOptions.includes('VERSION') && ( + + + Isoflow v{PACKAGE_VERSION} + + + )} + + )} diff --git a/src/config.ts b/src/config.ts index 2d02d46..a098df2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,10 @@ -import { Size, Coords, SceneInput, Connector } from 'src/types'; +import { + Size, + Coords, + SceneInput, + Connector, + MainMenuOptions +} from 'src/types'; import { customVars } from './styles/theme'; // TODO: This file could do with better organisation and convention for easier reading. @@ -53,3 +59,12 @@ export const INITIAL_SCENE: SceneInput = { textBoxes: [], rectangles: [] }; +export const MAIN_MENU_OPTIONS: MainMenuOptions = [ + 'CLEAR', + 'OPEN', + 'SAVE_JSON', + 'CLEAR', + 'DISCORD', + 'GITHUB', + 'VERSION' +]; diff --git a/src/stores/uiStateStore.tsx b/src/stores/uiStateStore.tsx index 2088ec4..267e9b4 100644 --- a/src/stores/uiStateStore.tsx +++ b/src/stores/uiStateStore.tsx @@ -11,6 +11,7 @@ import { UiStateStore } from 'src/types'; const initialState = () => { return createStore((set, get) => { return { + mainMenuOptions: [], editorMode: 'EXPLORABLE_READONLY', mode: getStartingMode('EXPLORABLE_READONLY'), iconCategoriesState: [], @@ -31,6 +32,9 @@ const initialState = () => { zoom: 1, rendererSize: { width: 0, height: 0 }, actions: { + setMainMenuOptions: (mainMenuOptions) => { + set({ mainMenuOptions }); + }, setEditorMode: (mode) => { set({ editorMode: mode, mode: getStartingMode(mode) }); }, diff --git a/src/types/common.ts b/src/types/common.ts index 8cda0ad..17049de 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -30,3 +30,14 @@ export const EditorModeEnum = { EXPLORABLE_READONLY: 'EXPLORABLE_READONLY', EDITABLE: 'EDITABLE' } as const; + +export const MainMenuOptionsEnum = { + OPEN: 'OPEN', + SAVE_JSON: 'SAVE_JSON', + CLEAR: 'CLEAR', + GITHUB: 'GITHUB', + DISCORD: 'DISCORD', + VERSION: 'VERSION' +} as const; + +export type MainMenuOptions = (keyof typeof MainMenuOptionsEnum)[]; diff --git a/src/types/inputs.ts b/src/types/inputs.ts index a1fa1ec..e4d8beb 100644 --- a/src/types/inputs.ts +++ b/src/types/inputs.ts @@ -9,7 +9,7 @@ import { connectorStyleEnum } from 'src/validation/sceneItems'; import { sceneInput } from 'src/validation/scene'; -import type { EditorModeEnum } from './common'; +import type { EditorModeEnum, MainMenuOptions } from './common'; export type ConnectorStyleEnum = z.infer; export type IconInput = z.infer; @@ -26,6 +26,7 @@ export type InitialScene = Partial & { export interface IsoflowProps { initialScene?: InitialScene; + mainMenuOptions?: MainMenuOptions; onSceneUpdated?: (scene: SceneInput) => void; width?: number | string; height?: number | string; diff --git a/src/types/ui.ts b/src/types/ui.ts index 7cd6b8b..7467256 100644 --- a/src/types/ui.ts +++ b/src/types/ui.ts @@ -1,4 +1,4 @@ -import { Coords, Size, EditorModeEnum } from './common'; +import { Coords, Size, EditorModeEnum, MainMenuOptions } from './common'; import { SceneItem, Connector, SceneItemReference } from './scene'; import { IconInput } from './inputs'; @@ -154,6 +154,7 @@ export type IconCollectionStateWithIcons = IconCollectionState & { }; export interface UiState { + mainMenuOptions: MainMenuOptions; editorMode: keyof typeof EditorModeEnum; iconCategoriesState: IconCollectionState[]; mode: Mode; @@ -168,6 +169,7 @@ export interface UiState { } export interface UiStateActions { + setMainMenuOptions: (options: MainMenuOptions) => void; setEditorMode: (mode: keyof typeof EditorModeEnum) => void; setIconCategoriesState: (iconCategoriesState: IconCollectionState[]) => void; resetUiState: () => void;