Merge pull request #2268 from serghey-rodin/feature/r-1.0.0.7

Release UI 1.0.0.7
This commit is contained in:
Anton Reutov 2022-10-11 10:59:25 +03:00 committed by GitHub
commit 94d60267a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 483 additions and 284 deletions

View file

@ -4396,9 +4396,9 @@
}
},
"dayjs": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
"version": "1.11.4",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.4.tgz",
"integrity": "sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g=="
},
"debug": {
"version": "3.1.0",

View file

@ -13,7 +13,7 @@
"axios": "^0.21.4",
"bootstrap": "^4.3.1",
"classname": "0.0.0",
"dayjs": "^1.10.7",
"dayjs": "^1.11.4",
"jquery": "^3.5.1",
"node-sass": "^4.14.1",
"perfect-scrollbar": "^1.5.3",

View file

@ -1,49 +1,49 @@
import axios from "axios";
import { getAuthToken } from "src/utils/token";
import axios from 'axios'
import { getAuthToken } from 'src/utils/token'
const BASE_URL = window.location.origin;
const webApiUri = '/api/v1/list/db/index.php';
const addDbApiUri = '/api/v1/add/db/index.php';
const optionalDbInfoUri = '/api/v1/add/db/index.php';
const dbInfoUri = '/api/v1/edit/db/index.php';
const updateDatabaseUri = '/api/v1/edit/db/index.php';
const BASE_URL = window.location.origin
const webApiUri = '/api/v1/list/db/index.php'
const addDbApiUri = '/api/v1/add/db/index.php'
const optionalDbInfoUri = '/api/v1/add/db/index.php'
const dbInfoUri = '/api/v1/edit/db/index.php'
const updateDatabaseUri = '/api/v1/edit/db/index.php'
export const getDatabaseList = () => {
return axios.get(BASE_URL + webApiUri);
return axios.get(BASE_URL + webApiUri)
}
export const bulkAction = (action, domainNameSystems) => {
const formData = new FormData();
formData.append("action", action);
formData.append("token", getAuthToken());
const formData = new FormData()
formData.append('action', action)
formData.append('token', getAuthToken())
domainNameSystems.forEach(domainNameSystem => {
formData.append("database[]", domainNameSystem);
});
domainNameSystems.forEach((domainNameSystem) => {
formData.append('database[]', domainNameSystem)
})
return axios.post(BASE_URL + '/api/v1/bulk/db/', formData);
};
return axios.post(BASE_URL + '/api/v1/bulk/db/', formData)
}
export const handleAction = uri => {
export const handleAction = (uri) => {
return axios.get(BASE_URL + uri, {
params: {
token: getAuthToken()
}
});
token: getAuthToken(),
},
})
}
export const getDbOptionalInfo = () => {
return axios.get(BASE_URL + optionalDbInfoUri);
return axios.get(BASE_URL + optionalDbInfoUri)
}
export const addDatabase = data => {
let formDataObject = new FormData();
export const addDatabase = (data) => {
let formDataObject = new FormData()
for (let key in data) {
formDataObject.append(key, data[key]);
formDataObject.append(key, data[key])
}
return axios.post(BASE_URL + addDbApiUri, formDataObject);
return axios.post(BASE_URL + addDbApiUri, formDataObject)
}
export const dbCharsets = [
@ -69,6 +69,7 @@ export const dbCharsets = [
'latin5',
'armscii8',
'utf8',
'utf8mb4',
'ucs2',
'cp866',
'keybcs2',
@ -82,29 +83,29 @@ export const dbCharsets = [
'binary',
'geostd8',
'cp932',
'eucjpms'
];
'eucjpms',
]
export const getDatabaseInfo = database => {
export const getDatabaseInfo = (database) => {
return axios.get(BASE_URL + dbInfoUri, {
params: {
database,
token: getAuthToken()
}
});
token: getAuthToken(),
},
})
}
export const updateDatabase = (data, database) => {
let formDataObject = new FormData();
let formDataObject = new FormData()
for (let key in data) {
formDataObject.append(key, data[key]);
formDataObject.append(key, data[key])
}
return axios.post(BASE_URL + updateDatabaseUri, formDataObject, {
params: {
database,
token: getAuthToken()
}
});
}
token: getAuthToken(),
},
})
}

View file

