UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

516 lines (435 loc) • 17 kB
"use strict"; var $ = require("../core/renderer"), eventsEngine = require("../events/core/events_engine"), fx = require("../animation/fx"), clickEvent = require("../events/click"), devices = require("../core/devices"), extend = require("../core/utils/extend").extend, getPublicElement = require("../core/utils/dom").getPublicElement, iteratorUtils = require("../core/utils/iterator"), isPlainObject = require("../core/utils/type").isPlainObject, registerComponent = require("../core/component_registrator"), eventUtils = require("../events/utils"), CollectionWidget = require("./collection/ui.collection_widget.edit"), deferredUtils = require("../core/utils/deferred"), when = deferredUtils.when, Deferred = deferredUtils.Deferred, BindableTemplate = require("./widget/bindable_template"), iconUtils = require("../core/utils/icon"), isDefined = require("../core/utils/type").isDefined, themes = require("./themes"); var ACCORDION_CLASS = "dx-accordion", ACCORDION_WRAPPER_CLASS = "dx-accordion-wrapper", ACCORDION_ITEM_CLASS = "dx-accordion-item", ACCORDION_ITEM_OPENED_CLASS = "dx-accordion-item-opened", ACCORDION_ITEM_CLOSED_CLASS = "dx-accordion-item-closed", ACCORDION_ITEM_TITLE_CLASS = "dx-accordion-item-title", ACCORDION_ITEM_BODY_CLASS = "dx-accordion-item-body", ACCORDION_ITEM_TITLE_CAPTION_CLASS = "dx-accordion-item-title-caption", ACCORDION_ITEM_DATA_KEY = "dxAccordionItemData"; /** * @name dxAccordion * @publicName dxAccordion * @inherits CollectionWidget * @module ui/accordion * @export default */ var Accordion = CollectionWidget.inherit({ _activeStateUnit: "." + ACCORDION_ITEM_CLASS, _getDefaultOptions: function _getDefaultOptions() { return extend(this.callBase(), { /** * @name dxAccordionOptions.hoverStateEnabled * @publicName hoverStateEnabled * @type boolean * @default true * @inheritdoc */ hoverStateEnabled: true, /** * @name dxAccordionOptions.height * @publicName height * @type number|string|function * @default undefined * @type_function_return number|string */ height: undefined, /** * @name dxAccordionOptions.itemTitleTemplate * @publicName itemTitleTemplate * @type template|function * @default "title" * @type_function_param1 itemData:object * @type_function_param2 itemIndex:number * @type_function_param3 itemElement:dxElement * @type_function_return string|Node|jQuery */ itemTitleTemplate: "title", /** * @name dxAccordionOptions.onItemTitleClick * @publicName onItemTitleClick * @type function(e)|string * @extends Action * @type_function_param1 e:object * @type_function_param1_field4 itemData:object * @type_function_param1_field5 itemElement:dxElement * @type_function_param1_field6 itemIndex:number * @action */ onItemTitleClick: null, /** * @name dxAccordionOptions.selectedIndex * @publicName selectedIndex * @type number * @default 0 */ selectedIndex: 0, /** * @name dxAccordionOptions.collapsible * @publicName collapsible * @type boolean * @default false */ collapsible: false, /** * @name dxAccordionOptions.multiple * @publicName multiple * @type boolean * @default false */ multiple: false, /** * @name dxAccordionOptions.animationDuration * @publicName animationDuration * @type number * @default 300 */ animationDuration: 300, /** * @name dxAccordionOptions.deferRendering * @publicName deferRendering * @type boolean * @default true */ deferRendering: true, /** * @name dxAccordionOptions.itemTemplate * @publicName itemTemplate * @type template|function * @default "item" * @type_function_param1 itemData:object * @type_function_param2 itemIndex:number * @type_function_param3 itemElement:dxElement * @type_function_return string|Node|jQuery */ selectionByClick: true, activeStateEnabled: true, _itemAttributes: { role: "tab" }, _animationEasing: "ease" }); }, _defaultOptionsRules: function _defaultOptionsRules() { return this.callBase().concat([{ device: function device() { return devices.real().deviceType === "desktop" && !devices.isSimulator(); }, options: { /** * @name dxAccordionOptions.focusStateEnabled * @publicName focusStateEnabled * @type boolean * @default true @for desktop * @inheritdoc */ focusStateEnabled: true } }, { device: function device() { return themes.isMaterial(); }, options: { itemTitleTemplate: function itemTitleTemplate(data) { return $("<div>").text(data.title).addClass(ACCORDION_ITEM_TITLE_CAPTION_CLASS); }, /** * @name dxAccordionOptions.animationDuration * @publicName animationDuration * @type number * @default 200 @for Material */ animationDuration: 200, _animationEasing: "cubic-bezier(0.4, 0, 0.2, 1)" } }]); }, _itemElements: function _itemElements() { return this._itemContainer().children(this._itemSelector()); }, _init: function _init() { this.callBase(); this.option("selectionRequired", !this.option("collapsible")); this.option("selectionMode", this.option("multiple") ? "multiple" : "single"); var $element = this.$element(); $element.addClass(ACCORDION_CLASS); this._$container = $("<div>").addClass(ACCORDION_WRAPPER_CLASS); $element.append(this._$container); }, _initTemplates: function _initTemplates() { this.callBase(); /** * @name dxAccordionItemTemplate * @publicName dxAccordionItemTemplate * @inherits CollectionWidgetItemTemplate * @type object */ /** * @name dxAccordionItemTemplate.title * @publicName title * @type String */ /** * @name dxAccordionItemTemplate.icon * @publicName icon * @type String */ this._defaultTemplates["title"] = new BindableTemplate(function ($container, data) { if (isPlainObject(data)) { if (data.title) { $container.text(data.title); } $container.append(iconUtils.getImageContainer(data.icon)); } else { $container.text(String(data)); } }, ["title", "icon"], this.option("integrationOptions.watchMethod")); }, _initMarkup: function _initMarkup() { this._deferredItems = []; this.callBase(); this.setAria({ "role": "tablist", "multiselectable": this.option("multiple") }); }, _render: function _render() { this.callBase(); this._updateItemHeightsWrapper(true); this._attachItemTitleClickAction(); }, _itemDataKey: function _itemDataKey() { return ACCORDION_ITEM_DATA_KEY; }, _itemClass: function _itemClass() { return ACCORDION_ITEM_CLASS; }, _itemContainer: function _itemContainer() { return this._$container; }, _itemTitles: function _itemTitles() { return this._itemElements().find("." + ACCORDION_ITEM_TITLE_CLASS); }, _itemContents: function _itemContents() { return this._itemElements().find("." + ACCORDION_ITEM_BODY_CLASS); }, _getItemData: function _getItemData(target) { return $(target).parent().data(this._itemDataKey()) || this.callBase.apply(this, arguments); }, _executeItemRenderAction: function _executeItemRenderAction(itemData) { if (itemData.type) { return; } this.callBase.apply(this, arguments); }, _itemSelectHandler: function _itemSelectHandler(e) { if ($(e.target).closest(this._itemContents()).length) { return; } this.callBase.apply(this, arguments); }, _renderItemContent: function _renderItemContent(args) { var itemTitle = this.callBase(extend({}, args, { contentClass: ACCORDION_ITEM_TITLE_CLASS, templateProperty: "titleTemplate", defaultTemplateName: this.option("itemTitleTemplate") })); var deferred = new Deferred(); if (isDefined(this._deferredItems[args.index])) { this._deferredItems[args.index] = deferred; } else { this._deferredItems.push(deferred); } if (!this.option("deferRendering") || this._getSelectedItemIndices().indexOf(args.index) >= 0) { deferred.resolve(); } deferred.done(this.callBase.bind(this, extend({}, args, { contentClass: ACCORDION_ITEM_BODY_CLASS, container: getPublicElement($("<div>").appendTo($(itemTitle).parent())) }))); }, _attachItemTitleClickAction: function _attachItemTitleClickAction() { var itemSelector = "." + ACCORDION_ITEM_TITLE_CLASS, eventName = eventUtils.addNamespace(clickEvent.name, this.NAME); eventsEngine.off(this._itemContainer(), eventName, itemSelector); eventsEngine.on(this._itemContainer(), eventName, itemSelector, this._itemTitleClickHandler.bind(this)); }, _itemTitleClickHandler: function _itemTitleClickHandler(e) { this._itemDXEventHandler(e, "onItemTitleClick"); }, _renderSelection: function _renderSelection(addedSelection, removedSelection) { this._itemElements().addClass(ACCORDION_ITEM_CLOSED_CLASS); this.setAria("hidden", true, this._itemContents()); this._updateItems(addedSelection, removedSelection); }, _updateSelection: function _updateSelection(addedSelection, removedSelection) { this._updateItems(addedSelection, removedSelection); this._updateItemHeightsWrapper(false); }, _updateItems: function _updateItems(addedSelection, removedSelection) { var $items = this._itemElements(), that = this; iteratorUtils.each(addedSelection, function (_, index) { that._deferredItems[index].resolve(); var $item = $items.eq(index).addClass(ACCORDION_ITEM_OPENED_CLASS).removeClass(ACCORDION_ITEM_CLOSED_CLASS); that.setAria("hidden", false, $item.find("." + ACCORDION_ITEM_BODY_CLASS)); }); iteratorUtils.each(removedSelection, function (_, index) { var $item = $items.eq(index).removeClass(ACCORDION_ITEM_OPENED_CLASS); that.setAria("hidden", true, $item.find("." + ACCORDION_ITEM_BODY_CLASS)); }); }, _updateItemHeightsWrapper: function _updateItemHeightsWrapper(skipAnimation) { if (this.option("templatesRenderAsynchronously")) { this._animationTimer = setTimeout(function () { this._updateItemHeights(skipAnimation); }.bind(this)); } else { this._updateItemHeights(skipAnimation); } }, _updateItemHeights: function _updateItemHeights(skipAnimation) { var that = this, deferredAnimate = that._deferredAnimate, itemHeight = this._splitFreeSpace(this._calculateFreeSpace()); clearTimeout(this._animationTimer); return when.apply($, iteratorUtils.map(this._itemElements(), function (item) { return that._updateItemHeight($(item), itemHeight, skipAnimation); })).done(function () { if (deferredAnimate) { deferredAnimate.resolveWith(that); } }); }, _updateItemHeight: function _updateItemHeight($item, itemHeight, skipAnimation) { var $title = $item.children("." + ACCORDION_ITEM_TITLE_CLASS); if (fx.isAnimating($item)) { fx.stop($item); } var startItemHeight = $item.outerHeight(), finalItemHeight = $item.hasClass(ACCORDION_ITEM_OPENED_CLASS) ? itemHeight + $title.outerHeight() || $item.height("auto").outerHeight() : $title.outerHeight(); return this._animateItem($item, startItemHeight, finalItemHeight, skipAnimation, !!itemHeight); }, _animateItem: function _animateItem($element, startHeight, endHeight, skipAnimation, fixedHeight) { var d; if (skipAnimation || startHeight === endHeight) { $element.css("height", endHeight); d = new Deferred().resolve(); } else { d = fx.animate($element, { type: "custom", from: { height: startHeight }, to: { height: endHeight }, duration: this.option("animationDuration"), easing: this.option("_animationEasing") }); } return d.done(function () { if ($element.hasClass(ACCORDION_ITEM_OPENED_CLASS) && !fixedHeight) { $element.css("height", ""); } $element.not("." + ACCORDION_ITEM_OPENED_CLASS).addClass(ACCORDION_ITEM_CLOSED_CLASS); }); }, _splitFreeSpace: function _splitFreeSpace(freeSpace) { if (!freeSpace) { return freeSpace; } return freeSpace / this.option("selectedItems").length; }, _calculateFreeSpace: function _calculateFreeSpace() { var height = this.option("height"); if (height === undefined || height === "auto") { return; } var $titles = this._itemTitles(), itemsHeight = 0; iteratorUtils.each($titles, function (_, title) { itemsHeight += $(title).outerHeight(); }); return this.$element().height() - itemsHeight; }, _visibilityChanged: function _visibilityChanged(visible) { if (visible) { this._dimensionChanged(); } }, _dimensionChanged: function _dimensionChanged() { this._updateItemHeights(true); }, _clean: function _clean() { clearTimeout(this._animationTimer); this.callBase(); }, _optionChanged: function _optionChanged(args) { switch (args.name) { case "animationDuration": case "onItemTitleClick": case "_animationEasing": break; case "collapsible": this.option("selectionRequired", !this.option("collapsible")); break; case "itemTitleTemplate": case "height": case "deferRendering": this._invalidate(); break; case "multiple": this.option("selectionMode", args.value ? "multiple" : "single"); break; default: this.callBase(args); } }, /** * @name dxAccordionMethods.expandItem * @publicName expandItem(index) * @param1 index:numeric * @return Promise<void> */ expandItem: function expandItem(index) { this._deferredAnimate = new Deferred(); this.selectItem(index); return this._deferredAnimate.promise(); }, /** * @name dxAccordionMethods.collapseItem * @publicName collapseItem(index) * @param1 index:numeric * @return Promise<void> */ collapseItem: function collapseItem(index) { this._deferredAnimate = new Deferred(); this.unselectItem(index); return this._deferredAnimate.promise(); }, /** * @name dxAccordionMethods.updateDimensions * @publicName updateDimensions() * @return Promise<void> */ updateDimensions: function updateDimensions() { return this._updateItemHeights(false); } }); registerComponent("dxAccordion", Accordion); module.exports = Accordion;