Skip to content

Commit e540485

Browse files
authored
Merge pull request #602 from ZenUml/feature/share-preview-card
feat(SharePanel, PreviewCard): add author info and optimize view
2 parents e18e363 + 9b1e34c commit e540485

File tree

10 files changed

+662
-340
lines changed

10 files changed

+662
-340
lines changed

src/assets/og_bg_default.jpg

375 KB
Loading

src/assets/tutorial.png

100 KB
Loading

src/components/ContentWrap.jsx

Lines changed: 191 additions & 114 deletions
Large diffs are not rendered by default.

src/components/PopOver.jsx

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Component, createRef } from 'preact';
22
import PropTypes from 'prop-types';
33

4-
54
export class Popover extends Component {
65
constructor(props) {
76
super(props);
@@ -12,40 +11,61 @@ export class Popover extends Component {
1211
}
1312

1413
componentDidMount() {
15-
document.addEventListener('click', this.handleDocumentClick, true);
14+
document.addEventListener(
15+
'click',
16+
this.handleDocumentClick.bind(this),
17+
true
18+
);
1619
}
1720

1821
componentWillUnmount() {
19-
document.removeEventListener('click', this.handleDocumentClick);
22+
document.removeEventListener('click', this.handleDocumentClick.bind(this));
2023
}
2124

2225
togglePopover = () => {
23-
this.setState((prevState) => ({
24-
isVisible: !prevState.isVisible,
25-
}));
26+
if (this.props.isVisible !== undefined) {
27+
this.props.onVisibilityChange &&
28+
this.props.onVisibilityChange(!this.props.isVisible);
29+
} else {
30+
this.setState((prevState) => ({
31+
isVisible: !prevState.isVisible,
32+
}));
33+
}
2634
};
2735

2836
handleDocumentClick = (event) => {
29-
if (this.popoverRef.current && !this.popoverRef.current.contains(event.target)) {
37+
if (
38+
this.popoverRef.current &&
39+
!this.popoverRef.current.contains(event.target)
40+
) {
41+
this.props.onVisibilityChange && this.props.onVisibilityChange(false);
3042
this.setState({ isVisible: false });
3143
}
3244
};
3345

34-
3546
render() {
36-
const { trigger, content } = this.props;
37-
const { isVisible } = this.state;
47+
const { trigger, content, closeOnBlur, placement, hasArrow, hasShadow } =
48+
this.props;
49+
const isVisible =
50+
this.props.isVisible !== undefined
51+
? this.props.isVisible
52+
: this.state.isVisible;
3853

3954
return (
40-
<div className="popover" >
55+
<div className={`popover`}>
4156
<div className="popover-trigger" onClick={this.togglePopover}>
4257
{trigger}
4358
</div>
4459
{isVisible && (
4560
<>
46-
<div className="popover-backdrop" />
47-
<div className="popover-content" ref={this.popoverRef}>
48-
<div className="popover-arrow"></div>
61+
{closeOnBlur && <div className="popover-backdrop" />}
62+
<div
63+
className={`popover-content ${placement} ${
64+
hasShadow ? 'shadow' : ''
65+
}`}
66+
ref={this.popoverRef}
67+
>
68+
{hasArrow && <div className={`popover-arrow`}></div>}
4969
{content}
5070
</div>
5171
</>
@@ -55,8 +75,13 @@ export class Popover extends Component {
5575
}
5676
}
5777

58-
5978
Popover.propTypes = {
6079
trigger: PropTypes.node.isRequired,
6180
content: PropTypes.node.isRequired,
62-
}
81+
isVisible: PropTypes.bool,
82+
onVisibilityChange: PropTypes.func,
83+
closeOnBlur: PropTypes.bool,
84+
hasArrow: PropTypes.bool,
85+
hasShadow: PropTypes.bool,
86+
placement: PropTypes.oneOf(['top', 'bottom']),
87+
};

src/components/PreviewCard.jsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,37 @@
1-
import PropTypes from "prop-types";
1+
import PropTypes from 'prop-types';
22

3-
export function PreviewCard({ title, description, image }) {
3+
export function PreviewCard({ title, author, description, imageBase64 }) {
44
return (
55
<div className="preview-card">
66
<div className="preview-card__content">
77
<div className="preview-card__title">
88
<img src="https://zenuml.cn/favicon.ico" />
9-
<span>{title}</span>
9+
<span className="text-ellipsis">{title}</span>
10+
</div>
11+
<div className="preview-card__subtitle">
12+
<span class="material-symbols-outlined">account_circle</span>
13+
<span className="text-ellipsis">Created by {author}</span>
1014
</div>
1115
<div className="preview-card__description">
12-
<span>{description}</span>
16+
<p>{description}</p>
17+
</div>
18+
<div className="preview-card__footer">
19+
<img src="https://zenuml.cn/favicon.ico" />
20+
<span>ZenUML</span>
1321
</div>
1422
</div>
1523
<div className="preview-card__image">
16-
<img src={image} />
24+
<div className="overlay">
25+
<img src={imageBase64} />
26+
</div>
1727
</div>
1828
</div>
1929
);
2030
}
2131

2232
PreviewCard.propTypes = {
2333
title: PropTypes.string.isRequired,
34+
author: PropTypes.string.isRequired,
2435
description: PropTypes.string.isRequired,
25-
image: PropTypes.string.isRequired,
36+
imageBase64: PropTypes.string,
2637
};

src/components/SharePanel.jsx

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
1-
21
import { Component } from 'preact';
32
import PropTypes from 'prop-types';
43
import { PreviewCard } from './PreviewCard';
54
import { syncDiagram, getShareLink } from '../services/syncService';
5+
import { Popover } from './PopOver';
6+
import { Button } from './common';
67

78
export class SharePanel extends Component {
89
constructor(props) {
910
super(props);
1011
this.state = {
1112
isLoading: true,
1213
link: '',
13-
}
14+
isTooltipVisible: false,
15+
};
1416
}
1517

1618
async componentDidMount() {
17-
const result = await syncDiagram(this.props.currentItem);
19+
await this.syncDiagram(this.props.currentItem);
20+
}
21+
22+
async componentDidUpdate(prevProps) {
23+
if (prevProps.currentItem !== this.props.currentItem) {
24+
await this.syncDiagram(this.props.currentItem);
25+
}
26+
}
27+
28+
async syncDiagram(currentItem) {
29+
const result = await syncDiagram(currentItem);
30+
if (!result) {
31+
return;
32+
}
1833
this.setState({
1934
isLoading: false,
2035
link: getShareLink(result),
@@ -24,53 +39,78 @@ export class SharePanel extends Component {
2439
handleCopyLink = () => {
2540
const { link } = this.state;
2641
navigator.clipboard.writeText(link);
27-
}
42+
this.setState({
43+
isTooltipVisible: true,
44+
});
45+
setTimeout(() => {
46+
this.setState({ isTooltipVisible: false });
47+
}, 3000);
48+
};
2849

2950
render() {
51+
const { author, currentItem } = this.props;
3052
const { link, isLoading } = this.state;
3153

32-
3354
return (
3455
<div className="share-panel">
35-
<h3 style={{ marginTop: '4px' }}>Share the Diagram on Confluence<sup>*</sup></h3>
56+
<h3 style={{ marginTop: '4px' }}>
57+
Share the Diagram on Confluence<sup>*</sup>
58+
</h3>
3659
<>
3760
<div>
3861
<p>Paste the link on Confluence and select "Display as a Card"</p>
39-
<img width={200} height={100} style="background: #acacac" />
62+
<img style="width: 100%;" src="../assets/tutorial.png" />
4063
</div>
4164
<br />
4265
<div>
4366
<h4 style="margin-bottom: 8px;">Preview</h4>
44-
<div className="preview" >
67+
<div className="preview">
4568
<PreviewCard
46-
title="ZenUML Sequence"
47-
description="ZenUML Sequence"
48-
image="https://zenuml.cn/storage/diagrams/3/79.png"
69+
title={currentItem.title}
70+
author={author}
71+
description="Click and check the latest diagram. Install our Confluence plugin for an enhanced expperience when viewing in Confluence."
72+
imageBase64={currentItem.imageBase64}
73+
/>
74+
<Popover
75+
isVisible={this.state.isTooltipVisible}
76+
placement={'top'}
77+
hasShadow={true}
78+
trigger={
79+
<Button
80+
aria-label="Copy link"
81+
className="button icon-button copy-button"
82+
title={link}
83+
onClick={this.handleCopyLink}
84+
disabled={isLoading}
85+
>
86+
{isLoading ? (
87+
<div className="loader" />
88+
) : (
89+
<span className="material-symbols-outlined">link</span>
90+
)}
91+
<span>Copy link</span>
92+
</Button>
93+
}
94+
content={
95+
<div className="tooltip">
96+
<span class="material-symbols-outlined">check_circle</span>
97+
<span>Link copied to clipboard</span>
98+
</div>
99+
}
49100
/>
50-
<button
51-
aria-label="Copy link"
52-
className="button icon-button copy-button" title={link}
53-
onClick={this.handleCopyLink}
54-
>
55-
{isLoading ? <div className="loader" /> :
56-
<span className="material-symbols-outlined">
57-
link
58-
</span>}
59-
<span>Copy link</span>
60-
</button>
61101
</div>
62102
</div>
63-
<span className="footnote">* Anyone with the link can view the diagram. The view is optimised for Confluence.</span>
103+
<span className="footnote">
104+
* Anyone with the link can view the diagram. The view is optimised
105+
for Confluence.
106+
</span>
64107
</>
65108
</div>
66109
);
67110
}
68111
}
69112

70113
SharePanel.propTypes = {
71-
id: PropTypes.string,
72-
dsl: PropTypes.string,
73-
email: PropTypes.string,
74-
image: PropTypes.string,
114+
author: PropTypes.string,
75115
currentItem: PropTypes.object,
76116
};

src/components/Tooltip.jsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React, { Component } from 'react';
2+
3+
class Tooltip extends Component {
4+
state = {
5+
isTooltipVisible: false,
6+
};
7+
8+
handleMouseOver = () => {
9+
this.setState({ isTooltipVisible: true });
10+
};
11+
12+
handleMouseOut = () => {
13+
this.setState({ isTooltipVisible: false });
14+
};
15+
16+
render() {
17+
return (
18+
<div>
19+
{this.props.children}
20+
{this.state.isTooltipVisible && (
21+
<div className="tooltip">{this.props.children}</div>
22+
)}
23+
</div>
24+
);
25+
}
26+
}

0 commit comments

Comments
 (0)