Skip to content

Commit 1afdb65

Browse files
authored
Merge pull request #618 from ZenUml/feat/embedable
feat: introduce embed mode
2 parents 05348e7 + b8c0a25 commit 1afdb65

File tree

8 files changed

+225
-110
lines changed

8 files changed

+225
-110
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"dependencies": {
6161
"@emmetio/codemirror-plugin": "^1.1.3",
6262
"@zenuml/core": "^3.14.5",
63+
"clsx": "^2.0.0",
6364
"code-blast-codemirror": "chinchang/code-blast-codemirror#web-maker",
6465
"codemirror": "^5.65.16",
6566
"dom-to-image": "github:MichalBryxi/dom-to-image",

src/assets/external-link.svg

Lines changed: 8 additions & 0 deletions
Loading

src/components/EmbedHeader.jsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export default function EmbedHeader(props) {
2+
return (
3+
<div className="embed-header">
4+
<div className="embed-header__left">
5+
<div className="header-logo">
6+
<img src="assets/zenuml-icon.png" alt="zenuml logo" />
7+
</div>
8+
<div className="tit">{this.props.title || 'Untitled'}</div>
9+
</div>
10+
<div className="embed-header__right">
11+
<a
12+
className="embed-header__external"
13+
title="Edit on app.zenuml.com"
14+
target="_blank"
15+
href={this.props.link}
16+
>
17+
<img src="assets/external-link.svg" alt="link logo" />
18+
</a>
19+
</div>
20+
</div>
21+
);
22+
}

src/components/Tabs.jsx

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Tab from './Tab';
55
class Tabs extends Component {
66
static propTypes = {
77
children: PropTypes.instanceOf(Array).isRequired,
8-
}
8+
};
99

1010
constructor(props) {
1111
super(props);
@@ -16,36 +16,32 @@ class Tabs extends Component {
1616
this.state = {
1717
activeTab: this.props.children[0].props.label,
1818
};
19-
}
19+
};
2020
onClickTabItem = async (tab) => {
21-
await this.setState({activeTab: tab});
21+
await this.setState({ activeTab: tab });
2222
this.props.onChange(tab);
23-
}
23+
};
2424

