Add delay render support.

This commit is contained in:
Paulo Gustavo Veiga 2022-11-16 21:12:33 -08:00
parent a1f0fa6281
commit 046215898e
10 changed files with 110 additions and 39 deletions

View File

@ -11,10 +11,16 @@ context('Relationship Topics', () => {
cy.contains('Try it Now!').first().click(); cy.contains('Try it Now!').first().click();
cy.get('[test-id="11-15-relationship"]').first().click({ force: true }); cy.get('[test-id="11-15-relationship"]').first().click({ force: true });
cy.get('[test-id="11-15-relationship"]').should('exist');
cy.matchImageSnapshot('addRelationship'); cy.matchImageSnapshot('addRelationship');
}); });
it('Delete Relationship', () => { it('Delete Relationship', () => {
cy.contains('Features').first().click();
cy.get(`[aria-label="Add Relationship"]`).first().click();
cy.contains('Try it Now!').first().click();
cy.get('[test-id="11-15-relationship"]').click({ force: true }); cy.get('[test-id="11-15-relationship"]').click({ force: true });
cy.get('body').type('{backspace}'); cy.get('body').type('{backspace}');

View File

@ -32,7 +32,7 @@ class Editor {
} }
isMapLoadded(): boolean { isMapLoadded(): boolean {
return this.component?.getDesigner()?.getMindmap() != null; return this.component.isLoaded();
} }
save(minor: boolean): void { save(minor: boolean): void {
@ -57,9 +57,9 @@ class Editor {
mapId: string, mapId: string,
persistenceManager: PersistenceManager, persistenceManager: PersistenceManager,
widgetManager: WidgetManager, widgetManager: WidgetManager,
): void { ): Promise<void> {
this.component.buildDesigner(persistenceManager, widgetManager); this.component.buildDesigner(persistenceManager, widgetManager);
this.component.loadMap(mapId); return this.component.loadMap(mapId);
} }
registerEvents(canvasUpdate: (timestamp: number) => void, capability: Capability): void { registerEvents(canvasUpdate: (timestamp: number) => void, capability: Capability): void {

View File

@ -79,10 +79,12 @@ const Editor = ({
const capability = new Capability(options.mode, mapInfo.isLocked()); const capability = new Capability(options.mode, mapInfo.isLocked());
const mindplotRef = useCallback((component: MindplotWebComponent) => { const mindplotRef = useCallback((component: MindplotWebComponent) => {
// Initialized model ...
const model = new Model(component); const model = new Model(component);
model.loadMindmap(mapInfo.getId(), persistenceManager, widgetManager);
model.registerEvents(setCanvasUpdate, capability); // Force refresh after map load ...
model.loadMindmap(mapInfo.getId(), persistenceManager, widgetManager).then(() => {
setCanvasUpdate(Date.now());
});
setModel(model); setModel(model);
}, []); }, []);
@ -137,7 +139,7 @@ const Editor = ({
message={mapInfo.isLocked() ? mapInfo.getLockedMessage() : ''} message={mapInfo.isLocked() ? mapInfo.getLockedMessage() : ''}
/> />
{!model && ( {!model?.isMapLoadded() && (
<SpinnerCentered> <SpinnerCentered>
<Vortex <Vortex
visible={true} visible={true}

View File

@ -42,9 +42,10 @@ const container = document.getElementById('root');
const root = createRoot(container!); const root = createRoot(container!);
root.render( root.render(
<Editor <Editor
mapInfo={new MapInfoImpl('welcome', 'Develop Map Title', false)} mapInfo={new MapInfoImpl(mapId, 'Develop Map Title', false)}
options={options} options={options}
persistenceManager={persistence} persistenceManager={persistence}
onAction={(action) => console.log('action called:', action)} onAction={(action) => console.log('action called:', action)}
onLoad={initialization} onLoad={initialization}
/>); />,
);

File diff suppressed because one or more lines are too long

View File

@ -577,14 +577,12 @@ class Designer extends Events {
return { zoom: model.getZoom() }; return { zoom: model.getZoom() };
} }
/** loadMap(mindmap: Mindmap): Promise<void> {
* @param {mindplot.Mindmap} mindmap
* @throws will throw an error if mindmapModel is null or undefined
*/
loadMap(mindmap: Mindmap): void {
$assert(mindmap, 'mindmapModel can not be null'); $assert(mindmap, 'mindmapModel can not be null');
this._mindmap = mindmap; this._mindmap = mindmap;
this._workspace.enableQueueRender(true);
// Init layout manager ... // Init layout manager ...
const size = { width: 25, height: 25 }; const size = { width: 25, height: 25 };
const layoutManager = new LayoutManager(mindmap.getCentralTopic().getId(), size); const layoutManager = new LayoutManager(mindmap.getCentralTopic().getId(), size);
@ -601,23 +599,29 @@ class Designer extends Events {
// Building node graph ... // Building node graph ...
const branches = mindmap.getBranches(); const branches = mindmap.getBranches();
const nodesGraph: Topic[] = [];
branches.forEach((branch) => { branches.forEach((branch) => {
const nodeGraph = this.nodeModelToTopic(branch); const nodeGraph = this.nodeModelToTopic(branch);
nodeGraph.setBranchVisibility(true); nodesGraph.push(nodeGraph);
}); });
// Connect relationships ...
const relationships = mindmap.getRelationships();
relationships.forEach((relationship) => this._relationshipModelToRelationship(relationship));
// Place the focus on the Central Topic // Place the focus on the Central Topic
const centralTopic = this.getModel().getCentralTopic(); const centralTopic = this.getModel().getCentralTopic();
this.goToNode(centralTopic); this.goToNode(centralTopic);
// Finally, sort the map ... return this._workspace.enableQueueRender(false).then(() => {
EventBus.instance.fireEvent('forceLayout'); // Connect relationships ...
const relationships = mindmap.getRelationships();
relationships.forEach((relationship) => this._relationshipModelToRelationship(relationship));
this.fireEvent('loadSuccess'); // Render nodes ...
nodesGraph.forEach((topic) => topic.setVisibility(true));
// Finally, sort the map ...
EventBus.instance.fireEvent('forceLayout');
this.fireEvent('loadSuccess');
});
} }
getMindmap(): Mindmap { getMindmap(): Mindmap {

View File

@ -3,7 +3,6 @@ import buildDesigner from './DesignerBuilder';
import DesignerOptionsBuilder from './DesignerOptionsBuilder'; import DesignerOptionsBuilder from './DesignerOptionsBuilder';
import EditorRenderMode from './EditorRenderMode'; import EditorRenderMode from './EditorRenderMode';
import LocalStorageManager from './LocalStorageManager'; import LocalStorageManager from './LocalStorageManager';
import Mindmap from './model/Mindmap';
import PersistenceManager from './PersistenceManager'; import PersistenceManager from './PersistenceManager';
import WidgetManager from './WidgetManager'; import WidgetManager from './WidgetManager';
import mindplotStyles from './styles/mindplot-styles'; import mindplotStyles from './styles/mindplot-styles';
@ -27,12 +26,12 @@ export type MindplotWebComponentInterface = {
class MindplotWebComponent extends HTMLElement { class MindplotWebComponent extends HTMLElement {
private _shadowRoot: ShadowRoot; private _shadowRoot: ShadowRoot;
private _mindmap: Mindmap;
private _designer: Designer; private _designer: Designer;
private saveRequired: boolean; private saveRequired: boolean;
private _isLoaded: boolean;
constructor() { constructor() {
super(); super();
this._shadowRoot = this.attachShadow({ mode: 'open' }); this._shadowRoot = this.attachShadow({ mode: 'open' });
@ -77,6 +76,16 @@ class MindplotWebComponent extends HTMLElement {
}); });
this.registerShortcuts(); this.registerShortcuts();
this._designer.addEvent('loadSuccess', (): void => {
this._isLoaded = true;
});
return this._designer;
}
isLoaded(): boolean {
return this._isLoaded;
} }
private registerShortcuts() { private registerShortcuts() {
@ -100,10 +109,9 @@ class MindplotWebComponent extends HTMLElement {
* Load map in designer throught persistence manager instance * Load map in designer throught persistence manager instance
* @param id the map id to be loaded. * @param id the map id to be loaded.
*/ */
async loadMap(id: string): Promise<void> { loadMap(id: string): Promise<void> {
const instance = PersistenceManager.getInstance(); const instance = PersistenceManager.getInstance();
this._mindmap = await instance.load(id); return instance.load(id).then((mindmap) => this._designer.loadMap(mindmap));
this._designer.loadMap(this._mindmap);
} }
/** /**

View File

@ -61,12 +61,11 @@ abstract class PersistenceManager {
return result; return result;
} }
load(mapId: string): Promise<Mindmap> { async load(mapId: string): Promise<Mindmap> {
$assert(mapId, 'mapId can not be null'); $assert(mapId, 'mapId can not be null');
return this.loadMapDom(mapId).then((document) => { // eslint-disable-next-line arrow-body-style
console.log(`Loading map with is ${mapId}}`); const document = await this.loadMapDom(mapId);
return PersistenceManager.loadFromDom(mapId, document); return PersistenceManager.loadFromDom(mapId, document);
});
} }
triggerError(error: PersistenceError) { triggerError(error: PersistenceError) {

View File

@ -35,6 +35,10 @@ class Workspace {
private _visibleAreaSize: SizeType; private _visibleAreaSize: SizeType;
private _renderQueue: Element2D[];
private queueRenderEnabled: boolean;
constructor(screenManager: ScreenManager, zoom: number, isReadOnly: boolean) { constructor(screenManager: ScreenManager, zoom: number, isReadOnly: boolean) {
// Create a suitable container ... // Create a suitable container ...
$assert(screenManager, 'Div container can not be null'); $assert(screenManager, 'Div container can not be null');
@ -66,6 +70,8 @@ class Workspace {
}); });
this.setZoom(zoom, true); this.setZoom(zoom, true);
this._renderQueue = [];
} }
private _adjustWorkspace(): void { private _adjustWorkspace(): void {
@ -97,6 +103,14 @@ class Workspace {
} }
append(shape: Element2D): void { append(shape: Element2D): void {
if (this.queueRenderEnabled) {
this._renderQueue.push(shape);
} else {
this.appendInternal(shape);
}
}
private appendInternal(shape: Element2D): void {
if ($defined(shape.addToWorkspace)) { if ($defined(shape.addToWorkspace)) {
shape.addToWorkspace(this); shape.addToWorkspace(this);
} else { } else {
@ -104,6 +118,37 @@ class Workspace {
} }
} }
enableQueueRender(value: boolean): Promise<void> {
this.queueRenderEnabled = value;
let result = Promise.resolve();
if (!value) {
result = this.processRenderQueue(this._renderQueue.reverse(), 300);
}
return result;
}
private processRenderQueue(renderQueue: Element2D[], batch: number): Promise<void> {
function delay(t: number) {
return new Promise((resolve) => setTimeout(resolve, t));
}
let result: Promise<void>;
if (renderQueue.length > 0) {
result = new Promise((resolve: (queue: Element2D[]) => void) => {
for (let i = 0; i < batch && renderQueue.length > 0; i++) {
const elem = renderQueue.pop();
this.appendInternal(elem);
}
resolve(renderQueue);
}).then((queue) => delay(30).then(() => this.processRenderQueue(queue, batch)));
} else {
result = Promise.resolve();
}
return result;
}
removeChild(shape: Element2D): void { removeChild(shape: Element2D): void {
// Element is a node, not a web2d element? // Element is a node, not a web2d element?
if ($defined(shape.removeFromWorkspace)) { if ($defined(shape.removeFromWorkspace)) {

View File

@ -159,12 +159,7 @@ class TextPeer extends ElementPeer {
} }
getWidth() { getWidth() {
let computedWidth = this._native.getBBox().width; const computedWidth = this._native.getBBox().width;
if (computedWidth === 0) {
const bbox = this._native.getBBox();
computedWidth = bbox.width;
}
let width = parseInt(computedWidth, 10); let width = parseInt(computedWidth, 10);
width += this._font.getWidthMargin(); width += this._font.getWidthMargin();
return width; return width;