1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-05-12 09:55:53 +00:00

Dtable homepage ()

* dtable homepage

* style

* add search dtable

* warning

* logo url

* dtable-logo
This commit is contained in:
zxj96 2019-09-27 17:57:57 +08:00 committed by Daniel Pan
parent c34969ce4b
commit 77c7012907
10 changed files with 352 additions and 26 deletions

View File

@ -7,13 +7,6 @@
height: 50px;
}
.dtable-header .dtable-logo {
padding-right: 6px;
font-size: 36px;
line-height: 36px;
color: #FEAC74;
}
.dtable-header .dtable-text {
display: flex;
align-items: center;
@ -23,6 +16,10 @@
.dtable-side-nav {
padding: 12px;
flex: auto;
display: flex;
flex-direction: column;
overflow: auto;
}
.dtable-nav-title {
@ -69,14 +66,17 @@
}
.dtable-header .common-toolbar>div {
padding-right: 1rem;
cursor: pointer;
margin-left: 0.5rem;
}
.dtable-center .cur-view-title {
.dtable-center .cur-view-header {
line-height: 50px;
font-size: 1.5rem;
padding-left: 12px;
padding: 0 16px;
}
.dtable-center .cur-view-header .cur-view-title {
border-bottom: 1px solid #eee;
}
@ -154,6 +154,7 @@
display: flex;
justify-content: space-between;
flex-wrap: wrap;
min-height: 0;
}
.workspace .table-item-container .table-item {
width: 48%;
@ -178,6 +179,7 @@
width: 15%;
height: 100%;
text-align: right;
position: relative;
}
.workspace .table-item .table-dropdown-menu .table-dropdown-menu-icon {
width: 24px;

View File

@ -27,7 +27,7 @@ class DTableWorkspaceCommon extends React.Component {
isShowSharedDialog: false,
isShowAPITokenDialog: false,
currentTable: null,
}
};
}
componentDidMount() {
@ -138,7 +138,7 @@ class DTableWorkspaceCommon extends React.Component {
render() {
let { isDataLoading } = this.state;
if (isDataLoading) {
return <Loading />
return <Loading />;
}
let { workspace } = this.props;

View File

@ -51,11 +51,11 @@ class DTableWorkspaceShared extends React.Component {
render() {
if (this.state.isDataLoading) {
return <Loading />
return <Loading />;
}
if (!this.state.tableList.length) {
return "";
return '';
}
return (

View File

@ -23,7 +23,7 @@ class MainPanelDTables extends React.Component {
isWorkspaceListLoading: true,
isSharedTableListLoading: true,
isShowCreateDialog: false
}
};
}
componentDidMount() {
@ -89,7 +89,7 @@ class MainPanelDTables extends React.Component {
render() {
let { isWorkspaceListLoading, isSharedTableListLoading } = this.state;
if (isWorkspaceListLoading || isSharedTableListLoading) {
return <Loading />
return <Loading />;
}
let { workspaceList, sharedTableList } = this.state;
@ -104,8 +104,10 @@ class MainPanelDTables extends React.Component {
return (
<Fragment>
<div className="main-panel-center dtable-center">
<div className="main-panel-container d-flex flex-1 flex-column">
<div className="cur-view-title">DTable</div>
<div className="cur-view-container d-flex flex-1 flex-column">
<div className="cur-view-header">
<div className="cur-view-title">DTable</div>
</div>
<div className="cur-view-content">
{this.state.errorMsg &&
<p className="error text-center">{this.state.errorMsg}</p>
@ -115,7 +117,7 @@ class MainPanelDTables extends React.Component {
<DTableWorkspaceCommon workspace={personalWorkspace} />
<DTableWorkspaceShared tableList={sharedTableList} />
{groupWorkspaceList.length > 0 && groupWorkspaceList.map((workspace, index) => {
return (<DTableWorkspaceCommon key={index} workspace={workspace} />)
return (<DTableWorkspaceCommon key={index} workspace={workspace} />);
})}
<button className="btn btn-secondary dtable-add-btn mb-4" onClick={this.onCreateTableToggle}>{gettext('New DTable')}</button>
</Fragment>

View File

@ -1,10 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Router } from '@reach/router'
import { Router } from '@reach/router';
import Notification from '../../components/common/notification';
import Account from '../../components/common/account';
import Search from './search/search';
import MainPanelDTables from './main-panel-dtables';
import MainPanelApps from './main-panel-apps';
import MainPanelTempletes from './main-panel-templetes';
import '../../css/search.css';
const siteRoot = window.app.config.siteRoot;
@ -15,14 +19,25 @@ const propTypes = {
class MainPanel extends React.Component {
onSearchedClick = (item) => {
let url = siteRoot + 'workspace/' + item.workspace_id + '/dtable/' + item.name + '/';
window.open(url)
}
render() {
let searchPlaceholder = this.props.searchPlaceholder || 'Search Dtables';
return (
<div className="main-panel">
<div className="main-panel-north dtable-header">
<div className="common-toolbar">
<div className="search" title={'Search'}>Search</div>
<div className="notification" title={'Notification'}>Notification</div>
<div className="avatar" title={'Avatar'}></div>
<Search
placeholder={searchPlaceholder}
onSearchedClick={this.onSearchedClick}
/>
<Notification />
<Account />
</div>
</div>
<Router className="reach-router">

View File

@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
const propTypes = {
item: PropTypes.object.isRequired,
onItemClickHandler: PropTypes.func.isRequired,
};
class SearchResultItem extends React.Component {
onClickHandler = () => {
var item = this.props.item;
this.props.onItemClickHandler(item);
}
render() {
let item = this.props.item;
return (
<li className="search-result-item" onClick={this.onClickHandler}>
<span className="sf3-font sf3-font-table"></span>
<div className="item-content">
<div className="item-name ellipsis">{item.name}</div>
{/* <div className="item-link ellipsis">{item.repo_name}/{item.link_content}</div> */}
{/* <div className="item-text ellipsis" dangerouslySetInnerHTML={{__html: item.content}}></div> */}
</div>
</li>
);
}
}
SearchResultItem.propTypes = propTypes;
export default SearchResultItem;

View File

@ -0,0 +1,272 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import MediaQuery from 'react-responsive';
import { seafileAPI } from '../../../utils/seafile-api';
import { gettext, siteRoot, username } from '../../../utils/constants';
import SearchResultItem from './search-result-item';
import More from '../../../components/more';
import Workspace from '../model/workspace';
const propTypes = {
isPublic: PropTypes.bool,
repoID: PropTypes.string,
placeholder: PropTypes.string,
onSearchedClick: PropTypes.func.isRequired,
};
class Search extends Component {
constructor(props) {
super(props);
this.state = {
width: 'default',
value: '',
resultItems: [],
isMaskShow: false,
isResultShow: false,
isResultGetted: false,
isCloseShow: false,
isSearchInputShow: false, // for mobile
};
this.inputValue = '';
}
onFocusHandler = () => {
this.setState({
width: '30rem',
isMaskShow: true,
isCloseShow: true
});
}
onCloseHandler = () => {
this.resetToDefault();
}
onItemClickHandler = (item) => {
this.resetToDefault();
this.props.onSearchedClick(item);
}
onChangeHandler = (event) => {
let _this = this;
this.setState({value: event.target.value});
let newValue = event.target.value;
if (this.inputValue === newValue.trim()) {
return false;
}
this.inputValue = newValue.trim();
if (this.inputValue === '' || _this.getValueLength(this.inputValue) < 3) {
this.setState({
isResultShow: false,
isResultGetted: false
});
return false;
}
let queryData = {
q: newValue,
};
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(_this.getSearchResult(queryData), 500);
}
getSearchResult(queryData) {
this.setState({
isResultShow: true,
isResultGetted: false
});
this.sendRequest(queryData);
}
sendRequest(queryData) { //search dtable
seafileAPI.listWorkspaces().then((res) => {
let workspaceList = res.data.workspace_list.filter(item => {
return new Workspace(item);
});
let dtableNameList = [];
workspaceList.forEach(dtable => {
let resultData = dtable.table_list.filter(item => {
return item.name.indexOf(queryData.q) > -1;
});
if (resultData.length > 0) {
dtableNameList.push(...resultData);
}
});
this.setState({
resultItems: dtableNameList,
isResultGetted: true,
});
});
}
getValueLength(str) {
var i = 0, code, len = 0;
for (; i < str.length; i++) {
code = str.charCodeAt(i);
if (code == 10) { //solve enter problem
len += 2;
} else if (code < 0x007f) {
len += 1;
} else if (code >= 0x0080 && code <= 0x07ff) {
len += 2;
} else if (code >= 0x0800 && code <= 0xffff) {
len += 3;
}
}
return len;
}
resetToDefault() {
this.inputValue = null;
this.setState({
width: '',
value: '',
isMaskShow: false,
isCloseShow: false,
isResultShow: false,
isResultGetted: false,
resultItems: [],
isSearchInputShow: false,
});
}
onShowMore = () => {
let repoID = this.props.repoID;
let newValue = this.state.value;
let queryData = {
q: newValue,
search_repo: repoID ? repoID : 'all',
search_ftypes: 'all',
};
let params = '';
for (let key in queryData) {
params += key + '=' + queryData[key] + '&';
}
window.location = siteRoot + 'search/?' + params.slice(0, params.length - 1);
}
renderSearchResult() {
if (!this.state.isResultShow) {
return;
}
if (!this.state.isResultGetted || this.getValueLength(this.inputValue) < 3) {
return (
<span className="loading-icon loading-tip"></span>
);
}
if (!this.state.resultItems.length) {
return (
<div className="search-result-none">{gettext('No results matching.')}</div>
);
}
let isShowMore = this.state.resultItems.length >= 5 ? true : false;
return (
<ul className="search-result-list">
{this.state.resultItems.map(item => {
return (
<SearchResultItem
key={item.id}
item={item}
onItemClickHandler={this.onItemClickHandler}
/>
);
})}
{isShowMore && <More onShowMore={this.onShowMore}/>}
</ul>
);
}
onSearchToggle = () => {
this.setState({
isSearchInputShow: !this.state.isSearchInputShow,
isMaskShow: !this.state.isMaskShow,
});
}
onSearchPage = () => {
window.location.href = siteRoot + 'search/';
}
render() {
let width = this.state.width !== 'default' ? this.state.width : '';
let style = {'width': width};
return (
<Fragment>
<MediaQuery query="(min-width: 768px)">
<div className="search">
<div className={`search-mask ${this.state.isMaskShow ? '' : 'hide'}`} onClick={this.onCloseHandler}></div>
<div className="search-container">
<div className="input-icon">
<i className="search-icon-left input-icon-addon fas fa-search"></i>
<input
type="text"
className="form-control search-input"
name="query"
placeholder={this.props.placeholder}
style={style}
value={this.state.value}
onFocus={this.onFocusHandler}
onChange={this.onChangeHandler}
autoComplete="off"
/>
{(this.state.isCloseShow && username) &&
<i className='search-icon-right input-icon-addon fas fa-external-link-alt search-icon-arrow'
onClick={this.onSearchPage}></i>
}
{this.state.isCloseShow && <i className='search-icon-right input-icon-addon fas fa-times' onClick={this.onCloseHandler}></i>}
</div>
<div className="search-result-container">
{this.renderSearchResult()}
</div>
</div>
</div>
</MediaQuery>
<MediaQuery query="(max-width: 767.8px)">
<div className="search-icon-container">
<i className="search-icon fas fa-search" onClick={this.onSearchToggle}></i>
</div>
{this.state.isSearchInputShow &&
<div className="search">
<div className={`search-mask ${this.state.isMaskShow ? '' : 'hide'}`} onClick={this.onCloseHandler}></div>
<div className="search-container">
<div className="input-icon">
<i className="search-icon-left input-icon-addon fas fa-search"></i>
<input
type="text"
className="form-control search-input"
name="query"
placeholder={this.props.placeholder}
style={style}
value={this.state.value}
onFocus={this.onFocusHandler}
onChange={this.onChangeHandler}
autoComplete="off"
/>
{(this.state.isCloseShow && username) &&
<i className='search-icon-right input-icon-addon fas fa-external-link-alt search-icon-arrow'
onClick={this.onSearchPage}></i>
}
{this.state.isCloseShow && <i className='search-icon-right input-icon-addon fas fa-times' onClick={this.onCloseHandler}></i>}
</div>
<div className="search-result-container">
{this.renderSearchResult()}
</div>
</div>
</div>
}
</MediaQuery>
</Fragment>
);
}
}
Search.propTypes = propTypes;
export default Search;

View File

@ -21,12 +21,13 @@ class SidePanel extends React.Component {
}
render() {
let imgUrl = siteRoot + 'media/img/dtable-logo.png'
return (
<div className="side-panel">
<div className="side-panel-north dtable-header">
<i className="sf3-font sf3-font-dtable-logo dtable-logo"></i>
<span className="dtable-text">DTable</span>
<div className="dtable-logo">
<img src={imgUrl} height="32" />
</div>
</div>
<div className="side-panel-center">
<div className="dtable-side-nav">

BIN
media/img/dtable-logo.png Normal file

Binary file not shown.

After

(image error) Size: 4.4 KiB

View File

@ -28,6 +28,7 @@
window.app = {
config: {
siteRoot: '{{ SITE_ROOT }}',
avatarURL: '{{ avatar_url }}'
},
pageOptions: {
server: '{{ service_url }}',