2525
static modifyChildren(child, visible) {
2626
const className = [child.props.className, visible ? '' : 'hide'].join(' ');
2727

2828
const props = {
29-
className
29+
className,
3030
};
3131

3232
return React.cloneElement(child, props);
3333
}
3434
render() {
3535
const {
3636
onClickTabItem,
37-
props: {
38-
children,
39-
},
40-
state: {
41-
activeTab,
42-
}
37+
props: { children },
38+
state: { activeTab },
4339
} = this;
4440
return (
4541
<div className="tabs" style="height:100%">
4642
<ol className="tab-list editor-nav">
4743
{children.map((child) => {
48-
const { label,lineOfCode } = child.props;
44+
const { label, lineOfCode } = child.props;
4945
return (
5046
<Tab
5147
activeTab={activeTab}
@@ -57,16 +53,15 @@ class Tabs extends Component {
5753
);
5854
})}
5955
</ol>
60-
<div className="tab-content" style="height: calc(100% - 45px); overflow-y:auto;-webkit-overflow-scrolling: touch;">
61-
{
62-
React.Children.map(children,
63-
(child) => {
64-
if (child.props.label !== activeTab) {
65-
return React.Children.map(child.props.children, c => Tabs.modifyChildren(c, false));
66-
}
67-
return child.props.children;
68-
})
69-
}
56+
<div className="tab-content">
57+
{React.Children.map(children, (child) => {
58+
if (child.props.label !== activeTab) {
59+
return React.Children.map(child.props.children, (c) =>
60+
Tabs.modifyChildren(c, false)
61+
);
62+
}
63+
return child.props.children;
64+
})}
7065
</div>
7166
</div>
7267
);

src/components/app.jsx

Lines changed: 107 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ import JSZip from 'jszip';
4747
import { loadSubscriptionToApp } from '../javascript/firebase/subscription';
4848
import { currentBrowserTab } from '../services/browserService';
4949
import { syncDiagram, getShareLink } from '../services/syncService';
50+
import clsx from 'clsx';
51+
import EmbedHeader from './EmbedHeader.jsx';
5052

5153
if (module.hot) {
5254
require('preact/debug');
@@ -110,10 +112,12 @@ export default class App extends Component {
110112
lightVersion: false,
111113
lineWrap: true,
112114
infiniteLoopTimeout: 1000,
113-
layoutMode: 2,
115+
layoutMode: 1,
114116
isJs13kModeOn: false,
115117
autoCloseTags: true,
116118
};
119+
this.searchParams = new URLSearchParams(location.search);
120+
this.isEmbed = this.searchParams.get('embed');
117121
this.prefs = {};
118122
if (window.zenumlDesktop) {
119123
// hack savedItems, so we can load them on the desktop, without this object, the log in saveBtnClickHandler will not work.
@@ -192,8 +196,16 @@ export default class App extends Component {
192196
(result) => {
193197
this.toggleLayout(result.layoutMode);
194198
this.state.prefs.layoutMode = result.layoutMode;
195-
if (result.code) {
196-
lastCode = result.code;
199+
let urlCode;
200+
try {
201+
urlCode = JSON.parse(
202+
decodeURIComponent(this.searchParams.get('code'))
203+
);
204+
} catch (err) {
205+
console.error(err);
206+
}
207+
if (urlCode || result.code) {
208+
lastCode = urlCode || result.code;
197209
}
198210
}
199211
);
@@ -565,44 +577,46 @@ BookLibService.Borrow(id) {
565577
});
566578

567579
// Editor keyboard shortucuts
568-
window.addEventListener('keydown', async (event) => {
569-
// TODO: refactor common listener code
570-
// Ctrl/⌘ + S
571-
if ((event.ctrlKey || event.metaKey) && event.keyCode === 83) {
572-
event.preventDefault();
573-
this.saveItem();
574-
trackEvent('ui', 'saveItemKeyboardShortcut');
575-
}
576-
// Ctrl/⌘ + Shift + 5
577-
if (
578-
(event.ctrlKey || event.metaKey) &&
579-
event.shiftKey &&
580-
event.keyCode === 53
581-
) {
582-
event.preventDefault();
583-
this.contentWrap.setPreviewContent(true, true);
584-
trackEvent('ui', 'previewKeyboardShortcut');
585-
} else if ((event.ctrlKey || event.metaKey) && event.keyCode === 79) {
586-
// Ctrl/⌘ + O
587-
event.preventDefault();
588-
await this.openSavedItemsPane();
589-
trackEvent('ui', 'openCreationKeyboardShortcut');
590-
} else if (
591-
(event.ctrlKey || event.metaKey) &&
592-
event.shiftKey &&
593-
event.keyCode === 191
594-
) {
595-
// Ctrl/⌘ + Shift + ?
596-
event.preventDefault();
597-
await this.setState({
598-
isKeyboardShortcutsModalOpen:
599-
!this.state.isKeyboardShortcutsModalOpen,
600-
});
601-
trackEvent('ui', 'showKeyboardShortcutsShortcut');
602-
} else if (event.keyCode === 27) {
603-
await this.closeSavedItemsPane();
604-
}
605-
});
580+
if (!this.isEmbed) {
581+
window.addEventListener('keydown', async (event) => {
582+
// TODO: refactor common listener code
583+
// Ctrl/⌘ + S
584+
if ((event.ctrlKey || event.metaKey) && event.keyCode === 83) {
585+
event.preventDefault();
586+
this.saveItem();
587+
trackEvent('ui', 'saveItemKeyboardShortcut');
588+
}
589+
// Ctrl/⌘ + Shift + 5
590+
if (
591+
(event.ctrlKey || event.metaKey) &&
592+
event.shiftKey &&
593+
event.keyCode === 53
594+
) {
595+
event.preventDefault();
596+
this.contentWrap.setPreviewContent(true, true);
597+
trackEvent('ui', 'previewKeyboardShortcut');
598+
} else if ((event.ctrlKey || event.metaKey) && event.keyCode === 79) {
599+
// Ctrl/⌘ + O
600+
event.preventDefault();
601+
await this.openSavedItemsPane();
602+
trackEvent('ui', 'openCreationKeyboardShortcut');
603+
} else if (
604+
(event.ctrlKey || event.metaKey) &&
605+
event.shiftKey &&
606+
event.keyCode === 191
607+
) {
608+
// Ctrl/⌘ + Shift + ?
609+
event.preventDefault();
610+
await this.setState({
611+
isKeyboardShortcutsModalOpen:
612+
!this.state.isKeyboardShortcutsModalOpen,
613+
});
614+
trackEvent('ui', 'showKeyboardShortcutsShortcut');
615+
} else if (event.keyCode === 27) {
616+
await this.closeSavedItemsPane();
617+
}
618+
});
619+
}
606620

607621
// Basic Focus trapping
608622
window.addEventListener('focusin', (e) => {
@@ -674,10 +688,11 @@ BookLibService.Borrow(id) {
674688
}
675689
// Remove all layout classes
676690
[1, 2, 3, 4, 5].forEach((layoutNumber) => {
677-
window[`layoutBtn${layoutNumber}`].classList.remove('selected');
691+
window[`layoutBtn${layoutNumber}`] &&
692+
window[`layoutBtn${layoutNumber}`].classList.remove('selected');
678693
document.body.classList.remove(`layout-${layoutNumber}`);
679694
});
680-
$('#layoutBtn' + mode).classList.add('selected');
695+
$('#layoutBtn' + mode) && $('#layoutBtn' + mode).classList.add('selected');
681696
document.body.classList.add('layout-' + mode);
682697

683698
await this.setState({ currentLayoutMode: mode }, () => {
@@ -1368,10 +1383,12 @@ BookLibService.Borrow(id) {
13681383
}
13691384

13701385
render() {
1386+
// remove field imageBase64 from currentItem and save it to a local variable as a copy
1387+
const { imageBase64, ...currentItem } = this.state.currentItem;
13711388
return (
13721389
<div>
1373-
<div class="main-container">
1374-
{window.zenumlDesktop ? null : (
1390+
<div class={clsx('main-container', this.isEmbed && 'embed-app')}>
1391+
{window.zenumlDesktop || this.isEmbed ? null : (
13751392
<MainHeader
13761393
externalLibCount={this.state.externalLibCount}
13771394
openBtnHandler={this.openBtnClickHandler.bind(this)}
@@ -1390,6 +1407,16 @@ BookLibService.Borrow(id) {
13901407
unsavedEditCount={this.state.unsavedEditCount}
13911408
/>
13921409
)}
1410+
{this.isEmbed && (
1411+
<EmbedHeader
1412+
title={this.searchParams.get('title')}
1413+
link={
1414+
`/?code=${JSON.stringify(
1415+
currentItem
1416+
)}&title=${this.searchParams.get('title')}` || ''
1417+
}
1418+
/>
1419+
)}
13931420
<ContentWrap
13941421
currentLayoutMode={this.state.currentLayoutMode}
13951422
currentItem={this.state.currentItem}
@@ -1403,39 +1430,41 @@ BookLibService.Borrow(id) {
14031430
onEditorFocus={this.editorFocusHandler.bind(this)}
14041431
onSplitUpdate={this.splitUpdateHandler.bind(this)}
14051432
/>
1406-
<Footer
1407-
prefs={this.state.prefs}
1408-
layoutBtnClickHandler={this.layoutBtnClickHandler.bind(this)}
1409-
helpBtnClickHandler={async () =>
1410-
await this.setState({ isHelpModalOpen: true })
1411-
}
1412-
settingsBtnClickHandler={async () =>
1413-
await this.setState({ isSettingsModalOpen: true })
1414-
}
1415-
notificationsBtnClickHandler={this.notificationsBtnClickHandler.bind(
1416-
this
1417-
)}
1418-
supportDeveloperBtnClickHandler={this.supportDeveloperBtnClickHandler.bind(
1419-
this
1420-
)}
1421-
detachedPreviewBtnHandler={this.detachedPreviewBtnHandler.bind(
1422-
this
1423-
)}
1424-
codepenBtnClickHandler={this.codepenBtnClickHandler.bind(this)}
1425-
saveHtmlBtnClickHandler={this.saveHtmlBtnClickHandler.bind(this)}
1426-
keyboardShortcutsBtnClickHandler={async () =>
1427-
await this.setState({ isKeyboardShortcutsModalOpen: true })
1428-
}
1429-
screenshotBtnClickHandler={this.screenshotBtnClickHandler.bind(
1430-
this
1431-
)}
1432-
onJs13KHelpBtnClick={this.js13KHelpBtnClickHandler.bind(this)}
1433-
onJs13KDownloadBtnClick={this.js13KDownloadBtnClickHandler.bind(
1434-
this
1435-
)}
1436-
hasUnseenChangelog={this.state.hasUnseenChangelog}
1437-
codeSize={this.state.codeSize}
1438-
/>
1433+
{this.isEmbed ? null : (
1434+
<Footer
1435+
prefs={this.state.prefs}
1436+
layoutBtnClickHandler={this.layoutBtnClickHandler.bind(this)}
1437+
helpBtnClickHandler={async () =>
1438+
await this.setState({ isHelpModalOpen: true })
1439+
}
1440+
settingsBtnClickHandler={async () =>
1441+
await this.setState({ isSettingsModalOpen: true })
1442+
}
1443+
notificationsBtnClickHandler={this.notificationsBtnClickHandler.bind(
1444+
this
1445+
)}
1446+
supportDeveloperBtnClickHandler={this.supportDeveloperBtnClickHandler.bind(
1447+
this
1448+
)}
1449+
detachedPreviewBtnHandler={this.detachedPreviewBtnHandler.bind(
1450+
this
1451+
)}
1452+
codepenBtnClickHandler={this.codepenBtnClickHandler.bind(this)}
1453+
saveHtmlBtnClickHandler={this.saveHtmlBtnClickHandler.bind(this)}
1454+
keyboardShortcutsBtnClickHandler={async () =>
1455+
await this.setState({ isKeyboardShortcutsModalOpen: true })
1456+
}
1457+
screenshotBtnClickHandler={this.screenshotBtnClickHandler.bind(
1458+
this
1459+
)}
1460+
onJs13KHelpBtnClick={this.js13KHelpBtnClickHandler.bind(this)}
1461+
onJs13KDownloadBtnClick={this.js13KDownloadBtnClickHandler.bind(
1462+
this
1463+
)}
1464+
hasUnseenChangelog={this.state.hasUnseenChangelog}
1465+
codeSize={this.state.codeSize}
1466+
/>
1467+
)}
14391468
</div>
14401469

14411470
<SavedItemPane

src/computes.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,13 @@ export function computeJs(
178178
const cursor = e.data && e.data.cursor;
179179
180180
if (code && app) {
181-
app.render(code, { enableMultiTheme: false, onContentChange: (code) => {
182-
window.parent.postMessage({ code })
183-
}});
181+
app.render(code, {
182+
enableMultiTheme: false,
183+
onContentChange: (code) => {
184+
window.parent.postMessage({ code })
185+
},
186+
stickyOffset: Number(new URLSearchParams(window.location.search).get('stickyOffset') || 0)
187+
});
184188
}
185189
186190
if(app && (cursor !== null || cursor !== undefined)) {

0 commit comments

Comments
 (0)