1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-04 16:31:13 +00:00

repo-tags-show (#2494)

This commit is contained in:
WangJianhui666
2018-11-02 15:34:34 +08:00
committed by Daniel Pan
parent 973ca9cf35
commit a63e8a4009
13 changed files with 457 additions and 39 deletions

View File

@@ -0,0 +1,102 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input } from 'reactstrap';
import { gettext, repoID } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
const propTypes = {
toggleCancel: PropTypes.func.isRequired,
};
class CreateTagDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
tagName: '',
tagColor: '',
newTag: {},
colorList: ['lime', 'teal', 'azure', 'green', 'blue', 'purple', 'pink', 'indigo'],
};
this.newInput = React.createRef();
}
inputNewName = (e) => {
this.setState({
tagName: e.target.value,
});
}
selectTagcolor = (e) => {
this.setState({
tagColor: e.target.value,
});
}
createTag = () => {
let name = this.state.tagName;
let color = this.state.tagColor;
seafileAPI.createRepoTag(repoID, name, color).then(() =>{
this.props.toggleCancel();
});
}
handleKeyPress = (e) => {
if (e.key === 'Enter') {
this.createTag();
}
}
toggle = () => {
this.props.toggleCancel();
}
componentDidMount() {
this.setState({
tagColor: this.state.colorList[0]
});
this.newInput.focus();
this.newInput.setSelectionRange(0, 0);
}
render() {
let colorList = this.state.colorList;
return (
<Modal isOpen={true} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{gettext('New Tag')}</ModalHeader>
<ModalBody>
<div className="tag-create">
<p>{gettext('Name')}</p>
<Input onKeyPress={this.handleKeyPress} innerRef={input => {this.newInput = input;}} placeholder={gettext('name')} value={this.state.tagName} onChange={this.inputNewName}/>
<div className="form-group color-chooser">
<label className="form-label">{gettext('Select a color')}</label>
<div className="row gutters-xs">
{colorList.map((item, index)=>{
var className = 'colorinput-color bg-' + item;
return (
<div key={index} className="col-auto" onChange={this.selectTagcolor}>
<label className="colorinput">
{index===0 ?
<input name="color" type="radio" value={item} className="colorinput-input" defaultChecked onClick={this.selectTagcolor}></input> :
<input name="color" type="radio" value={item} className="colorinput-input" onClick={this.selectTagcolor}></input>}
<span className={className}></span>
</label>
</div>
);
})
}
</div>
</div>
</div>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.createTag}>{gettext('Save')}</Button>
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
</ModalFooter>
</Modal>
);
}
}
CreateTagDialog.propTypes = propTypes;
export default CreateTagDialog;

View File

@@ -19,7 +19,7 @@ class Delete extends React.Component {
let name = this.props.currentNode.name;
return (
<Modal isOpen={true} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{gettext('Delete')}</ModalHeader>
<ModalHeader toggle={this.toggle}>{gettext('Delete Tag')}</ModalHeader>
<ModalBody>
<p>{gettext('Are you sure to delete')}{' '}<b>{name}</b> ?</p>
</ModalBody>

View File

@@ -0,0 +1,97 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { gettext, repoID } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import RepoTag from '../../models/repo-tag';
import '../../css/repo-tag.css';
const tagListItemPropTypes = {
item: PropTypes.object.isRequired,
onTagUpdate: PropTypes.func.isRequired,
};
class TagListItem extends React.Component {
onTagUpdate = () => {
this.props.onTagUpdate(this.props.item);
}
render() {
return(
<li className="tag-list-item">
<span className="tag-demo" style={{background: this.props.item.color}}>{this.props.item.name}</span>
<i className="tag-edit fa fa-pencil" onClick={this.onTagUpdate}></i>
</li>
);
}
}
TagListItem.propTypes = tagListItemPropTypes;
const listTagPropTypes = {
onListTagCancel: PropTypes.func.isRequired,
onCreateRepoTag: PropTypes.func.isRequired,
onUpdateRepoTag: PropTypes.func.isRequired,
};
class ListTagDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
repotagList: [],
};
}
componentDidMount() {
seafileAPI.listRepoTags(repoID).then(res => {
let repotagList = [];
res.data.repo_tags.forEach(item => {
let repo_tag = new RepoTag(item);
repotagList.push(repo_tag);
});
this.setState({
repotagList: repotagList,
});
});
}
toggle = () => {
this.props.onListTagCancel();
}
render() {
return (
<Fragment>
<Modal isOpen={true} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{gettext('Tag List')}</ModalHeader>
<ModalBody>
{
this.state.repotagList.length === 0 &&
<div className="tag-list tag-list-container">
{gettext('Click new tag button to create tags.')}
</div>
}
{ this.state.repotagList.length > 0 &&
<ul className="tag-list tag-list-container">
{this.state.repotagList.map((repoTag, index) => {
return (
<TagListItem key={index} item={repoTag} onTagUpdate={this.props.onUpdateRepoTag}/>
);
})}
</ul>
}
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.props.onCreateRepoTag}>{gettext('New Tag')}</Button>
<Button color="secondary" onClick={this.toggle}>{gettext('Close')}</Button>
</ModalFooter>
</Modal>
</Fragment>
);
}
}
ListTagDialog.propTypes = listTagPropTypes;
export default ListTagDialog;

View File

