coral-spectrum/coral-component-panelstack/src/scripts/PanelStack.js
/**
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import {BaseComponent} from '../../../coral-base-component';
import {SelectableCollection} from '../../../coral-collection';
import {Decorator} from '../../../coral-decorator';
const CLASSNAME = '_coral-PanelStack';
/**
@class Coral.PanelStack
@classdesc A PanelStack component holding a collection of panels. It wraps content, keeping only the selected panel in view.
@htmltag coral-panelstack
@extends {HTMLElement}
@extends {BaseComponent}
*/
const PanelStack = Decorator(class extends BaseComponent(HTMLElement) {
/** @ignore */
constructor() {
super();
// Attach events
this._delegateEvents({
// private
'coral-panel:_selectedchanged': '_onItemSelectedChanged'
});
// Used for eventing
this._oldSelection = null;
// Init the collection mutation observer
this.items._startHandlingItems(true);
}
/**
The Collection Interface that allows interacting with the items that the component contains.
@type {SelectableCollection}
@readonly
*/
get items() {
// just init on demand
if (!this._items) {
this._items = new SelectableCollection({
host: this,
itemTagName: 'coral-panel',
// allows panels to be nested
itemSelector: ':scope > coral-panel',
onlyHandleChildren: true,
onItemAdded: this._validateSelection,
onItemRemoved: this._validateSelection
});
}
return this._items;
}
/**
The selected item of the PanelStack.
@type {HTMLElement}
@readonly
*/
get selectedItem() {
return this.items._getLastSelected();
}
/** @private */
_onItemSelectedChanged(event) {
event.stopImmediatePropagation();
this._validateSelection(event.target);
}
/** @private */
_validateSelection(item) {
const selectedItems = this.items._getAllSelected();
// Last selected item wins if multiple selection while not allowed
item = item || selectedItems[selectedItems.length - 1];
if (item && item.hasAttribute('selected') && selectedItems.length > 1) {
selectedItems.forEach((selectedItem) => {
if (selectedItem !== item) {
// Don't trigger change events
this._preventTriggeringEvents = true;
selectedItem.removeAttribute('selected');
}
});
// We can trigger change events again
this._preventTriggeringEvents = false;
}
this._triggerChangeEvent();
}
/** @private */
_triggerChangeEvent() {
const selectedItem = this.selectedItem;
const oldSelection = this._oldSelection;
if (!this._preventTriggeringEvents && selectedItem !== oldSelection) {
this.trigger('coral-panelstack:change', {
oldSelection: oldSelection,
selection: selectedItem
});
this._oldSelection = selectedItem;
}
}
/** @ignore */
render() {
super.render();
this.classList.add(CLASSNAME);
this.setAttribute('role', 'presentation');
// Don't trigger events once connected
this._preventTriggeringEvents = true;
this._validateSelection();
this._preventTriggeringEvents = false;
this._oldSelection = this.selectedItem;
}
/**
Triggered when {@link PanelStack} selected panel has changed.
@typedef {CustomEvent} coral-panelstack:change
@property {Panel} detail.selection
The new selected panel.
@property {Panel} detail.oldSelection
The prior selected panel.
*/
});
export default PanelStack;