@ -34,10 +34,10 @@ const Hotkeys = props => {
</li>
<li>
<span className="name">n</span>
<span className="description">{i18n['New Fille']}</span>
<span className="description">{i18n['New File']}</span>
</li>
<li>
<span className="name">F7</span>
<span className="name">F6</span>
<span className="description">{i18n['New Folder']}</span>
</li>
<li>
@ -56,10 +56,6 @@ const Hotkeys = props => {
<span className="name">F5</span>
<span className="description">{i18n['Copy']}</span>
</li>
<li>
<span className="name">F5</span>
<span className="description">{i18n['Copy']}</span>
</li>
<li>
<span className="name">F8 / Del</span>
<span className="description">{i18n['Delete']}</span>
@ -107,7 +103,7 @@ const Hotkeys = props => {
<span className="description">{i18n['Open File / Enter Directory']}</span>
</li>
<li>
<span className="name">F4</span>
<span className="name">F3</span>
<span className="description">{i18n['Edit File']}</span>
</li>
<li>

View file

@ -6,6 +6,17 @@ import Row from '../Row/Row';
import '../List.scss';
class DirectoryList extends Component {
constructor(props) {
super(props);
this.state = {
orderType: "descending",
sortingType: "Type",
itemsSelected: [],
listingItems: [],
cursor: 0
};
}
static propTypes = {
changePathAfterToggle: PropTypes.func,
openCertainDirectory: PropTypes.func,
@ -27,13 +38,6 @@ class DirectoryList extends Component {
data: PropTypes.array
}
state = {
orderType: "descending",
sortingType: "Type",
itemsSelected: [],
cursor: 0
};
UNSAFE_componentWillMount = () => {
if (localStorage.getItem(`${this.props.list}Sorting`) && localStorage.getItem(`${this.props.list}Order`)) {
this.setState({ sortingType: localStorage.getItem(`${this.props.list}Sorting`), orderType: localStorage.getItem(`${this.props.list}Order`) });
@ -117,20 +121,21 @@ class DirectoryList extends Component {
}
handleLiSelection = (e) => {
const { data, isActive, modalVisible, changePath, path } = this.props;
const { isActive, modalVisible, changePath, path } = this.props;
const { cursor } = this.state;
const { listing } = this.getDataBySortingType()
if (!isActive || modalVisible) {
return;
}
if (e.keyCode === 40) {
if (cursor === data.listing.length - 1) {
if (cursor === listing.length - 1) {
return;
}
if (e.shiftKey) {
let name = data.listing[cursor].name;
if (e.shiftKey) {
let name = listing[cursor].name;
this.addToSelection(name);
}
@ -145,7 +150,7 @@ class DirectoryList extends Component {
}
if (e.shiftKey) {
let name = data.listing[cursor].name;
let name = listing[cursor - 1].name;
this.addToSelection(name);
}
@ -160,9 +165,15 @@ class DirectoryList extends Component {
}
passData = () => {
const { data, passData } = this.props;
const { name, permissions, type } = data.listing[this.state.cursor];
passData(this.state.cursor, name, permissions, type);
const { passData: passDataToParent } = this.props;
const { firstItem, listing } = this.getDataBySortingType()
if (this.state.cursor === 0) {
const { name, permissions, type } = firstItem;
passDataToParent(this.state.cursor, name, permissions, type);
} else {
const { name, permissions, type } = listing[this.state.cursor - 1];
passDataToParent(this.state.cursor, name, permissions, type);
}
}
openDirectory = (name) => {
@ -231,50 +242,74 @@ class DirectoryList extends Component {
sortData = (a, b) => {
switch (this.state.sortingType) {
case "Type": return this.sortByType(a, b);
case "Size": if (a.type !== "d" && b.type !== "d") { return this.sortBySize(a, b) }; break;
case "Size": return this.sortBySize(a, b);
case "Date": return this.sortByDate(a, b);
case "Name": return this.sortByName(a, b);
default: return this.sortByType(a, b);
}
}
getDataBySortingType = () => {
let firstItem, listing = [];
this.props.data.listing.forEach(item => {
if (item.name === '' && item.type === 'd') {
firstItem = item
} else {
listing.push(item)
}
})
if (this.state.sortingType !== 'Type') {
listing = [
...listing.filter(item => item.type === 'd').sort((a, b) => this.sortByName(a, b)),
...listing.filter(item => item.type === 'f').sort((a, b) => this.sortData(a, b))
]
} else {
listing = listing.sort((a, b) => this.sortData(a, b))
}
return { firstItem, listing }
}
rows = () => {
const { isActive, modalVisible, path, download } = this.props;
const { cursor } = this.state;
const data = { ...this.props.data };
const { listing, firstItem } = this.getDataBySortingType()
if (data.listing.length !== 0) {
let sortedData = data.listing.sort((a, b) => this.sortData(a, b));
if (listing.length || firstItem) {
return (
sortedData.map((item, key) =>
(item.name !== "" && sortedData.length !== 0) ?
(<Row key={key}
selectOnClick={(cursor, name, permissions, type) => {
this.setState({ cursor });
this.props.passData(cursor, name, permissions, type);
}}
selectMultiple={() => this.addToSelection(item.name)}
selected={this.isSelected(item.name)}
openDirectory={this.openDirectory}
modalVisible={modalVisible}
activeRow={key === cursor}
isActiveList={isActive}
download={download}
cursor={key}
data={item}
path={path} />) :
(<Row key={key}
selectOnClick={(cursor, name, permissions, type) => {
this.setState({ cursor });
this.props.passData(cursor, name, permissions, type);
}}
openDirectory={this.moveBack}
modalVisible={modalVisible}
activeRow={key === cursor}
isActiveList={isActive}
cursor={key}
data={item}
path={path} />))
<>
<Row
selectOnClick={(cursor, name, permissions, type) => {
this.setState({ cursor });
this.props.passData(cursor, name, permissions, type);
}}
openDirectory={this.moveBack}
modalVisible={modalVisible}
activeRow={0 === cursor}
isActiveList={isActive}
cursor={0}
data={firstItem}
path={path} />
{
listing.map((item, key) => (
<Row
key={key + 1}
selectOnClick={(cursor, name, permissions, type) => {
this.setState({ cursor });
this.props.passData(cursor, name, permissions, type);
}}
selectMultiple={() => this.addToSelection(item.name)}
selected={this.isSelected(item.name)}
openDirectory={this.openDirectory}
modalVisible={modalVisible}
activeRow={key + 1 === cursor}
isActiveList={isActive}
download={download}
cursor={key + 1}
data={item}
path={path} />
))
}
</>
);
}
}

View file

@ -5,6 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faJs, faCss3, faPhp, faHtml5, faSass } from '@fortawesome/free-brands-svg-icons';
import './Row.scss';
import { connect } from 'react-redux';
import dayjs from 'dayjs'
class Row extends Component {
static propTypes = {
@ -127,6 +128,13 @@ class Row extends Component {
return (<span className="date">{getMonth} {getDay}</span>);
}
timeFormatter = (date = new Date(), time) => {
const year = dayjs(date).year()
const currentYear = dayjs().year()
if (year === currentYear) return time
return year
}
glyph = () => {
const { data: { type, name } } = this.props;
@ -182,7 +190,7 @@ class Row extends Component {
<span className="fOwner">{owner}</span>
<span className="fSize">{this.sizeFormatter(size)}</span>
<span className="fDate">{this.dateFormatter(date)}</span>
<span className="fTime">{time}</span>
<span className="fTime">{this.timeFormatter(date, time)}</span>
</li>
);
}

View file

@ -87,11 +87,11 @@
margin-left: 5px;
line-height: 34px;
font-size: 15px;
white-space: nowrap;
transition: all ease-out .3s;
white-space: nowrap;
transition: all ease-out 0.3s;
&:hover {
transition: all ease-out .2s;
transition: all ease-out 0.2s;
background: rgb(201, 199, 199);
border-radius: 4px;
cursor: pointer;
@ -100,7 +100,7 @@
}
.list .list-container ul li.active .fName .name:hover {
background: #F0B607;
background: #f0b607;
color: black;
}
@ -160,20 +160,22 @@ li.inactive {
background: rgb(220, 220, 220);
}
@media (max-width: 1400px){
.fPermissions, .fOwner {
display: none;
}
}
@media (max-width: 1100px){
.fDate {
display: none;
}
}
@media (max-width: 970px){
.fTime {
display: none;
@media (max-width: 1320px) {
.list .list-container ul li {
.fName {
width: 210px;
}
.fSize {
width: 75px;
}
.fDate {
width: 40px;
}
.fTime {
width: 50px;
}
.fPermissions {
margin: 0;
}
}
}

View file

@ -15,7 +15,6 @@ const Notifications = () => {
useEffect(() => {
if (!notifications.length) {
console.log(notifications);
fetchData();
}
}, [notifications]);

View file

@ -151,7 +151,7 @@
box-shadow: rgba(200, 200, 200, 0.5) 0px 5px 3px 0px;
}
@media (max-width: 1350px) {
@media (max-width: 1390px) {
.toolbar {
padding: 3px 10% 1px;
}

View file

@ -1,148 +1,185 @@
import React, { createRef, useEffect } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import './Menu.scss';
import { useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import React, { useCallback, useEffect, useRef } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import './Menu.scss'
import { useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
const Menu = (props) => {
const { i18n } = useSelector(state => state.session);
const inputFile = createRef();
const { i18n } = useSelector((state) => state.session)
const inputFile = useRef()
const handleUserKeyDown = useCallback((event) => hotKeys(event), [props])
useEffect(() => {
document.addEventListener("keydown", hotKeys);
return () => document.removeEventListener("keydown", hotKeys);
}, []);
document.addEventListener('keydown', handleUserKeyDown)
return () => document.removeEventListener('keydown', handleUserKeyDown)
}, [handleUserKeyDown])
const newFile = () => {
props.openModal("Add file");
props.openModal('Add file')
}
const newDirectory = () => {
props.openModal("Add directory");
props.openModal('Add directory')
}
const deleteFile = () => {
const { selection, openModal, cursor } = props;
const { selection, openModal, cursor } = props
if (selection.length === 0) {
if (cursor === 0) {
openModal("Nothing selected");
openModal('Nothing selected')
} else {
openModal("Delete");
openModal('Delete')
}
} else {
openModal("Delete", selection.length);
openModal('Delete', selection.length)
}
}
const rename = () => {
console.log(props)
if (props.cursor === 0) {
props.openModal("Nothing selected");
props.openModal('Nothing selected')
} else {
props.openModal("Rename");
props.openModal('Rename')
}
}
const permissions = () => {
if (props.cursor === 0) {
props.openModal("Nothing selected");
props.openModal('Nothing selected')
} else {
props.openModal("Permissions");
props.openModal('Permissions')
}
}
const move = () => {
const { selection, openModal, cursor } = props;
const { selection, openModal, cursor } = props
if (selection.length === 0) {
if (cursor === 0) {
openModal("Nothing selected");
openModal('Nothing selected')
} else {
openModal("Move");
openModal('Move')
}
} else {
openModal("Move", selection.length);
openModal('Move', selection.length)
}
}
const archive = () => {
const { selection, openModal, cursor } = props;
const { selection, openModal, cursor } = props
if (selection.length === 0) {
if (cursor === 0) {
openModal("Nothing selected");
openModal('Nothing selected')
} else {
openModal("Archive");
openModal('Archive')
}
} else {
openModal("Archive", selection.length);
openModal('Archive', selection.length)
}
}
const extract = () => {
if (props.cursor === 0) {
props.openModal("Nothing selected");
props.openModal('Nothing selected')
} else {
props.openModal("Extract");
props.openModal('Extract')
}
}
const copy = () => {
const { selection, openModal, cursor } = props;
const { selection, openModal, cursor } = props
if (selection.length === 0) {
if (cursor === 0) {
openModal("Nothing selected");
openModal('Nothing selected')
} else {
openModal("Copy");
openModal('Copy')
}
} else {
openModal("Copy", selection.length);
openModal('Copy', selection.length)
}
}
const upload = (e) => {
if (e.target.files.length === 0) {
return;
return
}
props.upload(e.target.files);
props.upload(e.target.files)
}
const download = () => {
if (props.cursor === 0) {
props.openModal("Nothing selected");
} else if (props.itemType === "d") {
props.openModal("Nothing selected", null, true);
props.openModal('Nothing selected')
} else if (props.itemType === 'd') {
props.openModal('Nothing selected', null, true)
} else {
props.download();
props.download()
}
}
const hotKeys = (e) => {
let isSearchInputFocused = document.querySelector('input:focus') || document.querySelector('textarea:focus');
if (props.modalVisible || isSearchInputFocused) return;
if (e.shiftKey && e.keyCode === 117) {
rename();
e.stopPropagation()
let isSearchInputFocused = document.querySelector('input:focus') || document.querySelector('textarea:focus')
if (props.modalVisible || isSearchInputFocused) return
if (e.shiftKey && e.keyCode === 118) {
e.preventDefault()
rename()
return
}
switch (e.keyCode) {
case 46: return deleteFile();
case 65: return archive();
case 68: return download();
case 77: return move();
case 78: return newFile();
case 85: return inputFile.click();
case 113: return rename();
case 115: return permissions();
case 116: return copy();
case 118: return newDirectory();
case 119: return deleteFile();
default: break;
// u
case 85:
e.preventDefault();
return inputFile.current.click()
// n
case 78:
e.preventDefault()
return newFile()
// F6
case 118:
e.preventDefault()
return newDirectory()
// d
case 68:
e.preventDefault()
return download()
// F2
case 113:
e.preventDefault()
return rename()
// m
case 77:
e.preventDefault()
return move()
// F4
case 115:
e.preventDefault()
return copy()
// a
case 65:
e.preventDefault()
return archive()
// F8
case 119:
e.preventDefault()
return deleteFile()
// Del
case 46:
e.preventDefault()
return deleteFile()
// F3
case 114:
e.preventDefault()
return permissions()
default:
break
}
}
let matchArchive = props.name.match(/.zip|.tgz|.tar.gz|.gzip|.tbz|.tar.bz|.gz|.zip|.tar|.rar/g);
let matchArchive = props.name.match(/.zip|.tgz|.tar.gz|.gzip|.tbz|.tar.bz|.gz|.zip|.tar|.rar/g)
return (
<div className="menu">
@ -153,30 +190,80 @@ const Menu = (props) => {
</div>
<div className="btn-group" role="group" aria-label="First group">
<input type="file" className="upload" multiple onChange={upload} ref={inputFile} />
<button type="button" className="btn btn-light" id="upload" onClick={() => inputFile.current.click()}>{i18n.UPLOAD}</button>
<button type="button" className="btn btn-light big" onClick={newFile}>{i18n['NEW FILE']}</button>
<button type="button" className="btn btn-light small" onClick={newFile} title={i18n['NEW FILE']}><FontAwesomeIcon icon="file" className="icon file" /></button>
<button type="button" className="btn btn-light big" onClick={newDirectory}>{i18n['NEW DIR']}</button>
<button type="button" className="btn btn-light small" onClick={newDirectory} title={i18n['NEW DIR']}><FontAwesomeIcon icon="folder" className="icon folder-close" /></button>
<button type="button" className="btn btn-light big" onClick={download}>{i18n.DOWNLOAD}</button>
<button type="button" className="btn btn-light small" onClick={download} title={i18n.DOWNLOAD}><FontAwesomeIcon icon="download" className="icon download" /></button>
<button type="button" className="btn btn-light big" onClick={rename}>{i18n.RENAME}</button>
<button type="button" className="btn btn-light small" onClick={rename} title={i18n.RENAME}><FontAwesomeIcon icon="italic" className="icon italic" /></button>
<button type="button" className="btn btn-light big" onClick={permissions}>{i18n.RIGHTS}</button>
<button type="button" className="btn btn-light small" onClick={permissions} title={i18n.RIGHTS}><FontAwesomeIcon icon="user" className="icon user" /></button>
<button type="button" className="btn btn-light big" onClick={copy}>{i18n.COPY}</button>
<button type="button" className="btn btn-light small" onClick={copy} title={i18n.COPY}><FontAwesomeIcon icon="copy" className="icon copy" /></button>
<button type="button" className="btn btn-light big" onClick={move}>{i18n.MOVE}</button>
<button type="button" className="btn btn-light small" onClick={move} title={i18n.MOVE}><FontAwesomeIcon icon="paste" className="icon paste" /></button>
{matchArchive ? null : <button type="button" className="btn btn-light big" onClick={archive}>{i18n.ARCHIVE}</button>}
{matchArchive ? null : <button type="button" className="btn btn-light small" onClick={archive} title={i18n.ARCHIVE}><FontAwesomeIcon icon="book" className="icon book" /></button>}
{matchArchive ? <button type="button" className="btn btn-light big" onClick={extract}>{i18n.EXTRACT}</button> : null}
{matchArchive ? <button type="button" className="btn btn-light small" onClick={extract} title={i18n.EXTRACT}><FontAwesomeIcon icon="box-open" className="icon open" /></button> : null}
<button type="button" className="btn btn-light big delete" onClick={deleteFile} >{i18n.DELETE}</button>
<button type="button" className="btn btn-light small" onClick={deleteFile} title={i18n.DELETE}><FontAwesomeIcon icon="trash" className="icon trash" /></button>
<button type="button" className="btn btn-light" id="upload" onClick={() => inputFile.current.click()}>
{i18n.UPLOAD}
</button>
<button type="button" className="btn btn-light big" onClick={newFile}>
{i18n['NEW FILE']}
</button>
<button type="button" className="btn btn-light small" onClick={newFile} title={i18n['NEW FILE']}>
<FontAwesomeIcon icon="file" className="icon file" />
</button>
<button type="button" className="btn btn-light big" onClick={newDirectory}>
{i18n['NEW DIR']}
</button>
<button type="button" className="btn btn-light small" onClick={newDirectory} title={i18n['NEW DIR']}>
<FontAwesomeIcon icon="folder" className="icon folder-close" />
</button>
<button type="button" className="btn btn-light big" onClick={download}>
{i18n.DOWNLOAD}
</button>
<button type="button" className="btn btn-light small" onClick={download} title={i18n.DOWNLOAD}>
<FontAwesomeIcon icon="download" className="icon download" />
</button>
<button type="button" className="btn btn-light big" onClick={rename}>
{i18n.RENAME}
</button>
<button type="button" className="btn btn-light small" onClick={rename} title={i18n.RENAME}>
<FontAwesomeIcon icon="italic" className="icon italic" />
</button>
<button type="button" className="btn btn-light big" onClick={permissions}>
{i18n.RIGHTS}
</button>
<button type="button" className="btn btn-light small" onClick={permissions} title={i18n.RIGHTS}>
<FontAwesomeIcon icon="user" className="icon user" />
</button>
<button type="button" className="btn btn-light big" onClick={copy}>
{i18n.COPY}
</button>
<button type="button" className="btn btn-light small" onClick={copy} title={i18n.COPY}>
<FontAwesomeIcon icon="copy" className="icon copy" />
</button>
<button type="button" className="btn btn-light big" onClick={move}>
{i18n.MOVE}
</button>
<button type="button" className="btn btn-light small" onClick={move} title={i18n.MOVE}>
<FontAwesomeIcon icon="paste" className="icon paste" />
</button>
{matchArchive ? null : (
<button type="button" className="btn btn-light big" onClick={archive}>
{i18n.ARCHIVE}
</button>
)}
{matchArchive ? null : (
<button type="button" className="btn btn-light small" onClick={archive} title={i18n.ARCHIVE}>
<FontAwesomeIcon icon="book" className="icon book" />
</button>
)}
{matchArchive ? (
<button type="button" className="btn btn-light big" onClick={extract}>
{i18n.EXTRACT}
</button>
) : null}
{matchArchive ? (
<button type="button" className="btn btn-light small" onClick={extract} title={i18n.EXTRACT}>
<FontAwesomeIcon icon="box-open" className="icon open" />
</button>
) : null}
<button type="button" className="btn btn-light big delete" onClick={deleteFile}>
{i18n.DELETE}
</button>
<button type="button" className="btn btn-light small" onClick={deleteFile} title={i18n.DELETE}>
<FontAwesomeIcon icon="trash" className="icon trash" />
</button>
</div>
</div>
);
)
}
export default Menu;
export default Menu

View file

@ -1,9 +1,27 @@
import React from 'react';
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
const AddDirectory = (props) => {
const [value, setValue] = useState(null)
const { i18n } = useSelector(state => state.session);
const [hasError, setHasError] = useState(value !== null && !value.length)
const onChange = (e) => {
setValue(e.target.value)
}
const save = () => {
if (!value) {
setHasError(true)
return;
}
props.save()
}
const cancel = () => {
props.close()
}
return (
<div className="modal-content">
@ -11,11 +29,12 @@ const AddDirectory = (props) => {
<h3 className="modal-title directory" >{i18n['Create directory']}</h3>
</div>
<div className="modal-body">
<input type="text" ref={props.reference} autoFocus></input>
<input type="text" onChange={onChange} ref={props.reference}></input>
{hasError && <small className='error'>{i18n['Directory name cannot be empty']}</small>}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger mr-auto" onClick={props.close}>{i18n.Cancel}</button>
<button type="button" className="btn btn-primary" onClick={props.save}>{i18n.Create}</button>
<button type="button" className="btn btn-danger mr-auto" onClick={cancel}>{i18n.Cancel}</button>
<button type="button" className="btn btn-primary" onClick={save}>{i18n.Create}</button>
</div>
</div>
);

View file

@ -1,8 +1,26 @@
import React from 'react';
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
const AddFile = (props) => {
const [value, setValue] = useState(null)
const { i18n } = useSelector(state => state.session);
const [hasError, setHasError] = useState(value !== null && !value.length)
const onChange = (e) => {
setValue(e.target.value)
}
const save = () => {
if (!value) {
setHasError(true)
return;
}
props.save()
}
const cancel = () => {
props.close()
}
return (
<div className="modal-content">
@ -10,11 +28,12 @@ const AddFile = (props) => {
<h3 className="modal-title" >{i18n['Create file']}</h3>
</div>
<div className="modal-body">
<input type="text" ref={props.reference}></input>
<input type="text" onChange={onChange} ref={props.reference}></input>
{hasError && <small className='error'>{i18n['File name cannot be empty']}</small>}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger mr-auto" onClick={props.close}>{i18n.Cancel}</button>
<button type="button" className="btn btn-primary" onClick={props.save}>{i18n.Create}</button>
<button type="button" className="btn btn-danger mr-auto" onClick={cancel}>{i18n.Cancel}</button>
<button type="button" className="btn btn-primary" onClick={save}>{i18n.Create}</button>
</div>
</div>
);

View file

@ -1,4 +1,4 @@
import React, { Component } from 'react';
import React, { useEffect } from 'react';
import AddFile from './AddFile';
import AddDirectory from './AddDirectory';
import Rename from './Rename';
@ -9,86 +9,83 @@ import Move from './Move';
import Archive from './Archive';
import Extract from './Extract';
import Copy from './Copy';
import './Modal.scss';
import Replace from './Replace';
import './Modal.scss';
class Modal extends Component {
const Modal = (props) => {
useEffect(() => {
window.addEventListener("click", closeOutside);
document.addEventListener("keydown", hotkeys);
componentDidMount = () => {
window.addEventListener("click", this.closeOutside);
document.addEventListener("keydown", this.hotkeys);
}
return () => {
window.removeEventListener("click", closeOutside);
document.removeEventListener("keydown", hotkeys);
}
}, [])
componentWillUnmount = () => {
window.removeEventListener("click", this.closeOutside);
document.removeEventListener("keydown", this.hotkeys);
}
hotkeys = (e) => {
const hotkeys = (e) => {
if (e.keyCode === 27) {
this.closeModal();
closeModal();
} else if (e.keyCode === 13) {
this.saveAndClose();
saveAndClose();
}
}
saveAndClose = () => {
this.props.onClick();
this.props.onClose();
const saveAndClose = () => {
props.onClick();
props.onClose();
}
changePermissions = (permissions) => {
this.props.onChangePermissions(permissions);
const changePermissions = (permissions) => {
props.onChangePermissions(permissions);
}
replace = (file) => {
this.props.onClick(file);
this.props.onClose();
const replace = (file) => {
props.onClick(file);
props.onClose();
}
onChange = (e) => {
this.props.onChangeValue(e.target.value);
const onChange = (e) => {
props.onChangeValue(e.target.value);
}
closeModal = () => {
this.props.onClose();
const closeModal = () => {
props.onClose();
}
closeOutside = (e) => {
const closeOutside = (e) => {
let modal = document.getElementById("modal");
if (e.target === modal) {
this.props.onClose();
props.onClose();
}
}
content = () => {
const { type, reference, fName, permissions, items, path, files, notAvailable } = this.props;
const content = () => {
const { type, reference, fName, permissions, items, path, files, notAvailable } = props;
switch (type) {
case 'Copy': return <Copy close={this.closeModal} save={this.saveAndClose} reference={reference} onChange={this.onChange} name={type} fName={fName} items={items} path={path} />;
case 'Move': return <Move close={this.closeModal} save={this.saveAndClose} reference={reference} onChange={this.onChange} name={type} fName={fName} items={items} path={path} />;
case 'Permissions': return <Permissions close={this.closeModal} save={this.saveAndClose} changePermissions={this.changePermissions} fName={fName} permissions={permissions} />;
case 'Extract': return <Extract close={this.closeModal} save={this.saveAndClose} reference={reference} onChange={this.onChange} name={type} fName={fName} path={path} />;
case 'Archive': return <Archive close={this.closeModal} save={this.saveAndClose} reference={reference} onChange={this.onChange} items={items} name={type} fName={fName} path={path} />;
case 'Rename': return <Rename close={this.closeModal} save={this.saveAndClose} reference={reference} onChange={this.onChange} name={type} fName={fName} />;
case 'Add directory': return <AddDirectory close={this.closeModal} save={this.saveAndClose} reference={reference} />;
case 'Delete': return <Delete close={this.closeModal} save={this.saveAndClose} fName={fName} items={items} />;
case 'Add file': return <AddFile close={this.closeModal} save={this.saveAndClose} reference={reference} />;
case 'Replace': return <Replace close={this.closeModal} replace={(files) => this.replace(files)} files={files} />
case 'Nothing selected': return <NothingSelected close={this.closeModal} notAvailable={notAvailable} />;
case 'Copy': return <Copy close={closeModal} save={saveAndClose} reference={reference} onChange={onChange} name={type} fName={fName} items={items} path={path} />;
case 'Move': return <Move close={closeModal} save={saveAndClose} reference={reference} onChange={onChange} name={type} fName={fName} items={items} path={path} />;
case 'Permissions': return <Permissions close={closeModal} save={saveAndClose} changePermissions={changePermissions} fName={fName} permissions={permissions} />;
case 'Extract': return <Extract close={closeModal} save={saveAndClose} reference={reference} onChange={onChange} name={type} fName={fName} path={path} />;
case 'Archive': return <Archive close={closeModal} save={saveAndClose} reference={reference} onChange={onChange} items={items} name={type} fName={fName} path={path} />;
case 'Rename': return <Rename close={closeModal} save={saveAndClose} reference={reference} onChange={onChange} name={type} fName={fName} />;
case 'Add directory': return <AddDirectory close={closeModal} save={saveAndClose} reference={reference} />;
case 'Delete': return <Delete close={closeModal} save={saveAndClose} fName={fName} items={items} />;
case 'Add file': return <AddFile close={closeModal} save={saveAndClose} reference={reference} />;
case 'Replace': return <Replace close={closeModal} replace={(files) => replace(files)} files={files} />
case 'Nothing selected': return <NothingSelected close={closeModal} notAvailable={notAvailable} />;
default:
break;
}
}
render() {
return (
<div>
<div className="modal" id="modal">
{this.content()}
</div>
return (
<div>
<div className="modal" id="modal">
{content()}
</div>
);
}
</div>
);
}
export default Modal;

View file

@ -12,7 +12,7 @@
height: 100%;
overflow: auto;
background-color: $black;
background-color: rgba(0,0,0,0.4);
background-color: rgba(0, 0, 0, 0.4);
.modal-content {
box-shadow: 0 2px 11px 0 rgba(0, 0, 0, 0.5);
@ -25,10 +25,23 @@
margin-top: 100px;
.modal-body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow-wrap: anywhere;
font-size: 17px;
margin-bottom: 40px;
small {
margin-top: 5px;
font-size: 13px;
&.error {
color: red;
}
}
input {
background: #333;
border: 1px solid #111;
@ -44,6 +57,7 @@
word-break: break-word;
h3 {
font-size: 20px;
color: $secondaryLight;
}
@ -66,7 +80,7 @@
border-color: none;
background: none;
text-transform: uppercase;
font-size: 12px;
font-size: 11px;
&:focus {
outline: none;
@ -98,7 +112,7 @@
border-color: $primaryLight;
color: $hoverButtonText;
}
&:hover {
background: $primaryActive;
border-color: $primaryActive;
@ -106,7 +120,7 @@
}
}
}
.header .quot {
color: $secondaryActive;
}
@ -144,17 +158,18 @@
right: 15px;
}
}
.permissions {
height: auto;
width: 30%;
margin-top: 0;
.error, &:focus {
.error,
&:focus {
border: 1px solid red;
}
input[type="text"] {
input[type='text'] {
size: 40px;
width: 60px;
margin: auto auto 10px auto;
@ -189,4 +204,4 @@
}
}
}
}
}

View file

@ -21,7 +21,7 @@ const Preview = (props) => {
const hotkeys = e => {
if (e.keyCode === 121) {
props.onClose();
onClose();
}
}

View file

@ -14,6 +14,16 @@
}
}
.servers-wrapper .l-col {
display: flex;
align-items: center;
height: 55px;
.checkbox {
margin: 0px;
}
}
.servers-wrapper .r-col {
padding-left: 2rem;
@ -28,7 +38,7 @@
transform: translateX(-10px);
}
.stats div>span {
.stats div > span {
width: 100%;
}
}
}

View file

@ -222,6 +222,17 @@ button {
}
}
}
.content .servers-list .servers-wrapper .l-col {
display: flex;
align-items: center;
height: 55px;
margin-top: .75rem;
.checkbox {
margin: 0px;
}
}
}
@media (max-width: 900px) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/images/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Vesta</title><link href="/static/css/2.6c9f324a.chunk.css" rel="stylesheet"><link href="/static/css/main.55ab5a88.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script><script>!function(e){function r(r){for(var n,l,a=r[0],c=r[1],p=r[2],i=0,s=[];i<a.length;i++)l=a[i],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var a=this["webpackJsonpreact-control-panel"]=this["webpackJsonpreact-control-panel"]||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var p=0;p<a.length;p++)r(a[p]);var f=c;t()}([])</script><script src="/static/js/2.5dc90ea3.chunk.js"></script><script src="/static/js/main.57f35a42.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/images/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Vesta</title><link href="/static/css/2.6c9f324a.chunk.css" rel="stylesheet"><link href="/static/css/main.9f0c683e.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script><script>!function(e){function r(r){for(var n,l,a=r[0],c=r[1],p=r[2],i=0,s=[];i<a.length;i++)l=a[i],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var a=this["webpackJsonpreact-control-panel"]=this["webpackJsonpreact-control-panel"]||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var p=0;p<a.length;p++)r(a[p]);var f=c;t()}([])</script><script src="/static/js/2.7cb4195c.chunk.js"></script><script src="/static/js/main.a9be926e.chunk.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long