mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-16 23:29:49 +00:00
scroll-to-changed-nodes (#2564)
This commit is contained in:
@@ -87,32 +87,34 @@
|
|||||||
.review-side-panel-body {
|
.review-side-panel-body {
|
||||||
padding: 1rem 2rem;
|
padding: 1rem 2rem;
|
||||||
}
|
}
|
||||||
.review-side-panel-reviewers, .review-side-panel-author {
|
.review-side-panel-item {
|
||||||
border-bottom: 1px solid #e6e6dd;
|
border-bottom: 1px solid #e6e6dd;
|
||||||
padding: 1em 0;
|
padding: 1em 0;
|
||||||
}
|
}
|
||||||
.reviewer-info {
|
.reviewer-info, .author-info {
|
||||||
margin-bottom: 0.8125rem;
|
|
||||||
}
|
|
||||||
.reviewers-header, .author-header {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
align-items: center;
|
||||||
margin-bottom: 0.8125rem;
|
|
||||||
}
|
|
||||||
.reviewers-header i {
|
|
||||||
color: #c8c8c8;
|
|
||||||
}
|
|
||||||
.reviewers-header i:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
color: #a4a4a4;
|
|
||||||
}
|
}
|
||||||
.review-side-panel-header {
|
.review-side-panel-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 2px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #666;
|
color: #666;
|
||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
}
|
}
|
||||||
.reviewer-avatar, .author-avatar {
|
.review-side-panel-item i {
|
||||||
|
color: #c8c8c8;
|
||||||
|
margin: 5px 5px 0 0;
|
||||||
|
}
|
||||||
|
.review-side-panel-item i:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #a4a4a4;
|
||||||
|
}
|
||||||
|
.review-side-panel-avatar {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
height: 1.5rem;
|
||||||
|
width: 1.5rem;
|
||||||
}
|
}
|
||||||
.reviewer-name, .author-name {
|
.reviewer-name, .author-name {
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
|
@@ -48,16 +48,22 @@ class DraftReview extends React.Component {
|
|||||||
reviewers: [],
|
reviewers: [],
|
||||||
activeTab: 'reviewInfo',
|
activeTab: 'reviewInfo',
|
||||||
historyList: [],
|
historyList: [],
|
||||||
totalReversionCount: 0
|
totalReversionCount: 0,
|
||||||
|
changedNodes: [],
|
||||||
};
|
};
|
||||||
this.selectedText = '';
|
this.selectedText = '';
|
||||||
this.newIndex = null;
|
this.newIndex = null;
|
||||||
this.oldIndex = null;
|
this.oldIndex = null;
|
||||||
|
this.changeIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.initialContent();
|
this.initialContent();
|
||||||
document.addEventListener('selectionchange', this.setBtnPosition);
|
document.addEventListener('selectionchange', this.setBtnPosition);
|
||||||
|
let that = this;
|
||||||
|
setTimeout(() => {
|
||||||
|
that.getChangedNodes();
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialContent = () => {
|
initialContent = () => {
|
||||||
@@ -164,6 +170,12 @@ class DraftReview extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onSwitchShowDiff = () => {
|
onSwitchShowDiff = () => {
|
||||||
|
if (!this.state.isShowDiff) {
|
||||||
|
let that = this;
|
||||||
|
setTimeout(() => {
|
||||||
|
that.getChangedNodes();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
isShowDiff: !this.state.isShowDiff,
|
isShowDiff: !this.state.isShowDiff,
|
||||||
});
|
});
|
||||||
@@ -337,6 +349,50 @@ class DraftReview extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getChangedNodes = () => {
|
||||||
|
const nodes = this.refs.diffViewer.value.document.nodes;
|
||||||
|
let keys = [];
|
||||||
|
let lastDiffState = '';
|
||||||
|
nodes.map((node) => {
|
||||||
|
if (node.data.get("diff_state") === 'diff-added' && lastDiffState !== 'diff-added') {
|
||||||
|
keys.push(node.key);
|
||||||
|
}
|
||||||
|
else if (node.data.get("diff_state") === 'diff-removed' && lastDiffState !== 'diff-removed') {
|
||||||
|
keys.push(node.key);
|
||||||
|
}
|
||||||
|
lastDiffState = node.data.get("diff_state");
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
changedNodes: keys
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToChangedNode = (scroll) => {
|
||||||
|
if (this.state.changedNodes.length == 0) return;
|
||||||
|
if (scroll === 'up') {
|
||||||
|
this.changeIndex++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.changeIndex--;
|
||||||
|
}
|
||||||
|
if (this.changeIndex > this.state.changedNodes.length - 1) {
|
||||||
|
this.changeIndex = 0;
|
||||||
|
}
|
||||||
|
if (this.changeIndex < 0) {
|
||||||
|
this.changeIndex = this.state.changedNodes.length - 1;
|
||||||
|
}
|
||||||
|
const win = window;
|
||||||
|
let key = this.state.changedNodes[this.changeIndex];
|
||||||
|
const element = win.document.querySelector(`[data-key="${key}"]`);
|
||||||
|
const scroller = this.findScrollContainer(element, win);
|
||||||
|
const isWindow = scroller == win.document.body || scroller == win.document.documentElement;
|
||||||
|
if (isWindow) {
|
||||||
|
win.scrollTo(0, element.offsetTop);
|
||||||
|
} else {
|
||||||
|
scroller.scrollTop = element.offsetTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.getCommentsNumber();
|
this.getCommentsNumber();
|
||||||
this.listReviewers();
|
this.listReviewers();
|
||||||
@@ -510,33 +566,15 @@ class DraftReview extends React.Component {
|
|||||||
<TabContent activeTab={this.state.activeTab}>
|
<TabContent activeTab={this.state.activeTab}>
|
||||||
<TabPane tabId="reviewInfo">
|
<TabPane tabId="reviewInfo">
|
||||||
<div className="review-side-panel-body">
|
<div className="review-side-panel-body">
|
||||||
<div className="review-side-panel-reviewers">
|
<SidePanelReviewers
|
||||||
<div className="reviewers-header">
|
reviewers={this.state.reviewers}
|
||||||
<div className="review-side-panel-header">{gettext('Reviewers')}</div>
|
toggleAddReviewerDialog={this.toggleAddReviewerDialog}/>
|
||||||
<i className="fa fa-cog" onClick={this.toggleAddReviewerDialog}></i>
|
<SidePanelAuthor/>
|
||||||
</div>
|
{ this.state.isShowDiff &&
|
||||||
{ this.state.reviewers.length > 0 ?
|
<SidePanelChanges
|
||||||
this.state.reviewers.map((item, index = 0, arr) => {
|
changedNumber={this.state.changedNodes.length}
|
||||||
return (
|
scrollToChangedNode={this.scrollToChangedNode}/>
|
||||||
<div className="reviewer-info" key={index}>
|
}
|
||||||
<img className="avatar reviewer-avatar" src={item.avatar_url} alt=""/>
|
|
||||||
<span className="reviewer-name">{item.user_name}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
:
|
|
||||||
<span>{gettext('No reviewer yet.')}</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div className="review-side-panel-author">
|
|
||||||
<div className="author-header">
|
|
||||||
<div className="review-side-panel-header">{gettext('Author')}</div>
|
|
||||||
</div>
|
|
||||||
<div className="author-info">
|
|
||||||
<img className="avatar author-avatar" src={authorAvatar} alt=""/>
|
|
||||||
<span className="author-name">{author}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
{ this.state.reviewStatus == "finished"? '':
|
{ this.state.reviewStatus == "finished"? '':
|
||||||
@@ -565,6 +603,73 @@ class DraftReview extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SidePanelReviewers extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="review-side-panel-item">
|
||||||
|
<div className="review-side-panel-header">{gettext('Reviewers')}
|
||||||
|
<i className="fa fa-cog" onClick={this.props.toggleAddReviewerDialog}></i>
|
||||||
|
</div>
|
||||||
|
{ this.props.reviewers.length > 0 ?
|
||||||
|
this.props.reviewers.map((item, index = 0, arr) => {
|
||||||
|
return (
|
||||||
|
<div className="reviewer-info" key={index}>
|
||||||
|
<img className="avatar review-side-panel-avatar" src={item.avatar_url} alt=""/>
|
||||||
|
<span className="reviewer-name">{item.user_name}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
:
|
||||||
|
<span>{gettext('No reviewer yet.')}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SidePanelAuthor extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="review-side-panel-item">
|
||||||
|
<div className="review-side-panel-header">{gettext('Author')}</div>
|
||||||
|
<div className="author-info">
|
||||||
|
<img className="avatar review-side-panel-avatar" src={authorAvatar} alt=""/>
|
||||||
|
<span className="author-name">{author}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SidePanelChanges extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="review-side-panel-item">
|
||||||
|
<div className="review-side-panel-header">{gettext('Changes')}</div>
|
||||||
|
<div className="changes-info">
|
||||||
|
<span>{gettext(`Number of changes: ${this.props.changedNumber}`)}</span>
|
||||||
|
{ this.props.changedNumber > 0 &&
|
||||||
|
<div>
|
||||||
|
<i className="fa fa-arrow-circle-up" onClick={() => { this.props.scrollToChangedNode('down');}}></i>
|
||||||
|
<i className="fa fa-arrow-circle-down" onClick={() => { this.props.scrollToChangedNode('up');}}></i>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ReactDOM.render (
|
ReactDOM.render (
|
||||||
<DraftReview />,
|
<DraftReview />,
|
||||||
document.getElementById('wrapper')
|
document.getElementById('wrapper')
|
||||||
|
Reference in New Issue
Block a user