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:
committed by
Daniel Pan
parent
973ca9cf35
commit
a63e8a4009
102
frontend/src/components/dialog/create-tag-dialog.js
Normal file
102
frontend/src/components/dialog/create-tag-dialog.js
Normal 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;
|
@@ -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>
|
||||
|
97
frontend/src/components/dialog/list-tag-dialog.js
Normal file
97
frontend/src/components/dialog/list-tag-dialog.js
Normal 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;
|
115
frontend/src/components/dialog/update-tag-dialog.js
Normal file
115
frontend/src/components/dialog/update-tag-dialog.js
Normal 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;
|
@@ -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 (
|
||||
|
Reference in New Issue
Block a user