Merged in editor-actions-by-mode (pull request #63)

Editor actions by mode

* Toolbar configuration for all EditorRenderMode and locked maps

* fix unlock
This commit is contained in:
Gonzalo Martinez 2022-10-07 01:58:24 +00:00 committed by Paulo Veiga
parent f3a11f2eb1
commit c6f04742d8
7 changed files with 218 additions and 113 deletions

View File

@ -105,6 +105,7 @@ export const ToolbarSubmenu = (props: {
> >
<div style={{ display: 'flex' }} onScroll={(e) => e.stopPropagation()}> <div style={{ display: 'flex' }} onScroll={(e) => e.stopPropagation()}>
{props.configuration.options?.map((o, i) => { {props.configuration.options?.map((o, i) => {
if (o?.visible === false) return null;
if (!o?.render) { if (!o?.render) {
return ( return (
<ToolbarMenuItem <ToolbarMenuItem

View File

@ -38,7 +38,7 @@ export interface ToolbarOptionConfiguration {
*/ */
useClickToClose?: boolean; useClickToClose?: boolean;
/** /**
* if false the nmenu entry or button is not visible. Entries with custom render will ignore this property * if false the nmenu entry or button is not visible. Also custom render will be ignored.
*/ */
visible?: boolean; visible?: boolean;
} }

View File

@ -33,7 +33,7 @@ import Box from '@mui/material/Box';
import LogoTextBlackSvg from '../../../images/logo-text-black.svg'; import LogoTextBlackSvg from '../../../images/logo-text-black.svg';
import Palette from '@mui/icons-material/Square'; import Palette from '@mui/icons-material/Square';
import SquareOutlined from '@mui/icons-material/SquareOutlined'; import SquareOutlined from '@mui/icons-material/SquareOutlined';
import { $msg, Designer, EditorRenderMode } from '@wisemapping/mindplot'; import { $msg, Designer } from '@wisemapping/mindplot';
import { ToolbarOptionConfiguration } from './ToolbarOptionConfigurationInterface'; import { ToolbarOptionConfiguration } from './ToolbarOptionConfigurationInterface';
import { SwitchValueDirection, NodePropertyValueModelBuilder } from './ToolbarValueModelBuilder'; import { SwitchValueDirection, NodePropertyValueModelBuilder } from './ToolbarValueModelBuilder';
import { import {
@ -354,9 +354,14 @@ export function buildEditorAppBarConfiguration(
designer: Designer, designer: Designer,
onAction: (type: ToolbarActionType) => void, onAction: (type: ToolbarActionType) => void,
save: () => void, save: () => void,
editorMode: EditorRenderMode, showOnlyCommonActions: boolean,
isMobile: boolean, showAccessChangeActions: boolean,
showMapEntityActions: boolean,
showMindMapNodesActions: boolean,
showPersistenceActions: boolean,
): ToolbarOptionConfiguration[] { ): ToolbarOptionConfiguration[] {
if (!designer) return [];
let commonConfiguration = [ let commonConfiguration = [
{ {
icon: <ArrowBackIosNewOutlinedIcon />, icon: <ArrowBackIosNewOutlinedIcon />,
@ -366,104 +371,106 @@ export function buildEditorAppBarConfiguration(
{ {
render: () => <img src={LogoTextBlackSvg} />, render: () => <img src={LogoTextBlackSvg} />,
}, },
]; {
if (editorMode === 'viewonly' || editorMode === 'showcase' || isMobile) render: () => (
return commonConfiguration; <Tooltip title={designer.getMindmap().getCentralTopic().getText()}>
if (!designer) return []; <Typography
className="truncated"
const isEditor = variant="body1"
editorMode === 'edition-owner' || component="div"
editorMode === 'edition-editor' || sx={{ marginX: '1.5rem' }}
editorMode === 'edition-viewer';
if (isEditor) {
return [
...commonConfiguration,
{
render: () => (
<Tooltip title={designer.getMindmap().getCentralTopic().getText()}>
<Typography
className="truncated"
variant="body1"
component="div"
sx={{ marginX: '1.5rem' }}
>
{designer.getMindmap().getCentralTopic().getText()}
</Typography>
</Tooltip>
),
},
null,
{
render: () => (
<UndoAndRedoButton
configuration={{
icon: <UndoOutlinedIcon />,
tooltip: $msg('UNDO') + ' (' + $msg('CTRL') + ' + Z)',
onClick: () => designer.undo(),
}}
disabledCondition={(event) => event.undoSteps > 0}
></UndoAndRedoButton>
),
},
{
render: () => (
<UndoAndRedoButton
configuration={{
icon: <RedoOutlinedIcon />,
tooltip: $msg('REDO') + ' (' + $msg('CTRL') + ' + Shift + Z)',
onClick: () => designer.redo(),
}}
disabledCondition={(event) => event.redoSteps > 0}
></UndoAndRedoButton>
),
},
null,
{
icon: <RestoreOutlinedIcon />,
tooltip: $msg('HISTORY'),
onClick: () => onAction('history'),
},
{
icon: <SaveOutlinedIcon />,
tooltip: $msg('SAVE') + ' (' + $msg('CTRL') + ' + S)',
onClick: save,
},
{
render: () => <Typography component="div" sx={{ flexGrow: 1 }} />,
},
{
icon: <PrintOutlinedIcon />,
tooltip: $msg('PRINT'),
onClick: () => onAction('print'),
},
{
icon: <FileDownloadOutlinedIcon />,
tooltip: $msg('EXPORT'),
onClick: () => onAction('export'),
},
{
icon: <CloudUploadOutlinedIcon />,
onClick: () => onAction('publish'),
tooltip: $msg('PUBLISH'),
disabled: () => editorMode !== 'edition-owner',
},
{
render: () => (
<Button
variant="contained"
onClick={() => onAction('share')}
disabled={editorMode !== 'edition-owner'}
> >
{$msg('COLLABORATE')} {designer.getMindmap().getCentralTopic().getText()}
</Button> </Typography>
), </Tooltip>
}, ),
{ },
icon: <HelpOutlineOutlinedIcon />, ];
onClick: () => onAction('info'),
tooltip: $msg('MAP_INFO'), const exportConfiguration = {
}, icon: <FileDownloadOutlinedIcon />,
]; tooltip: $msg('EXPORT'),
} onClick: () => onAction('export'),
};
const helpConfiguration = {
icon: <HelpOutlineOutlinedIcon />,
onClick: () => onAction('info'),
tooltip: $msg('MAP_INFO'),
};
const appBarDivisor = {
render: () => <Typography component="div" sx={{ flexGrow: 1 }} />,
};
if (showOnlyCommonActions)
return [...commonConfiguration, appBarDivisor, exportConfiguration, helpConfiguration];
return [
...commonConfiguration,
null,
{
render: () => (
<UndoAndRedoButton
configuration={{
icon: <UndoOutlinedIcon />,
tooltip: $msg('UNDO') + ' (' + $msg('CTRL') + ' + Z)',
onClick: () => designer.undo(),
}}
disabledCondition={(event) => event.undoSteps > 0}
></UndoAndRedoButton>
),
visible: showMindMapNodesActions,
},
{
render: () => (
<UndoAndRedoButton
configuration={{
icon: <RedoOutlinedIcon />,
tooltip: $msg('REDO') + ' (' + $msg('CTRL') + ' + Shift + Z)',
onClick: () => designer.redo(),
}}
disabledCondition={(event) => event.redoSteps > 0}
></UndoAndRedoButton>
),
visible: showMindMapNodesActions,
},
null,
{
icon: <RestoreOutlinedIcon />,
tooltip: $msg('HISTORY'),
onClick: () => onAction('history'),
visible: showPersistenceActions,
},
{
icon: <SaveOutlinedIcon />,
tooltip: $msg('SAVE') + ' (' + $msg('CTRL') + ' + S)',
onClick: save,
visible: showPersistenceActions,
},
appBarDivisor,
{
icon: <PrintOutlinedIcon />,
tooltip: $msg('PRINT'),
onClick: () => onAction('print'),
visible: showMapEntityActions,
},
exportConfiguration,
{
icon: <CloudUploadOutlinedIcon />,
onClick: () => onAction('publish'),
tooltip: $msg('PUBLISH'),
disabled: () => !showAccessChangeActions,
},
{
render: () => (
<Button
variant="contained"
onClick={() => onAction('share')}
disabled={!showAccessChangeActions}
>
{$msg('COLLABORATE')}
</Button>
),
},
helpConfiguration,
];
} }

View File

@ -96,10 +96,15 @@ const Editor = ({
const [mindplotComponent, setMindplotComponent]: [MindplotWebComponent | undefined, Function] = const [mindplotComponent, setMindplotComponent]: [MindplotWebComponent | undefined, Function] =
useState(); useState();
const editMode = const {
options.mode === 'edition-owner' || editMode,
options.mode === 'edition-editor' || showOnlyCommonActions,
options.mode === 'edition-viewer'; showAccessChangeActions,
showMapEntityActions,
showMindMapNodesActions,
showPersistenceActions,
} = getToolsVisibilityConfiguration(options, isMobile);
const editorTheme: Theme = theme ? theme : defaultEditorTheme; const editorTheme: Theme = theme ? theme : defaultEditorTheme;
const [toolbarsRerenderSwitch, setToolbarsRerenderSwitch] = useState(0); const [toolbarsRerenderSwitch, setToolbarsRerenderSwitch] = useState(0);
const toolbarConfiguration = useRef([]); const toolbarConfiguration = useRef([]);
@ -200,12 +205,24 @@ const Editor = ({
() => { () => {
mindplotComponent.save(true); mindplotComponent.save(true);
}, },
options.mode, showOnlyCommonActions,
isMobile, showAccessChangeActions,
showMapEntityActions,
showMindMapNodesActions,
showPersistenceActions,
); );
menubarConfiguration.push({ if (options.mode !== 'showcase') {
render: () => accountConfiguration, menubarConfiguration.push({
}); render: () => accountConfiguration,
});
}
useEffect(() => {
return () => {
mindplotComponent.unlockMap();
};
}, []);
// if the Toolbar is not hidden before the variable 'isMobile' is defined, it appears intermittently when the page loads // if the Toolbar is not hidden before the variable 'isMobile' is defined, it appears intermittently when the page loads
// if the Toolbar is not rendered, Menu.ts cant find buttons for create event listeners // if the Toolbar is not rendered, Menu.ts cant find buttons for create event listeners
// so, with this hack the Toolbar is rendered but no visible until the variable 'isMobile' is defined // so, with this hack the Toolbar is rendered but no visible until the variable 'isMobile' is defined
@ -225,7 +242,7 @@ const Editor = ({
> >
{widgetManager.getEditorContent()} {widgetManager.getEditorContent()}
</Popover> </Popover>
{editMode && !isMobile && ( {showMindMapNodesActions && (
<Toolbar <Toolbar
configurations={toolbarConfiguration.current} configurations={toolbarConfiguration.current}
rerender={toolbarsRerenderSwitch} rerender={toolbarsRerenderSwitch}
@ -252,3 +269,20 @@ const Editor = ({
); );
}; };
export default Editor; export default Editor;
function getToolsVisibilityConfiguration(options: EditorOptions, isMobile: any) {
const editMode = options.mode === 'edition-owner' || options.mode === 'edition-editor';
const showcaseMode = options.mode === 'showcase';
const showMindMapNodesActions = (editMode || showcaseMode) && !isMobile && !options.locked;
const showMapEntityActions = editMode && !isMobile;
const showAccessChangeActions = options.mode === 'edition-owner' && !isMobile;
const showPersistenceActions = editMode && !isMobile && !options.locked;
const showOnlyCommonActions = options.mode === 'viewonly' || isMobile;
return {
editMode,
showOnlyCommonActions,
showAccessChangeActions,
showMapEntityActions,
showMindMapNodesActions,
showPersistenceActions,
};
}

View File

@ -50,6 +50,10 @@
<ul> <ul>
<li><a href="/showcase.html">Sample</a></li> <li><a href="/showcase.html">Sample</a></li>
</ul> </ul>
<p><span class="section">Editor Mode (locked):</span>Example on how mindplot can be used for mindmap edition. Browser local storage is used for persistance.</p>
<ul>
<li><a href="/editorlocked.html">Sample</a></li>
</ul>
</div> </div>
</body> </body>

View File

@ -0,0 +1,53 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import Editor, { EditorOptions } from '../../../../src/index';
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
const initialization = (designer: Designer) => {
designer.addEvent('loadSuccess', () => {
const elem = document.getElementById('mindmap-comp');
if (elem) {
elem.classList.add('ready');
}
});
};
const persistence = new LocalStorageManager('samples/{id}.wxml', false, false);
const mapId = 'welcome';
const options: EditorOptions = {
zoom: 0.8,
locked: true,
lockedMsg: 'Blockeado',
mapTitle: 'Develop WiseMapping',
mode: 'edition-editor',
locale: 'en',
enableKeyboardEvents: true,
};
ReactDOM.render(
<Editor
mapId={mapId}
options={options}
persistenceManager={persistence}
onAction={(action) => console.log('action called:', action)}
onLoad={initialization}
/>,
document.getElementById('root'),
);

View File

@ -11,6 +11,7 @@ const playgroundConfig = {
viewmode: path.resolve(__dirname, './test/playground/map-render/js/viewmode'), viewmode: path.resolve(__dirname, './test/playground/map-render/js/viewmode'),
editor: path.resolve(__dirname, './test/playground/map-render/js/editor'), editor: path.resolve(__dirname, './test/playground/map-render/js/editor'),
showcase: path.resolve(__dirname, './test/playground/map-render/js/showcase'), showcase: path.resolve(__dirname, './test/playground/map-render/js/showcase'),
editorlocked: path.resolve(__dirname, './test/playground/map-render/js/editorlocked'),
}, },
output: { output: {
path: path.resolve(__dirname, 'test/playground/dist'), path: path.resolve(__dirname, 'test/playground/dist'),
@ -53,6 +54,11 @@ const playgroundConfig = {
filename: 'showcase.html', filename: 'showcase.html',
template: 'test/playground/map-render/html/showcase.html', template: 'test/playground/map-render/html/showcase.html',
}), }),
new HtmlWebpackPlugin({
chunks: ['editorlocked'],
filename: 'editorlocked.html',
template: 'test/playground/map-render/html/editor.html',
}),
], ],
}; };