mirror of
https://github.com/markmanx/isoflow.git
synced 2025-01-31 23:22:31 +00:00
feat: allows main menu to be customised
This commit is contained in:
parent
5d51aab722
commit
46ce637cd0
8 changed files with 130 additions and 39 deletions
|
@ -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` |
|
||||
| `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']` |
|
|
@ -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 () => {
|
||||
|
|
|
@ -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 (
|
||||
<UiElement>
|
||||
<IconButton Icon={<MenuIcon />} name="Main menu" onClick={onToggleMenu} />
|
||||
|
@ -117,38 +139,65 @@ export const MainMenu = () => {
|
|||
}}
|
||||
>
|
||||
<Card sx={{ py: 1 }}>
|
||||
<MenuItem onClick={onOpenScene} Icon={<FolderOpenIcon />}>
|
||||
Open
|
||||
</MenuItem>
|
||||
<MenuItem onClick={onSaveAs} Icon={<DownloadIcon />}>
|
||||
Download diagram
|
||||
</MenuItem>
|
||||
<MenuItem onClick={onClearCanvas} Icon={<DeleteOutlineIcon />}>
|
||||
Clear the canvas
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
return gotoUrl(`${REPOSITORY_URL}`);
|
||||
}}
|
||||
Icon={<GitHubIcon />}
|
||||
>
|
||||
GitHub
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
return gotoUrl('https://discord.gg/QYPkvZth7D');
|
||||
}}
|
||||
Icon={<QuestionAnswerIcon />}
|
||||
>
|
||||
Discord
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Isoflow v{PACKAGE_VERSION}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
{mainMenuOptions.includes('OPEN') && (
|
||||
<MenuItem onClick={onOpenScene} Icon={<FolderOpenIcon />}>
|
||||
Open
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{mainMenuOptions.includes('SAVE_JSON') && (
|
||||
<MenuItem onClick={onSaveAs} Icon={<DownloadIcon />}>
|
||||
Download diagram
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{mainMenuOptions.includes('CLEAR') && (
|
||||
<MenuItem onClick={onClearCanvas} Icon={<DeleteOutlineIcon />}>
|
||||
Clear the canvas
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{sectionVisibility.links && (
|
||||
<>
|
||||
<Divider />
|
||||
|
||||
{mainMenuOptions.includes('GITHUB') && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
return gotoUrl(`${REPOSITORY_URL}`);
|
||||
}}
|
||||
Icon={<GitHubIcon />}
|
||||
>
|
||||
GitHub
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{mainMenuOptions.includes('DISCORD') && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
return gotoUrl('https://discord.gg/QYPkvZth7D');
|
||||
}}
|
||||
Icon={<QuestionAnswerIcon />}
|
||||
>
|
||||
Discord
|
||||
</MenuItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{sectionVisibility.version && (
|
||||
<>
|
||||
<Divider />
|
||||
|
||||
{mainMenuOptions.includes('VERSION') && (
|
||||
<MenuItem>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Isoflow v{PACKAGE_VERSION}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
</Menu>
|
||||
</UiElement>
|
||||
|
|
|
@ -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'
|
||||
];
|
||||
|
|
|
@ -11,6 +11,7 @@ import { UiStateStore } from 'src/types';
|
|||
const initialState = () => {
|
||||
return createStore<UiStateStore>((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) });
|
||||
},
|
||||
|
|
|
@ -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)[];
|
||||
|
|
|
@ -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<typeof connectorStyleEnum>;
|
||||
export type IconInput = z.infer<typeof iconInput>;
|
||||
|
@ -26,6 +26,7 @@ export type InitialScene = Partial<SceneInput> & {
|
|||
|
||||
export interface IsoflowProps {
|
||||
initialScene?: InitialScene;
|
||||
mainMenuOptions?: MainMenuOptions;
|
||||
onSceneUpdated?: (scene: SceneInput) => void;
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue