modeMenuEvt(value)}
- />
+ <>
+
+
+ }
+ style={{ backgroundColor: isPageOn ? vogues.color.white : ''}}
+ onClick={togglePagePanel}
+ />
+
+
+ }
+ style={{ backgroundColor: isAnimationOn ? vogues.color.white : ''}}
+ onClick={toggleAnimationPanel}
+ />
+
+
+ >
)
});
diff --git a/src/wireframes/components/headers/PresentHeader.tsx b/src/wireframes/components/headers/PresentHeader.tsx
index 0baca98..f3df387 100644
--- a/src/wireframes/components/headers/PresentHeader.tsx
+++ b/src/wireframes/components/headers/PresentHeader.tsx
@@ -9,135 +9,30 @@
import { FundProjectionScreenOutlined } from "@ant-design/icons";
import { Button, message } from "antd";
import * as React from "react";
-import { getFilteredDiagrams, getEditor, useStore, Diagram, compileSlides } from "@app/wireframes/model";
-import { AbstractControl } from "@app/wireframes/shapes/utils/abstract-control";
-import * as svg from '@svgdotjs/svg.js';
-import { getPlugin } from "@app/wireframes/shapes/utils/abstract-plugin";
-import { Color } from "@app/core/utils/color";
-import { shapes } from "@app/const";
import { useState } from "react";
+import { useServer } from "../actions";
+import { useDispatch } from "react-redux";
export const PresentHeader = React.memo(() => {
- const html = document.querySelector('.editor-diagram')?.innerHTML;
- const messageKey = 'PRESENT';
-
- const diagrams = useStore(getFilteredDiagrams);
- const editor = useStore(getEditor);
+ const dispatch = useDispatch();
+ const forServer = useServer();
const [messageApi, contextHolder] = message.useMessage();
const [loading, setLoading] = useState(false);
-
- const getItem = (diagram: Diagram, str: string) => {
- // Split text using `=` symbol, ignoring those inside curly brackets
- const regex = /=(?![^{]*})/;
- const [id, json] = str.split(regex);
-
- // Get item
- let item = diagram.items.get(id);
- if (!item || !json) return { item: item, id: id };
-
- // Parse str -> json
- let corrected = json.replace(/'/g, '"');
- let jsonObj: {[index: string]: string} = JSON.parse(corrected);
-
- // Modify appearance if there are valid specifications
- // e.g. Shape1 = {'TEXT': 'Hello, world!'}
- const allKeys = Object.values(shapes.key);
- for (let [key, value] of Object.entries(jsonObj)) {
- if (!allKeys.includes(key)) continue; // Safe-check
-
- if (key.endsWith('COLOR')) {
- const color = Color.fromValue(value).toNumber();
- item = item.setAppearance(key, color);
- } else {
- item = item.setAppearance(key, value);
- }
- }
-
- return { item: item, id: id };
- }
-
- const getSlides = () => {
- let frame3D: string[][][] = new Array(diagrams.length);
-
- // Get frames
- diagrams.map((diagram, i) => {
- const frames = diagram.frames ?? [];
- frame3D[i] = [];
-
- frames.map((frame, j) => {
- const usedIDs: string[] = [];
- frame3D[i][j] = [];
-
- for (let k = frame.length; k > 0; k--) {
- const { item, id } = getItem(diagram, frame[k - 1]);
- if (!item || usedIDs.includes(id)) continue;
- usedIDs.push(id);
-
- // Get svg
- const svgControl = new AbstractControl(getPlugin(item.renderer));
- const svgElement: svg.Element = svgControl.render(item, undefined);
- const svgCode = svgElement.node.outerHTML;
-
- // Push object to the head of parent map
- frame3D[i][j] = [svgCode, ...frame3D[i][j]];
- }
- })
- })
-
- // Reshape from 3D to 2D
- let frame2D = [];
- for (let row of frame3D) for (let e of row) frame2D.push(e);
-
- return {
- fileName: editor.id,
- title: editor.name,
- backgroundColor: editor.color.toString(),
- size: [editor.size.x, editor.size.y],
- config: editor.revealConfig,
- frame: frame2D,
- };
- }
-
- const fetchApi = async () => {
- const { fileName, title, size, backgroundColor, config, frame } = getSlides();
-
- if (!html) {
- messageApi.error('Empty slide. Cannot perform action');
- return;
- }
-
- // Start compiling
- setLoading(true);
- messageApi.open({
- key: messageKey,
- type: 'loading',
- content: 'Preparing presentation...',
- });
-
- try {
- const linkPresentation = await compileSlides(fileName, title, size, backgroundColor, config, frame);
-
- messageApi.open({
- key: messageKey,
- type: 'success',
- content: 'Preparing completed. Your presentation will be opened in a new tab.',
- duration: 2,
- });
- setLoading(false);
-
- window.open(linkPresentation);
- } catch (err) {
- messageApi.error(`${err}`);
- setLoading(false);
- }
- }
+ const messageKey = 'PRESENT';
return (
<>
{contextHolder}
}
- onClick={fetchApi}
+ onClick={() => {
+ setLoading(true);
+ try {
+ dispatch(forServer.slide(messageApi, messageKey));
+ } finally {
+ setLoading(false)
+ }
+ }}
className="header-cta-right"
type="text" shape='round'
loading={loading}
@@ -146,4 +41,4 @@ export const PresentHeader = React.memo(() => {
>
)
-});
+});
\ No newline at end of file
diff --git a/src/wireframes/components/headers/ScriptHeader.tsx b/src/wireframes/components/headers/ScriptHeader.tsx
deleted file mode 100644
index f111cd7..0000000
--- a/src/wireframes/components/headers/ScriptHeader.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * codeslide.net
- *
- * @license
- * Forked from mydraft.cc by Sebastian Stehle
- * Copyright (c) Do Duc Quan. All rights reserved.
-*/
-
-import React from "react"
-import { SelectOutlined } from "@ant-design/icons"
-import { Button, message } from "antd"
-import { useDispatch } from "react-redux";
-import { changeFrames, getDiagram, parseFrames, useStore } from "@app/wireframes/model";
-
-export const ScriptHeader = React.memo(() => {
- const dispatch = useDispatch();
- const diagram = useStore(getDiagram);
- const [messageApi, contextHolder] = message.useMessage();
-
- if (!diagram) return null;
-
- const fetchFrames = async () => {
- const script = diagram.script;
-
- if (!script) {
- messageApi.error('Empty script. Cannot perform action');
- return;
- };
-
- try {
- const frames = await parseFrames(script);
- dispatch(changeFrames(diagram.id, frames));
- messageApi.success('Script is loaded successfully');
- } catch (error) {
- messageApi.error(`${error}`);
- }
- }
-
- return (
- <>
- {contextHolder}
- }
- onClick={fetchFrames} >
- Load script
-
- >
- )
-})
\ No newline at end of file
diff --git a/src/wireframes/components/headers/index.ts b/src/wireframes/components/headers/index.ts
index 05ba420..39e2a20 100644
--- a/src/wireframes/components/headers/index.ts
+++ b/src/wireframes/components/headers/index.ts
@@ -2,5 +2,4 @@ export * from './ArrangeHeader';
export * from './FileHeader';
export * from './IdHeader';
export * from './ModeHeader';
-export * from './PresentHeader';
-export * from './ScriptHeader';
\ No newline at end of file
+export * from './PresentHeader';
\ No newline at end of file
diff --git a/src/wireframes/components/settings/PresentSetting.tsx b/src/wireframes/components/settings/PresentSetting.tsx
index 3a60119..57afb52 100644
--- a/src/wireframes/components/settings/PresentSetting.tsx
+++ b/src/wireframes/components/settings/PresentSetting.tsx
@@ -49,7 +49,7 @@ export const PresentSetting = React.memo(() => {
const handleSrcCancel = () => {
setIsScrChange(false);
- setRevealScr(JSON.stringify(editor.revealConfig, null, 4));
+ setRevealScr(editor.revealConfig);
}
return (
diff --git a/src/wireframes/components/styles/ToolView.scss b/src/wireframes/components/styles/ToolView.scss
index 6462e0e..e796d60 100644
--- a/src/wireframes/components/styles/ToolView.scss
+++ b/src/wireframes/components/styles/ToolView.scss
@@ -9,6 +9,7 @@
line-height: 38px;
justify-content: space-between;
align-items: center;
+ overflow-x: scroll;
}
&-toggle {
@@ -36,7 +37,6 @@
border-radius: 20px;
padding: 3px 10px;
flex-grow: 1;
- overflow-x: scroll;
}
&-scroll {
diff --git a/src/wireframes/components/tools/ZoomTool.tsx b/src/wireframes/components/tools/ZoomTool.tsx
index d777b19..1805094 100644
--- a/src/wireframes/components/tools/ZoomTool.tsx
+++ b/src/wireframes/components/tools/ZoomTool.tsx
@@ -18,14 +18,15 @@ import type { MenuProps } from 'antd';
export const ZoomTool = React.memo(() => {
const dispatch = useDispatch();
const editorSize = useStore(getEditor).size;
- const sidebarSize = useStore(s => s.ui.sidebarSize);
+ const sidebarWidth = useStore(s => s.ui.sidebarSize);
+ const isFooter = useStore(s => s.ui.footerSize) == vogues.common.previewHeight ? 1 : 0;
const [zoomValue, setZoomValue] = useState('Fit');
const zoomPad = {
- vertical: vogues.common.editorMargin * 2 + vogues.common.editorPad * 3 + vogues.common.headerHeight + vogues.common.shapeWidth + (vogues.common.previewHeight + vogues.common.editorMargin + vogues.common.previewPadBot) + vogues.common.selectionThickness * 4,
- horizontal: vogues.common.editorMargin * 4 + vogues.common.editorPad * 2 + vogues.common.sidebarShape + vogues.common.selectionThickness * 4,
+ vertical: vogues.common.editorMargin * 2 + vogues.common.editorPad * 3 + vogues.common.headerHeight + vogues.common.shapeWidth + isFooter * (vogues.common.previewHeight + vogues.common.editorMargin + vogues.common.previewPadBot) + vogues.common.selectionThickness * 4,
+ horizontal: vogues.common.editorMargin * 4 + vogues.common.editorPad * 3 + vogues.common.sidebarShape + vogues.common.selectionThickness * 4,
}
- const [areaSize, setAreaSize] = useState(new Vec2(window.innerWidth - zoomPad.horizontal - sidebarSize, window.innerHeight - zoomPad.vertical));
+ const [areaSize, setAreaSize] = useState(new Vec2(window.innerWidth - zoomPad.horizontal - sidebarWidth, window.innerHeight - zoomPad.vertical));
const isZoom = (key: string) => {
setZoomValue(key);
@@ -33,7 +34,7 @@ export const ZoomTool = React.memo(() => {
};
const getWindowSize = () => {
- setAreaSize(new Vec2(window.innerWidth - zoomPad.horizontal - sidebarSize, window.innerHeight - zoomPad.vertical));
+ setAreaSize(new Vec2(window.innerWidth - zoomPad.horizontal - sidebarWidth, window.innerHeight - zoomPad.vertical));
}
// Get area size value on resizing window
@@ -54,8 +55,7 @@ export const ZoomTool = React.memo(() => {
getWindowSize();
isZoom(zoomValue);
}
-
- }, [sidebarSize, editorSize]);
+ }, [isFooter, sidebarWidth, editorSize]);
const getZoomValue = (value: string) => {
switch (value) {
diff --git a/src/wireframes/model/actions/api.ts b/src/wireframes/model/actions/api.ts
index a7c76f4..587d7df 100644
--- a/src/wireframes/model/actions/api.ts
+++ b/src/wireframes/model/actions/api.ts
@@ -76,6 +76,9 @@ export const compileSlides = async (fileName: string, title: string, size: numbe
if (!response.ok) throw Error(response.statusText);
const data = await response.json();
- const link = `${SERVER_URL}/${data.link}`;
- return link;
+
+ const linkSlide = `${SERVER_URL}/${data.slidePath}`;
+ const linkPdf = `${SERVER_URL}/${data.pdfPath}`;
+
+ return { linkSlide, linkPdf };
}
\ No newline at end of file
diff --git a/src/wireframes/model/actions/items.ts b/src/wireframes/model/actions/items.ts
index 818f158..62fdf8b 100644
--- a/src/wireframes/model/actions/items.ts
+++ b/src/wireframes/model/actions/items.ts
@@ -98,24 +98,29 @@ export function buildItems(builder: ActionReducerMapBuilder) {
const { diagramId, itemId, id } = action.payload;
return state.updateDiagram(diagramId, diagram => {
- // Get current shape
+ // If current id is the same as new id, return
+ if (itemId === id) return diagram;
+
+ // If new id is already existed, throw error
+ if (diagram.items.has(id)) throw new Error(`Cannot perform action! Item with id ${id} already existed.`);
+
+ // If shape is not existed, return
const shape = diagram.items.get(itemId);
if (!shape) return diagram;
// Dublicate item with assigning new id
- let { id: defaultId, newDiagram } = IDHelper.nextId(diagram, shape.renderer);
const newProps = {
- id: id || defaultId,
+ id: id,
renderer: shape.renderer,
appearance: shape.appearance,
transform: shape.transform,
};
const newShape = DiagramItem.createShape(newProps);
- newDiagram = newDiagram.addShape(newShape).selectItems([id]);
+ diagram = diagram.addShape(newShape).selectItems([id]);
// Remove old item
- const set = DiagramItemSet.createFromDiagram([itemId], newDiagram);
- return newDiagram.removeItems(set!);
+ const set = DiagramItemSet.createFromDiagram([itemId], diagram);
+ return diagram.removeItems(set!);
});
})
.addCase(renameItems, (state, action) => {
@@ -139,7 +144,7 @@ export function buildItems(builder: ActionReducerMapBuilder) {
diagram = diagram.updateItems(set.allShapes.map(x => x.id), item => {
const boundsOld = item.bounds(diagram);
- const boundsNew = boundsOld.moveBy(boundsOld.size.mul(new Vec2(offsetByX, offsetByY)));
+ const boundsNew = boundsOld.moveBy(new Vec2(offsetByX, offsetByY));
return item.transformByBounds(boundsOld, boundsNew);
});
diff --git a/src/wireframes/model/actions/ui.ts b/src/wireframes/model/actions/ui.ts
index 47c4cf0..b39abee 100644
--- a/src/wireframes/model/actions/ui.ts
+++ b/src/wireframes/model/actions/ui.ts
@@ -33,6 +33,11 @@ export const setSidebarSize =
return { payload: { size } };
});
+export const setFooterSize =
+ createAction('ui/footerSize', (size: number) => {
+ return { payload: { size } };
+ });
+
export const setMode =
createAction('ui/mode', (mode: ModeType) => {
return { payload: { mode } };
@@ -75,6 +80,9 @@ export function ui(initialState: UIState): Reducer {
.addCase(setSidebarSize, (state, action) => {
state.sidebarSize = action.payload.size;
})
+ .addCase(setFooterSize, (state, action) => {
+ state.footerSize = action.payload.size;
+ })
.addCase(setMode, (state, action) => {
state.selectedMode = action.payload.mode;
})
diff --git a/src/wireframes/model/editor-state.ts b/src/wireframes/model/editor-state.ts
index 2e8e9c5..799e37e 100644
--- a/src/wireframes/model/editor-state.ts
+++ b/src/wireframes/model/editor-state.ts
@@ -7,7 +7,7 @@
*/
import { Color, ImmutableList, ImmutableMap, MathHelper, Record, Vec2 } from '@app/core/utils';
-import { scripts } from '@app/const';
+import { scripts, vogues } from '@app/const';
import { Diagram } from './diagram';
import { UndoableState } from './undoable-state';
@@ -105,8 +105,8 @@ export class EditorState extends Record {
diagrams: ImmutableMap.of(diagrams),
diagramIds: ImmutableList.of(diagramIds),
id: MathHelper.guid(),
- size: size || new Vec2(1280, 720),
- name: name || 'Untitled Presentation',
+ size: size || new Vec2(vogues.common.canvasWidth, vogues.common.canvasHeight),
+ name: name || vogues.common.projectName,
revealConfig: scripts.common.reveal,
};
diff --git a/src/wireframes/model/ui-state.ts b/src/wireframes/model/ui-state.ts
index 008fdf0..ac48596 100644
--- a/src/wireframes/model/ui-state.ts
+++ b/src/wireframes/model/ui-state.ts
@@ -19,9 +19,12 @@ export interface UIState {
// The info toast from any loading operation.
infoToast?: string;
- // The size for right sidebar
+ // The size for right sidebar.
sidebarSize: number;
+ // The size for pages section.
+ footerSize: number;
+
// The mode for the application.
selectedMode: ModeType;
@@ -41,10 +44,11 @@ export interface UIStateInStore {
export const createInitialUIState: () => UIState = () => {
return {
+ footerSize: vogues.common.previewHeight,
zoom: 1,
selectedColor: 'palette',
selectedMode: 'design',
selectedAnimation: 'script',
- sidebarSize: vogues.common.sidebarClose,
+ sidebarSize: vogues.common.close,
};
};