import React from "react";
import { useFavorites } from "../../../../../hooks/useFavorites";
import { FinsembleDraggable } from "../../../../legacyControls/FinsembleDraggable";
import { FinsembleDnDContext } from "../../../../legacyControls/FinsembleDnDContext";
import { ADVANCED_APP_LAUNCHER, DASHBOARDS, FAVORITES } from "../constants";
import { onClickA11y } from "../../../../utils";
const RenderButtons = (props) => {
    if (!props.showIcon)
        return React.createElement(React.Fragment, null);
    const { isEditing, canEdit, canDelete, attemptRename, cancelEdit, renameFolder, deleteFolder } = props;
    if (isEditing) {
        // We can't use onClick for these buttons because onBlur causes a re-render of these buttons which means mousedown and mouseup
        // technically don't fire on the same button. This means onClick is never fired, so we trigger the buttons on mousedown instead.
        // For more information see: https://www.eventbrite.com/engineering/a-story-of-a-react-re-rendering-bug/
        return (React.createElement("span", { className: "folder-action-icons" },
            React.createElement("i", { role: "button", id: "confirm-edit", className: "ff-check-mark-2", "aria-label": "Accept Rename", title: "Accept Rename", ...onClickA11y(attemptRename) }),
            React.createElement("i", { role: "button", id: "cancel-edit", className: "ff-close", "aria-label": "Cancel", title: "Cancel", ...onClickA11y(cancelEdit) })));
    }
    else {
        return (React.createElement("span", { className: "folder-action-icons" },
            canEdit && (React.createElement("i", { role: "button", className: "ff-adp-edit", "aria-label": "Rename", title: "Rename", ...onClickA11y(renameFolder) })),
            canDelete && (React.createElement("i", { role: "button", className: "ff-adp-trash-outline", "aria-label": "Delete Folder", title: "Delete Folder", ...onClickA11y(deleteFolder) }))));
    }
};
const RenderFolder = (props) => {
    const { onClick, onDrop: passedOnDrop, ...restOfProps } = props;
    const { addFavorite, isFavorited } = useFavorites();
    // If an app is dropped in the Favorites folder, favorite the app
    const onDrop = (e) => {
        passedOnDrop(e);
        const app = JSON.parse(e.dataTransfer.getData("app"));
        if (props.title === FAVORITES && !isFavorited(app.name)) {
            const favoriteApp = {
                id: app.name,
                category: "Application",
                name: app.name,
                icon: app.icon,
            };
            addFavorite(favoriteApp);
        }
    };
    return (React.createElement("div", { role: "tab", onDrop: onDrop, ...onClickA11y(onClick), ...restOfProps }, props.children));
};
export default class FoldersList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            renamingFolder: null,
            folderNameInput: "",
            isNameError: false,
        };
        // Reference to ontainer element of folder list
        this.listDiv = React.createRef();
        this.renameFolder = this.renameFolder.bind(this);
        this.changeFolderName = this.changeFolderName.bind(this);
        this.cancelEdit = this.cancelEdit.bind(this);
        this.keyPressed = this.keyPressed.bind(this);
        this.deleteFolder = this.deleteFolder.bind(this);
        this.onFocusRemove = this.onFocusRemove.bind(this);
        this.onDragEnd = this.onDragEnd.bind(this);
        this.onMouseMove = this.onMouseMove.bind(this);
        this.animateErrorInput = this.animateErrorInput.bind(this);
        // The last known mouse Y position
        this.mouseY = null;
        //Reference to a folder name input in error
        this.errorInput = React.createRef();
    }
    animateErrorInput() {
        var _a;
        if ((_a = this.errorInput) === null || _a === void 0 ? void 0 : _a.current) {
            this.errorInput.current.classList.remove("error");
            const flickerInput = setTimeout(() => {
                var _a;
                if ((_a = this.errorInput) === null || _a === void 0 ? void 0 : _a.current) {
                    this.errorInput.current.classList.add("error");
                }
                clearTimeout(flickerInput);
            }, 500);
        }
    }
    cancelEdit() {
        this.setState({
            renamingFolder: null,
            isNameError: false,
            folderNameInput: "",
        });
    }
    /**
     * Keeps a track of the last mouse's clientY position
     * @param {MouseEvent} event The mouse move event
     */
    onMouseMove(event) {
        this.mouseY = event.clientY;
    }
    onDragEnd({ oldIndex, newIndex }) {
        this.props.reorderFolder(oldIndex, newIndex);
    }
    onAppDrop(event, folder) {
        event.preventDefault();
        const app = JSON.parse(event.dataTransfer.getData("app"));
        // Do not do anything if its Advanced App Launcher or dashboards folder
        if ([ADVANCED_APP_LAUNCHER, DASHBOARDS].indexOf(folder) < 0) {
            this.props.addAppToFolder(folder, app);
        }
    }
    async onFolderClicked(folder) {
        this.props.updateActiveFolder(folder);
    }
    onFocusRemove() {
        if (this.state.isNameError) {
            this.animateErrorInput();
        }
        // If focus removed and nothing was type, then just hide
        // and consider it a rename cancel
        if (!this.state.folderNameInput) {
            // Cancel rename
            this.setState({
                renamingFolder: null,
                isNameError: false,
                folderNameInput: "",
            });
            return;
        }
        // Finally, all good and so we can rename the folder
        this.attemptRename();
    }
    UNSAFE_componentWillMount() {
        document.addEventListener("mousemove", this.onMouseMove);
    }
    componentWillUnmount() {
        document.removeEventListener("mousemove", this.onMouseMove);
    }
    renameFolder(name, e) {
        e.preventDefault();
        e.stopPropagation();
        if (!this.state.isNameError) {
            this.setState({
                renamingFolder: name,
            });
        }
    }
    changeFolderName(e) {
        this.setState({
            folderNameInput: e.target.value,
        });
    }
    deleteFolder(name, e) {
        e.stopPropagation();
        e.preventDefault();
        // Do not attempt to delete if user is renaming a folder
        !this.state.renamingFolder && this.props.deleteFolder(name);
    }
    keyPressed(e) {
        if (e.key === "Enter") {
            this.attemptRename();
        }
    }
    /**
     * To be called when user press Enter or when focus is removed
     */
    attemptRename() {
        const { folders } = this.props;
        const input = this.state.folderNameInput.trim();
        const oldName = this.state.renamingFolder;
        let newName = input;
        // Check user input to make sure its at least 1 character
        // made of string, number or both
        if (!/^([a-zA-Z0-9\s]{1,})$/.test(input)) {
            // Do not rename
            console.warn("A valid folder name is required. /^([a-zA-Z0-9s]{1,})$/");
            return this.setState({
                isNameError: true,
            });
        }
        // Check if the submission is the same text as the old name.
        // If false, renaming will be skipped
        let nameChanged = true;
        // We no longer allow leading or trailing spaces, but the trim on the oldName needs to remain for any existing folders
        if (oldName.trim() === newName)
            nameChanged = false;
        // Names must be unique, folders cant share same names
        if (folders[newName]) {
            let repeatedFolderIndex = 0;
            do {
                repeatedFolderIndex++;
            } while (Object.keys(folders).includes(`${newName}(${repeatedFolderIndex})`));
            newName = `${newName}(${repeatedFolderIndex})`;
        }
        this.setState({
            folderNameInput: "",
            renamingFolder: null,
            isNameError: false,
        }, () => {
            if (nameChanged) {
                this.props.renameFolder(oldName, newName);
            }
        });
    }
    /**
     * Given some data, renders the basic folder structure that cannot be unordered. Used for orderable folders too. Those just get wrapped in a Draggable
     */
    renderFolder(folder, folderName, index) {
        let className = "complex-menu-section-toggle";
        if (this.props.activeFolder === folderName) {
            className = `${className} active-section-toggle`;
        }
        if (folder.icon) {
            className = `${className} folder-with-icon`;
        }
        const { canDelete, canEdit } = folder;
        let nameField, isEditing = false;
        if (this.state.renamingFolder === folderName && canEdit) {
            const { isNameError } = this.state;
            nameField = (React.createElement("input", { id: "rename", value: this.state.folderNameInput, onChange: this.changeFolderName, onKeyPress: this.keyPressed, onBlur: this.onFocusRemove, className: isNameError ? "error" : "", autoFocus: true, ref: isNameError ? this.errorInput : null }));
            isEditing = true;
        }
        else if (folderName === "Advanced App Launcher") {
            nameField = "App Launcher";
        }
        else {
            nameField = folderName;
        }
        const showIcon = canEdit || canDelete;
        // This DOM will be rendered within a draggable (if the folder can be dragged), and a plain ol div if it cannot be dragged.
        return (React.createElement(RenderFolder, { onDragOver: (event) => event.preventDefault(), onDrop: (event) => this.onAppDrop(event, folderName), onClick: () => this.onFolderClicked(folderName), className: className, key: index, title: folderName },
            React.createElement("div", { className: "left-nav-label" },
                folder.icon && React.createElement("i", { className: folder.icon }),
                React.createElement("div", { className: "folder-name" }, nameField)),
            React.createElement(RenderButtons, { showIcon: showIcon, isEditing: isEditing, canEdit: canEdit, canDelete: canDelete, attemptRename: () => this.attemptRename(), cancelEdit: () => this.cancelEdit(), renameFolder: (event) => this.renameFolder(folderName, event), deleteFolder: (event) => this.deleteFolder(folderName, event) })));
    }
    /**
     * Render all folders that cannot be reordered.
     */
    renderUnorderableFolders() {
        const { folders, dragDisabled, foldersList } = this.props;
        const unorderableFolders = foldersList.filter((folderName) => dragDisabled.includes(folderName) && folderName in folders);
        return unorderableFolders.map((folderName, index) => {
            const folder = folders[folderName];
            folder.icon = "";
            return this.renderFolder(folder, folderName, index);
        });
    }
    /**
     * Renders all folders that can be reordered (user created folders).
     */
    renderOrderableFolders() {
        const { folders } = this.props;
        const orderableFolders = this.props.foldersList.filter((folderName) => !this.props.dragDisabled.includes(folderName) && folderName in folders);
        return orderableFolders.map((folderName, index) => {
            const folder = folders[folderName];
            return (React.createElement(FinsembleDraggable, { draggableId: folderName, key: index, index: index }, this.renderFolder(folder, folderName, index)));
        });
    }
    render() {
        return (React.createElement("div", { className: "advanced-app-launcher-nav-top" },
            React.createElement("div", { className: "folder-list", role: "tablist", ref: this.listDiv },
                this.renderUnorderableFolders(),
                React.createElement(FinsembleDnDContext, { onDragEnd: this.onDragEnd }, this.renderOrderableFolders()))));
    }
}
//# sourceMappingURL=FoldersList.js.map