@@ -0,0 +1,115 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input } from 'reactstrap';
import { gettext, repoID } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
const propTypes = {
currentTag: PropTypes.object,
toggleCancel: PropTypes.func.isRequired,
};
class UpdateTagDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
deleteRepoTag: false,
newName: this.props.currentTag.name,
newColor: this.props.currentTag.color,
colorList: ['lime', 'teal', 'azure', 'green', 'blue', 'purple', 'pink', 'indigo'],
};
this.newInput = React.createRef();
}
componentDidMount() {
this.newInput.focus();
this.newInput.setSelectionRange(0, -1);
}
inputNewName = (e) => {
this.setState({
newName: e.target.value,
});
}
selectNewcolor = (e) => {
this.setState({
newColor: e.target.value,
});
}
updateTag = () => {
let tag_id = this.props.currentTag.id;
let name = this.state.newName;
let color = this.state.newColor;
seafileAPI.updateRepoTag(repoID, tag_id, name, color).then(() => {
this.props.toggleCancel();
});
}
handleKeyPress = (e) => {
if (e.key === 'Enter') {
this.updateTag();
}
}
toggle = () => {
this.props.toggleCancel();
}
deleteTagClick = (item) => {
this.setState({
deleteRepoTag: !this.state.deleteRepoTag,
});
}
onDeleteTag = () => {
let tag = this.props.currentTag;
seafileAPI.deleteRepoTag(repoID, tag.id).then(() => {
this.props.toggleCancel();
});
}
render() {
let colorList = this.state.colorList;
return (
<Fragment>
<Modal isOpen={true} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{gettext('Edit Tag')}</ModalHeader>
<ModalBody>
<div className="tag-edit">
<p>{gettext('Name:')}</p>
<Input onKeyPress={this.handleKeyPress} innerRef={input => {this.newInput = input;}} placeholder="newName" value={this.state.newName} onChange={this.inputNewName}/>
<div className="form-group color-chooser">
<label className="form-label">{gettext('Select a color')}</label>
<div className="row gutters-xs">
{colorList.map((item, index)=>{
var className = 'colorinput-color bg-' + item;
return (
<div key={index} className="col-auto" onChange={this.selectNewcolor}>
<label className="colorinput">
{item===this.props.currentTag.color ?
<input name="color" type="radio" value={item} className="colorinput-input" defaultChecked onChange={this.selectNewcolor}></input> :
<input name="color" type="radio" value={item} className="colorinput-input" onChange={this.selectNewcolor}></input>}
<span className={className}></span>
</label>
</div>
);
})}
</div>
</div>
</div>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.updateTag}>{gettext('Save')}</Button>
<Button color="danger" onClick={this.onDeleteTag}>{gettext('Delete')}</Button>
</ModalFooter>
</Modal>
</Fragment>
);
}
}
UpdateTagDialog.propTypes = propTypes;
export default UpdateTagDialog;

View File

@@ -1,7 +1,10 @@
import React from 'react';
import React, { Fragment } from 'react';
import { gettext, repoID, slug, permission, siteRoot } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import PropTypes from 'prop-types';
import ListTagDialog from '../dialog/list-tag-dialog';
import CreateTagDialog from '../dialog/create-tag-dialog';
import UpdateTagDialog from '../dialog/update-tag-dialog';
const propTypes = {
filePath: PropTypes.string.isRequired
@@ -9,6 +12,35 @@ const propTypes = {
class PathToolbar extends React.Component {
constructor(props) {
super(props);
this.state = {
currentTag: null,
isListRepoTagShow: false,
isUpdateRepoTagShow: false,
isCreateRepoTagShow: false,
};
}
onListRepoTagToggle = () => {
this.setState({isListRepoTagShow: !this.state.isListRepoTagShow});
}
onCreateRepoTagToggle = () => {
this.setState({
isCreateRepoTagShow: !this.state.isCreateRepoTagShow,
isListRepoTagShow: !this.state.isListRepoTagShow,
});
}
onUpdateRepoTagToggle = (currentTag) => {
this.setState({
currentTag: currentTag,
isListRepoTagShow: !this.state.isListRepoTagShow,
isUpdateRepoTagShow: !this.state.isUpdateRepoTagShow,
});
}
isMarkdownFile(filePath) {
let lastIndex = filePath.lastIndexOf('/');
let name = filePath.slice(lastIndex + 1);
@@ -23,10 +55,34 @@ class PathToolbar extends React.Component {
let historyUrl = siteRoot + 'repo/history/' + repoID + '/?referer=' + encodeURIComponent(location.href);
if ( (name === slug || name === '') && !isFile && permission) {
return (
<ul className="path-toolbar">
<li className="toolbar-item"><a className="op-link sf2-icon-trash" href={trashUrl} title={gettext('Trash')} aria-label={gettext('Trash')}></a></li>
<li className="toolbar-item"><a className="op-link sf2-icon-history" href={historyUrl} title={gettext('History')} aria-label={gettext('History')}></a></li>
</ul>
<Fragment>
<ul className="path-toolbar">
<li className="toolbar-item"><a className="op-link sf2-icon-tag-manager" onClick={this.onListRepoTagToggle} title={gettext('Tags')} aria-label={gettext('Tags')}></a></li>
<li className="toolbar-item"><a className="op-link sf2-icon-trash" href={trashUrl} title={gettext('Trash')} aria-label={gettext('Trash')}></a></li>
<li className="toolbar-item"><a className="op-link sf2-icon-history" href={historyUrl} title={gettext('History')} aria-label={gettext('History')}></a></li>
</ul>
{
this.state.isListRepoTagShow &&
<ListTagDialog
onListTagCancel={this.onListRepoTagToggle}
onCreateRepoTag={this.onCreateRepoTagToggle}
onUpdateRepoTag={this.onUpdateRepoTagToggle}
/>
}
{
this.state.isCreateRepoTagShow &&
<CreateTagDialog
toggleCancel={this.onCreateRepoTagToggle}
/>
}
{
this.state.isUpdateRepoTagShow &&
<UpdateTagDialog
currentTag={this.state.currentTag}
toggleCancel={this.onUpdateRepoTagToggle}
/>
}
</Fragment>
);
} else if ( !isFile && permission) {
return (