Source: frontend-js-web/src/main/resources/META-INF/resources/liferay/compat/treeview/Treeview.es.js

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

import core from 'metal';
import Component from 'metal-component';
import Soy from 'metal-soy';

import KeyboardFocusManager from './../../keyboard-focus/KeyboardFocusManager.es';
import templates from './Treeview.soy';

/**
 * Treeview component.
 * @deprecated since 7.2
 */

class Treeview extends Component {
	/**
	 * @inheritDoc
	 */

	attached() {
		this.keyboardFocusManager_ = new KeyboardFocusManager(this, 'li')
			.setFocusHandler(this.handleNextFocus_.bind(this))
			.start();
		this.keyboardFocusManager_.on(
			KeyboardFocusManager.EVENT_FOCUSED,
			this.handleKeyboardFocused_.bind(this)
		);
	}

	/**
	 * @inheritDoc
	 */

	disposed() {
		this.keyboardFocusManager_.dispose();
		this.keyboardFocusManager_ = null;
	}

	/**
	 * Gets the node object from the `nodes` state that is located at the given
	 * index path.
	 * @param {!Array<number>} path An array of indexes indicating where the
	 *   searched node is located inside the `nodes` state.
	 * @return {!Object}
	 */

	getNodeObj(path) {
		var obj = this.nodes[path[0]];
		for (var i = 1; i < path.length; i++) {
			obj = obj.children[path[i]];
		}
		return obj;
	}

	/**
	 * Gets the treeview path for a given node.
	 * @param {!Element} node
	 * @return {!Array<string>}
	 * @protected
	 */

	getPath_(node) {
		return node.getAttribute('data-treeview-path').split('-');
	}

	/**
	 * Handles the `focused` event from `KeyboardFocusManager`. Stores the ref
	 * of the last focused tree item so that we can retain it in the tab order
	 * when the user leaves the tree.
	 * @param {!Object} data
	 * @protected
	 */

	handleKeyboardFocused_(data) {
		this.lastFocusedRef_ = data.ref;
	}

	/**
	 * Handles the left arrow being pressed. If the node is expanded, it will be
	 * closed. If it's closed, its parent's ref will be returned so it can be
	 * focused by `KeyboardFocusManager`.
	 * @param {!Array<string>} path
	 * @param {!Object} obj
	 * @return {?string}
	 * @protected
	 */

	handleLeftArrow_(path, obj) {
		if (obj.expanded) {
			obj.expanded = false;
			this.nodes = this.nodes;
		} else if (path.length > 1) {
			path.pop();
			return Treeview.NODE_REF_PREFIX + path.join('-');
		}
	}

	/**
	 * Handles focus through keyboard.
	 * @param {!Event} event
	 * @return {boolean|string|Element}
	 * @protected
	 */

	handleNextFocus_(event) {
		event.stopPropagation();

		const path = this.getPath_(event.delegateTarget);
		const obj = this.getNodeObj(path);
		switch (event.keyCode) {
			case 37:
				return this.handleLeftArrow_(path, obj);
			case 39:
				return this.handleRightArrow_(path, obj);
			default:
				// Use default behavior for other keys (like up/down arrows).

				return true;
		}
	}

	/**
	 * This is called when one of this tree view's nodes is clicked.
	 * @param {!Event} event
	 * @protected
	 */

	handleNodeClicked_(event) {
		this.toggleExpandedState_(event.delegateTarget.parentNode.parentNode);
	}

	/**
	 * This is called when one of this tree view's nodes receives a keypress.
	 * If the pressed key is ENTER or SPACE, the node's expanded state will be toggled.
	 * @param {!Event} event
	 * @protected
	 */

	handleNodeKeyUp_(event) {
		if (event.keyCode === 13 || event.keyCode === 32) {
			this.toggleExpandedState_(event.delegateTarget);
			event.stopPropagation();
		}
	}

	/**
	 * Handles the right arrow being pressed. If the node is closed, it will be
	 * expanded. If it's already expanded, the ref of its first child will be
	 * returned so it can be focused by `KeyboardFocusManager`.
	 * @param {!Array<string>} path
	 * @param {!Object} obj
	 * @return {?string}
	 * @protected
	 */

	handleRightArrow_(path, obj) {
		if (obj.expanded) {
			path.push(0);
			return Treeview.NODE_REF_PREFIX + path.join('-');
		} else if (obj.children) {
			obj.expanded = true;
			this.nodes = this.nodes;
		}
	}

	/**
	 * Toggles the expanded state for the given tree node.
	 * @param {!Element} node
	 * @protected
	 */

	toggleExpandedState_(node) {
		var nodeObj = this.getNodeObj(this.getPath_(node));
		nodeObj.expanded = !nodeObj.expanded;
		this.nodes = this.nodes;
	}
}
Soy.register(Treeview, templates);

// The prefix used for tree item nodes' refs.

Treeview.NODE_REF_PREFIX = 'node-';

/**
 * Treeview state definition.
 * @type {!Object}
 * @static
 */

Treeview.STATE = {
	/**
	 * The ref of the last item that has been focused, so that we can retain only
	 * that node in the tab order.
	 * @type {string}
	 */

	lastFocusedRef_: {
		internal: true,
		validator: core.isString
	},

	/**
	 * This tree view's nodes. Each node should have a name, and can optionally
	 * have nested children nodes. It should also indicate if its children are
	 * expanded or not.
	 * @type {Array<!{children: Array, expanded: boolean?, name: string}>}
	 * @default []
	 */

	nodes: {
		validator: Array.isArray,
		valueFn() {
			return [];
		}
	}
};

export {Treeview};
export default Treeview;