mirror of
				https://github.com/mickael-kerjean/filestash.git
				synced 2025-11-04 13:35:46 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			218 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import React from "react";
 | 
						|
import PropTypes from "prop-types";
 | 
						|
import { Link } from "react-router-dom";
 | 
						|
import { URL_FILES } from "../helpers/";
 | 
						|
import { NgIf, Icon, EventEmitter } from "./";
 | 
						|
import ReactCSSTransitionGroup from "react-addons-css-transition-group";
 | 
						|
 | 
						|
import "./breadcrumb.scss";
 | 
						|
 | 
						|
export class BreadCrumb extends React.Component {
 | 
						|
    constructor(props) {
 | 
						|
        super(props);
 | 
						|
        this.state = {
 | 
						|
            path: this._formatPath(props.path),
 | 
						|
        };
 | 
						|
    }
 | 
						|
 | 
						|
    UNSAFE_componentWillReceiveProps(props) {
 | 
						|
        this.setState({ path: this._formatPath(props.path) });
 | 
						|
    }
 | 
						|
 | 
						|
    _formatPath(full_path) {
 | 
						|
        let paths = full_path.split("/");
 | 
						|
        if (paths.slice(-1)[0] === "") {
 | 
						|
            paths.pop();
 | 
						|
        }
 | 
						|
        paths = paths.map((path, index) => {
 | 
						|
            const sub_path = paths.slice(0, index+1).join("/");
 | 
						|
            const label = path === "" ? (CONFIG.name || "Filestash") : path;
 | 
						|
            if (index === paths.length - 1) {
 | 
						|
                return { full: null, label: label };
 | 
						|
            } else {
 | 
						|
                return {
 | 
						|
                    full: sub_path + "/",
 | 
						|
                    label: label,
 | 
						|
                    minify: function() {
 | 
						|
                        if (index === 0) {
 | 
						|
                            return false;
 | 
						|
                        }
 | 
						|
                        if (paths.length <= (document.body.clientWidth > 800 ? 5 : 4)) {
 | 
						|
                            return false;
 | 
						|
                        }
 | 
						|
                        if (index > paths.length - (document.body.clientWidth > 1000? 4 : 3)) {
 | 
						|
                            return false;
 | 
						|
                        }
 | 
						|
                        return true;
 | 
						|
                    }(),
 | 
						|
                };
 | 
						|
            }
 | 
						|
        });
 | 
						|
        return paths;
 | 
						|
    }
 | 
						|
 | 
						|
    render(Element) {
 | 
						|
        if (new window.URL(location.href).searchParams.get("nav") === "false") return null;
 | 
						|
 | 
						|
        const Path = Element? Element : PathElement;
 | 
						|
        return (
 | 
						|
            <div className="component_breadcrumb" role="navigation">
 | 
						|
                <BreadCrumbContainer className={this.props.className + " no-select"}>
 | 
						|
                    <Logout />
 | 
						|
                    <ReactCSSTransitionGroup transitionName="breadcrumb" transitionLeave={true}
 | 
						|
                        transitionEnter={true} transitionLeaveTimeout={150}
 | 
						|
                        transitionEnterTimeout={200} transitionAppear={false}>
 | 
						|
                        {
 | 
						|
                            this.state.path.map((path, index) => {
 | 
						|
                                return (
 | 
						|
                                    <Path key={"breadcrumb_"+index}
 | 
						|
                                        className={"n" + index}
 | 
						|
                                        baseURL={index === 0 ? URL_FILES : this.props.baseURL || URL_FILES}
 | 
						|
                                        currentSelection={this.props.currentSelection} path={path}
 | 
						|
                                        isLast={this.state.path.length === index + 1}
 | 
						|
                                        needSaving={this.props.needSaving} />
 | 
						|
                                );
 | 
						|
                            })
 | 
						|
                        }
 | 
						|
                    </ReactCSSTransitionGroup>
 | 
						|
                </BreadCrumbContainer>
 | 
						|
            </div>
 | 
						|
        );
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
BreadCrumb.propTypes = {
 | 
						|
    path: PropTypes.string.isRequired,
 | 
						|
    needSaving: PropTypes.bool,
 | 
						|
};
 | 
						|
 | 
						|
const BreadCrumbContainer = (props) => {
 | 
						|
    return (
 | 
						|
        <div className={props.className}>
 | 
						|
            <div className="ul">
 | 
						|
                { props.children }
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
    );
 | 
						|
};
 | 
						|
 | 
						|
const Logout = (props) => {
 | 
						|
    const isRunningFromAnIframe = window.self !== window.top;
 | 
						|
    return (
 | 
						|
        <div className="li component_logout">
 | 
						|
            {
 | 
						|
                isRunningFromAnIframe ? null : (
 | 
						|
                    <Link to="/logout">
 | 
						|
                        <Icon name="power"/>
 | 
						|
                    </Link>
 | 
						|
                )
 | 
						|
            }
 | 
						|
        </div>
 | 
						|
    );
 | 
						|
};
 | 
						|
 | 
						|
const Saving = (props) => {
 | 
						|
    return (
 | 
						|
        <ReactCSSTransitionGroup transitionName="saving_indicator"
 | 
						|
            transitionLeave={true} transitionEnter={true} transitionAppear={true}
 | 
						|
            transitionLeaveTimeout={200} transitionEnterTimeout={500}
 | 
						|
            transitionAppearTimeout={500}>
 | 
						|
            <NgIf key={props.needSaving} className="component_saving"
 | 
						|
                cond={props.needSaving === true}>
 | 
						|
                *
 | 
						|
            </NgIf>
 | 
						|
        </ReactCSSTransitionGroup>
 | 
						|
    );
 | 
						|
};
 | 
						|
 | 
						|
const Separator = (props) => {
 | 
						|
    /* eslint-disable-next-line max-len */
 | 
						|
    const src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAA30lEQVQ4T63T7Q2CMBAG4OuVPdQNcAPdBCYwDdclCAQ3ACfRDXQDZQMHgNRcAoYApfWjv0jIPX3b3gn4wxJjI03TUAhRBkGwV0o9ffaYIEVRrJumuQHA3ReaILxzl+bCkNZ660ozi/QQIl4BoCKieAmyIlyU53lkjCld0CIyhIwxSmt9nEvkRLgoyzIuPggh4iRJqjHkhXTQAwBWUsqNUoq/38sL+TlJf7lf38ngdU5EFNme2adPFgGGrR2LiGcAqIko/LhjeXbatuVOraWUO58hnJ1iRKx8AetxXPHH/1+y62USursaSgAAAABJRU5ErkJggg==";
 | 
						|
    return (
 | 
						|
        <div className="component_separator">
 | 
						|
            <img alt="path_separator" width="16" height="16" src={src} />
 | 
						|
        </div>
 | 
						|
    );
 | 
						|
};
 | 
						|
 | 
						|
class PathElementWrapperComponent extends React.Component {
 | 
						|
    constructor(props) {
 | 
						|
        super(props);
 | 
						|
    }
 | 
						|
 | 
						|
    limitSize(str, is_highlight = false) {
 | 
						|
        if (is_highlight === true) {
 | 
						|
            if (str.length > 30) {
 | 
						|
                return str.substring(0, 12).trim() + "..." +
 | 
						|
                    str.substring(str.length - 10, str.length).trim();
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            if (str.length > 27) {
 | 
						|
                return str.substring(0, 20).trim() + "...";
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return str;
 | 
						|
    }
 | 
						|
 | 
						|
    render() {
 | 
						|
        let className = "component_path-element-wrapper";
 | 
						|
        if (this.props.highlight) {
 | 
						|
            className += " highlight";
 | 
						|
        }
 | 
						|
 | 
						|
        let href = this.props.baseURL + (this.props.path.full || "");
 | 
						|
        href = href
 | 
						|
            .replace(/\%/g, "%2525") // Hack to get the Link Component to work
 | 
						|
            // See ExistingThing in 'thing-existing.js'
 | 
						|
            .replace(/#/g, "%23")
 | 
						|
            .replace(/\?/g, "%3F");
 | 
						|
        href = href || "/";
 | 
						|
        href += location.search;
 | 
						|
 | 
						|
        return (
 | 
						|
            <div className={"li "+className}>
 | 
						|
                <NgIf cond={this.props.isLast === false}>
 | 
						|
                    <Link to={href} className="label">
 | 
						|
                        <NgIf cond={this.props.path.minify !== true}>
 | 
						|
                            { this.limitSize(this.props.path.label) }
 | 
						|
                        </NgIf>
 | 
						|
                        <NgIf cond={this.props.path.minify === true}>
 | 
						|
                            ...
 | 
						|
                            <span className="title">
 | 
						|
                                { this.limitSize(this.props.path.label, true) }
 | 
						|
                            </span>
 | 
						|
                        </NgIf>
 | 
						|
                    </Link>
 | 
						|
                    <Separator/>
 | 
						|
                </NgIf>
 | 
						|
                <NgIf cond={this.props.isLast === true} className="label">
 | 
						|
                    <div>{this.limitSize(this.props.path.label)}</div>
 | 
						|
                    <Saving needSaving={this.props.needSaving} />
 | 
						|
                </NgIf>
 | 
						|
            </div>
 | 
						|
        );
 | 
						|
    }
 | 
						|
}
 | 
						|
export const PathElementWrapper = EventEmitter(PathElementWrapperComponent);
 | 
						|
 | 
						|
 | 
						|
// just a hack to make it play nicely with react-dnd as it refuses to use our
 | 
						|
// custom component if it's not wrap by something it knows ...
 | 
						|
export class PathElement extends PathElementWrapper {
 | 
						|
    constructor(props) {
 | 
						|
        super(props);
 | 
						|
    }
 | 
						|
 | 
						|
    render(highlight = false) {
 | 
						|
        let className = "component_path-element";
 | 
						|
        if (this.props.isFirst) className += ` is-first`;
 | 
						|
        if (this.props.className) className += ` ${this.props.className}`;
 | 
						|
 | 
						|
        return (
 | 
						|
            <div className={className}>
 | 
						|
                <PathElementWrapper highlight={highlight} {...this.props} />
 | 
						|
            </div>
 | 
						|
        );
 | 
						|
    }
 | 
						|
}
 |