UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

591 lines (499 loc) • 19.7 kB
"use strict"; var $ = require("../core/renderer"), devices = require("../core/devices"), registerComponent = require("../core/component_registrator"), inflector = require("../core/utils/inflector"), iteratorUtils = require("../core/utils/iterator"), isDefined = require("../core/utils/type").isDefined, extend = require("../core/utils/extend").extend, windowUtils = require("../core/utils/window"), getPublicElement = require("../core/utils/dom").getPublicElement, ScrollView = require("./scroll_view"), CollectionWidget = require("./collection/ui.collection_widget.edit"); var TILEVIEW_CLASS = "dx-tileview", TILEVIEW_CONTAINER_CLASS = "dx-tileview-wrapper", TILEVIEW_ITEM_CLASS = "dx-tile", TILEVIEW_ITEM_SELECTOR = "." + TILEVIEW_ITEM_CLASS, TILEVIEW_ITEM_DATA_KEY = "dxTileData"; var CONFIGS = { "horizontal": { itemMainRatio: "widthRatio", itemCrossRatio: "heightRatio", baseItemMainDimension: "baseItemWidth", baseItemCrossDimension: "baseItemHeight", mainDimension: "width", crossDimension: "height", mainPosition: "left", crossPosition: "top" }, "vertical": { itemMainRatio: "heightRatio", itemCrossRatio: "widthRatio", baseItemMainDimension: "baseItemHeight", baseItemCrossDimension: "baseItemWidth", mainDimension: "height", crossDimension: "width", mainPosition: "top", crossPosition: "left" } }; /** * @name dxtileview * @publicName dxTileView * @inherits CollectionWidget * @module ui/tile_view * @export default */ var TileView = CollectionWidget.inherit({ _activeStateUnit: TILEVIEW_ITEM_SELECTOR, _getDefaultOptions: function _getDefaultOptions() { return extend(this.callBase(), { items: null, /** * @name dxTileViewOptions.direction * @publicName direction * @type Enums.Orientation * @default 'horizontal' */ direction: "horizontal", /** * @name dxTileViewOptions.hoverStateEnabled * @publicName hoverStateEnabled * @type boolean * @default true * @inheritdoc */ hoverStateEnabled: true, /** * @name dxTileViewOptions.showScrollbar * @publicName showScrollbar * @type boolean * @default false */ showScrollbar: false, /** * @name dxTileViewOptions.height * @publicName height * @type number|string * @default 500 */ height: 500, /** * @name dxTileViewOptions.baseItemwidth * @publicName baseItemWidth * @type number * @default 100 */ baseItemWidth: 100, /** * @name dxTileViewOptions.baseItemHeight * @publicName baseItemHeight * @type number * @default 100 */ baseItemHeight: 100, /** * @name dxTileViewOptions.itemMargin * @publicName itemMargin * @type number * @default 20 */ itemMargin: 20, /** * @name dxTileViewOptions.activeStateEnabled * @publicName activeStateEnabled * @type boolean * @default true */ activeStateEnabled: true, indicateLoading: true /** * @name dxTileViewItemTemplate * @publicName dxTileViewItemTemplate * @inherits CollectionWidgetItemTemplate * @type object */ /** * @name dxTileViewItemTemplate.widthRatio * @publicName widthRatio * @type number * @default 1 */ /** * @name dxTileViewItemTemplate.heightRatio * @publicName heightRatio * @type number * @default 1 */ /** * @name dxTileViewOptions.height * @publicName height * @type number|string|function * @default 500 * @type_function_return number|string */ /** * @name dxTileViewOptions.selectedIndex * @publicName selectedIndex * @hidden * @inheritdoc */ /** * @name dxTileViewOptions.selectedItem * @publicName selectedItem * @hidden * @inheritdoc */ /** * @name dxTileViewOptions.selectedItems * @publicName selectedItems * @hidden * @inheritdoc */ /** * @name dxTileViewOptions.selectedItemKeys * @publicName selectedItemKeys * @hidden * @inheritdoc */ /** * @name dxTileViewOptions.keyExpr * @publicName keyExpr * @hidden * @inheritdoc */ /** * @name dxTileViewOptions.onSelectionChanged * @publicName onSelectionChanged * @action * @hidden * @inheritdoc */ }); }, _defaultOptionsRules: function _defaultOptionsRules() { return this.callBase().concat([{ device: function device() { return devices.real().deviceType === "desktop" && !devices.isSimulator(); }, options: { /** * @name dxTileViewOptions.focusStateEnabled * @publicName focusStateEnabled * @type boolean * @default true @for desktop * @inheritdoc */ focusStateEnabled: true } }]); }, _itemClass: function _itemClass() { return TILEVIEW_ITEM_CLASS; }, _itemDataKey: function _itemDataKey() { return TILEVIEW_ITEM_DATA_KEY; }, _itemContainer: function _itemContainer() { return this._$container; }, _init: function _init() { this.callBase(); this.$element().addClass(TILEVIEW_CLASS); this._initScrollView(); }, _dataSourceLoadingChangedHandler: function _dataSourceLoadingChangedHandler(isLoading) { var scrollView = this._scrollView; if (!scrollView || !scrollView.startLoading) { return; } if (isLoading && this.option("indicateLoading")) { scrollView.startLoading(); } else { scrollView.finishLoading(); } }, _hideLoadingIfLoadIndicationOff: function _hideLoadingIfLoadIndicationOff() { if (!this.option("indicateLoading")) { this._dataSourceLoadingChangedHandler(false); } }, _initScrollView: function _initScrollView() { this._scrollView = this._createComponent(this.$element(), ScrollView, { direction: this.option("direction"), scrollByContent: true, useKeyboard: false, showScrollbar: this.option("showScrollbar") }); this._$container = $(this._scrollView.content()); this._$container.addClass(TILEVIEW_CONTAINER_CLASS); this._scrollView.option("onUpdated", this._renderGeometry.bind(this)); }, _initMarkup: function _initMarkup() { this.callBase(); this._cellsPerDimension = 1; this._renderGeometry(); this._updateScrollView(); this._fireContentReadyAction(); }, _updateScrollView: function _updateScrollView() { this._scrollView.option("direction", this.option("direction")); this._scrollView.update(); this._indicateLoadingIfAlreadyStarted(); }, _indicateLoadingIfAlreadyStarted: function _indicateLoadingIfAlreadyStarted() { if (this._isDataSourceLoading()) { this._dataSourceLoadingChangedHandler(true); } }, _renderGeometry: function _renderGeometry() { this._config = CONFIGS[this.option("direction")]; var items = this.option("items") || [], config = this._config, itemMargin = this.option("itemMargin"), maxItemCrossRatio = Math.max.apply(Math, iteratorUtils.map(items || [], function (item) { return Math.round(item[config.itemCrossRatio] || 1); })); var crossDimensionValue = windowUtils.hasWindow() ? this.$element()[config.crossDimension]() : parseInt(this.$element().get(0).style[config.crossDimension]); this._cellsPerDimension = Math.floor(crossDimensionValue / (this.option(config.baseItemCrossDimension) + itemMargin)); this._cellsPerDimension = Math.max(this._cellsPerDimension, maxItemCrossRatio); this._cells = []; this._cells.push(new Array(this._cellsPerDimension)); this._arrangeItems(items); if (windowUtils.hasWindow()) { this._$container[config.mainDimension](this._cells.length * this.option(config.baseItemMainDimension) + (this._cells.length + 1) * itemMargin); } }, _arrangeItems: function _arrangeItems(items) { var config = this._config, itemMainRatio = config.itemMainRatio, itemCrossRatio = config.itemCrossRatio, mainPosition = config.mainPosition; this._itemsPositions = []; iteratorUtils.each(items, function (index, item) { var currentItem = {}; currentItem[itemMainRatio] = item[itemMainRatio] || 1; currentItem[itemCrossRatio] = item[itemCrossRatio] || 1; currentItem.index = index; currentItem[itemMainRatio] = currentItem[itemMainRatio] <= 0 ? 0 : Math.round(currentItem[config.itemMainRatio]); currentItem[itemCrossRatio] = currentItem[itemCrossRatio] <= 0 ? 0 : Math.round(currentItem[config.itemCrossRatio]); var itemPosition = this._getItemPosition(currentItem); if (itemPosition[mainPosition] === -1) { itemPosition[mainPosition] = this._cells.push(new Array(this._cellsPerDimension)) - 1; } this._occupyCells(currentItem, itemPosition); this._arrangeItem(currentItem, itemPosition); this._itemsPositions.push(itemPosition); }.bind(this)); }, _getItemPosition: function _getItemPosition(item) { var config = this._config, mainPosition = config.mainPosition, crossPosition = config.crossPosition; var position = {}; position[mainPosition] = -1; position[crossPosition] = 0; for (var main = 0; main < this._cells.length; main++) { for (var cross = 0; cross < this._cellsPerDimension; cross++) { if (this._itemFit(main, cross, item)) { position[mainPosition] = main; position[crossPosition] = cross; break; } } if (position[mainPosition] > -1) { break; } } return position; }, _itemFit: function _itemFit(mainPosition, crossPosition, item) { var result = true, config = this._config, itemRatioMain = item[config.itemMainRatio], itemRatioCross = item[config.itemCrossRatio]; if (crossPosition + itemRatioCross > this._cellsPerDimension) { return false; } for (var main = mainPosition; main < mainPosition + itemRatioMain; main++) { for (var cross = crossPosition; cross < crossPosition + itemRatioCross; cross++) { if (this._cells.length - 1 < main) { this._cells.push(new Array(this._cellsPerDimension)); } else if (this._cells[main][cross] !== undefined) { result = false; break; } } } return result; }, _occupyCells: function _occupyCells(item, itemPosition) { var config = this._config, itemPositionMain = itemPosition[config.mainPosition], itemPositionCross = itemPosition[config.crossPosition], itemRatioMain = item[config.itemMainRatio], itemRatioCross = item[config.itemCrossRatio]; for (var main = itemPositionMain; main < itemPositionMain + itemRatioMain; main++) { for (var cross = itemPositionCross; cross < itemPositionCross + itemRatioCross; cross++) { this._cells[main][cross] = item.index; } } }, _arrangeItem: function _arrangeItem(item, itemPosition) { var config = this._config, itemPositionMain = itemPosition[config.mainPosition], itemPositionCross = itemPosition[config.crossPosition], itemRatioMain = item[config.itemMainRatio], itemRatioCross = item[config.itemCrossRatio], baseItemCross = this.option(config.baseItemCrossDimension), baseItemMain = this.option(config.baseItemMainDimension), itemMargin = this.option("itemMargin"); var cssProps = { display: itemRatioMain <= 0 || itemRatioCross <= 0 ? "none" : "" }, mainDimension = itemRatioMain * baseItemMain + (itemRatioMain - 1) * itemMargin, crossDimension = itemRatioCross * baseItemCross + (itemRatioCross - 1) * itemMargin; cssProps[config.mainDimension] = mainDimension < 0 ? 0 : mainDimension; cssProps[config.crossDimension] = crossDimension < 0 ? 0 : crossDimension; cssProps[config.mainPosition] = itemPositionMain * baseItemMain + (itemPositionMain + 1) * itemMargin; cssProps[config.crossPosition] = itemPositionCross * baseItemCross + (itemPositionCross + 1) * itemMargin; if (this.option("rtlEnabled")) { var offsetCorrection = this._$container.width(), baseItemWidth = this.option("baseItemWidth"), itemPositionX = itemPosition.left, offsetPosition = itemPositionX * baseItemWidth, itemBaseOffset = baseItemWidth + itemMargin, itemWidth = itemBaseOffset * item.widthRatio, subItemMargins = itemPositionX * itemMargin; cssProps.left = offsetCorrection - (offsetPosition + itemWidth + subItemMargins); } this._itemElements().eq(item.index).css(cssProps); }, _moveFocus: function _moveFocus(location) { var FOCUS_UP = "up", FOCUS_DOWN = "down", FOCUS_LEFT = this.option("rtlEnabled") ? "right" : "left", FOCUS_RIGHT = this.option("rtlEnabled") ? "left" : "right", FOCUS_PAGE_UP = "pageup", FOCUS_PAGE_DOWN = "pagedown"; var horizontalDirection = this.option("direction") === "horizontal", cells = this._cells, index = $(this.option("focusedElement")).index(), targetCol = this._itemsPositions[index].left, targetRow = this._itemsPositions[index].top; var colCount = (horizontalDirection ? cells : cells[0]).length; var rowCount = (horizontalDirection ? cells[0] : cells).length; var getCell = function getCell(col, row) { if (horizontalDirection) { return cells[col][row]; } return cells[row][col]; }; switch (location) { case FOCUS_PAGE_UP: case FOCUS_UP: while (targetRow > 0 && index === getCell(targetCol, targetRow)) { targetRow--; } if (targetRow < 0) { targetRow = 0; } break; case FOCUS_PAGE_DOWN: case FOCUS_DOWN: while (targetRow < rowCount && index === getCell(targetCol, targetRow)) { targetRow++; } if (targetRow === rowCount) { targetRow = rowCount - 1; } break; case FOCUS_RIGHT: while (targetCol < colCount && index === getCell(targetCol, targetRow)) { targetCol++; } if (targetCol === colCount) { targetCol = colCount - 1; } break; case FOCUS_LEFT: while (targetCol >= 0 && index === getCell(targetCol, targetRow)) { targetCol--; } if (targetCol < 0) { targetCol = 0; } break; default: this.callBase.apply(this, arguments); return; } var newTargetIndex = getCell(targetCol, targetRow); if (!isDefined(newTargetIndex)) { return; } var $newTarget = this._itemElements().eq(newTargetIndex); this.option("focusedElement", getPublicElement($newTarget)); this._scrollToItem($newTarget); }, _scrollToItem: function _scrollToItem($itemElement) { if (!$itemElement.length) { return; } var config = this._config, outerMainProp = "outer" + inflector.captionize(config.mainDimension), itemMargin = this.option("itemMargin"), itemPosition = $itemElement.position()[config.mainPosition], itemDimension = $itemElement[outerMainProp](), itemTail = itemPosition + itemDimension, scrollPosition = this.scrollPosition(), clientWidth = this.$element()[outerMainProp](); if (scrollPosition <= itemPosition && itemTail <= scrollPosition + clientWidth) { return; } if (scrollPosition > itemPosition) { this._scrollView.scrollTo(itemPosition - itemMargin); } else { this._scrollView.scrollTo(itemPosition + itemDimension - clientWidth + itemMargin); } }, _optionChanged: function _optionChanged(args) { switch (args.name) { case "showScrollbar": this._initScrollView(); break; case "disabled": this._scrollView.option("disabled", args.value); this.callBase(args); break; case "baseItemWidth": case "baseItemHeight": case "itemMargin": this._renderGeometry(); break; case "width": case "height": this.callBase(args); this._renderGeometry(); this._updateScrollView(); break; case "direction": this._renderGeometry(); this._updateScrollView(); break; case "indicateLoading": this._hideLoadingIfLoadIndicationOff(); break; default: this.callBase(args); } }, /** * @name dxtileviewmethods.scrollPosition * @publicName scrollPosition() * @return numeric */ scrollPosition: function scrollPosition() { return this._scrollView.scrollOffset()[this._config.mainPosition]; } }); registerComponent("dxTileView", TileView); module.exports = TileView;